// import React from 'react'; import { type MetaFunction } from "@remix-run/node"; import { useLoaderData, useNavigate, Form } from "@remix-run/react"; import { Card } from "~/components/ui/Card"; import { Button } from "~/components/ui/Button"; import { FileTag, links as fileTagLinks } from "~/components/ui/FileTag"; // import { FileTypeTag, links as fileTypeTagLinks } from "~/components/ui/FileTypeTag"; import { Tag } from "~/components/ui/Tag"; import homeStyles from "~/styles/pages/sys_overview.css?url"; import { getDocuments, type DocumentUI, type DocumentSearchParams } from "~/api/files/documents"; import { useState, useEffect } from "react"; import { getHomeData } from "~/api/home/home"; import dayjs from 'dayjs'; import type { UserRole } from '~/api/login/auth.server'; import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/node"; import { logout, getUserSession } from "~/api/login/auth.server"; // 文件处理状态选项 const fileProcessingStatusOptions = [ { value: "Waiting", label: "上传中", icon: "ri-loader-line", color: "blue" }, { value: "Cutting", label: "切分中", icon: "ri-loader-line", color: "purple" }, { value: "Extractioning", label: "抽取中", icon: "ri-loader-line", color: "cyan" }, { value: "Evaluationing", label: "评查中", icon: "ri-loader-line", color: "teal" }, { value: "Processed", label: "已完成", icon: "ri-check-line", color: "green" }, ]; export const links = () => [ { rel: "stylesheet", href: homeStyles }, ...fileTagLinks() ]; export const meta: MetaFunction = () => { return [ { title: "中国烟草AI合同及卷宗审核系统 - 首页" }, { name: "description", content: "AI审核系统首页" } ]; }; // API 响应的类型定义 // interface StatsData { // totalFiles: number; // reviewedFiles: number; // pendingFiles: number; // passRate: number; // } // 添加认证检查 export async function loader({ request }: LoaderFunctionArgs) { try { // 从根loader获取用户角色 const { userRole } = await getUserSession(request); // 返回默认值,实际数据将在客户端根据 sessionStorage 加载 return Response.json({ homeData: { todayPendingFiles: 0, monthlyReviewedFiles: 0, monthlyReviewGrowth: { value: 0, isUp: true }, monthlyPassRate: 0, passRateGrowth: { value: 0, isUp: true }, issuesDetected: 0, issuesGrowth: { value: 0, isUp: true } }, recentFiles: [], reviewType: null, userRole: userRole }); } catch (error) { // 错误处理 console.error('Failed to fetch dashboard data:', error); return Response.json( { error: '获取数据失败,请稍后重试' }, { status: 500 } ); } } // 处理登出请求 export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const intent = formData.get("intent"); if (intent === "logout") { return logout(request); } return null; } export default function Home() { const navigate = useNavigate(); const { homeData: initialHomeData, recentFiles: initialRecentFiles, userRole: serverUserRole } = useLoaderData(); const [recentFiles, setRecentFiles] = useState(initialRecentFiles || []); const [homeData, setHomeData] = useState(initialHomeData); const [currentDateTime, setCurrentDateTime] = useState({ date: '', time: '' }); const [isLoading, setIsLoading] = useState(true); const userRole = serverUserRole as UserRole; // 打印服务器端传递的用户角色 useEffect(() => { console.log('服务器返回的用户角色:', serverUserRole); }, [serverUserRole]); // 更新当前时间 useEffect(() => { // 使用dayjs格式化日期和时间 const updateDateTime = () => { const now = dayjs(); setCurrentDateTime({ date: now.format('YYYY年MM月DD日'), time: now.format('HH:mm:ss') }); }; // 立即更新一次 updateDateTime(); // 设置计时器,每秒更新一次 const timerID = setInterval(updateDateTime, 1000); // 清理函数,组件卸载时清除计时器 return () => clearInterval(timerID); }, []); // 处理登出操作 const handleLogout = () => { // 清除sessionStorage中的所有数据 if (typeof window !== 'undefined') { sessionStorage.removeItem('userRole'); sessionStorage.removeItem('reviewType'); sessionStorage.removeItem('previousReviewType'); // 可以根据需要清除其他会话数据 sessionStorage.clear(); } // 使用Form组件提交登出请求 const form = document.getElementById('logout-form') as HTMLFormElement; if (form) { form.submit(); } else { // 如果找不到表单,直接导航到登录页 navigate('/login'); } }; // 在客户端挂载时,根据 sessionStorage 中的 reviewType 加载正确的数据 useEffect(() => { const loadData = async () => { try { setIsLoading(true); // 从 sessionStorage 获取 reviewType const reviewType = sessionStorage.getItem('reviewType'); // 加载主页数据 const newHomeData = await getHomeData(reviewType || undefined); setHomeData(newHomeData); // 加载文档数据 const docs = await loadDocuments(reviewType); setRecentFiles(docs); setIsLoading(false); } catch (error) { console.error('加载数据失败:', error); setIsLoading(false); } }; loadData(); }, []); // 仅在组件挂载时执行一次 // 加载文档数据的函数 const loadDocuments = async (reviewType: string | null) => { try { const documentSearchParams: DocumentSearchParams = { page: 1, pageSize: 10 }; // 根据 reviewType 添加过滤条件 if (reviewType === 'contract') { documentSearchParams.documentType = '1'; const response = await getDocuments(documentSearchParams); if (!response.error && response.data) { return response.data.documents; } } else if (reviewType === 'record') { // 获取类型 2 的文档 const response1 = await getDocuments({ ...documentSearchParams, documentType: '2' }); // 获取类型 3 的文档 const response2 = await getDocuments({ ...documentSearchParams, documentType: '3' }); if (!response1.error && !response2.error && response1.data && response2.data) { // 合并文档并排序 const mergedDocs = [...response1.data.documents, ...response2.data.documents]; mergedDocs.sort((a, b) => new Date(b.updatedAt || '').getTime() - new Date(a.updatedAt || '').getTime() ); // 限制数量 return mergedDocs.slice(0, documentSearchParams.pageSize); } } else { // 没有特定类型,获取所有文档 const response = await getDocuments(documentSearchParams); if (!response.error && response.data) { return response.data.documents; } } return []; // 默认返回空数组 } catch (error) { console.error('加载文档数据失败:', error); return []; } }; // 监听 sessionStorage 中 reviewType 的变化 useEffect(() => { const handleStorageChange = async () => { const currentReviewType = sessionStorage.getItem('reviewType'); const previousReviewType = sessionStorage.getItem('previousReviewType'); // 如果 reviewType 发生变化 if (currentReviewType !== previousReviewType) { setIsLoading(true); // 更新主页数据 const newHomeData = await getHomeData(currentReviewType || undefined); setHomeData(newHomeData); // 更新文档数据 const docs = await loadDocuments(currentReviewType); setRecentFiles(docs); // 保存当前 reviewType 为上一次的值,用于比较 sessionStorage.setItem('previousReviewType', currentReviewType || ''); setIsLoading(false); } }; // 设置初始的 previousReviewType const initialReviewType = sessionStorage.getItem('reviewType'); sessionStorage.setItem('previousReviewType', initialReviewType || ''); // 设置定期检查 const checkInterval = setInterval(handleStorageChange, 1000); return () => { clearInterval(checkInterval); }; }, []); // 修改useEffect定时器,每10秒自动获取最近文档数据 // 按照定时器更新最近文档 useEffect(() => { // 避免在加载状态下进行自动更新 if (isLoading) return; const fetchLatestDocuments = async () => { const reviewType = sessionStorage.getItem('reviewType'); const docs = await loadDocuments(reviewType); setRecentFiles(docs); }; // 设置10秒的定时器 const timerID = setInterval(fetchLatestDocuments, 10000); // 组件卸载时清除定时器 return () => { clearInterval(timerID); }; }, [isLoading]); // 仅依赖 isLoading 状态 return (
{/* 登出表单 - 隐藏 */}
{/* 页面头部 */}

