function kpiCard(label, value, unit, desc, trend, widthPercent, tone = "up") { /** * 集团级页面渲染模块。 * 负责集团总览、集团上线率卡片、企业分析、指标分析和集团上线统计弹窗。 */ return `
${trend}
${label}
${value}${unit}
${desc}
`; } /** * 集团项目上线率卡片。 * 数值来自 groupSummary,而 groupSummary 会优先读取 projectMasterDataState.summary; * 接口失败时 desc 展示错误原因,同时数值回退到本地兜底汇总。 */ function onlineKpiCard(summary) { const trendText = projectMasterDataState.loading ? "加载中" : `${summary.onlineRate.toFixed(2)}%`; const statusText = projectMasterDataState.error ? `接口失败,已使用本地兜底数据:${projectMasterDataState.error}` : "QHSE使用项目数 / 申请不用项目数 / 主数据项目数"; return `
${trendText}
集团项目上线率
${summary.onlineActual}/${summary.noNeedProjectCount}/${summary.onlineExpected}
${statusText}
`; } /** * 面包屑渲染。 * 根据 state.view 组合集团、企业、项目、指标穿透层级,并附带当前维度和系统,方便用户确认筛选上下文。 */ function renderBreadcrumbs() { const parts = [`集团总览`]; if (state.view === "enterprise" || state.view === "project") { parts.push(`›${state.enterpriseName}`); } if (state.view === "project") { const project = getProjectById(state.projectId); parts.push(`›${project ? project.name : "项目明细"}`); } if (state.view === "indicatorTrend" || state.view === "indicatorEnterprise" || state.view === "indicatorProject") { const indicator = getSelectedIndicator(); parts.push(`›${indicator.name}`); } if (state.view === "indicatorProject") { parts.push(`›${state.enterpriseName}`); } parts.push(`当前维度:${dimLabel()}`); parts.push(`当前系统:${systemLabel()}`); breadcrumbs.innerHTML = parts.join(""); } /** * 全局主渲染入口。 * 事件模块只改 state,然后调用 render;这里集中决定当前应该渲染哪个视图,避免分散刷新局部 DOM。 */ function render() { applySystemVisibility(); document.querySelectorAll("[data-dim]").forEach((button) => { button.classList.toggle("active", button.dataset.dim === state.dimension); }); document.querySelectorAll("[data-system]").forEach((button) => { button.classList.toggle("active", button.dataset.system === state.system); }); renderBreadcrumbs(); if (state.view === "group") renderGroupView(); if (state.view === "enterprise") renderEnterpriseView(); if (state.view === "project") renderProjectView(); if (state.view === "indicatorTrend") app.innerHTML = renderIndicatorTrendView(); if (state.view === "indicatorProject") app.innerHTML = renderIndicatorProjectView(); renderDrillModal(); } /** * 集团总览主视图。 * 顶部卡片展示集团汇总,中部图表支持企业下钻,底部指标区支持指标穿透和集团报告预览。 */ function renderGroupView() { const summary = groupSummary(); const indicators = getWarningIndicators(); const activeEnterprises = enterprises.filter((enterprise) => enterprise.stats[state.dimension].projectCount > 0); const riskList = activeEnterprises .map((enterprise) => ({ enterprise, stats: enterprise.stats[state.dimension], score: riskScore(enterprise.stats[state.dimension]) })) .sort((a, b) => b.score - a.score); app.innerHTML = `
${onlineKpiCard(summary)} ${kpiCard("项目指标使用率", fmtPercent(summary.usageRate), "", "连续三天无更新视为未使用,用于发现用了但数据不动。", "+0.8%", summary.usageRate)} ${kpiCard("指标数据准确率", fmtPercent(summary.accuracyRate), "", "用于发现数据准不准。", "-0.6%", summary.accuracyRate, "down")} ${kpiCard("预警项目数", fmtNumber(summary.warningProjects), "个", `当前触发上线、使用、准确性风险的项目数,预警项 ${fmtNumber(summary.warningItems)} 条。`, "", clamp(100 - summary.warningProjects, 0, 100), "down")} ${kpiCard("预警企业", fmtNumber(summary.riskEnterprises), "家", "上线率、使用率、准确率任一维度触发预警的企业。", "督办", clamp(100 - summary.riskEnterprises * 10, 0, 100), "down")}
${renderFocusIndicators(indicators.slice(0, 8), 0, { highlightHigh: false })}

集团三率趋势曲线

近 7 日集团上线率、使用率、准确率变化。
上线 使用 准确
${renderGroupTrendChart(activeEnterprises)}

企业分析

按${dimLabel()}展示${systemLabel()}系统的企业上线率、使用率、准确率。点击企业柱子下钻到企业看板。
上线率 使用率 准确率
${renderGroupedRateChart(activeEnterprises, "enterprise", { height: 132 })}

集团在建项目使用分析报告

指标分析

${renderIndicatorAnalysis(indicators)}
`; } /** * 顶部重点指标条。 * 每张指标卡都带 data-indicator,事件代理会据此打开指标趋势或企业内项目穿透弹窗。 */ function renderFocusIndicators(items, offset, options = {}) { const highlightHigh = options.highlightHigh !== false; return items.map((item, index) => `
${item.name}
指标使用率:${item.usageRate}% 指标准确率:${indicatorAccuracy(item)}%
`).join(""); } /** * 指标分析中的迷你进度条。 * value 控制条宽,displayValue 控制右侧展示文案,便于同一组件同时展示百分比和项目数量。 */ function miniRate(label, value, color, displayValue = `${Math.round(value)}%`) { return `
${label} ${displayValue}
`; } /** * 集团指标分析列表。 * 以项目覆盖量、使用率、准确率三条迷你进度条展示指标风险,用于快速挑出需要穿透的指标。 */ function renderIndicatorAnalysis(indicators) { const maxProjects = Math.max(...indicators.map((item) => item.projects), 1); return `
${indicators.slice(0, 8).map((item) => { const level = item.usageRate < 65 ? { text: "重点督办", cls: "red" } : item.usageRate < 72 ? { text: "持续跟踪", cls: "orange" } : { text: "保持关注", cls: "green" }; return `
${item.name}
${miniRate("项目", Math.round((item.projects / maxProjects) * 100), COLORS.online, item.projects)} ${miniRate("使用", item.usageRate, COLORS.usage)} ${miniRate("准确", indicatorAccuracy(item), COLORS.accuracy)}
${level.text}
`; }).join("")}
`; } /** * 企业页指标分析数据派生。 * 在集团指标基础上叠加当前企业使用率和项目数量,生成更贴近企业维度的指标预警项。 */ function buildEnterpriseIndicatorItems(indicators, projects, stats) { const projectBase = Math.max(projects.length, 1); return indicators.slice(0, 8).map((item, index) => { const usageRate = clamp(Math.round(item.usageRate * 0.55 + stats.usageRate * 0.45 - (index % 3) * 2), 0, 100); const projectsCount = clamp(Math.round(projectBase * (0.55 + (index % 4) * 0.08)), 1, projectBase); const warningProjects = clamp(Math.round(projectsCount * (100 - usageRate) / 100 + (index % 2)), 0, projectsCount); return { ...item, projects: projectsCount, warningProjects, usageRate, accuracyRate: 0, missingUpdate: warningProjects + (index % 3), owner: ["安全管理部", "质量管理部", "项目管理部", "数据治理专员"][index % 4] }; }); } /** * 企业指标分析列表。 * 与集团指标列表共用展示结构,但风险文案更偏整改和跟踪,用于企业页面的责任短板展示。 */ function renderEnterpriseIndicatorAnalysis(items) { const maxProjects = Math.max(...items.map((item) => item.projects), 1); return `
${items.map((item) => { const level = item.warningProjects >= 6 || item.usageRate < 65 ? { text: "重点整改", cls: "red" } : item.warningProjects >= 3 || item.usageRate < 72 ? { text: "持续跟踪", cls: "orange" } : { text: "保持关注", cls: "green" }; return `
${item.name}
${miniRate("项目", Math.round((item.projects / maxProjects) * 100), COLORS.online, item.projects)} ${miniRate("使用", item.usageRate, COLORS.usage)} ${miniRate("准确", item.accuracyRate, COLORS.accuracy)}
${level.text}
`; }).join("")}
`; } function renderEnterpriseTable(riskList) { return `
${riskList.map(({ enterprise, stats, score }) => { const level = riskLevel(score); return ` `; }).join("")}
企业 项目数 上线率 使用率 准确率 预警项目 责任人 状态
${enterprise.name} ${stats.projectCount} ${fmtPercent(stats.onlineRate)} ${fmtPercent(stats.usageRate)} ${fmtPercent(stats.accuracyRate)} ${stats.warningProjects} ${enterprise.owner} ${level.text}
`; } function buildUsageReportRows(list) { return list.map((enterprise) => { const stats = enterprise.stats[state.dimension]; const usedProjects = Math.round(stats.projectCount * stats.usageRate / 100); return { name: enterprise.name, expected: stats.projectCount, used: usedProjects, usageRate: stats.projectCount ? Math.round(usedProjects / stats.projectCount * 100) : 0, trend: enterprise.trend.usage }; }); } /** * 集团上线统计弹窗表格。 * 表格行统一从 getProjectMasterRows 读取:接口成功展示接口行,接口失败展示本地兜底行。 * 国际、生态单位在这里加 green-row,高亮规则集中在表格渲染层。 */ function renderUsageReportTable(list) { const rows = getProjectMasterRows(); const headers = projectMasterDataState.loaded ? projectMasterDataHeaders : usageReportHeaders; const statusText = projectMasterDataState.loading ? `
正在获取接口数据...
` : projectMasterDataState.error ? `
接口请求失败,当前展示本地兜底数据:${projectMasterDataState.error}
` : ""; return ` ${statusText}
${headers.map((header) => ``).join("")} ${rows.map((row) => { const unit = row[1] || ""; const rowClass = [ row[0] === "合计" ? "summary-row" : "", ["国际", "生态"].includes(unit) ? "green-row" : "" ].filter(Boolean).join(" "); return ` ${row.map((cell, index) => ``).join("")} `; }).join("")}
${header}操作
${cell}${unit && rowClass.indexOf("summary-row") === -1 ? `` : ""}
`; } /** * “集团项目上线统计”弹窗内容。 * 弹窗标题由 project 模块 renderDrillModal 设置,这里只负责面板主体和表格。 */ function renderGroupOnlineStatsModal() { const activeEnterprises = enterprises.filter((enterprise) => enterprise.stats[state.dimension].projectCount > 0); return `

集团项目上线统计

${renderUsageReportTable(activeEnterprises)}
`; }