From 8fd4cca6526d5ad6bc1fe9e1909a38a6ba1d2548 Mon Sep 17 00:00:00 2001 From: fei550 <1420031550@qq.com> Date: Tue, 21 Jul 2026 23:25:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE=E6=B2=BB?= =?UTF-8?q?=E7=90=86=E2=80=9C=E7=94=A8=E4=B8=8D=E7=94=A8/=E5=8A=A8?= =?UTF-8?q?=E4=B8=8D=E5=8A=A8=E2=80=9D=E5=88=86=E6=9E=90=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next/README.md | 5 + next/src/adapters/dashboard.test.ts | 46 +++++++- next/src/adapters/dashboard.ts | 80 +++++++++++++ next/src/components/DrillModal.vue | 175 ++++++++++++++++++++-------- next/src/config/dashboard.ts | 2 + next/src/stores/dashboard.ts | 51 +++++++- next/src/styles/next.css | 31 +++++ next/src/types/dashboard.ts | 25 +++- next/src/views/GroupView.vue | 30 +++-- 9 files changed, 377 insertions(+), 68 deletions(-) diff --git a/next/README.md b/next/README.md index 7776b20..fd178c0 100644 --- a/next/README.md +++ b/next/README.md @@ -71,6 +71,7 @@ next/ - 监控状态:集团或当前公司的 `monitoring`、`loading`、`error`。 - 项目状态:`projectData`、`projectLoading`、`projectError`。 - 穿透状态:`dialog`、`selectedMetric`、`metricAnalysis`、`metricLoading`。 +- 动不动报告状态:`moveReport`、`moveReportLoading`、`moveReportError`。 集团和公司监控共用 `loadMonitoring`。新请求开始时会取消旧请求,并通过递增请求序号阻止旧响应覆盖最新筛选结果。项目详情和指标穿透分别使用独立 Action,避免页面组件自行维护请求状态。 @@ -81,6 +82,8 @@ next/ - `GetMetricMonitoringSituation`:集团监控。 - `GetUnitMetricMonitoringSituation`:公司监控。 - `GetGroupMetricAnalysis`:集团指标穿透。 +- `GetGroupMoveReportMetricData`:动不动报告动态指标列,`type=0/1` 分别对应安全/质量。 +- `GetGroupMetricAnalysisMoveReport`:动不动报告企业行,`range=0/1` 分别对应所有项目/新开工项目。 - `GetUnitMetricAnalysis`:公司指标穿透。 - `GetProjectMonitoringSituation`:项目详情。 @@ -94,6 +97,8 @@ next/ 组件只能读取适配后的 ViewModel。新增或调整接口字段时,应先修改 `types/` 和 `adapters/`,不要在 Vue 模板中增加字段兼容分支。 +动不动报告由两个接口共同组成:指标定义接口决定动态列顺序和中文名称,企业行接口通过 `MetricAnalysis[].Field` 绑定每列使用率。若指标定义接口临时返回空字段,适配层会使用当前监控接口的指标名称兜底,并对重复 `Field` 去重,避免生成空表头或重复列。 + ### 图表与大屏布局 `components/EChart.vue` 统一负责 ECharts 初始化、响应式更新、`ResizeObserver` 监听和 `dispose`。当前只注册柱状图、折线图、网格、图例、提示框和 Canvas 渲染器。 diff --git a/next/src/adapters/dashboard.test.ts b/next/src/adapters/dashboard.test.ts index 965a74b..463fe6d 100644 --- a/next/src/adapters/dashboard.test.ts +++ b/next/src/adapters/dashboard.test.ts @@ -1,6 +1,6 @@ /** 数据适配层回归测试:锁定接口兼容规则和核心统计口径。 */ import { describe, expect, it } from "vitest"; -import { normalizeMonitoring } from "./dashboard"; +import { normalizeMonitoring, normalizeMoveReport } from "./dashboard"; describe("normalizeMonitoring", () => { it("按业务口径归一化集团汇总、单位和指标数据", () => { @@ -33,4 +33,48 @@ describe("normalizeMonitoring", () => { expect(result.summary.onlineRate).toBe(0); expect(result.trend.dates).toEqual([]); }); + + it("按指标定义顺序生成动不动报告动态列并绑定使用率", () => { + const result = normalizeMoveReport( + [ + { Name: "安全检查", Field: "CheckNum" }, + { Name: "培训人次", Field: "TrainPersonNum" }, + { Name: "重复列", Field: "CheckNum" }, + ], + [{ + UnitName: "测试企业", + PassProjectNum: 8, + NotPassProjectNum: 2, + UsageRate: "80%", + MetricAnalysis: [ + { Field: "CheckNum", UsageRate: "75%" }, + { Field: "TrainPersonNum", UsageRate: 60 }, + ], + }], + ); + + expect(result.metrics).toEqual([ + { name: "安全检查", field: "CheckNum" }, + { name: "培训人次", field: "TrainPersonNum" }, + ]); + expect(result.rows[0]).toMatchObject({ + unitName: "测试企业", + passProjectNum: 8, + usageRate: "80%", + metricRates: { CheckNum: "75%", TrainPersonNum: "60%" }, + }); + }); + + it("指标定义接口为空时使用监控指标名称兜底", () => { + const result = normalizeMoveReport( + [{ Name: "", Field: "" }], + [{ UnitName: "测试企业", MetricAnalysis: [{ Field: "SafeTrainNum", UsageRate: "10%" }] }], + [{ + name: "安全教育培训人次", field: "SafeTrainNum", projects: 0, projectUsageNum: 0, + usageRate: 0, accuracyRate: 0, frequency: 0, sortIndex: 1, + }], + ); + + expect(result.metrics).toEqual([{ name: "安全教育培训人次", field: "SafeTrainNum" }]); + }); }); diff --git a/next/src/adapters/dashboard.ts b/next/src/adapters/dashboard.ts index e4d883c..fb2c519 100644 --- a/next/src/adapters/dashboard.ts +++ b/next/src/adapters/dashboard.ts @@ -7,6 +7,7 @@ import type { MasterRecordVM, MetricVM, MonitoringVM, + MoveReportVM, ProjectAnalysisVM, SummaryVM, UnitAnalysisVM, @@ -51,6 +52,8 @@ function normalizeRecord(item: Raw, index: number): MasterRecordVM { total: numberValue(value(item, "Total", "total")), onlineAutoTotal: numberValue(value(item, "YiShangXian", "yiShangXian")), onlineReportedTotal: numberValue(value(item, "TianBao", "tianBao")), + offlineTotal: numberValue(value(item, "WeiShangXian", "weiShangXian")), + closedRequestTotal: numberValue(value(item, "ShenQingGuanBi", "shenQingGuanBi")), masterProjectCount: numberValue(value(item, "MasterTotal", "masterTotal", "masterProjectCount")), qhseProjectCount: numberValue(value(item, "QHSETotal", "qhseTotal", "qhseProjectCount")), linkedTotal: numberValue(value(item, "ZhengChang", "zhengChang")), @@ -58,6 +61,7 @@ function normalizeRecord(item: Raw, index: number): MasterRecordVM { noNeedTotal: numberValue(value(item, "BuYong", "buYong")), noNeedCompletedTotal: numberValue(value(item, "BuYongYiWanGong", "buYongYiWanGong")), noNeedStoppedTotal: numberValue(value(item, "BuYongTingHuanJian", "buYongTingHuanJian")), + noNeedInapplicableTotal: numberValue(value(item, "BuYongBuShiYong", "buYongBuShiYong")), noNeedRestrictedTotal: numberValue(value(item, "BuYongTJShouXian", "buYongTJShouXian")), preProjectTotal: numberValue(value(item, "YuLiXiang", "yuLiXiang")), onlineRate: numberValue(value(item, "OnlineRate", "OnlinRate", "onlineRate", "onlinRate")), @@ -156,3 +160,79 @@ export function normalizeMonitoring(input: unknown): MonitoringVM { } }; } + +const percentText = (input: unknown): string => { + const text = textValue(input).trim(); + if (!text) return "-"; + return text.endsWith("%") ? text : `${text}%`; +}; + +/** + * 组合“动不动”报告的指标定义和企业行。 + * 指标定义接口是动态表头的首选来源;若接口字段暂时为空,则使用监控接口指标名称补齐,最后才回退到字段代码。 + */ +export function normalizeMoveReport( + definitionInput: unknown, + rowInput: unknown, + fallbackMetrics: MetricVM[] = [], +): MoveReportVM { + const definitions = Array.isArray(definitionInput) ? definitionInput as Raw[] : []; + const rawRows = Array.isArray(rowInput) ? rowInput as Raw[] : []; + const fallbackNames = new Map(); + fallbackMetrics.forEach((metric) => { + if (metric.field && metric.name && !fallbackNames.has(metric.field)) { + fallbackNames.set(metric.field, metric.name); + } + }); + + // 行接口决定实际需要展示的字段集合,避免定义接口异常时渲染整排空列。 + const rowFields: string[] = []; + const rowFieldSet = new Set(); + rawRows.forEach((row) => { + const analyses = value(row, "MetricAnalysis", "metricAnalysis"); + if (!Array.isArray(analyses)) return; + (analyses as Raw[]).forEach((item) => { + const field = textValue(value(item, "Field", "field")).trim(); + if (field && !rowFieldSet.has(field)) { + rowFieldSet.add(field); + rowFields.push(field); + } + }); + }); + + const metrics: MoveReportVM["metrics"] = []; + const metricFields = new Set(); + definitions.forEach((item) => { + const field = textValue(value(item, "Field", "field")).trim(); + const name = textValue(value(item, "Name", "name")).trim(); + if (!field || !name || metricFields.has(field)) return; + if (rowFieldSet.size && !rowFieldSet.has(field)) return; + metricFields.add(field); + metrics.push({ field, name }); + }); + rowFields.forEach((field) => { + if (metricFields.has(field)) return; + metricFields.add(field); + metrics.push({ field, name: fallbackNames.get(field) || field }); + }); + + const rows = rawRows.map((item) => { + const metricRates: Record = {}; + const analyses = value(item, "MetricAnalysis", "metricAnalysis"); + if (Array.isArray(analyses)) { + (analyses as Raw[]).forEach((analysis) => { + const field = textValue(value(analysis, "Field", "field")).trim(); + if (field) metricRates[field] = percentText(value(analysis, "UsageRate", "usageRate")); + }); + } + return { + unitName: textValue(value(item, "UnitName", "unitName")), + passProjectNum: numberValue(value(item, "PassProjectNum", "passProjectNum")), + notPassProjectNum: numberValue(value(item, "NotPassProjectNum", "notPassProjectNum")), + usageRate: percentText(value(item, "UsageRate", "usageRate")), + metricRates, + }; + }).filter((item) => item.unitName); + + return { metrics, rows }; +} diff --git a/next/src/components/DrillModal.vue b/next/src/components/DrillModal.vue index 15b72d2..6a67b75 100644 --- a/next/src/components/DrillModal.vue +++ b/next/src/components/DrillModal.vue @@ -66,6 +66,7 @@ const unitChart = computed(() => ({ const title = computed(() => { const labels = { online: "集团项目上线统计", + moveReport: "数据治理“动不动”分析报告", belowStandard: "未达标项目清单", unitUsage: "项目指标使用率(使用率=100*活跃项目/QHSE在建项目;项目中18个指标有一个指标有异动则视为活跃项目)", groupMetric: "指标穿透分析", @@ -74,20 +75,29 @@ const title = computed(() => { return store.dialog ? labels[store.dialog] : ""; }); +const dialogLoading = computed(() => + store.dialog === "moveReport" ? store.moveReportLoading : store.metricLoading, +); +const dialogError = computed(() => + store.dialog === "moveReport" ? store.moveReportError : store.metricError, +); +const loadingText = computed(() => + store.dialog === "moveReport" ? "正在获取动不动分析数据..." : "正在获取指标穿透数据...", +); +const onlineRateTotal = computed(() => { + const total = sum("total"); + return total ? sum("onlineAutoTotal") / total * 100 : 0; +}); +const moveTableMinWidth = computed(() => + Math.max(980, 420 + store.moveReport.metrics.length * 138), +); + const sum = (field: keyof MasterRecordVM) => store.monitoring.records.reduce( (total, row) => total + numberValue(row[field]), 0, ); -// “应使用项目数-条件受限”沿用原版表格口径,不能与“申请不用-条件受限”混用。 -const restricted = (row: MasterRecordVM) => - Math.max( - row.total - row.linkedTotal - row.preProjectTotal - row.pendingTotal, - 0, - ); -const restrictedTotal = computed(() => - store.monitoring.records.reduce((total, row) => total + restricted(row), 0), -); +const dateText = (value: string) => value ? value.split("T")[0] : "-"; function previewUnitReport(unit: string) { // 单位简称存在历史别名,先归一化再匹配固定文件名,避免按名称模糊拼接到错误文件。 @@ -132,17 +142,17 @@ function previewUnitReport(unit: string) { type="button" class="modal-close" aria-label="关闭" - @click="store.dialog = null" + @click="store.closeDialog()" > ×
-
- 正在获取指标穿透数据... +
+ {{ loadingText }}
-
- {{ store.metricError }} +
+ {{ dialogError }}
- 企业简称 - 上线项目数 - 上线率 - 应使用项目数 - 申请不用 - 主数据项目数 + 单位名称 + 项目上线率(%) + 应使用项目 + 已上线项目 + 未上线项目 + 申请关闭 + 主数据项目 + 上报日期 + 详情 - 自动取值 - 定期填报 总数 - 已关联项目 - 预立项项目 - 待建项目 - 条件受限 + 已关联 + 待关联 总数 - 已完工 + 待上线 停缓建 条件受限 + 总数 + 已完工/撤场 + 不适用 @@ -183,6 +195,21 @@ function previewUnitReport(unit: string) { row.name.includes('国际') || row.name.includes('生态'), }" > + {{ row.name }} + {{ row.onlineRate.toFixed(2) }}% + {{ row.total }} + {{ row.onlineAutoTotal }} + {{ row.linkedTotal }} + {{ row.preProjectTotal }} + {{ row.offlineTotal }} + {{ row.pendingTotal }} + {{ row.noNeedStoppedTotal }} + {{ row.noNeedRestrictedTotal }} + {{ row.closedRequestTotal }} + {{ row.noNeedCompletedTotal }} + {{ row.noNeedInapplicableTotal }} + {{ row.masterProjectCount }} + {{ dateText(row.reportDate) }} {{ row.name }} + >查看 + - - {{ row.onlineAutoTotal }} - {{ row.onlineReportedTotal }} - {{ row.onlineRate.toFixed(2) }}% - {{ row.total }} - {{ row.linkedTotal }} - {{ row.preProjectTotal }} - {{ row.pendingTotal }} - {{ restricted(row) }} - {{ row.noNeedTotal }} - {{ row.noNeedCompletedTotal }} - {{ row.noNeedStoppedTotal }} - {{ row.noNeedRestrictedTotal }} - {{ row.masterProjectCount }} 合计 - {{ sum("onlineAutoTotal") }} - {{ sum("onlineReportedTotal") }} - - {{ store.monitoring.summary.onlineRate.toFixed(2) }}% - + {{ onlineRateTotal.toFixed(2) }}% {{ sum("total") }} + {{ sum("onlineAutoTotal") }} {{ sum("linkedTotal") }} {{ sum("preProjectTotal") }} + {{ sum("offlineTotal") }} {{ sum("pendingTotal") }} - {{ restrictedTotal }} - {{ sum("noNeedTotal") }} - {{ sum("noNeedCompletedTotal") }} {{ sum("noNeedStoppedTotal") }} {{ sum("noNeedRestrictedTotal") }} - {{ sum("masterProjectCount") }} + {{ sum("closedRequestTotal") }} + {{ sum("noNeedCompletedTotal") }} + {{ sum("noNeedInapplicableTotal") }} + {{ sum("masterProjectCount") }} + - + - + + + +
+ + +
+
+ 当前暂无动不动分析数据。 +
+
+ + + + + + + + + + + + + + + + + + + + +
企业简称上线项目数使用率 + 核心指标使用率 +
达标项目数未达标项目 + {{ metricItem.name }} +
{{ row.unitName }}{{ row.passProjectNum }}{{ row.notPassProjectNum }}{{ row.usageRate }} + {{ row.metricRates[metricItem.field] || "-" }} +
diff --git a/next/src/config/dashboard.ts b/next/src/config/dashboard.ts index 3306c47..379736d 100644 --- a/next/src/config/dashboard.ts +++ b/next/src/config/dashboard.ts @@ -11,6 +11,8 @@ export const API_ENDPOINTS = { monitoring: `${API_BASE}/GetMetricMonitoringSituation`, unitMonitoring: `${API_BASE}/GetUnitMetricMonitoringSituation`, groupMetric: `${API_BASE}/GetGroupMetricAnalysis`, + groupMoveReportMetrics: `${API_BASE}/GetGroupMoveReportMetricData`, + groupMoveReport: `${API_BASE}/GetGroupMetricAnalysisMoveReport`, unitMetric: `${API_BASE}/GetUnitMetricAnalysis`, projectMonitoring: `${API_BASE}/GetProjectMonitoringSituation` } as const; diff --git a/next/src/stores/dashboard.ts b/next/src/stores/dashboard.ts index 7dd82db..ecd0e1c 100644 --- a/next/src/stores/dashboard.ts +++ b/next/src/stores/dashboard.ts @@ -5,11 +5,12 @@ import { computed, ref } from "vue"; import { defineStore } from "pinia"; import { API_ENDPOINTS, SYSTEMS, type DimensionKey, type SystemKey } from "../config/dashboard"; -import { normalizeMonitoring } from "../adapters/dashboard"; +import { normalizeMonitoring, normalizeMoveReport } from "../adapters/dashboard"; import { getJson } from "../services/http"; -import type { DialogKind, MasterRecordVM, MonitoringVM } from "../types/dashboard"; +import type { DialogKind, MasterRecordVM, MonitoringVM, MoveReportVM } from "../types/dashboard"; const emptyMonitoring = (): MonitoringVM => normalizeMonitoring({}); +const emptyMoveReport = (): MoveReportVM => ({ metrics: [], rows: [] }); function allowedSystemsFromUrl(): SystemKey[] { // 兼容外部系统沿用的 ?type=1,2 参数;非法值回退为全部系统。 @@ -36,8 +37,13 @@ export const useDashboardStore = defineStore("dashboard", () => { const metricAnalysis = ref | null>(null); const metricLoading = ref(false); const metricError = ref(""); + const moveReport = ref(emptyMoveReport()); + const moveReportLoading = ref(false); + const moveReportError = ref(""); let monitoringRequest = 0; let monitoringController: AbortController | null = null; + let moveReportRequest = 0; + let moveReportController: AbortController | null = null; const apiType = computed(() => SYSTEMS.find((item) => item.key === system.value)?.type ?? 0); const apiRange = computed(() => dimension.value === "new" ? 1 : 0); @@ -103,7 +109,45 @@ export const useDashboardStore = defineStore("dashboard", () => { } } + async function openMoveReport() { + // 动态表头和企业数据必须使用同一组 type/range 快照,防止切换筛选时新旧结果混用。 + const request = ++moveReportRequest; + moveReportController?.abort(); + moveReportController = new AbortController(); + dialog.value = "moveReport"; + moveReportLoading.value = true; + moveReportError.value = ""; + moveReport.value = emptyMoveReport(); + try { + const [definitions, rows] = await Promise.all([ + getJson(API_ENDPOINTS.groupMoveReportMetrics, { + type: apiType.value, + }, { signal: moveReportController.signal }), + getJson(API_ENDPOINTS.groupMoveReport, { + type: apiType.value, + range: apiRange.value, + }, { signal: moveReportController.signal }), + ]); + if (request !== moveReportRequest) return; + moveReport.value = normalizeMoveReport(definitions.data, rows.data, allMetrics.value); + } catch (reason) { + if (request !== moveReportRequest) return; + moveReportError.value = reason instanceof Error ? reason.message : "动不动分析报告加载失败"; + } finally { + if (request === moveReportRequest) moveReportLoading.value = false; + } + } + + function closeDialog() { + if (dialog.value === "moveReport") { + moveReportRequest += 1; + moveReportController?.abort(); + } + dialog.value = null; + } + function setFilters(nextSystem: SystemKey, nextDimension: DimensionKey) { + closeDialog(); system.value = nextSystem; dimension.value = nextDimension; } @@ -115,6 +159,7 @@ export const useDashboardStore = defineStore("dashboard", () => { return { allowedSystems, system, dimension, loading, error, monitoring, currentUnitId, currentUnitName, projectData, projectLoading, projectError, dialog, selectedMetric, selectedMetricVM, metricAnalysis, - metricLoading, metricError, allMetrics, loadMonitoring, loadProject, openMetric, setFilters, findRecord + metricLoading, metricError, moveReport, moveReportLoading, moveReportError, allMetrics, + loadMonitoring, loadProject, openMetric, openMoveReport, closeDialog, setFilters, findRecord }; }); diff --git a/next/src/styles/next.css b/next/src/styles/next.css index 144c4d5..aef1b06 100644 --- a/next/src/styles/next.css +++ b/next/src/styles/next.css @@ -15,7 +15,19 @@ .usage-dot { background: #5ee1b5; } .accuracy-dot { background: #ffc857; } .trend-panel .chart-box, .enterprise-overview .chart-box { width: 100%; min-width: 0; } +.group-left-stack { grid-template-rows: minmax(0, 1fr) repeat(2, auto); } .group-left-stack .usage-report-panel { width: 100%; margin: 0; } +.group-left-stack .report-entry-panel { + min-height: 62px; + padding: 12px 16px; + border: 1px solid rgba(91, 180, 255, 0.28); + color: inherit; + background: rgba(4, 35, 64, 0.72); + font: inherit; + text-align: left; +} +.report-entry-panel .panel-header { align-items: center; margin: 0; } +.report-entry-panel:hover { border-color: rgba(91, 214, 255, 0.58); background: rgba(10, 57, 91, 0.86); } /* 全局穿透弹窗和表格状态。 */ .drill-modal { display: grid; } .drill-modal[hidden] { display: none; } @@ -24,6 +36,25 @@ .table-link { color: var(--cyan); } .chart-click { cursor: pointer; } +/* “动不动”报告按接口指标数量横向扩展,固定列和动态列保持稳定宽度。 */ +.move-report-panel .table-wrap { max-height: 610px; } +.move-report-table { width: 100%; table-layout: fixed; } +.move-report-table th, +.move-report-table td { min-width: 118px; text-align: center; } +.move-report-table th { + white-space: normal; + line-height: 1.35; + border: 1px solid rgba(186, 232, 255, 0.62); + background: linear-gradient(180deg, #1c99d9, #054876); +} +.move-report-table thead tr:first-child th { top: 0; z-index: 4; } +.move-report-table thead tr:nth-child(2) th { top: 40px; z-index: 3; } +.move-report-table .move-metric-group { text-align: center; } +.move-metric-group span { position: static; display: inline-block; } +.move-report-table th:first-child, +.move-report-table td:first-child { width: 112px; min-width: 112px; } +.move-report-table tbody tr:first-child td { color: #ffffff; font-weight: 900; background: rgba(73, 214, 255, 0.14); } + @media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; animation-duration: .01ms !important; } } diff --git a/next/src/types/dashboard.ts b/next/src/types/dashboard.ts index ce39fee..c5bfd7b 100644 --- a/next/src/types/dashboard.ts +++ b/next/src/types/dashboard.ts @@ -36,6 +36,8 @@ export interface MasterRecordVM { total: number; onlineAutoTotal: number; onlineReportedTotal: number; + offlineTotal: number; + closedRequestTotal: number; masterProjectCount: number; qhseProjectCount: number; linkedTotal: number; @@ -43,6 +45,7 @@ export interface MasterRecordVM { noNeedTotal: number; noNeedCompletedTotal: number; noNeedStoppedTotal: number; + noNeedInapplicableTotal: number; noNeedRestrictedTotal: number; preProjectTotal: number; onlineRate: number; @@ -51,6 +54,26 @@ export interface MasterRecordVM { sortIndex: number; } +/** “动不动”报告的动态指标列定义。 */ +export interface MoveReportMetricVM { + name: string; + field: string; +} + +/** “动不动”报告的企业行,动态指标按字段名存入 metricRates。 */ +export interface MoveReportRowVM { + unitName: string; + passProjectNum: number; + notPassProjectNum: number; + usageRate: string; + metricRates: Record; +} + +export interface MoveReportVM { + metrics: MoveReportMetricVM[]; + rows: MoveReportRowVM[]; +} + /** 集团或公司级顶部汇总模型。 */ export interface SummaryVM { onlineActual: number; @@ -136,4 +159,4 @@ export interface AppFilters { dimension: DimensionKey; } -export type DialogKind = "online" | "belowStandard" | "unitUsage" | "groupMetric" | "unitMetric" | null; +export type DialogKind = "online" | "moveReport" | "belowStandard" | "unitUsage" | "groupMetric" | "unitMetric" | null; diff --git a/next/src/views/GroupView.vue b/next/src/views/GroupView.vue index 16d8dca..812fc09 100644 --- a/next/src/views/GroupView.vue +++ b/next/src/views/GroupView.vue @@ -220,21 +220,27 @@ onMounted(() => store.loadMonitoring());
暂无企业分析数据
-
-

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

- +

数据治理“用不用”分析报告

+ 查看
-
+ +
@@ -249,7 +255,7 @@ onMounted(() => store.loadMonitoring());