系统概览

{currentDateTime.date} | {currentDateTime.time}
{userRole === 'developer' ? '管' : '用'}

{userRole === 'developer' ? '系统管理员' : '普通用户'}

{/*

{userRole === 'developer' ? '超级管理员' : '标准权限'}

*/}
{/* 登出操作 */}
{/* 统计卡片区域 */}
{/* 快捷访问区域 */}
{/* 最近文档区域 */} 查看全部} className="mt-6" >
{recentFiles.map((file: DocumentUI) => (
{file.name}
{file.typeName} · {file.updatedAt}
{(() => { const fileStatus = file.fileStatus || "-"; const status = fileProcessingStatusOptions.find(s => s.value === fileStatus) || fileProcessingStatusOptions[0]; const isSpinning = fileStatus !== "Processed"; return (
{status.label}
); })()}
))}
); } // 统计卡片组件 interface StatCardProps { title: string; value: number | string; icon: string; trend?: { value: number; isUp: boolean; }; } function StatCard({ title, value, icon, trend }: StatCardProps) { return (
{title}
{value}
{trend && (
{trend.value}% 较上月
)}
); } // 快捷方式组件 interface ShortcutItemProps { icon: string; label: string; to: string; } function ShortcutItem({ icon, label, to }: ShortcutItemProps) { return ( ); }