import { useState } from "react"; import { useSearchParams, Link } from "@remix-run/react"; import { type MetaFunction, type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/node"; import { Card } from "~/components/ui/Card"; import { Button } from "~/components/ui/Button"; import { Table } from "~/components/ui/Table"; import { Pagination } from "~/components/ui/Pagination"; import { StatusBadge } from "~/components/ui/StatusBadge"; import { FileTypeTag } from "~/components/ui/FileTypeTag"; import { FileTag } from "~/components/ui/FileTag"; import { FilterPanel, FilterSelect, SearchFilter, DateRangeFilter } from "~/components/ui/FilterPanel"; import documentsIndexStyles from "~/styles/pages/documents_index.css?url"; // 导入样式 export function links() { return [ { rel: "stylesheet", href: documentsIndexStyles } ]; } // 元数据 export const meta: MetaFunction = () => { return [ { title: "文档列表 - 中国烟草AI合同及卷宗审核系统" }, { name: "description", content: "查看和管理系统中的所有文档,包括合同、许可证和行政处罚决定书等" }, ]; }; interface DocumentItem { id: string; name: string; documentNumber: string; type: string; typeName: string; size: number; status: string; issues: number | null; uploadTime: string; fileType: string; tags?: string[]; } // 数据加载器 export const loader = async ({ request }: LoaderFunctionArgs) => { // 获取URL查询参数 const url = new URL(request.url); const search = url.searchParams.get("search") || ""; const documentType = url.searchParams.get("documentType") || ""; const status = url.searchParams.get("status") || ""; const documentNumber = url.searchParams.get("documentNumber") || ""; const dateFrom = url.searchParams.get("dateFrom") || ""; const dateTo = url.searchParams.get("dateTo") || ""; const page = parseInt(url.searchParams.get("page") || "1", 10); const pageSize = parseInt(url.searchParams.get("pageSize") || "20", 10); // 在实际应用中,这里会调用API获取数据 // const response = await fetch(`/api/documents?search=${search}&...`); // const data = await response.json(); // 使用模拟数据 const mockData = { documents: [ { id: "1", name: "2023年度烟草销售框架合同.pdf", documentNumber: "XS20230001", type: "sales-contract", typeName: "销售合同", size: 2.5 * 1024 * 1024, // 2.5MB status: "pass", issues: 0, uploadTime: "2023-10-15 15:30", fileType: "pdf" }, { id: "2", name: "设备采购合同-打印机.docx", documentNumber: "CG20230052", type: "purchase-contract", typeName: "采购合同", size: 1.2 * 1024 * 1024, // 1.2MB status: "warning", issues: 3, uploadTime: "2023-10-14 09:15", fileType: "docx" }, { id: "3", name: "烟草零售许可证.pdf", documentNumber: "ZM2023100345", type: "license", typeName: "专卖许可证", size: 0.8 * 1024 * 1024, // 0.8MB status: "pending", issues: null, uploadTime: "2023-10-13 14:20", fileType: "pdf" }, { id: "4", name: "非法售烟行政处罚决定书.docx", documentNumber: "CF20230087", type: "punishment", typeName: "行政处罚决定书", size: 1.5 * 1024 * 1024, // 1.5MB status: "processing", issues: null, uploadTime: "2023-10-10 16:45", fileType: "docx" }, { id: "5", name: "烟草种植承包协议-2023.pdf", documentNumber: "CB20230024", type: "agreement", typeName: "承包协议", size: 3.2 * 1024 * 1024, // 3.2MB status: "fail", issues: 8, uploadTime: "2023-10-09 10:30", fileType: "pdf", tags: ["测试"] }, ], total: 156, page, pageSize }; // 返回数据 return Response.json(mockData); }; // 处理表单提交和删除等操作 export const action = async ({ request }: ActionFunctionArgs) => { const formData = await request.formData(); const action = formData.get("_action"); // 在实际应用中,这里会根据action类型调用相应的API // 例如删除文档,批量删除,等等 if (action === "delete") { const id = formData.get("id"); // await fetch(`/api/documents/${id}`, { method: "DELETE" }); return Response.json({ success: true, message: "文档已成功删除" }); } if (action === "batchDelete") { const ids = formData.getAll("ids"); // await fetch(`/api/documents/batch-delete`, { // method: "POST", // body: JSON.stringify({ ids }), // headers: { "Content-Type": "application/json" } // }); return Response.json({ success: true, message: `已成功删除${ids.length}个文档` }); } // 未知操作 return Response.json({ success: false, message: "未知操作" }, { status: 400 }); }; // 文档类型选项 const documentTypeOptions = [ { value: "sales-contract", label: "销售合同" }, { value: "purchase-contract", label: "采购合同" }, { value: "license", label: "专卖许可证" }, { value: "punishment", label: "行政处罚决定书" }, { value: "agreement", label: "承包协议" }, ]; // 文档状态选项 const documentStatusOptions = [ { value: "pending", label: "待审核" }, { value: "processing", label: "审核中" }, { value: "pass", label: "通过" }, { value: "warning", label: "警告" }, { value: "fail", label: "不通过" }, ]; // 格式化文件大小 const formatFileSize = (bytes: number) => { if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }; // 获取文档类型标签背景颜色 // 此函数已不再需要,改用 FileTypeTag 组件 // const getDocumentTypeTagColor = (type: string): string => { // const colorMap: Record = { // "sales-contract": "blue", // "purchase-contract": "green", // "license": "purple", // "punishment": "yellow", // "agreement": "orange", // "default": "gray" // }; // return colorMap[type] || colorMap.default; // }; export default function DocumentsIndex() { const [searchParams, setSearchParams] = useSearchParams(); const [selectedRowKeys, setSelectedRowKeys] = useState([]); // 从URL获取当前筛选条件 const search = searchParams.get("search") || ""; const documentType = searchParams.get("documentType") || ""; const status = searchParams.get("status") || ""; const documentNumber = searchParams.get("documentNumber") || ""; const dateFrom = searchParams.get("dateFrom") || ""; const dateTo = searchParams.get("dateTo") || ""; const currentPage = parseInt(searchParams.get("page") || "1", 10); const pageSize = parseInt(searchParams.get("pageSize") || "20", 10); // API 返回的模拟数据 const mockData = { documents: [ { id: "1", name: "2023年度烟草销售框架合同.pdf", documentNumber: "XS20230001", type: "sales-contract", typeName: "销售合同", size: 2.5 * 1024 * 1024, // 2.5MB status: "pass", issues: 0, uploadTime: "2023-10-15 15:30", fileType: "pdf" }, { id: "2", name: "设备采购合同-打印机.docx", documentNumber: "CG20230052", type: "purchase-contract", typeName: "采购合同", size: 1.2 * 1024 * 1024, // 1.2MB status: "warning", issues: 3, uploadTime: "2023-10-14 09:15", fileType: "docx" }, { id: "3", name: "烟草零售许可证.pdf", documentNumber: "ZM2023100345", type: "license", typeName: "专卖许可证", size: 0.8 * 1024 * 1024, // 0.8MB status: "pending", issues: null, uploadTime: "2023-10-13 14:20", fileType: "pdf" }, { id: "4", name: "非法售烟行政处罚决定书.docx", documentNumber: "CF20230087", type: "punishment", typeName: "行政处罚决定书", size: 1.5 * 1024 * 1024, // 1.5MB status: "processing", issues: null, uploadTime: "2023-10-10 16:45", fileType: "docx" }, { id: "5", name: "烟草种植承包协议-2023.pdf", documentNumber: "CB20230024", type: "agreement", typeName: "承包协议", size: 3.2 * 1024 * 1024, // 3.2MB status: "fail", issues: 8, uploadTime: "2023-10-09 10:30", fileType: "pdf", tags: ["测试"] }, ], total: 156, page: currentPage, pageSize }; // 分页处理函数 const handlePageChange = (page: number) => { searchParams.set("page", page.toString()); setSearchParams(searchParams); }; // 每页条数变更处理函数 const handlePageSizeChange = (size: number) => { searchParams.set("pageSize", size.toString()); searchParams.set("page", "1"); // 重置到第一页 setSearchParams(searchParams); }; // 处理文档名称搜索 const handleNameSearch = (value: string) => { const params = new URLSearchParams(searchParams); if (value) { params.set("search", value); } else { params.delete("search"); } params.set("page", "1"); // 重置页码 setSearchParams(params); }; // 处理文档编号变更 const handleDocumentNumberChange = (value: string) => { const params = new URLSearchParams(searchParams); if (value) { params.set("documentNumber", value); } else { params.delete("documentNumber"); } params.set("page", "1"); // 重置页码 setSearchParams(params); }; // 处理文档类型变更 const handleDocumentTypeChange = (e: React.ChangeEvent) => { const params = new URLSearchParams(searchParams); if (e.target.value) { params.set("documentType", e.target.value); } else { params.delete("documentType"); } params.set("page", "1"); // 重置页码 setSearchParams(params); }; // 处理状态变更 const handleStatusChange = (e: React.ChangeEvent) => { const params = new URLSearchParams(searchParams); if (e.target.value) { params.set("status", e.target.value); } else { params.delete("status"); } params.set("page", "1"); // 重置页码 setSearchParams(params); }; // 处理日期范围变更 const handleDateChange = (field: 'dateFrom' | 'dateTo', value: string) => { const params = new URLSearchParams(searchParams); if (value) { params.set(field, value); } else { params.delete(field); } params.set("page", "1"); // 重置页码 setSearchParams(params); }; // 重置搜索条件 const handleReset = () => { setSearchParams(new URLSearchParams({ page: "1", pageSize: pageSize.toString() })); }; // 行选择变更处理 const handleRowSelectionChange = (id: string) => { if (selectedRowKeys.includes(id)) { setSelectedRowKeys(selectedRowKeys.filter(key => key !== id)); } else { setSelectedRowKeys([...selectedRowKeys, id]); } }; // 全选处理 const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedRowKeys(mockData.documents.map(doc => doc.id)); } else { setSelectedRowKeys([]); } }; // 删除确认 const confirmDelete = (id: string, name: string) => { if (window.confirm(`确认删除文档 "${name}"?`)) { // 在实际应用中这里会提交表单到action处理 console.log('删除文档:', id, name); // 更新选中行 setSelectedRowKeys(selectedRowKeys.filter(key => key !== id)); } }; // 批量删除确认 const confirmBatchDelete = () => { if (selectedRowKeys.length === 0) { alert('请至少选择一个文档'); return; } if (window.confirm(`确认删除选中的 ${selectedRowKeys.length} 个文档?`)) { // 在实际应用中这里会提交表单到action处理 console.log('批量删除文档IDs:', selectedRowKeys); // 清空选中行 setSelectedRowKeys([]); } }; // 表格列定义 const columns = [ { title: ( handleSelectAll(e.target.checked)} /> ), key: "selection", width: "50px", render: (_: unknown, record: DocumentItem) => ( handleRowSelectionChange(record.id)} /> ) }, { title: "文档名称", key: "name", render: (_: unknown, record: DocumentItem) => (
{record.name}
{record.tags && record.tags.map((tag: string) => ( {tag} ))}
) }, { title: "文档编号", key: "documentNumber", render: (_: unknown, record: DocumentItem) => ( {record.documentNumber} ) }, { title: "文件大小", key: "size", render: (_: unknown, record: DocumentItem) => formatFileSize(record.size) }, { title: "审核状态", key: "status", render: (_: unknown, record: DocumentItem) => ( ) }, { title: "问题数量", key: "issues", render: (_: unknown, record: DocumentItem) => ( record.issues === null ? "-" : record.issues ) }, { title: "上传时间", key: "uploadTime", render: (_: unknown, record: DocumentItem) => record.uploadTime }, { title: "操作", key: "actions", width: "280px", render: (_: unknown, record: DocumentItem) => (
{record.status === "pending" ? ( 开始审核 ) : record.status === "processing" ? ( 查看进度 ) : ( 查看 )} 修改
) } ]; return (
{/* 页面头部 */}

文档列表

{/* 搜索筛选区 */} } noActionDivider={true} > handleDateChange('dateFrom', value)} onEndDateChange={(value) => handleDateChange('dateTo', value)} className="flex-1" simple={true} /> {/* 数据表格 */}
{mockData.total} 条记录
{/* 分页 */} ); } // 错误边界处理 export function ErrorBoundary() { return (

出错了

加载文档列表时出现错误。请刷新页面或联系系统管理员。

); }