重新构建路由和配置样式文件
This commit is contained in:
@@ -0,0 +1,414 @@
|
||||
import React, { useState } from 'react';
|
||||
import { json, type MetaFunction } from '@remix-run/node';
|
||||
import { useLoaderData, useParams } from '@remix-run/react';
|
||||
import { Button } from '~/components/ui/Button';
|
||||
import { Card } from '~/components/ui/Card';
|
||||
import { Breadcrumb } from '~/components/layout/Breadcrumb';
|
||||
import type { ReviewResult, RuleCheckResult } from '~/models/review';
|
||||
import type { File } from '~/models/file';
|
||||
import { RULE_CHECK_STATUS_LABELS, RULE_CHECK_STATUS_COLORS } from '~/models/review';
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return [
|
||||
{ title: "中国烟草AI合同及卷宗审核系统 - 评查详情" },
|
||||
{ name: "description", content: "文件评查详情页面" }
|
||||
];
|
||||
};
|
||||
|
||||
export const handle = {
|
||||
breadcrumb: '评查详情'
|
||||
};
|
||||
|
||||
interface LoaderData {
|
||||
file: File;
|
||||
reviewResult: ReviewResult;
|
||||
reviewPoints: RuleCheckResult[];
|
||||
fileContent?: string; // 模拟文件内容
|
||||
}
|
||||
|
||||
export async function loader({ params }) {
|
||||
const { reviewId } = params;
|
||||
|
||||
// 模拟数据,实际项目中应从API获取
|
||||
const file: File = {
|
||||
id: "1",
|
||||
fileName: "2023年度烟草专卖零售许可证.pdf",
|
||||
fileType: "application/pdf",
|
||||
documentTypeId: "2",
|
||||
documentTypeName: "专卖许可证",
|
||||
fileSize: 1024 * 1024 * 2.5, // 2.5MB
|
||||
uploaderId: "1",
|
||||
uploaderName: "张三",
|
||||
status: "completed",
|
||||
reviewStatus: "pass",
|
||||
createdAt: "2023-12-24 14:30",
|
||||
updatedAt: "2023-12-24 16:45"
|
||||
};
|
||||
|
||||
const reviewResult: ReviewResult = {
|
||||
id: reviewId,
|
||||
fileId: "1",
|
||||
fileName: "2023年度烟草专卖零售许可证.pdf",
|
||||
totalPoints: 15,
|
||||
passPoints: 6,
|
||||
warningPoints: 7,
|
||||
errorPoints: 2,
|
||||
score: 80,
|
||||
reviewStatus: "warning",
|
||||
reviewedAt: "2023-12-24 16:45",
|
||||
reviewerId: "system",
|
||||
reviewerName: "AI系统",
|
||||
createdAt: "2023-12-24 14:35",
|
||||
updatedAt: "2023-12-24 16:45"
|
||||
};
|
||||
|
||||
const reviewPoints: RuleCheckResult[] = [
|
||||
{
|
||||
id: "1",
|
||||
reviewResultId: reviewId,
|
||||
ruleId: "1",
|
||||
ruleName: "合同主体信息完整性检查",
|
||||
status: "pass",
|
||||
location: "第1页 第3段",
|
||||
content: "甲方:XX烟草公司,地址:XX市XX区XX路XX号,法定代表人:张XX",
|
||||
suggestion: "主体信息完整,符合规范",
|
||||
manualReviewed: false,
|
||||
createdAt: "2023-12-24 14:40",
|
||||
updatedAt: "2023-12-24 14:40"
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
reviewResultId: reviewId,
|
||||
ruleId: "2",
|
||||
ruleName: "许可证编号格式检查",
|
||||
status: "warning",
|
||||
location: "第1页 第5段",
|
||||
content: "许可证编号:(2023)12345",
|
||||
suggestion: "许可证编号格式不完全符合规范,建议修改为'烟零许(2023)12345号'",
|
||||
manualReviewed: true,
|
||||
createdAt: "2023-12-24 14:40",
|
||||
updatedAt: "2023-12-24 15:20"
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
reviewResultId: reviewId,
|
||||
ruleId: "3",
|
||||
ruleName: "许可证有效期检查",
|
||||
status: "fail",
|
||||
location: "第1页 第8段",
|
||||
content: "有效期:自2023年1月1日",
|
||||
suggestion: "许可证缺少有效期截止日期,必须明确注明有效期限",
|
||||
manualReviewed: false,
|
||||
createdAt: "2023-12-24 14:40",
|
||||
updatedAt: "2023-12-24 14:40"
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
reviewResultId: reviewId,
|
||||
ruleId: "4",
|
||||
ruleName: "经营场所信息检查",
|
||||
status: "pass",
|
||||
location: "第1页 第12段",
|
||||
content: "经营场所:XX市XX区XX街XX号,面积:120平方米",
|
||||
suggestion: "经营场所信息完整",
|
||||
manualReviewed: false,
|
||||
createdAt: "2023-12-24 14:40",
|
||||
updatedAt: "2023-12-24 14:40"
|
||||
}
|
||||
];
|
||||
|
||||
// 模拟文件内容,实际项目中应从API获取或使用专用组件展示
|
||||
const fileContent = `烟草专卖零售许可证
|
||||
发证机关:XX市烟草专卖局
|
||||
发证日期:2023年1月1日
|
||||
|
||||
甲方:XX烟草公司,地址:XX市XX区XX路XX号,法定代表人:张XX
|
||||
|
||||
零售单位名称:XX便利店
|
||||
许可证编号:(2023)12345
|
||||
法定代表人/负责人:李XX
|
||||
经营者类型:个体工商户
|
||||
有效期:自2023年1月1日
|
||||
联系电话:123-4567890
|
||||
|
||||
经营场所:XX市XX区XX街XX号,面积:120平方米
|
||||
零售烟草制品品种:卷烟、雪茄烟
|
||||
|
||||
特别说明:本许可证不得伪造、变造、转让、涂改。
|
||||
`;
|
||||
|
||||
return json<LoaderData>({
|
||||
file,
|
||||
reviewResult,
|
||||
reviewPoints,
|
||||
fileContent
|
||||
});
|
||||
}
|
||||
|
||||
export default function ReviewDetail() {
|
||||
const { file, reviewResult, reviewPoints, fileContent } = useLoaderData<typeof loader>();
|
||||
const [activeTab, setActiveTab] = useState('tab-preview');
|
||||
const [selectedPoint, setSelectedPoint] = useState<string | null>(null);
|
||||
|
||||
const handleTabChange = (tabId: string) => {
|
||||
setActiveTab(tabId);
|
||||
};
|
||||
|
||||
const handlePointSelect = (pointId: string) => {
|
||||
setSelectedPoint(pointId === selectedPoint ? null : pointId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{ title: '评查结果', to: '/reviews' },
|
||||
{ title: '评查详情', to: `/reviews/${reviewResult.id}` }
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<div className="flex items-center">
|
||||
<h2 className="text-xl font-medium">{file.fileName}</h2>
|
||||
<span className={`ml-3 status-badge status-${reviewResult.reviewStatus === 'pass' ? 'success' : reviewResult.reviewStatus === 'warning' ? 'warning' : 'error'}`}>
|
||||
<i className={`ri-${reviewResult.reviewStatus === 'pass' ? 'checkbox-circle' : reviewResult.reviewStatus === 'warning' ? 'error-warning' : 'close-circle'}-line mr-1`}></i>
|
||||
{reviewResult.reviewStatus === 'pass' ? '通过' : reviewResult.reviewStatus === 'warning' ? '警告' : '不通过'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-x-2">
|
||||
<Button type="default" icon="ri-download-line">
|
||||
导出评查报告
|
||||
</Button>
|
||||
<Button type="primary" icon="ri-check-double-line">
|
||||
完成评查
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tab-container">
|
||||
<div className="tab-nav">
|
||||
<div
|
||||
className={`tab-nav-item ${activeTab === 'tab-preview' ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange('tab-preview')}
|
||||
>
|
||||
<i className="ri-file-text-line"></i> 评查结果
|
||||
</div>
|
||||
<div
|
||||
className={`tab-nav-item ${activeTab === 'tab-suggestion' ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange('tab-suggestion')}
|
||||
>
|
||||
<i className="ri-lightbulb-line"></i> AI智能分析
|
||||
</div>
|
||||
<div
|
||||
className={`tab-nav-item ${activeTab === 'tab-fileinfo' ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange('tab-fileinfo')}
|
||||
>
|
||||
<i className="ri-information-line"></i> 文件信息
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tab-content">
|
||||
<div className={`tab-pane ${activeTab === 'tab-preview' ? 'active' : ''}`}>
|
||||
<div className="flex flex-col lg:flex-row lg:h-[calc(100vh-250px)]">
|
||||
{/* 文件内容预览 */}
|
||||
<div className="w-full lg:w-2/3 h-full mb-4 lg:mb-0 lg:pr-4">
|
||||
<div className="bg-white p-4 rounded-md shadow-sm h-full overflow-y-auto">
|
||||
<pre className="whitespace-pre-wrap font-sans text-gray-800">
|
||||
{fileContent}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 评查点列表 */}
|
||||
<div className="w-full lg:w-1/3 h-full lg:pl-4">
|
||||
<div className="review-points-panel h-full flex flex-col">
|
||||
<div className="review-panel-header py-2 px-4 flex items-center bg-primary-light">
|
||||
<i className="ri-file-list-check-line text-primary mr-2"></i>
|
||||
<span className="font-medium text-primary">评查结果</span>
|
||||
</div>
|
||||
|
||||
{/* 评查统计 */}
|
||||
<div className="review-statistics bg-white border-b border-gray-100 py-3 px-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center">
|
||||
<div className="w-7 h-7 bg-gray-100 rounded-md flex items-center justify-center">
|
||||
<span className="text-sm font-semibold text-gray-600">{reviewResult.totalPoints}</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 ml-1">总计</span>
|
||||
</div>
|
||||
<div className="h-8 border-r border-gray-200"></div>
|
||||
<div className="flex items-center">
|
||||
<div className="w-7 h-7 bg-green-50 rounded-md flex items-center justify-center">
|
||||
<span className="text-sm font-semibold text-success">{reviewResult.passPoints}</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 ml-1">通过</span>
|
||||
</div>
|
||||
<div className="h-8 border-r border-gray-200"></div>
|
||||
<div className="flex items-center">
|
||||
<div className="w-7 h-7 bg-yellow-50 rounded-md flex items-center justify-center">
|
||||
<span className="text-sm font-semibold text-warning">{reviewResult.warningPoints}</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 ml-1">警告</span>
|
||||
</div>
|
||||
<div className="h-8 border-r border-gray-200"></div>
|
||||
<div className="flex items-center">
|
||||
<div className="w-7 h-7 bg-red-50 rounded-md flex items-center justify-center">
|
||||
<span className="text-sm font-semibold text-error">{reviewResult.errorPoints}</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 ml-1">错误</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 评查点列表 */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{reviewPoints.map(point => (
|
||||
<div
|
||||
key={point.id}
|
||||
className={`review-point-item ${selectedPoint === point.id ? 'bg-gray-50' : ''}`}
|
||||
onClick={() => handlePointSelect(point.id)}
|
||||
>
|
||||
<div className="review-point-header">
|
||||
<div className="review-point-title">{point.ruleName}</div>
|
||||
<span className={`status-badge status-${RULE_CHECK_STATUS_COLORS[point.status]}`}>
|
||||
<i className={`ri-${point.status === 'pass' ? 'checkbox-circle' : point.status === 'warning' ? 'error-warning' : 'close-circle'}-line mr-1`}></i>
|
||||
{RULE_CHECK_STATUS_LABELS[point.status]}
|
||||
</span>
|
||||
</div>
|
||||
<div className="review-point-location">
|
||||
<i className="ri-file-list-line mr-1"></i>
|
||||
<span>{point.location}</span>
|
||||
</div>
|
||||
|
||||
{selectedPoint === point.id && (
|
||||
<div className="mt-2 pt-2 border-t border-gray-100">
|
||||
<div className="text-xs text-gray-600 mb-1">
|
||||
<span className="font-medium">内容:</span>
|
||||
<span>{point.content}</span>
|
||||
</div>
|
||||
<div className="text-xs text-gray-600">
|
||||
<span className="font-medium">建议:</span>
|
||||
<span>{point.suggestion}</span>
|
||||
</div>
|
||||
<div className="mt-2 flex justify-between">
|
||||
<div className="text-xs text-gray-500">
|
||||
{point.manualReviewed &&
|
||||
<span><i className="ri-user-line mr-1"></i>人工审核</span>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<Button type="default" size="small">
|
||||
<i className="ri-edit-line mr-1"></i>修改
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`tab-pane ${activeTab === 'tab-suggestion' ? 'active' : ''}`}>
|
||||
<Card>
|
||||
<div className="text-lg font-medium mb-4 text-gray-800">AI智能分析意见</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<div className="font-medium text-gray-700 mb-2">总体评价</div>
|
||||
<div className="p-3 bg-gray-50 rounded-md text-gray-600">
|
||||
该烟草专卖零售许可证文档基本符合规范要求,但存在部分问题需要修改。主要包括许可证编号格式不标准和缺少有效期截止日期两个问题,其中缺少有效期截止日期属于严重问题,必须补充完善。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<div className="font-medium text-gray-700 mb-2">主要问题</div>
|
||||
<ul className="list-disc pl-5 space-y-2 text-gray-600">
|
||||
<li><span className="text-warning font-medium">许可证编号格式不规范</span> - 当前格式为"(2023)12345",应修改为"烟零许(2023)12345号"</li>
|
||||
<li><span className="text-error font-medium">缺少许可证有效期截止日期</span> - 仅注明了起始日期"自2023年1月1日",缺少截止日期</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="font-medium text-gray-700 mb-2">修改建议</div>
|
||||
<ul className="list-decimal pl-5 space-y-2 text-gray-600">
|
||||
<li>规范许可证编号格式,在前面添加"烟零许",在后面添加"号"</li>
|
||||
<li>补充完善许可证有效期,明确注明截止日期,如"自2023年1月1日至2023年12月31日"</li>
|
||||
<li>建议对零售烟草制品品种部分进行细化描述,列明具体品牌类别</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className={`tab-pane ${activeTab === 'tab-fileinfo' ? 'active' : ''}`}>
|
||||
<Card>
|
||||
<div className="text-lg font-medium mb-4 text-gray-800">文件基本信息</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<table className="w-full">
|
||||
<tbody>
|
||||
<tr className="border-b border-gray-100">
|
||||
<td className="py-2 text-gray-500 w-1/3">文件名称</td>
|
||||
<td className="py-2 text-gray-800">{file.fileName}</td>
|
||||
</tr>
|
||||
<tr className="border-b border-gray-100">
|
||||
<td className="py-2 text-gray-500">文件类型</td>
|
||||
<td className="py-2 text-gray-800">{file.documentTypeName}</td>
|
||||
</tr>
|
||||
<tr className="border-b border-gray-100">
|
||||
<td className="py-2 text-gray-500">文件大小</td>
|
||||
<td className="py-2 text-gray-800">{(file.fileSize / (1024 * 1024)).toFixed(2)} MB</td>
|
||||
</tr>
|
||||
<tr className="border-b border-gray-100">
|
||||
<td className="py-2 text-gray-500">上传人</td>
|
||||
<td className="py-2 text-gray-800">{file.uploaderName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 text-gray-500">上传时间</td>
|
||||
<td className="py-2 text-gray-800">{file.createdAt}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<table className="w-full">
|
||||
<tbody>
|
||||
<tr className="border-b border-gray-100">
|
||||
<td className="py-2 text-gray-500 w-1/3">评查状态</td>
|
||||
<td className="py-2 text-gray-800">
|
||||
<span className={`status-badge status-${reviewResult.reviewStatus === 'pass' ? 'success' : reviewResult.reviewStatus === 'warning' ? 'warning' : 'error'}`}>
|
||||
<i className={`ri-${reviewResult.reviewStatus === 'pass' ? 'checkbox-circle' : reviewResult.reviewStatus === 'warning' ? 'error-warning' : 'close-circle'}-line mr-1`}></i>
|
||||
{reviewResult.reviewStatus === 'pass' ? '通过' : reviewResult.reviewStatus === 'warning' ? '警告' : '不通过'}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="border-b border-gray-100">
|
||||
<td className="py-2 text-gray-500">评查得分</td>
|
||||
<td className="py-2 text-gray-800">{reviewResult.score} 分</td>
|
||||
</tr>
|
||||
<tr className="border-b border-gray-100">
|
||||
<td className="py-2 text-gray-500">评查完成时间</td>
|
||||
<td className="py-2 text-gray-800">{reviewResult.reviewedAt}</td>
|
||||
</tr>
|
||||
<tr className="border-b border-gray-100">
|
||||
<td className="py-2 text-gray-500">评查人</td>
|
||||
<td className="py-2 text-gray-800">{reviewResult.reviewerName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 text-gray-500">评查总点数</td>
|
||||
<td className="py-2 text-gray-800">{reviewResult.totalPoints} 项</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
import React from 'react';
|
||||
import { json, type MetaFunction } from '@remix-run/node';
|
||||
import { useLoaderData, Link } from '@remix-run/react';
|
||||
import { Button } from '~/components/ui/Button';
|
||||
import { Card } from '~/components/ui/Card';
|
||||
import { Table } from '~/components/ui/Table';
|
||||
import type { ReviewResult } from '~/models/review';
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return [
|
||||
{ title: "中国烟草AI合同及卷宗审核系统 - 评查结果" },
|
||||
{ name: "description", content: "文件评查结果列表" }
|
||||
];
|
||||
};
|
||||
|
||||
export const handle = {
|
||||
breadcrumb: '评查结果'
|
||||
};
|
||||
|
||||
interface LoaderData {
|
||||
reviews: ReviewResult[];
|
||||
totalCount: number;
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export async function loader({ request }) {
|
||||
// 解析查询参数
|
||||
const url = new URL(request.url);
|
||||
const keyword = url.searchParams.get("keyword") || "";
|
||||
const status = url.searchParams.get("status") || "";
|
||||
const startDate = url.searchParams.get("startDate") || "";
|
||||
const endDate = url.searchParams.get("endDate") || "";
|
||||
const page = parseInt(url.searchParams.get("page") || "1", 10);
|
||||
const pageSize = parseInt(url.searchParams.get("pageSize") || "10", 10);
|
||||
|
||||
// 模拟数据,实际项目中应从API获取
|
||||
const reviews: ReviewResult[] = [
|
||||
{
|
||||
id: "1",
|
||||
fileId: "1",
|
||||
fileName: "2023年度烟草专卖零售许可证.pdf",
|
||||
totalPoints: 15,
|
||||
passPoints: 11,
|
||||
warningPoints: 3,
|
||||
errorPoints: 1,
|
||||
score: 85,
|
||||
reviewStatus: "warning",
|
||||
reviewedAt: "2023-12-24 16:45",
|
||||
reviewerId: "system",
|
||||
reviewerName: "AI系统",
|
||||
createdAt: "2023-12-24 14:35",
|
||||
updatedAt: "2023-12-24 16:45"
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
fileId: "2",
|
||||
fileName: "烟草零售合同协议书.docx",
|
||||
totalPoints: 20,
|
||||
passPoints: 18,
|
||||
warningPoints: 2,
|
||||
errorPoints: 0,
|
||||
score: 92,
|
||||
reviewStatus: "pass",
|
||||
reviewedAt: "2023-12-23 10:30",
|
||||
reviewerId: "user1",
|
||||
reviewerName: "李四",
|
||||
createdAt: "2023-12-23 09:15",
|
||||
updatedAt: "2023-12-23 10:30"
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
fileId: "3",
|
||||
fileName: "烟草采购清单2023.xlsx",
|
||||
totalPoints: 12,
|
||||
passPoints: 5,
|
||||
warningPoints: 3,
|
||||
errorPoints: 4,
|
||||
score: 60,
|
||||
reviewStatus: "fail",
|
||||
reviewedAt: "2023-12-22 18:20",
|
||||
reviewerId: "system",
|
||||
reviewerName: "AI系统",
|
||||
createdAt: "2023-12-22 17:45",
|
||||
updatedAt: "2023-12-22 18:20"
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
fileId: "4",
|
||||
fileName: "2023年第三季度烟草销售报告.pdf",
|
||||
totalPoints: 18,
|
||||
passPoints: 16,
|
||||
warningPoints: 2,
|
||||
errorPoints: 0,
|
||||
score: 94,
|
||||
reviewStatus: "pass",
|
||||
reviewedAt: "2023-12-21 14:10",
|
||||
reviewerId: "user2",
|
||||
reviewerName: "王五",
|
||||
createdAt: "2023-12-21 13:30",
|
||||
updatedAt: "2023-12-21 14:10"
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
fileId: "5",
|
||||
fileName: "烟草品牌授权书.pdf",
|
||||
totalPoints: 10,
|
||||
passPoints: 6,
|
||||
warningPoints: 3,
|
||||
errorPoints: 1,
|
||||
score: 75,
|
||||
reviewStatus: "warning",
|
||||
reviewedAt: "2023-12-20 11:25",
|
||||
reviewerId: "system",
|
||||
reviewerName: "AI系统",
|
||||
createdAt: "2023-12-20 10:50",
|
||||
updatedAt: "2023-12-20 11:25"
|
||||
}
|
||||
];
|
||||
|
||||
// 根据查询条件过滤结果
|
||||
let filteredReviews = [...reviews];
|
||||
|
||||
if (keyword) {
|
||||
filteredReviews = filteredReviews.filter(review =>
|
||||
review.fileName.toLowerCase().includes(keyword.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
filteredReviews = filteredReviews.filter(review =>
|
||||
review.reviewStatus === status
|
||||
);
|
||||
}
|
||||
|
||||
if (startDate) {
|
||||
const start = new Date(startDate);
|
||||
filteredReviews = filteredReviews.filter(review =>
|
||||
new Date(review.reviewedAt) >= start
|
||||
);
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
const end = new Date(endDate);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
filteredReviews = filteredReviews.filter(review =>
|
||||
new Date(review.reviewedAt) <= end
|
||||
);
|
||||
}
|
||||
|
||||
// 分页
|
||||
const totalCount = filteredReviews.length;
|
||||
const totalPages = Math.ceil(totalCount / pageSize);
|
||||
const startIndex = (page - 1) * pageSize;
|
||||
const pagedReviews = filteredReviews.slice(startIndex, startIndex + pageSize);
|
||||
|
||||
return json<LoaderData>({
|
||||
reviews: pagedReviews,
|
||||
totalCount,
|
||||
currentPage: page,
|
||||
totalPages
|
||||
});
|
||||
}
|
||||
|
||||
export default function ReviewsList() {
|
||||
const { reviews, totalCount, currentPage, totalPages } = useLoaderData<typeof loader>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "文件名称",
|
||||
key: "fileName",
|
||||
render: (review: ReviewResult) => (
|
||||
<Link
|
||||
to={`/reviews/${review.id}`}
|
||||
className="text-primary hover:text-primary-dark transition-colors"
|
||||
>
|
||||
{review.fileName}
|
||||
</Link>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "评查状态",
|
||||
key: "reviewStatus",
|
||||
render: (review: ReviewResult) => (
|
||||
<span className={`status-badge status-${review.reviewStatus === 'pass' ? 'success' : review.reviewStatus === 'warning' ? 'warning' : 'error'}`}>
|
||||
<i className={`ri-${review.reviewStatus === 'pass' ? 'checkbox-circle' : review.reviewStatus === 'warning' ? 'error-warning' : 'close-circle'}-line mr-1`}></i>
|
||||
{review.reviewStatus === 'pass' ? '通过' : review.reviewStatus === 'warning' ? '警告' : '不通过'}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "评查得分",
|
||||
key: "score",
|
||||
render: (review: ReviewResult) => (
|
||||
<span className={`font-medium ${review.score >= 90 ? 'text-success' : review.score >= 70 ? 'text-warning' : 'text-error'}`}>
|
||||
{review.score}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "评查点",
|
||||
key: "points",
|
||||
render: (review: ReviewResult) => (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-xs px-2 py-1 bg-gray-100 rounded-full">{review.totalPoints}项</span>
|
||||
<span className="text-xs px-2 py-1 bg-green-50 text-success rounded-full">{review.passPoints}</span>
|
||||
<span className="text-xs px-2 py-1 bg-yellow-50 text-warning rounded-full">{review.warningPoints}</span>
|
||||
<span className="text-xs px-2 py-1 bg-red-50 text-error rounded-full">{review.errorPoints}</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "评查时间",
|
||||
key: "reviewedAt",
|
||||
render: (review: ReviewResult) => review.reviewedAt
|
||||
},
|
||||
{
|
||||
title: "评查人",
|
||||
key: "reviewerName",
|
||||
render: (review: ReviewResult) => (
|
||||
<span className="flex items-center">
|
||||
<i className={`ri-${review.reviewerId === 'system' ? 'robot-line' : 'user-line'} mr-1 ${review.reviewerId === 'system' ? 'text-primary' : 'text-gray-600'}`}></i>
|
||||
{review.reviewerName}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "actions",
|
||||
render: (review: ReviewResult) => (
|
||||
<div className="space-x-2">
|
||||
<Link
|
||||
to={`/reviews/${review.id}`}
|
||||
className="btn-text"
|
||||
>
|
||||
<i className="ri-search-line mr-1"></i>
|
||||
查看详情
|
||||
</Link>
|
||||
<button className="btn-text">
|
||||
<i className="ri-download-line mr-1"></i>
|
||||
导出报告
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-4 flex justify-between items-center">
|
||||
<h2 className="text-xl font-medium">评查结果列表</h2>
|
||||
<Link to="/files/upload" className="btn-primary">
|
||||
<i className="ri-upload-cloud-line mr-1"></i>
|
||||
上传新文件
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Card className="mb-4">
|
||||
<form className="filter-form">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="form-group">
|
||||
<label htmlFor="keyword" className="form-label">关键词</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
id="keyword"
|
||||
name="keyword"
|
||||
className="form-input pl-8"
|
||||
placeholder="文件名称"
|
||||
/>
|
||||
<i className="ri-search-line absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="status" className="form-label">评查状态</label>
|
||||
<select id="status" name="status" className="form-select">
|
||||
<option value="">全部状态</option>
|
||||
<option value="pass">通过</option>
|
||||
<option value="warning">警告</option>
|
||||
<option value="fail">不通过</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="startDate" className="form-label">开始日期</label>
|
||||
<input type="date" id="startDate" name="startDate" className="form-input" />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="endDate" className="form-label">结束日期</label>
|
||||
<input type="date" id="endDate" name="endDate" className="form-input" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button type="reset" className="btn-default mr-2">
|
||||
<i className="ri-refresh-line mr-1"></i>
|
||||
重置
|
||||
</button>
|
||||
<button type="submit" className="btn-primary">
|
||||
<i className="ri-search-line mr-1"></i>
|
||||
查询
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div className="mb-3 text-gray-500">
|
||||
共 <span className="text-primary">{totalCount}</span> 条评查结果
|
||||
</div>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={reviews}
|
||||
rowKey="id"
|
||||
pagination={{
|
||||
current: currentPage,
|
||||
pageSize: 10,
|
||||
total: totalCount,
|
||||
totalPages: totalPages
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user