fix: 1. 接入入口模块的管理接口,优化样式。
2. 将查看文档评查结果详情对接接口,采用接口的方式进行查询。
This commit is contained in:
@@ -16,11 +16,11 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const filePath = url.searchParams.get("path");
|
||||
const isPreview = url.searchParams.get("preview") === "true";
|
||||
|
||||
console.log("📄 [PDF Proxy] 请求参数:", {
|
||||
path: filePath,
|
||||
preview: url.searchParams.get("preview"),
|
||||
isPreview: isPreview
|
||||
});
|
||||
// console.log("📄 [PDF Proxy] 请求参数:", {
|
||||
// path: filePath,
|
||||
// preview: url.searchParams.get("preview"),
|
||||
// isPreview: isPreview
|
||||
// });
|
||||
|
||||
if (!filePath) {
|
||||
return new Response("缺少文件路径参数", { status: 400 });
|
||||
@@ -70,14 +70,14 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
if (isPreview) {
|
||||
// 在浏览器中预览
|
||||
headers['Content-Disposition'] = `inline; filename="${encodeURIComponent(fileName)}"`;
|
||||
console.log("📄 [PDF Proxy] 设置为预览模式 (inline)");
|
||||
// console.log("📄 [PDF Proxy] 设置为预览模式 (inline)");
|
||||
} else {
|
||||
// 强制下载(默认行为)
|
||||
headers['Content-Disposition'] = `attachment; filename="${encodeURIComponent(fileName)}"`;
|
||||
console.log("📄 [PDF Proxy] 设置为下载模式 (attachment)");
|
||||
// console.log("📄 [PDF Proxy] 设置为下载模式 (attachment)");
|
||||
}
|
||||
|
||||
console.log("📄 [PDF Proxy] 响应头:", headers);
|
||||
// console.log("📄 [PDF Proxy] 响应头:", headers);
|
||||
|
||||
// 返回文件
|
||||
return new Response(blob, { headers });
|
||||
|
||||
@@ -356,17 +356,17 @@ export default function CrossCheckingResult() {
|
||||
const fileInfo = {
|
||||
fileName: document.name || "未知文件名",
|
||||
path: document.path || "未知路径",
|
||||
contractNumber: document.documentNumber || "未知编号",
|
||||
fileSize: document.size ? formatFileSize(document.size) : "未知大小",
|
||||
contractNumber: document.documentNumber || document.document_number || "未知编号",
|
||||
fileSize: document.size ? formatFileSize(document.size) : document.file_size ? formatFileSize(document.file_size) : "未知大小",
|
||||
// 文件格式类型
|
||||
fileFormat: document.fileType ? document.fileType.toUpperCase() : "未知格式",
|
||||
pageCount: document.pageCount || 0,
|
||||
uploadTime: document.uploadTime || "未知时间",
|
||||
pageCount: document.pageCount || document.page_count || 0,
|
||||
uploadTime: document.uploadTime || document.created_at || "未知时间",
|
||||
uploadUser: document.uploadUser || "未知用户",
|
||||
auditStatus: document.auditStatus || 0,
|
||||
legalBasis: document.legalBasis || {},
|
||||
// 文件类型(1:合同,2:卷宗。。。)
|
||||
fileType: document.type || ""
|
||||
fileType: document.type || document.type_id ? document.type_id.toString() : ''
|
||||
};
|
||||
|
||||
// 创建包含真实文档数据的评查数据对象
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSearchParams, useNavigate, useLoaderData, useRouteLoaderData } from "@remix-run/react";
|
||||
import { ActionFunctionArgs, LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
|
||||
import { useSearchParams, useNavigate, useLoaderData, useRouteLoaderData, useRevalidator } from "@remix-run/react";
|
||||
import { ClientLoaderFunctionArgs, MetaFunction } from "@remix-run/react";
|
||||
import { Table } from "~/components/ui/Table";
|
||||
import { Card } from "~/components/ui/Card";
|
||||
import { Button } from "~/components/ui/Button";
|
||||
@@ -45,91 +45,56 @@ interface LoaderData {
|
||||
pageSize: number;
|
||||
currentPage: number;
|
||||
error?: string;
|
||||
frontendJWT?: string | null;
|
||||
}
|
||||
|
||||
// 加载函数 - 获取入口模块列表
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
// 🔑 客户端加载函数 - 在浏览器端执行,axios-client 会自动添加 JWT
|
||||
export async function clientLoader({ request }: ClientLoaderFunctionArgs) {
|
||||
try {
|
||||
// 获取用户会话信息
|
||||
const { getUserSession } = await import("~/api/login/auth.server");
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
|
||||
const url = new URL(request.url);
|
||||
const name = url.searchParams.get('name') || undefined;
|
||||
const area = url.searchParams.get('area') || undefined;
|
||||
const page = parseInt(url.searchParams.get('page') || '1', 10);
|
||||
const pageSize = parseInt(url.searchParams.get('pageSize') || '10', 10);
|
||||
|
||||
// 构建搜索参数
|
||||
// 构建搜索参数(注意:API使用page_size而不是pageSize)
|
||||
const searchParams: EntryModuleSearchParams = {
|
||||
name,
|
||||
area,
|
||||
page,
|
||||
pageSize
|
||||
page_size: pageSize // API使用page_size
|
||||
};
|
||||
|
||||
const modulesResponse = await getEntryModules(searchParams, frontendJWT);
|
||||
// ✅ 不需要传递 JWT,axios-client 会自动从 localStorage 读取并添加
|
||||
const modulesResponse = await getEntryModules(searchParams);
|
||||
|
||||
if (modulesResponse.error) {
|
||||
console.error("获取入口模块失败:", modulesResponse.error);
|
||||
console.error("❌ [clientLoader] 获取入口模块失败:", modulesResponse.error);
|
||||
throw new Error(modulesResponse.error);
|
||||
}
|
||||
const modulesResult = modulesResponse.data?.modules || [];
|
||||
|
||||
return Response.json({
|
||||
const modulesResult = modulesResponse.data?.modules || [];
|
||||
const totalCount = modulesResponse.data?.total || modulesResult.length;
|
||||
|
||||
return {
|
||||
modules: modulesResult,
|
||||
total: modulesResponse.data?.total || modulesResult.length,
|
||||
total: totalCount,
|
||||
pageSize,
|
||||
currentPage: page,
|
||||
frontendJWT
|
||||
});
|
||||
currentPage: page
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("加载入口模块列表失败:", error);
|
||||
return Response.json(
|
||||
{
|
||||
modules: [],
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
error: error instanceof Error ? error.message : "加载入口模块列表失败"
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
return {
|
||||
modules: [],
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
error: error instanceof Error ? error.message : "加载入口模块列表失败"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 动作函数 - 处理删除请求
|
||||
export async function action({ request }: ActionFunctionArgs) {
|
||||
// 获取表单数据
|
||||
const formData = await request.formData();
|
||||
const id = formData.get("id") as string;
|
||||
const intent = formData.get("intent") as string;
|
||||
const { getUserSession } = await import("~/api/login/auth.server");
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
|
||||
if (intent === "delete" && id) {
|
||||
try {
|
||||
const result = await deleteEntryModule(parseInt(id), frontendJWT || undefined);
|
||||
|
||||
if (result.error) {
|
||||
return Response.json({ success: false, error: result.error }, { status: 500 });
|
||||
}
|
||||
|
||||
return Response.json({ success: true });
|
||||
} catch (error) {
|
||||
return Response.json(
|
||||
{ success: false, error: error instanceof Error ? error.message : "删除入口模块失败" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Response.json({ success: false, error: "无效的操作" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 地区选项
|
||||
const AREA_OPTIONS = [
|
||||
{ value: "", label: "全部地区" },
|
||||
{ value: "梅州", label: "梅州" },
|
||||
{ value: "云浮", label: "云浮" },
|
||||
{ value: "揭阳", label: "揭阳" },
|
||||
@@ -142,9 +107,11 @@ export default function EntryModulesList() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const revalidator = useRevalidator();
|
||||
|
||||
// 获取加载器数据
|
||||
const { modules, total, error, frontendJWT } = useLoaderData<LoaderData>();
|
||||
const loaderData = useLoaderData<LoaderData>();
|
||||
const { modules, total, error } = loaderData;
|
||||
|
||||
// 获取用户角色并判断权限
|
||||
const rootData = useRouteLoaderData("root") as { userRole: string };
|
||||
@@ -218,30 +185,23 @@ export default function EntryModulesList() {
|
||||
type: "warning",
|
||||
confirmText: "删除",
|
||||
cancelText: "取消",
|
||||
confirmDelay: 4,
|
||||
confirmDelay: 3,
|
||||
onConfirm: async () => {
|
||||
setIsDeleting(true);
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('id', id.toString());
|
||||
formData.append('intent', 'delete');
|
||||
|
||||
const response = await fetch('/entry-modules', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
// 直接调用 API 删除函数
|
||||
const result = await deleteEntryModule(id);
|
||||
|
||||
if (result.success) {
|
||||
toastService.success('删除成功!');
|
||||
// 刷新页面
|
||||
window.location.reload();
|
||||
// 重新验证数据,刷新表格
|
||||
revalidator.revalidate();
|
||||
} else {
|
||||
toastService.error(`删除失败: ${result.error || '未知错误'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除入口模块失败:', error);
|
||||
toastService.error(`删除失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
@@ -272,42 +232,36 @@ export default function EntryModulesList() {
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
key: 'id',
|
||||
title: 'ID',
|
||||
width: '80px',
|
||||
render: (row: EntryModule) => row.id
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
title: '模块名称',
|
||||
width: '200px',
|
||||
render: (row: EntryModule) => (
|
||||
<span className="font-medium text-gray-900">{row.name}</span>
|
||||
render: (_: any, record: EntryModule) => (
|
||||
<span className="font-medium text-gray-900">{record.name}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
title: '描述',
|
||||
width: '250px',
|
||||
render: (row: EntryModule) => (
|
||||
<span className="text-gray-600">{row.description || '-'}</span>
|
||||
render: (_: any, record: EntryModule) => (
|
||||
<span className="text-gray-600">{record.description || '-'}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'logo',
|
||||
title: 'Logo图片',
|
||||
width: '150px',
|
||||
render: (row: EntryModule) => {
|
||||
if (!row.path) {
|
||||
render: (_: any, record: EntryModule) => {
|
||||
if (!record.path) {
|
||||
return <span className="text-gray-400">未上传</span>;
|
||||
}
|
||||
const logoUrl = `${DOCUMENT_URL}${row.path}`;
|
||||
const logoUrl = `${DOCUMENT_URL}${record.path}`;
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt={row.name}
|
||||
alt={record.name}
|
||||
className="h-8 w-8 object-contain rounded"
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
@@ -330,17 +284,20 @@ export default function EntryModulesList() {
|
||||
key: 'areas',
|
||||
title: '适用地区',
|
||||
width: '200px',
|
||||
render: (row: EntryModule) => (
|
||||
render: (_: any, record: EntryModule) => (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{row.areas && row.areas.length > 0 ? (
|
||||
row.areas.map((area, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-block px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded"
|
||||
>
|
||||
{area}
|
||||
</span>
|
||||
))
|
||||
{record.areas && record.areas.length > 0 ? (
|
||||
record.areas
|
||||
.filter(areaConfig => areaConfig.enabled !== false) // 只显示启用的地区
|
||||
.sort((a, b) => a.sort_order - b.sort_order) // 按排序号排序
|
||||
.map((areaConfig, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-block px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded"
|
||||
>
|
||||
{areaConfig.area}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<span className="text-gray-400">未设置</span>
|
||||
)}
|
||||
@@ -351,34 +308,34 @@ export default function EntryModulesList() {
|
||||
key: 'created_at',
|
||||
title: '创建时间',
|
||||
width: '180px',
|
||||
render: (row: EntryModule) =>
|
||||
row.created_at ? new Date(row.created_at).toLocaleString('zh-CN') : '-'
|
||||
render: (_: any, record: EntryModule) =>
|
||||
record.created_at ? new Date(record.created_at).toLocaleString('zh-CN') : '-'
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
title: '操作',
|
||||
width: '150px',
|
||||
render: (row: EntryModule) => (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => handleEdit(row.id!)}
|
||||
width: '180px',
|
||||
render: (_: any, record: EntryModule) => (
|
||||
<div className="operations-cell">
|
||||
<button
|
||||
onClick={() => handleEdit(record.id!)}
|
||||
className="operation-btn"
|
||||
disabled={!hasEditPermission}
|
||||
title={hasEditPermission ? "编辑" : "无权限"}
|
||||
title={hasEditPermission ? "编辑入口模块" : "无权限"}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
onClick={() => handleDelete(row.id!)}
|
||||
disabled={isDeleting || !hasEditPermission}
|
||||
title={hasEditPermission ? "删除" : "无权限"}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
<i className="ri-edit-line"></i> {hasEditPermission ? '编辑' : '查看'}
|
||||
</button>
|
||||
{hasEditPermission && (
|
||||
<button
|
||||
type="button"
|
||||
className="operation-btn !text-[--color-error]"
|
||||
onClick={() => handleDelete(record.id!)}
|
||||
disabled={isDeleting}
|
||||
title="删除入口模块"
|
||||
>
|
||||
<i className="ri-delete-bin-line"></i> 删除
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -406,11 +363,6 @@ export default function EntryModulesList() {
|
||||
|
||||
{/* 筛选面板 */}
|
||||
<FilterPanel onReset={handleReset}>
|
||||
<SearchFilter
|
||||
placeholder="请输入入口模块名称"
|
||||
defaultValue={name}
|
||||
onSearch={handleNameSearch}
|
||||
/>
|
||||
<FilterSelect
|
||||
label="适用地区"
|
||||
name="area"
|
||||
@@ -418,12 +370,20 @@ export default function EntryModulesList() {
|
||||
options={AREA_OPTIONS}
|
||||
onChange={handleFilterChange}
|
||||
/>
|
||||
<SearchFilter
|
||||
label="模块名称"
|
||||
placeholder="请输入入口模块名称"
|
||||
value={name}
|
||||
onSearch={handleNameSearch}
|
||||
className="filter-item-wide"
|
||||
/>
|
||||
</FilterPanel>
|
||||
|
||||
{/* 表格 */}
|
||||
<Table
|
||||
columns={columns}
|
||||
data={modules || []}
|
||||
dataSource={modules || []}
|
||||
rowKey="id"
|
||||
loading={false}
|
||||
emptyText="暂无入口模块数据"
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useNavigate, useSearchParams, useLoaderData } from "@remix-run/react";
|
||||
import { ActionFunctionArgs, LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
|
||||
import { ClientLoaderFunctionArgs, MetaFunction } from "@remix-run/react";
|
||||
import { Card } from "~/components/ui/Card";
|
||||
import { Button } from "~/components/ui/Button";
|
||||
import { toastService } from "~/components/ui/Toast";
|
||||
@@ -9,9 +9,18 @@ import {
|
||||
getEntryModuleById,
|
||||
createEntryModule,
|
||||
updateEntryModule,
|
||||
type EntryModule
|
||||
type EntryModule,
|
||||
type AreaConfig
|
||||
} from "~/api/entry-modules/entry-modules";
|
||||
import { API_BASE_URL, DOCUMENT_URL } from "~/config/api-config";
|
||||
import entryModulesStyles from "~/styles/pages/entry-modules.css?url";
|
||||
|
||||
// 引入CSS样式
|
||||
export function links() {
|
||||
return [
|
||||
{ rel: "stylesheet", href: entryModulesStyles }
|
||||
];
|
||||
}
|
||||
|
||||
// 页面元数据
|
||||
export const meta: MetaFunction = () => {
|
||||
@@ -33,38 +42,31 @@ export const handle = {
|
||||
interface LoaderData {
|
||||
module?: EntryModule;
|
||||
error?: string;
|
||||
frontendJWT?: string | null;
|
||||
}
|
||||
|
||||
// 加载函数 - 获取入口模块数据(编辑模式)
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
// 🔑 客户端加载函数 - 在浏览器端执行,axios-client 会自动添加 JWT
|
||||
export async function clientLoader({ request }: ClientLoaderFunctionArgs) {
|
||||
try {
|
||||
const { getUserSession } = await import("~/api/login/auth.server");
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
|
||||
const url = new URL(request.url);
|
||||
const id = url.searchParams.get('id');
|
||||
|
||||
if (id) {
|
||||
const moduleResponse = await getEntryModuleById(parseInt(id), frontendJWT);
|
||||
// ✅ 不需要传递 JWT,axios-client 会自动处理
|
||||
const moduleResponse = await getEntryModuleById(parseInt(id));
|
||||
if (moduleResponse.error) {
|
||||
throw new Error(moduleResponse.error);
|
||||
}
|
||||
return Response.json({
|
||||
module: moduleResponse.data,
|
||||
frontendJWT
|
||||
});
|
||||
return {
|
||||
module: moduleResponse.data
|
||||
};
|
||||
}
|
||||
|
||||
return Response.json({ frontendJWT });
|
||||
return {};
|
||||
} catch (error) {
|
||||
console.error("加载入口模块失败:", error);
|
||||
return Response.json(
|
||||
{
|
||||
error: error || "加载入口模块失败",
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : "加载入口模块失败"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +83,7 @@ const AREA_OPTIONS = [
|
||||
export default function EntryModuleNew() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { module, error, frontendJWT } = useLoaderData<LoaderData>();
|
||||
const { module, error } = useLoaderData<LoaderData>();
|
||||
|
||||
const id = searchParams.get('id');
|
||||
const isEditMode = !!id;
|
||||
@@ -89,7 +91,10 @@ export default function EntryModuleNew() {
|
||||
// 表单状态
|
||||
const [name, setName] = useState(module?.name || '');
|
||||
const [description, setDescription] = useState(module?.description || '');
|
||||
const [selectedAreas, setSelectedAreas] = useState<string[]>(module?.areas || []);
|
||||
// 🔑 从 AreaConfig[] 提取地区名称数组
|
||||
const [selectedAreas, setSelectedAreas] = useState<string[]>(
|
||||
module?.areas ? module.areas.map(a => a.area) : []
|
||||
);
|
||||
const [logoFile, setLogoFile] = useState<File | null>(null);
|
||||
const [logoPreview, setLogoPreview] = useState<string | null>(
|
||||
module?.path ? `${DOCUMENT_URL}${module.path}` : null
|
||||
@@ -168,10 +173,14 @@ export default function EntryModuleNew() {
|
||||
formData.append('file', logoFile);
|
||||
formData.append('folder', 'entryModule');
|
||||
|
||||
// ✅ 不需要手动添加 Authorization 头
|
||||
// fetch 可以自动使用浏览器的认证信息,或者我们也可以使用 axios
|
||||
const token = localStorage.getItem('access_token');
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/admin/upload`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${frontendJWT}`
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
@@ -210,18 +219,21 @@ export default function EntryModuleNew() {
|
||||
logoPath = await uploadLogo();
|
||||
}
|
||||
|
||||
// 🔑 准备提交数据
|
||||
// areas 字段会在 API 层自动转换为 AreaConfig[] 格式
|
||||
const moduleData = {
|
||||
name: name.trim(),
|
||||
description: description.trim() || undefined,
|
||||
path: logoPath,
|
||||
areas: selectedAreas
|
||||
areas: selectedAreas // 字符串数组,API会自动转换
|
||||
};
|
||||
|
||||
// ✅ 不需要传递 JWT,axios-client 会自动处理
|
||||
let result;
|
||||
if (isEditMode) {
|
||||
result = await updateEntryModule(parseInt(id!), moduleData, frontendJWT);
|
||||
result = await updateEntryModule(parseInt(id!), moduleData);
|
||||
} else {
|
||||
result = await createEntryModule(moduleData, frontendJWT);
|
||||
result = await createEntryModule(moduleData);
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
@@ -280,7 +292,7 @@ export default function EntryModuleNew() {
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="请输入模块名称,如:合同管理"
|
||||
maxLength={255}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -291,7 +303,7 @@ export default function EntryModuleNew() {
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
placeholder="请输入模块描述"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md"
|
||||
rows={4}
|
||||
/>
|
||||
</div>
|
||||
@@ -349,7 +361,7 @@ export default function EntryModuleNew() {
|
||||
type="checkbox"
|
||||
checked={selectedAreas.includes(option.value)}
|
||||
onChange={() => handleAreaToggle(option.value)}
|
||||
className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
||||
className="w-4 h-4 border-gray-300 rounded cursor-pointer"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">{option.label}</span>
|
||||
</label>
|
||||
|
||||
+35
-15
@@ -29,7 +29,7 @@ import { type MetaFunction, type LoaderFunctionArgs, type ActionFunctionArgs } f
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate, useLoaderData, useFetcher } from "@remix-run/react";
|
||||
import reviewsStyles from "~/styles/reviews.css?url";
|
||||
import { getReviewPoints, updateReviewResult, confirmReviewResults } from "~/api/evaluation_points/reviews";
|
||||
import { getReviewPoints, getReviewPoints_fromApi, updateReviewResult, confirmReviewResults } from "~/api/evaluation_points/reviews";
|
||||
import { toastService } from "~/components/ui/Toast";
|
||||
|
||||
// 导入评查详情页面组件
|
||||
@@ -189,8 +189,11 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const { getUserSession } = await import("~/api/login/auth.server");
|
||||
const { userInfo, frontendJWT } = await getUserSession(request);
|
||||
|
||||
// 获取评查点数据,传递request对象
|
||||
const reviewData = await getReviewPoints(id, request);
|
||||
// 🆕 使用新的后端API获取评查点数据(单次请求替代原7次请求)
|
||||
const reviewData = await getReviewPoints_fromApi(id, request);
|
||||
|
||||
// ⚠️ 原方法已注释(保留以备回退)
|
||||
// const reviewData = await getReviewPoints(id, request);
|
||||
|
||||
if ('error' in reviewData && reviewData.error) {
|
||||
console.error("[Reviews Loader] 获取评查点数据错误:", reviewData.error);
|
||||
@@ -309,6 +312,23 @@ export default function ReviewDetails() {
|
||||
message: string;
|
||||
} | null>(null);
|
||||
|
||||
// 🐛 调试:打印 loader 返回的完整数据到浏览器控制台
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
console.group('📦 [Reviews] Loader 数据');
|
||||
// console.log('完整数据:', loaderData);
|
||||
console.log('文档信息:', document);
|
||||
// console.log('评查点数量:', reviewPoints?.length);
|
||||
// console.log('评查点数量:', reviewPoints);
|
||||
// console.log('统计信息:', statistics);
|
||||
// console.log('评查信息:', reviewInfo);
|
||||
// console.log('比对文档:', comparison_document);
|
||||
// console.log('用户信息:', loaderData.userInfo);
|
||||
// console.log('JWT Token (前20位):', frontendJWT?.substring(0, 20) + '...');
|
||||
console.groupEnd();
|
||||
}
|
||||
}, [loaderData, document, reviewPoints, statistics, reviewInfo, comparison_document, frontendJWT]);
|
||||
|
||||
// loader 数据加载出错
|
||||
useEffect(()=>{
|
||||
loadingBarService.hide();
|
||||
@@ -340,17 +360,17 @@ export default function ReviewDetails() {
|
||||
const fileInfo = {
|
||||
fileName: document.name || "未知文件名",
|
||||
path: document.path || "未知路径",
|
||||
contractNumber: document.documentNumber || "未知编号",
|
||||
fileSize: document.size ? formatFileSize(document.size) : "未知大小",
|
||||
contractNumber: document.documentNumber || document.document_number || "未知编号",
|
||||
fileSize: document.size ? formatFileSize(document.size) : document.file_size ? formatFileSize(document.file_size) : "未知大小",
|
||||
// 文件格式类型
|
||||
fileFormat: document.fileType ? document.fileType.toUpperCase() : "未知格式",
|
||||
pageCount: document.pageCount || 0,
|
||||
uploadTime: document.uploadTime || "未知时间",
|
||||
pageCount: document.pageCount || document.page_count || 0,
|
||||
uploadTime: document.uploadTime || document.created_at || "未知时间",
|
||||
uploadUser: document.uploadUser || "未知用户",
|
||||
auditStatus: document.auditStatus || 0,
|
||||
legalBasis: document.legalBasis || {},
|
||||
// 文件类型(1:合同,2:卷宗。。。)
|
||||
fileType: document.type || ""
|
||||
fileType: document.type || document.type_id ? document.type_id.toString() : ''
|
||||
};
|
||||
|
||||
// 创建包含真实文档数据的评查数据对象
|
||||
@@ -725,13 +745,13 @@ export default function ReviewDetails() {
|
||||
{/* 左侧:文件预览 */}
|
||||
<div className="w-full lg:w-[65%]">
|
||||
{(() => {
|
||||
console.log('[Reviews] 准备渲染FilePreview', {
|
||||
hasDocument: !!document,
|
||||
documentPath: document?.path,
|
||||
targetPage,
|
||||
hasCharPositions: !!charPositions,
|
||||
charPositionsLength: charPositions?.length
|
||||
});
|
||||
// console.log('[Reviews] 准备渲染FilePreview', {
|
||||
// hasDocument: !!document,
|
||||
// documentPath: document?.path,
|
||||
// targetPage,
|
||||
// hasCharPositions: !!charPositions,
|
||||
// charPositionsLength: charPositions?.length
|
||||
// });
|
||||
return (
|
||||
<FilePreview
|
||||
fileContent={document}
|
||||
|
||||
@@ -881,11 +881,6 @@ export default function RolePermissions() {
|
||||
// 从 getRolePermissions 结果中提取已分配的权限ID
|
||||
const assignedPermissionIds = rolePermissions.map(p => p.permission_id);
|
||||
|
||||
console.log('🔍 [handleSelectRole] 角色权限数据:');
|
||||
console.log(' - routePermissionsMap:', permMap);
|
||||
console.log(' - rolePermissions:', rolePermissions);
|
||||
console.log(' - assignedPermissionIds:', assignedPermissionIds);
|
||||
|
||||
setRoutePermissionsMap(permMap);
|
||||
setSelectedRouteIds(routeIds);
|
||||
setSelectedPermissionIds(assignedPermissionIds); // 使用实际已分配的权限ID
|
||||
|
||||
Reference in New Issue
Block a user