328 lines
10 KiB
TypeScript
328 lines
10 KiB
TypeScript
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>
|
|
);
|
|
}
|