Commit 05865f2c authored by Andy-bubu's avatar Andy-bubu

feat: add dedicated pie chart palette

parent ac1721ba
......@@ -21,6 +21,8 @@ app.js (固定浅色主题初始化)
- `src/config/application.js`:稳定 `APPLICATION_NAME`、展示名和 base;路由、应用切换与授权判断从同一应用标识派生。
- `src/design-system/`:权威主题、Tokens 和跨应用 Shell 样式;页面不得复制这些规则。
- `src/design-system/chartPalette.js`:只为 Pie/Donut 解析独立 `--rc-pie-chart-*` Token,并按完整稳定分类 key 域确定性消解 16 色内的冲突;不替代普通图表 `chartTokens.js`。
- `src/components/XinyuanEChart.jsx`:业务中立的 ECharts 边界,仅对 `series.type === 'pie'` 注入专用扇区色、同色引导线、间隙和圆角,同时保留 radius、center、label、legend、tooltip、事件、ARIA、resize 与键盘焦点;非 Pie series 不改色。
- `AppShell/DashboardSidebar/DashboardNavbar/ApplicationSwitcherDialog`:业务无关的共享壳层。
- `ApplicationSwitcherDialog` 从既有 `SiteContext.site.group` 读取动态分组,从 `GET /application/info` 与 `UserContext.applications` 合并真实可访问应用;分组仅影响呈现,不改变路由、会话或跳转。
- `SubjectWorkbench`:把现有 Subject/View/动作配置适配为工作台 props,不定义组件外观。
......@@ -45,6 +47,8 @@ Grid View 的 `viewAuth` 在查询前过滤;没有可访问 View 时数据链
根主题固定为 `light`,内容画布保持浅色,Sidebar 通过独立 Token 保持深色;Portal 跟随内容区主题。应用切换数据来自真实 application info;登出调用现有 logout API 后清理登录态并回到 base-aware 登录路径。
Pie/Donut 是普通图表色板之外的独立例外:前六个 `--xinyuan-pie-chart-*` 为品牌红、灰蓝、灰紫、暖金、暖棕、石板灰,扩展色保持低饱和且无绿色。适配器覆盖页面旧扇区颜色,并让 ECharts 的 Legend/Tooltip 标记跟随数据项视觉色;普通 Bar/Line/Ranking、KPI、HeroUI Primary 与页面 Surface 不读取该色板。
## 历史源码隔离
`.umirc.ts` 只挂载 Login、Layout 和七个 SubjectWorkbench 路由。`src/pages/problem_rectification`、`src/catalyst-ui-kit` 与 demo services 是仓库历史源码,不属于当前运行图。审计必须分别报告“活动路由扫描”和“全仓扫描”,不能用历史命中否定活动迁移,也不能宣称全仓已清零。
......
......@@ -23,6 +23,7 @@
- [x] 新增记录 Modal:4xl 居中弹窗、blur Backdrop、标准关闭按钮、可滚动 Body、两列动态表单和次级/主操作层级
- [x] Select、Autocomplete、DatePicker、DropZone 直接 compound API
- [x] 活动路由 KPI/图表判定为不适用
- [x] Pie/Donut 独立 16 色 Token 与共享 ECharts 适配器已按问题整改基准预置;活动路由无真实饼图,未制造页面或业务数据
- [x] Grid `shortTitle` 严格布尔隐藏标题展示列,标题数据与查询协议保留
- [x] Detail `shortTitle` 随活动详情 View 隐藏记录标题与 title 字段
- [x] View/字段授权只读取 `integrity_practice_management` 应用项;零 View 不查询、不展示操作,单 View 隐藏 Tabs
......@@ -76,6 +77,7 @@
- [ ] 全仓旧组件扫描为零:未完成。未挂载历史 `src/pages/problem_rectification` 与 `src/catalyst-ui-kit` 仍保留,且不进入当前应用路由。
- [ ] KPI/图表 Tooltip 页面验收:不适用。七条活动路由没有这些业务模块,未伪造数据或页面。
- [ ] Pie/Donut 真实数据像素验收:待活动业务提供真实图表后验证 2/3/4/6/6+ 分类、Tooltip、Legend 与筛选/排序稳定性;当前仅完成共享映射自动检查和无业务页面回归。
- [ ] 五项功能全部真实线上失败/重试矩阵:未完成。当前列表、详情和通知接口可用,但没有制造材料部分成功、业务保存失败或通知 1/99/100 的业务数据;不得以浏览器夹具替代真实线上状态验收。
## 2026-08-26 五项功能追加验收
......
......@@ -16,7 +16,10 @@ Inter 禁止通过 `src/tailwind.css` 导入。Tailwind v4 内容扫描必须排
## 品牌与图表色板
- 鑫元品牌主色固定为 `#DA241A`。HeroUI v3 的 `--accent`、`--accent-hover`、`--focus` 以及共享 `--rc-brand-primary` 均不得被页面或深色主题改写为其他色相;primary 色阶仅作为兼容别名维护。
- 图表统一使用 `--rc-chart-1` 至 `--rc-chart-16`,由 `src/design-system/chartTokens.js` 提供 ECharts 的解析值。顺序为品牌红、低饱和暖色、灰紫中性色和蓝灰色,禁止绿色、荧光色和彩虹式相邻对撞;单系列始终使用 `chart-1`。
- 普通 Bar / Line / Ranking 图表继续使用 `--xinyuan-chart-1` 至 `--xinyuan-chart-16` 及其 `--rc-chart-*` 别名,现有值、顺序和语义映射保持不变;单系列始终使用 `chart-1`。
- Pie / Donut 独立使用 `--xinyuan-pie-chart-1` 至 `--xinyuan-pie-chart-16` 及其 `--rc-pie-chart-*` 别名。前六色固定为 `#DA241A`、`#3F5F86`、`#8A668F`、`#C28A3D`、`#8A6253`、`#667080`;后续仅使用批准的低饱和灰蓝、灰紫、暖棕和暖灰,不得出现绿色、青绿色、荧光色或彩虹序列。
- Pie / Donut 必须通过 `XinyuanEChart` 和 `createResolvedXinyuanPieChartColorMap()` 按稳定分类 key 确定性分配颜色,同图 16 类以内冲突消解且不重复。筛选、排序、Tab 或主题切换不得按当前数组下标重新定义分类颜色;扇区、引导线、Legend 和 Tooltip 圆点使用同一视觉色。
- 饼图专用色板不影响 HeroUI 主色、KPI 或普通图表;未来新增饼图/环形图禁止在页面写 `color`、扇区 `itemStyle.color` 或统一灰色 `labelLine.lineStyle.color`。
- KPI 图标色调复用同一图表色板的暖色/灰紫/蓝灰衍生值,卡片表面保持中性,不以高饱和渐变区分指标。
## 组件合同
......@@ -38,7 +41,7 @@ Inter 禁止通过 `src/tailwind.css` 导入。Tailwind v4 内容扫描必须排
- 通知:Navbar 直接组合 `Badge.Anchor + Badge + Button + Tooltip`,Bell 位于全局动作区;通知页使用平台真实接口和 HeroUI Tabs/SearchField/Pagination/Card。
- 文件:可编辑 file 字段统一使用 HeroUI Tabs、Autocomplete 和 Pro DropZone;材料中心选择及本地上传都只向业务草稿写 `{name,type,size,url}`。
- 远程引用:使用 HeroUI `Autocomplete`/`Popover.Root + Button + SearchField + controlled ListBox`。多选触发器固定 40px 高并只显示“已选择 N 项”,候选长标题单行省略且通过 `title` 暴露全文;弹层最大 24rem、移动端收敛到 `100vw - 24px`、列表最大 288px。多选后保持打开,单选后关闭;有值时显示官方 `Autocomplete.ClearButton`,清空分别回传空数组或原协议要求的空值,不改变 ID 保存协议。
- KPI/图表:仅在真实业务路由存在时使用 Pro KPI、PieChart、BarChart、ComposedChart、ChartTooltip;当前七条业务路由无 KPI/图表,不制造测试模块。
- KPI/图表:仅在真实业务路由存在时使用批准的共享实现;当前七条业务路由无 KPI/图表,不制造测试模块。未来的 ECharts Pie/Donut 直接使用 `src/components/XinyuanEChart.jsx`,图表外层仍由 HeroUI/HeroUI Pro Card 承载。
## 基准同步记录
......
......@@ -52,6 +52,8 @@
- 字段:HeroUI Input/TextArea/Checkbox/Select/Autocomplete/DatePicker,Pro DropZone;Select 选项仅来自 `settings.options`,用户/引用仅保存 ID,日期继续转 ISO。
- Portal/暗色:根主题固定 `light`,Sidebar 使用独立深色 Token;Modal、Dropdown、Autocomplete、Select、DatePicker 均继承内容区主题并实测打开态。
- KPI/图表:七条活动路由无 KPI 或图表模块,不适用;没有制造测试模块或业务数字。相关 Pro 子路径已验证可导入。
- Pie/Donut 专项(2026-09-08):活动路由仍无饼图,未新增演示页面或伪造数据。全仓仅 `src/pages/problem_rectification/dashboard/index.jsx` 的三处未挂载历史环图调用;其扇区颜色已移交共享 `XinyuanEChart`,页面旧 `color`、扇区色和统一灰色引导线覆盖已移除。新增独立 16 色 `--xinyuan-pie-chart-*` / `--rc-pie-chart-*`,普通 `--xinyuan-chart-*` / `--rc-chart-*` 的值、顺序和语义未改。
- Pie/Donut 颜色来源:`chartPalette.js` 先规范化并排序稳定分类 key,再以确定性哈希和线性冲突消解分配专用 Token;16 类以内同图不重复。该共享能力仅对 `series.type === 'pie'` 生效,未来活动业务出现真实饼图时必须直接接入,不得在页面恢复私有色板。
## 浏览器验收
......
import {
createResolvedXinyuanPieChartColorMap,
getXinyuanEChartsTheme,
} from '@/design-system/chartPalette';
import ReactECharts from 'echarts-for-react';
import { useCallback, useEffect, useMemo, useRef } from 'react';
function withoutColor(style) {
if (!style || typeof style !== 'object') return style;
const { color: _legacyColor, ...rest } = style;
return rest;
}
function applyPieTheme(series, theme) {
if (series?.type !== 'pie') return series;
const colorMap = createResolvedXinyuanPieChartColorMap(
Array.isArray(series.data) ? series.data.map((item) => item?.name) : [],
);
const data = Array.isArray(series.data)
? series.data.map((item, index) => {
if (!item || typeof item !== 'object' || Array.isArray(item)) return item;
const key = String(item.name ?? '').trim();
return {
...item,
itemStyle: {
...item.itemStyle,
borderColor: theme.sectorGap,
borderRadius: theme.sectorRadius,
borderWidth: theme.sectorGapWidth,
color: colorMap.get(key) || theme.piePalette[index % theme.piePalette.length],
},
};
})
: series.data;
return {
...series,
color: theme.piePalette,
itemStyle: {
...series.itemStyle,
borderColor: theme.sectorGap,
borderRadius: theme.sectorRadius,
borderWidth: theme.sectorGapWidth,
},
labelLine: series.labelLine ? {
...series.labelLine,
lineStyle: withoutColor(series.labelLine.lineStyle),
} : series.labelLine,
data,
padAngle: series.padAngle ?? 1,
};
}
export default function XinyuanEChart({
ariaLabel,
className = '',
height = 320,
onEvents,
option,
}) {
const chartRef = useRef(null);
const hideTooltip = useCallback(() => {
chartRef.current?.getEchartsInstance?.().dispatchAction({ type: 'hideTip' });
}, []);
useEffect(() => {
window.addEventListener('resize', hideTooltip);
return () => window.removeEventListener('resize', hideTooltip);
}, [hideTooltip]);
const themedOption = useMemo(() => {
const theme = getXinyuanEChartsTheme();
const series = Array.isArray(option?.series)
? option.series.map((item) => applyPieTheme(item, theme))
: applyPieTheme(option?.series, theme);
return {
animationDuration: 320,
animationDurationUpdate: 240,
animationEasing: 'cubicOut',
aria: {
enabled: true,
decal: { show: false },
...option?.aria,
},
color: option?.color || theme.palette,
...option,
series,
tooltip: option?.tooltip ? {
appendToBody: true,
backgroundColor: theme.tooltipBackground,
borderColor: theme.tooltipBorder,
borderWidth: 1,
className: 'rc-echarts-tooltip',
confine: true,
enterable: false,
padding: [10, 12],
renderMode: 'html',
...option.tooltip,
textStyle: {
color: theme.tooltipForeground,
fontFamily: theme.fontFamily,
fontSize: 12,
...option.tooltip?.textStyle,
},
} : undefined,
};
}, [option]);
return (
<div
aria-label={ariaLabel}
className={`rc-chart-interaction w-full min-w-0 ${className}`.trim()}
role="img"
tabIndex={0}
>
<ReactECharts
lazyUpdate
notMerge
onEvents={{ globalout: hideTooltip, ...onEvents }}
option={themedOption}
opts={{ renderer: 'canvas' }}
ref={chartRef}
style={{ height, width: '100%' }}
/>
</div>
);
}
export const XINYUAN_CHART_PALETTE = Object.freeze(
Array.from({ length: 16 }, (_, index) => `var(--rc-chart-${index + 1})`),
);
// Pie and donut slices need more hue separation than linear series. Keep this
// palette independent so bar/line/ranking charts retain their approved colors.
export const XINYUAN_PIE_CHART_PALETTE = Object.freeze(
Array.from({ length: 16 }, (_, index) => `var(--rc-pie-chart-${index + 1})`),
);
function normalizeChartKey(key) {
return String(key ?? '').trim();
}
function hashChartKey(key) {
return Array.from(key).reduce(
(value, character) => ((value * 31) + character.codePointAt(0)) >>> 0,
0,
);
}
export function getXinyuanPieChartColor(index = 0) {
return XINYUAN_PIE_CHART_PALETTE[
Math.max(0, Number(index) || 0) % XINYUAN_PIE_CHART_PALETTE.length
];
}
function readRootToken(tokenName, fallback = '') {
if (typeof window === 'undefined' || typeof document === 'undefined') {
return fallback;
}
return window.getComputedStyle(document.documentElement)
.getPropertyValue(tokenName)
.trim() || fallback;
}
const concreteColorCache = new Map();
function convertOklchToRgb(value) {
const match = String(value).trim().match(
/^oklch\(\s*([\d.]+)%?\s+([\d.]+)%?\s+([\d.-]+)(?:deg)?\s*\)$/i,
);
if (!match) return value;
const lightnessValue = Number(match[1]);
const chromaValue = Number(match[2]);
const lightness = lightnessValue > 1 ? lightnessValue / 100 : lightnessValue;
const chroma = chromaValue > 1 ? chromaValue / 100 : chromaValue;
const hue = (Number(match[3]) * Math.PI) / 180;
const a = chroma * Math.cos(hue);
const b = chroma * Math.sin(hue);
const l = (lightness + 0.3963377774 * a + 0.2158037573 * b) ** 3;
const m = (lightness - 0.1055613458 * a - 0.0638541728 * b) ** 3;
const s = (lightness - 0.0894841775 * a - 1.291485548 * b) ** 3;
const channels = [
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,
].map((channel) => {
const clamped = Math.max(0, Math.min(1, channel));
const encoded = clamped <= 0.0031308
? 12.92 * clamped
: 1.055 * (clamped ** (1 / 2.4)) - 0.055;
return Math.round(encoded * 255);
});
return `rgb(${channels.join(', ')})`;
}
function readConcreteColorToken(tokenName, fallback = '') {
const raw = readRootToken(tokenName, fallback);
const cached = concreteColorCache.get(tokenName);
if (cached?.raw === raw) return cached.value;
if (typeof document === 'undefined' || !raw) {
return raw || fallback;
}
const probe = document.createElement('span');
probe.style.color = raw;
if (!probe.style.color) return fallback;
probe.style.position = 'absolute';
probe.style.pointerEvents = 'none';
document.body?.appendChild(probe);
const resolved = convertOklchToRgb(window.getComputedStyle(probe).color || fallback);
probe.remove();
concreteColorCache.set(tokenName, { raw, value: resolved });
return resolved;
}
export function resolveXinyuanChartColor(color, fallback = '') {
const match = String(color || '').trim().match(/^var\((--[^)]+)\)$/);
return match
? readConcreteColorToken(match[1], fallback)
: String(color || fallback);
}
export function createResolvedXinyuanPieChartColorMap(keys = []) {
const stableKeys = [...new Set(keys.map(normalizeChartKey).filter(Boolean))].sort();
const colorMap = new Map();
const usedIndexes = new Set();
stableKeys.forEach((key) => {
const preferredIndex = hashChartKey(key) % XINYUAN_PIE_CHART_PALETTE.length;
let paletteIndex = preferredIndex;
for (let offset = 0; offset < XINYUAN_PIE_CHART_PALETTE.length; offset += 1) {
const candidate = (preferredIndex + offset) % XINYUAN_PIE_CHART_PALETTE.length;
if (!usedIndexes.has(candidate)) {
paletteIndex = candidate;
usedIndexes.add(candidate);
break;
}
}
colorMap.set(key, resolveXinyuanChartColor(getXinyuanPieChartColor(paletteIndex)));
});
return colorMap;
}
export function getXinyuanEChartsTheme() {
const documentFont = typeof document === 'undefined'
? 'Inter, sans-serif'
: window.getComputedStyle(document.body).fontFamily;
return {
axis: readRootToken('--xinyuan-chart-axis', '#737373'),
fontFamily: documentFont || 'Inter, sans-serif',
foreground: readConcreteColorToken(
'--foreground',
readRootToken('--xinyuan-chart-tooltip-foreground', '#222222'),
),
grid: readRootToken('--xinyuan-chart-grid', '#e8e8e8'),
label: readRootToken('--xinyuan-chart-label', '#737373'),
palette: Array.from({ length: 16 }, (_, index) => (
readConcreteColorToken(`--xinyuan-chart-${index + 1}`, '#667085')
)),
piePalette: Array.from({ length: 16 }, (_, index) => (
readConcreteColorToken(`--xinyuan-pie-chart-${index + 1}`, '#667080')
)),
sectorGap: readConcreteColorToken('--xinyuan-chart-sector-gap', '#ffffff'),
sectorGapWidth: Number.parseFloat(
readRootToken('--xinyuan-chart-sector-gap-width', '2'),
) || 2,
sectorRadius: Number.parseFloat(
readRootToken('--xinyuan-chart-sector-radius', '10'),
) || 10,
tooltipBackground: readRootToken('--xinyuan-chart-tooltip-background', '#ffffff'),
tooltipBorder: readRootToken('--xinyuan-chart-tooltip-border', '#dedede'),
tooltipForeground: readRootToken('--xinyuan-chart-tooltip-foreground', '#222222'),
tooltipShadow: readRootToken(
'--xinyuan-chart-tooltip-shadow',
'0 16px 40px rgb(15 23 42 / 14%)',
),
};
}
......@@ -91,7 +91,25 @@
--xinyuan-chart-14: #8A6F63;
--xinyuan-chart-15: #9A8174;
--xinyuan-chart-16: #B5A69D;
/* Pie / donut only. Keep this palette independent from linear charts. */
--xinyuan-pie-chart-1: #DA241A;
--xinyuan-pie-chart-2: #3F5F86;
--xinyuan-pie-chart-3: #8A668F;
--xinyuan-pie-chart-4: #C28A3D;
--xinyuan-pie-chart-5: #8A6253;
--xinyuan-pie-chart-6: #667080;
--xinyuan-pie-chart-7: #74879A;
--xinyuan-pie-chart-8: #9A8174;
--xinyuan-pie-chart-9: #6F6674;
--xinyuan-pie-chart-10: #B87958;
--xinyuan-pie-chart-11: #61758F;
--xinyuan-pie-chart-12: #807783;
--xinyuan-pie-chart-13: #B0A4A0;
--xinyuan-pie-chart-14: #AFA5B1;
--xinyuan-pie-chart-15: #897A73;
--xinyuan-pie-chart-16: #556B88;
--xinyuan-chart-sector-gap: var(--surface);
--xinyuan-chart-sector-gap-width: 2px;
--xinyuan-chart-sector-radius: 10px;
--xinyuan-chart-grid: #e8e8e8;
--xinyuan-chart-axis: #737373;
......
......@@ -180,6 +180,23 @@
--rc-chart-14: var(--xinyuan-chart-14);
--rc-chart-15: var(--xinyuan-chart-15);
--rc-chart-16: var(--xinyuan-chart-16);
/* Pie / donut series are intentionally separate from linear chart colors. */
--rc-pie-chart-1: var(--xinyuan-pie-chart-1);
--rc-pie-chart-2: var(--xinyuan-pie-chart-2);
--rc-pie-chart-3: var(--xinyuan-pie-chart-3);
--rc-pie-chart-4: var(--xinyuan-pie-chart-4);
--rc-pie-chart-5: var(--xinyuan-pie-chart-5);
--rc-pie-chart-6: var(--xinyuan-pie-chart-6);
--rc-pie-chart-7: var(--xinyuan-pie-chart-7);
--rc-pie-chart-8: var(--xinyuan-pie-chart-8);
--rc-pie-chart-9: var(--xinyuan-pie-chart-9);
--rc-pie-chart-10: var(--xinyuan-pie-chart-10);
--rc-pie-chart-11: var(--xinyuan-pie-chart-11);
--rc-pie-chart-12: var(--xinyuan-pie-chart-12);
--rc-pie-chart-13: var(--xinyuan-pie-chart-13);
--rc-pie-chart-14: var(--xinyuan-pie-chart-14);
--rc-pie-chart-15: var(--xinyuan-pie-chart-15);
--rc-pie-chart-16: var(--xinyuan-pie-chart-16);
--rc-chart-primary: var(--rc-chart-1);
--rc-chart-risk: var(--rc-chart-1);
--rc-chart-warning: var(--rc-chart-3);
......
......@@ -15,6 +15,7 @@ import {
TableHeader,
TableRow,
} from '@/catalyst-ui-kit/javascript/table';
import XinyuanEChart from '@/components/XinyuanEChart';
import ReactECharts from 'echarts-for-react';
import clsx from 'clsx';
import dayjs from 'dayjs';
......@@ -179,9 +180,8 @@ function FocusIssueDetailDialog({ issueType, items = [], onClose }) {
);
}
function buildDonutOption(data, colors) {
function buildDonutOption(data) {
return {
color: colors,
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(15,23,42,0.9)',
......@@ -211,9 +211,7 @@ function buildDonutOption(data, colors) {
c: { fontSize: 11, color: '#64748b' },
},
},
labelLine: {
lineStyle: { color: '#cbd5e1' },
},
labelLine: { show: true },
data,
},
],
......@@ -571,7 +569,6 @@ function buildBubbleMatrixOption(data = []) {
function buildQualityRatingOption(data) {
return {
color: [CHART_COLOR_SET[10], CHART_COLOR_SET[5], CHART_COLOR_SET[0]],
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(15,23,42,0.9)',
......@@ -602,9 +599,7 @@ function buildQualityRatingOption(data) {
c: { fontSize: 12, color: '#64748b' },
},
},
labelLine: {
lineStyle: { color: '#cbd5e1' },
},
labelLine: { show: true },
data,
},
],
......@@ -679,7 +674,7 @@ export function ProblemRectificationDashboardPage({ dashboardMode = 'overview' }
};
const progressOption = useMemo(
() => buildDonutOption(summary.progressDistribution, CHART_COLOR_SET),
() => buildDonutOption(summary.progressDistribution),
[summary.progressDistribution],
);
const activeCategoryData = summary.categoryDistributions?.[activeCategoryLevel] || [];
......@@ -688,7 +683,7 @@ export function ProblemRectificationDashboardPage({ dashboardMode = 'overview' }
[activeCategoryData],
);
const rootCauseOption = useMemo(
() => buildDonutOption(summary.rootCauseDistribution, CHART_COLOR_SET),
() => buildDonutOption(summary.rootCauseDistribution),
[summary.rootCauseDistribution],
);
const sourceComparisonOption = useMemo(
......@@ -887,7 +882,11 @@ export function ProblemRectificationDashboardPage({ dashboardMode = 'overview' }
description="按整改进度统计数量与占比,直观看当前积压主要落在哪个状态。"
>
{summary.progressDistribution.length > 0 ? (
<ReactECharts option={progressOption} style={{ height: 340 }} />
<XinyuanEChart
ariaLabel="整改进度分布环形图"
height={340}
option={progressOption}
/>
) : (
<EmptyBlock title="暂无整改进度分布数据" />
)}
......@@ -942,7 +941,11 @@ export function ProblemRectificationDashboardPage({ dashboardMode = 'overview' }
description="按问题原因分类统计,便于识别制度、流程或执行层面的共性成因。"
>
{summary.rootCauseDistribution.length > 0 ? (
<ReactECharts option={rootCauseOption} style={{ height: 360 }} />
<XinyuanEChart
ariaLabel="问题根本原因分布环形图"
height={360}
option={rootCauseOption}
/>
) : (
<EmptyBlock title="暂无问题原因统计数据" />
)}
......@@ -1030,7 +1033,11 @@ export function ProblemRectificationDashboardPage({ dashboardMode = 'overview' }
description="按六项整改评估字段自动计算并展示高、中、低分布;任一负向维度为低,至少两个正向维度为高,其余有效结果为中。"
>
{summary.qualityRatingDistribution.length > 0 ? (
<ReactECharts option={qualityRatingOption} style={{ height: 380 }} />
<XinyuanEChart
ariaLabel="综合整改质效结果环形图"
height={380}
option={qualityRatingOption}
/>
) : (
<EmptyBlock title="暂无综合整改质效结果数据" />
)}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment