import React, { useCallback, useEffect, useMemo, useState } from 'react'; import DriverBack from '~/pages/Container/Driver/components/DriverBack'; import EnterpriseTypeChart from './components/EnterpriseTypeChart'; import ProjectTypeChart from './components/ProjectTypeChart'; import ProjectCompletionTable from './components/ProjectCompletionTable'; import './index.css'; import { STATISTIC_CARDS, INFO_ALERTS, PROJECT_SUMMARY, ENTERPRISE_TYPE_STATS, PROJECT_TYPE_DISTRIBUTION, PROJECT_TOTAL, PROJECT_COMPLETION_LIST, } from './mockData'; import { fetchProjectNodeStats, fetchNotices, fetchIndustryStat, fetchEvalTypeRatio, fetchProjectExecution, } from '~/api/institution'; // ==================== UI 展示常量(与原型保持一致,非数据映射) ==================== const NODE_CHARS = ['险', '合', '组', '场', '报', '内', '技', '控']; const NODE_COLORS = ['blue', 'violet', 'green', 'orange', 'blue', 'violet', 'orange', 'green']; function mockIndustryToApiFormat() { return { list: ENTERPRISE_TYPE_STATS.categories.map((name, i) => ({ industryCode: '', industryName: name, projectCount: ENTERPRISE_TYPE_STATS.projectCount[i], statutoryProjectCount: ENTERPRISE_TYPE_STATS.statutoryCount[i], })), }; } function mockEvalTypeToApiFormat() { return { totalProjectCount: PROJECT_TOTAL, items: PROJECT_TYPE_DISTRIBUTION.map(item => ({ evalTypeCode: '', evalTypeName: item.name, count: item.value, })), }; } function mockExecutionToApiFormat() { return { list: PROJECT_COMPLETION_LIST.map(item => ({ projectId: item.id, projectNo: item.projectCode, projectName: item.projectName, customerName: item.evalCompany, evalTypeCode: '', evalTypeName: item.evalType, projectLeaderName: item.projectLeader, planEndDate: item.endDate, })), }; } // ==================== 子组件 ==================== function StatusCard({ item }) { return (
{item.char}
{item.title} {item.value}
); } function ReminderCard({ item }) { return (
{item.char}
{item.title} {item.value}
); } // ==================== 主组件 ==================== export default function InstitutionDashboard() { const handleReminderClick = useCallback(() => {}, []); // 各 API 的原始返回数据(null 表示接口未成功,走 mock) const [nodeStatsApi, setNodeStatsApi] = useState(null); const [noticesApi, setNoticesApi] = useState(null); const [industryStatApi, setIndustryStatApi] = useState(null); const [evalTypeRatioApi, setEvalTypeRatioApi] = useState(null); const [projectExecutionApi, setProjectExecutionApi] = useState(null); useEffect(() => { fetchProjectNodeStats() .then(setNodeStatsApi) .catch(() => {}); fetchNotices() .then(setNoticesApi) .catch(() => {}); fetchIndustryStat() .then(setIndustryStatApi) .catch(() => {}); fetchEvalTypeRatio() .then(setEvalTypeRatioApi) .catch(() => {}); fetchProjectExecution() .then(setProjectExecutionApi) .catch(() => {}); }, []); // 衍生展示数据:API 成功则用 API 字段直接构建,失败则回退 mock const statusCards = useMemo(() => { if (!nodeStatsApi) return STATISTIC_CARDS; const { nodeStats, archivedProjectCount, delayedProjectCount } = nodeStatsApi; const nodes = (nodeStats || []).map((node, i) => ({ key: node.nodeCode || `NODE_${i}`, title: node.nodeName, value: Number(node.pendingCount) || 0, char: NODE_CHARS[i] || node.nodeName.charAt(1) || '-', colorClass: NODE_COLORS[i] || 'blue', })); nodes.push( { key: 'archived', title: '已归档项目', value: Number(archivedProjectCount) || 0, char: '档', colorClass: 'cyan' }, { key: 'delayed', title: '项目延期', value: Number(delayedProjectCount) || 0, char: '延', colorClass: 'red' }, ); return nodes; }, [nodeStatsApi]); const projectSummary = useMemo(() => { if (!nodeStatsApi) return PROJECT_SUMMARY; return { total: Number(nodeStatsApi.totalProjects) || 0, statutory: Number(nodeStatsApi.statutoryProjects) || 0, }; }, [nodeStatsApi]); const alertItems = useMemo(() => { if (!noticesApi) return INFO_ALERTS; return [ { key: 'qualCheck', title: '资质现场审查通知', value: Number(noticesApi.qualOnSiteReviewCount) || 0, char: '审', colorClass: 'yellow' }, { key: 'supervisionCheck', title: '监督检查通知', value: Number(noticesApi.inspectionCount) || 0, char: '检', colorClass: 'red' }, { key: 'unreadNotice', title: '未读监管通知', value: Number(noticesApi.unreadRegulatoryCount) || 0, char: '未', colorClass: 'blue' }, ]; }, [noticesApi]); const industryStatData = useMemo(() => { return (industryStatApi && industryStatApi.list && industryStatApi.list.length > 0) ? industryStatApi : mockIndustryToApiFormat(); }, [industryStatApi]); const evalTypeRatioData = useMemo(() => { return (evalTypeRatioApi && evalTypeRatioApi.items && evalTypeRatioApi.items.length > 0) ? evalTypeRatioApi : mockEvalTypeToApiFormat(); }, [evalTypeRatioApi]); const projectExecutionData = useMemo(() => { return (projectExecutionApi && projectExecutionApi.list && projectExecutionApi.list.length > 0) ? projectExecutionApi : mockExecutionToApiFormat(); }, [projectExecutionApi]); const legendColors = useMemo(() => ({ project: '#4285f4', statutory: '#ffab31', }), []); return (
{/* ===== 第一行:项目节点统计 + 通知提醒 ===== */}

当前项目节点统计

项目总数:{projectSummary.total} 法定项目:{projectSummary.statutory}
{statusCards.map(item => ( ))}

通知提醒

{alertItems.map(item => ( ))}
{/* ===== 第二行:服务行业项目统计 + 评价类别占比 ===== */}

服务行业项目统计

项目数 法定项目

评价类别占比

{/* ===== 第三行:项目执行情况 ===== */} {}} />
); }