356 lines
11 KiB
TypeScript
356 lines
11 KiB
TypeScript
|
||
import { Modal } from '../ui/Modal';
|
||
import { Table } from '../ui/Table';
|
||
import { Button } from '../ui/Button';
|
||
import { FileIcon } from '../ui/FileIcon';
|
||
import { FileTypeTag } from '../ui/FileTypeTag';
|
||
import { StatusBadge } from '../ui/StatusBadge';
|
||
import { Pagination } from '../ui/Pagination';
|
||
import { LoadingIndicator } from '../ui/SkeletonScreen';
|
||
import { updateDocumentAuditStatus, type TaskDocument } from '~/api/cross-checking/cross-files'; // 更新导入
|
||
import { toastService } from '../ui/Toast';
|
||
import { formatDate } from '~/utils';
|
||
|
||
// 导出样式链接
|
||
export const links = () => [];
|
||
|
||
interface DocumentListModalProps {
|
||
isOpen: boolean;
|
||
onClose: () => void;
|
||
title: string;
|
||
files: TaskDocument[]; // 更新类型
|
||
onViewFile?: (fileId: string) => void;
|
||
loading?: boolean;
|
||
// 分页相关属性
|
||
currentPage?: number;
|
||
pageSize?: number;
|
||
total?: number;
|
||
onPageChange?: (page: number) => void;
|
||
onPageSizeChange?: (size: number) => void;
|
||
}
|
||
|
||
export function DocumentListModal({
|
||
isOpen,
|
||
onClose,
|
||
title,
|
||
files,
|
||
onViewFile,
|
||
loading = false,
|
||
// 分页属性,使用默认值
|
||
currentPage = 1,
|
||
pageSize = 10,
|
||
total = 0,
|
||
onPageChange,
|
||
onPageSizeChange
|
||
}: DocumentListModalProps) {
|
||
|
||
// 查看评查文件
|
||
const handleReviewFileClick = async (fileId: string, auditStatus: number | null) => {
|
||
// 检查audit_status是否为0,如果是则更新为2
|
||
if (auditStatus === 0 || auditStatus === null) {
|
||
try {
|
||
// TODO: 不需要传递userId,直接使用fileId找到对应文档,然后更新文档状态
|
||
// 更新文档状态
|
||
const updatedFile = await updateDocumentAuditStatus(fileId, 2);
|
||
console.log('更新后的文档状态:', updatedFile);
|
||
} catch (error) {
|
||
console.error('更新文件审核状态时出错:', error);
|
||
toastService.error(`更新文件审核状态时出错:${error instanceof Error ? error.message : '未知错误'}`);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 如果有自定义的查看处理函数,则调用它
|
||
if (onViewFile) {
|
||
onViewFile(fileId);
|
||
}
|
||
};
|
||
|
||
// 审核状态选项及样式 - 与documents._index.tsx保持一致
|
||
const auditStatusMapping: Record<string, { label: string; color: string; icon: string }> = {
|
||
"-1": { label: "不通过", color: "red", icon: "ri-close-line" },
|
||
"-2": { label: "警告", color: "yellow", icon: "ri-alert-line" },
|
||
"0": { label: "待审核", color: "blue", icon: "ri-time-line" },
|
||
"1": { label: "通过", color: "green", icon: "ri-check-line" },
|
||
"2": { label: "审核中", color: "purple", icon: "ri-search-line" },
|
||
};
|
||
|
||
// 渲染审核状态
|
||
const renderAuditStatus = (file: TaskDocument) => {
|
||
// 处理audit_status为null或undefined的情况,默认为0(待审核)
|
||
const auditStatus = file.audit_status != null ? file.audit_status : 0;
|
||
const statusKey = auditStatus.toString();
|
||
const statusInfo = auditStatusMapping[statusKey] || auditStatusMapping["0"];
|
||
|
||
return (
|
||
<div className={`inline-flex items-center px-2 py-1 rounded-full text-xs bg-${statusInfo.color}-100 text-${statusInfo.color}-800`}>
|
||
<i className={`${statusInfo.icon} mr-1`}></i>
|
||
<span>{statusInfo.label}</span>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
|
||
// 渲染问题摘要
|
||
const renderIssues = (file: TaskDocument) => {
|
||
// 如果文件有问题信息
|
||
if (file.issues && file.issues.length > 0) {
|
||
// 最多显示2个问题
|
||
const displayIssues = file.issues.slice(0, 2);
|
||
|
||
return (
|
||
<div className="text-sm">
|
||
{displayIssues.map((issue, index) => (
|
||
<div key={index} className="mb-1">
|
||
<i className={`ri-circle-fill mr-1 ${
|
||
issue.severity === 'error' ? 'text-red-400' :
|
||
issue.severity === 'warning' ? 'text-yellow-400' :
|
||
'text-blue-400'
|
||
}`}></i>
|
||
{issue.message}
|
||
</div>
|
||
))}
|
||
|
||
{file.issues.length > 2 && (
|
||
<div className="text-secondary mt-1">
|
||
还有 {file.issues.length - 2} 个问题...
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 如果没有问题信息,根据状态显示
|
||
if (file.evaluations_status === 1) {
|
||
return (
|
||
<div className="text-sm text-success">
|
||
<i className="ri-check-double-line mr-1"></i>所有评查点均通过
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 其他状态显示占位符
|
||
return <div className="text-sm text-secondary">-</div>;
|
||
};
|
||
|
||
// 获取文件大小的友好显示
|
||
const formatFileSize = (bytes: number) => {
|
||
if (bytes === 0) return '0 B';
|
||
const k = 1024;
|
||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||
};
|
||
|
||
// 定义表格列配置
|
||
const columns = [
|
||
{
|
||
title: "文件名称",
|
||
key: "fileName",
|
||
width: "30%",
|
||
render: (_: unknown, file: TaskDocument) => (
|
||
<div className="flex">
|
||
<div className="flex-shrink-0 flex items-center self-center">
|
||
<FileIcon fileName={file.file_name} className="text-lg w-10 h-10" />
|
||
</div>
|
||
<div className="min-w-0 flex-1 flex flex-col py-2 ml-2">
|
||
<div className="font-normal text-base break-words whitespace-normal leading-normal" title={file.file_name}>{file.file_name}</div>
|
||
<div className="text-xs text-secondary mt-2">
|
||
文件编号:{file.file_code}
|
||
</div>
|
||
<div className="text-xs text-secondary mt-1">
|
||
大小:{formatFileSize(file.file_size)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
},
|
||
{
|
||
title: "文件类型",
|
||
key: "fileType",
|
||
width: "8%",
|
||
render: (_: unknown, file: TaskDocument) => (
|
||
<FileTypeTag
|
||
type="other"
|
||
typeName={file.file_type_name}
|
||
text={file.file_type_name}
|
||
size="sm"
|
||
showIcon={false}
|
||
colorMode="light"
|
||
/>
|
||
)
|
||
},
|
||
{
|
||
title: "上传时间",
|
||
key: "uploadTime",
|
||
width: "8%",
|
||
render: (_: unknown, file: TaskDocument) => {
|
||
const uploadTime = formatDate(file.upload_time).split(' ');
|
||
const date = uploadTime[0];
|
||
const time = uploadTime[1];
|
||
return (
|
||
<div>
|
||
<span className="text-base">{date}</span> {/* 2025-07-22 */}
|
||
<br />
|
||
<span className="text-xs text-secondary">{time}</span> {/* 10:00:00 */}
|
||
</div>
|
||
);
|
||
}
|
||
},
|
||
{
|
||
title: "评查统计",
|
||
key: "reviewStatus",
|
||
width: "10%",
|
||
render: (_: unknown, file: TaskDocument) =>
|
||
// 要文件切分处理完之后,再显示评查统计
|
||
file.status === 'Processed' ? (
|
||
<div>
|
||
{file.pass_count > 0 && (
|
||
<StatusBadge
|
||
status="pass"
|
||
text={`通过(${file.pass_count})`}
|
||
showIcon={true}
|
||
className="my-2"
|
||
/>
|
||
)}
|
||
{file.warning_count > 0 && (
|
||
<StatusBadge
|
||
status="warning"
|
||
text={`警告(${file.warning_count})`}
|
||
showIcon={true}
|
||
className="my-2"
|
||
/>
|
||
)}
|
||
{file.fail_count > 0 && (
|
||
<StatusBadge
|
||
status="fail"
|
||
text={`不通过(${file.fail_count})`}
|
||
showIcon={true}
|
||
className="my-2"
|
||
/>
|
||
)}
|
||
{/* {file.manual_count > 0 && (
|
||
<StatusBadge
|
||
status="pending"
|
||
text={`需人工(${file.manual_count})`}
|
||
showIcon={true}
|
||
className="my-2"
|
||
/>
|
||
)} */}
|
||
</div>
|
||
|
||
) : (
|
||
<div className="text-sm">
|
||
-
|
||
</div>
|
||
)
|
||
},
|
||
{
|
||
title: "评查分数",
|
||
key: "score",
|
||
width: "8%",
|
||
render: (_: unknown, file: TaskDocument) => (
|
||
<div className="text-left">
|
||
{file.final_score ? (
|
||
<span className={`font-medium ${
|
||
file.final_score >= 90 ? 'text-green-600' :
|
||
file.final_score >= 70 ? 'text-yellow-600' :
|
||
'text-red-600'
|
||
}`}>
|
||
{file.final_score}
|
||
</span>
|
||
) : (
|
||
<span className="text-gray-400">-</span>
|
||
)}
|
||
</div>
|
||
)
|
||
},
|
||
{
|
||
title: '审核状态',
|
||
key: 'auditStatus',
|
||
width: '8%',
|
||
render: (_: unknown, file: TaskDocument) => renderAuditStatus(file)
|
||
},
|
||
{
|
||
title: "问题摘要",
|
||
key: "issues",
|
||
width: "20%",
|
||
render: (_: unknown, file: TaskDocument) => renderIssues(file)
|
||
},
|
||
{
|
||
title: "操作",
|
||
key: "operation",
|
||
width: "auto",
|
||
render: (_: unknown, file: TaskDocument) => (
|
||
<>
|
||
<Button
|
||
type="default"
|
||
size="small"
|
||
icon="ri-eye-line"
|
||
onClick={() => handleReviewFileClick(file.document_id.toString(), file.audit_status)}
|
||
disabled={file.status !== 'Processed'}
|
||
className="mr-2"
|
||
>
|
||
查看
|
||
</Button>
|
||
</>
|
||
)
|
||
}
|
||
];
|
||
|
||
return (
|
||
<Modal
|
||
isOpen={isOpen}
|
||
onClose={onClose}
|
||
title={title}
|
||
size="full"
|
||
className="document-list-modal"
|
||
>
|
||
<div className="px-6 py-4">
|
||
{loading ? (
|
||
// 显示loading状态
|
||
<div className="py-8">
|
||
<LoadingIndicator text="正在加载文档列表..." />
|
||
</div>
|
||
) : files.length === 0 ? (
|
||
// 无数据状态
|
||
<div className="text-center py-8 text-gray-500">
|
||
暂无文档数据
|
||
</div>
|
||
) : (
|
||
// 有数据时显示表格和分页
|
||
<>
|
||
<div className="mb-4 flex items-center">
|
||
<i className="ri-file-list-3-line text-primary text-lg mr-2"></i>
|
||
<span className="text-sm text-secondary">共</span>
|
||
<span className="text-base font-normal text-primary ml-1 mr-1">{total || files.length}</span>
|
||
<span className="text-sm text-secondary">个文档</span>
|
||
</div>
|
||
|
||
<Table
|
||
columns={columns}
|
||
dataSource={files}
|
||
rowKey="document_id"
|
||
emptyText="暂无文件数据"
|
||
className="files-table table-auto-height"
|
||
/>
|
||
|
||
{/* 分页组件 - 只有在提供了分页回调函数且总数大于每页大小时才显示 */}
|
||
{onPageChange && total > 0 && (
|
||
<Pagination
|
||
currentPage={currentPage}
|
||
total={total}
|
||
pageSize={pageSize}
|
||
onChange={onPageChange || (() => {})}
|
||
onPageSizeChange={onPageSizeChange}
|
||
showTotal={true}
|
||
showPageSizeChanger={!!onPageSizeChange}
|
||
pageSizeOptions={[10, 20, 30, 50]}
|
||
/>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
</Modal>
|
||
);
|
||
} |