Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
problem_rectification
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
problem_rectification
Commits
9ad0e27c
Commit
9ad0e27c
authored
Sep 08, 2026
by
Andy-bubu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add dedicated pie chart color palette
parent
5b7bbdec
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
109 additions
and
18 deletions
+109
-18
ui-architecture.md
docs/ui-architecture.md
+1
-0
xinyuan-design-system.md
docs/xinyuan-design-system.md
+2
-0
XinyuanEChart.jsx
src/components/XinyuanEChart.jsx
+26
-1
chartPalette.js
src/design-system/chartPalette.js
+39
-0
theme.css
src/design-system/theme.css
+17
-0
tokens.css
src/design-system/tokens.css
+17
-0
index.jsx
src/pages/problem_rectification/dashboard/index.jsx
+7
-17
No files found.
docs/ui-architecture.md
View file @
9ad0e27c
...
...
@@ -42,6 +42,7 @@
-
Tabs:直接使用 HeroUI Tabs compound API。页面级、模块级和详情级都按文字内容确定各项宽度,只在 compound slot 的
`className`
上设置
`Tabs/ListContainer: w-fit max-w-full min-w-0`
、
`List: w-max min-w-max`
、
`Tab: w-auto min-w-max flex-none whitespace-nowrap`
。标签始终单行完整展示,项目不得均分或固定 Tab 宽度,溢出只由
`Tabs.ListContainer`
的官方
`ScrollShadow`
处理。选中态、尺寸、圆角、颜色、阴影和动效采用官方默认,仅由
`theme.css`
提供主题值;禁止全局选择
`.tabs__*`
重画外观。
-
字段来源切换(例如“材料中心 / 本地上传”)使用 HeroUI
`primary`
Tabs;Tabs 根、Panel 和 Panel 内容容器为
`w-full min-w-0`
,Panel 仅使用
`mt-3 p-0`
调整布局间距,列表仍保持文字自适应宽度,Panel 内选择框和上传控件统一
`fullWidth`
,确保控件与弹窗字段列通栏对齐。
-
图表:HeroUI Pro PieChart、BarChart、ComposedChart、KPI 和 ChartTooltip;共享
`chartPalette.js`
导出
`tokens.css`
中的 8 个分类色、语义图表色引用及同图唯一分配器。分类图表必须一次传入完整系列 key,在 8 个系列内确定性消除哈希撞色;页面不得直接用
`success/danger/warning/foreground`
组装色板。图表 Card 主标题在 HeroUI
`Card.Header`
内统一左对齐并与正文左边缘一致;分类层级、范围和统计口径参数以紧凑
`secondary`
Autocomplete 位于 Header 右侧同一行,真正的并列视图切换才使用 HeroUI Tabs。禁止按图表类型切换为居中标题。图例文字通过公开 label slot 使用
`--rc-chart-legend-font-size`
/
`--rc-chart-legend-line-height`
,不得覆盖第三方图表内部类。交叉维度数据(如问题来源 × 检查单位)优先使用带单元格数量的 ECharts Heatmap,非零单元使用统一语义主色、零值保持当前 Surface,避免多系列柱形在窄卡片内重叠;保留原统计数据与业务回调,不保留 ECharts 私有主题。
-
饼图 / 环形图是普通分类色板之外的专用例外:共享适配器使用
`--rc-pie-chart-*`
,前六个颜色依次为品牌红、灰蓝、灰紫、暖金、暖棕、石板灰,仅用于扇区;柱状图、折线图、数据卡片和页面主色继续使用原
`--rc-chart-*`
Token。按稳定分类 key 确定性取色,扇区数量不足时不填充多余颜色,超过六类从批准的低饱和扩展色继续取用,禁止绿色或彩虹色。
-
图表 Tooltip:共享
`colorizeXinyuanChartTooltipPayload`
在进入 HeroUI Pro
`ChartTooltipContent`
前把 Tooltip indicator 颜色归一化为当前图形 mark/图例的同一 Token,并排除
`none`
/
`transparent`
。环图、单系列柱图和多系列组合图均显示官方 8px 圆点;页面不复制 Tooltip DOM 或样式。
-
图表焦点:HeroUI Pro 图表根统一使用
`rc-chart-interaction`
。共享层通过
`:focus:not(:focus-visible)`
隐藏鼠标点击产生的浏览器原生蓝框,通过
`:focus-visible`
和主题
`--focus`
保留键盘焦点;不得禁用 Recharts
`accessibilityLayer`
,也不得修改图表点击回调。
-
KPI:页面直接组合 HeroUI Pro
`KPI`
compound API,Card 根使用
`rc-card-surface rc-dashboard-card`
,
`KPI.Icon`
使用共享
`rc-metric-icon-surface`
。共享九色 Token 解决 Pro 原生
`status`
只有三种时的无背景回退,浅底统一为 9% tone(neutral 8%)。
`.rc-kpi-grid data-count`
负责 1–9 项栅格和无显式
`data-tone`
时的顺序唯一配色;业务可覆盖语义 tone,但同组不得重复图标或颜色。超过九项拆分模块,页面不得自定义色值、尺寸和背景。
...
...
docs/xinyuan-design-system.md
View file @
9ad0e27c
...
...
@@ -163,6 +163,8 @@ Surface 用于表达页面内的包含关系,Overlay 用于表达浮动关系
`#DA241A`
、
`#B83A32`
、
`#C95A52`
、
`#D47A70`
、
`#A65A45`
、
`#B87958`
、
`#C99772`
、
`#8A6F63`
、
`#9A8174`
、
`#6F6674`
、
`#807783`
、
`#938B96`
、
`#4F6380`
、
`#61758F`
、
`#74879A`
、
`#B0A4A0`
-
饼图 / 环形图使用独立的
`--xinyuan-pie-chart-1`
至
`--xinyuan-pie-chart-16`
(别名
`--rc-pie-chart-*`
)色板,不改动普通柱状图、折线图、排名图或 UI 主色。前六个 Token 固定为
`#DA241A`
、
`#3F5F86`
、
`#8A668F`
、
`#C28A3D`
、
`#8A6253`
、
`#667080`
,按稳定分类 key 确定性取用并用冲突消解保证同图不重复;超过六类继续使用同组低饱和灰蓝、灰紫、暖棕和暖灰扩展色,禁止引入绿色、青绿色、荧光色和彩虹色。过滤、排序和主题切换不能改变同一分类的身份颜色。
-
单系列统一使用
`--rc-chart-primary`
(品牌红);同一指标的状态使用同色相深浅;多系列按稳定业务 key 确定性分配协调色,不按实时排序改色。语义映射为:风险
`--rc-chart-risk`
、提醒
`--rc-chart-warning`
、正向结果
`--rc-chart-positive`
(灰蓝)、对比
`--rc-chart-comparison`
、中性
`--rc-chart-neutral`
。
-
数字卡片使用
`@heroui-pro/react/kpi`
。
...
...
src/components/XinyuanEChart.jsx
View file @
9ad0e27c
import
{
createResolvedXinyuanPieChartColorMap
,
getXinyuanEChartsTheme
,
}
from
'@/design-system/chartPalette'
;
import
ReactECharts
from
'echarts-for-react'
;
...
...
@@ -95,15 +96,39 @@ export default function XinyuanEChart({
const
applyPieTheme
=
(
series
)
=>
{
if
(
series
?.
type
!==
'pie'
)
return
series
;
const
pieColorMap
=
createResolvedXinyuanPieChartColorMap
(
Array
.
isArray
(
series
.
data
)
?
series
.
data
.
map
((
item
)
=>
item
?.
name
)
:
[],
);
const
pieData
=
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
,
// Pie/donut colors always come from the dedicated palette. This
// keeps adjacent sectors distinguishable and prevents legacy
// page-level colors from leaking into the specialized treatment.
color
:
pieColorMap
.
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
,
},
data
:
forceItemStyle
(
series
.
d
ata
,
{
data
:
forceItemStyle
(
pieD
ata
,
{
borderColor
:
theme
.
sectorGap
,
borderRadius
:
theme
.
sectorRadius
,
borderWidth
:
theme
.
sectorGapWidth
,
...
...
src/design-system/chartPalette.js
View file @
9ad0e27c
...
...
@@ -17,6 +17,12 @@ export const XINYUAN_CHART_PALETTE = Object.freeze([
'var(--rc-chart-16)'
,
]);
// 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
}
)`
),
);
export
const
XINYUAN_CHART_SEMANTIC
=
Object
.
freeze
({
primary
:
'var(--rc-chart-primary)'
,
risk
:
'var(--rc-chart-risk)'
,
...
...
@@ -46,6 +52,12 @@ export function getXinyuanChartColor(key, fallbackIndex = 0) {
return
XINYUAN_CHART_PALETTE
[
hashChartKey
(
stableKey
)
%
XINYUAN_CHART_PALETTE
.
length
];
}
export
function
getXinyuanPieChartColor
(
index
=
0
)
{
return
XINYUAN_PIE_CHART_PALETTE
[
Math
.
max
(
0
,
Number
(
index
)
||
0
)
%
XINYUAN_PIE_CHART_PALETTE
.
length
];
}
export
function
createXinyuanChartColorMap
(
keys
=
[])
{
const
stableKeys
=
[...
new
Set
(
keys
.
map
(
normalizeChartKey
).
filter
(
Boolean
))]
.
sort
();
...
...
@@ -154,6 +166,30 @@ export function createResolvedXinyuanChartColorMap(keys = []) {
);
}
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
,
keyIndex
)
=>
{
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'
...
...
@@ -169,6 +205,9 @@ export function getXinyuanEChartsTheme() {
palette
:
Array
.
from
({
length
:
16
},
(
_
,
index
)
=>
(
readRootToken
(
`--xinyuan-chart-
${
index
+
1
}
`
,
'#667085'
)
)),
piePalette
:
Array
.
from
({
length
:
16
},
(
_
,
index
)
=>
(
readConcreteColorToken
(
`--xinyuan-pie-chart-
${
index
+
1
}
`
,
'#667080'
)
)),
barRadius
:
Number
.
parseFloat
(
readRootToken
(
'--xinyuan-chart-bar-radius'
,
'8'
),
)
||
8
,
...
...
src/design-system/theme.css
View file @
9ad0e27c
...
...
@@ -93,6 +93,23 @@
--xinyuan-chart-14
:
#61758F
;
--xinyuan-chart-15
:
#74879A
;
--xinyuan-chart-16
:
#B0A4A0
;
/* Pie / donut only: higher-hue-separation palette. Keep independent from the categorical palette above. */
--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-bar-radius
:
8px
;
--xinyuan-chart-sector-gap
:
var
(
--surface
);
--xinyuan-chart-sector-gap-width
:
2px
;
...
...
src/design-system/tokens.css
View file @
9ad0e27c
...
...
@@ -165,6 +165,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 bar/line categorical 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-2
);
--rc-chart-warning
:
var
(
--rc-chart-6
);
...
...
src/pages/problem_rectification/dashboard/index.jsx
View file @
9ad0e27c
import
{
DataTable
}
from
'@/components/DataTable'
;
import
{
createResolvedXinyuanChartColorMap
,
getXinyuanChartColor
,
createResolvedXinyuan
Pie
ChartColorMap
,
getXinyuan
Pie
ChartColor
,
getXinyuanEChartsTheme
,
resolveXinyuanChartColor
,
XINYUAN_CHART_SEMANTIC
,
...
...
@@ -164,7 +164,7 @@ function FocusIssueDetailDialog({ issueType, items = [], onClose }) {
);
}
function
DonutChart
({
data
,
centerLabel
,
colorForItem
})
{
function
DonutChart
({
data
,
centerLabel
})
{
const
total
=
data
.
reduce
((
sum
,
item
)
=>
sum
+
Number
(
item
.
value
||
0
),
0
);
const
[
viewportWidth
,
setViewportWidth
]
=
useState
(
()
=>
(
typeof
window
===
'undefined'
?
1440
:
window
.
innerWidth
),
...
...
@@ -178,7 +178,7 @@ function DonutChart({ data, centerLabel, colorForItem }) {
const
option
=
useMemo
(()
=>
{
const
theme
=
getXinyuanEChartsTheme
();
const
colorMap
=
createResolvedXinyuanChartColorMap
(
data
.
map
((
item
)
=>
item
.
name
));
const
colorMap
=
createResolvedXinyuan
Pie
ChartColorMap
(
data
.
map
((
item
)
=>
item
.
name
));
const
legendData
=
data
.
map
((
item
)
=>
String
(
item
.
name
??
''
).
trim
()).
filter
(
Boolean
);
const
estimatedLegendWidth
=
legendData
.
reduce
(
(
width
,
label
)
=>
width
+
(
Array
.
from
(
label
).
length
*
13
)
+
28
,
...
...
@@ -220,11 +220,8 @@ function DonutChart({ data, centerLabel, colorForItem }) {
data
:
data
.
map
((
item
,
index
)
=>
({
...
item
,
itemStyle
:
{
color
:
resolveXinyuanChartColor
(
colorForItem
?.(
item
,
index
)
||
colorMap
.
get
(
String
(
item
.
name
??
''
).
trim
())
||
getXinyuanChartColor
(
item
.
name
,
index
),
),
color
:
colorMap
.
get
(
String
(
item
.
name
??
''
).
trim
())
||
resolveXinyuanChartColor
(
getXinyuanPieChartColor
(
index
)),
},
})),
label
:
{
...
...
@@ -267,7 +264,7 @@ function DonutChart({ data, centerLabel, colorForItem }) {
}],
tooltip
:
{
trigger
:
'item'
,
valueFormatter
:
(
value
)
=>
`
${
formatCount
(
value
)}
条`
},
};
},
[
centerLabel
,
colorForItem
,
data
,
total
,
viewportWidth
]);
},
[
centerLabel
,
data
,
total
,
viewportWidth
]);
return
<
XinyuanEChart
ariaLabel=
{
`${centerLabel}分布图`
}
option=
{
option
}
/>;
}
...
...
@@ -759,13 +756,6 @@ export function ProblemRectificationDashboardPage({ dashboardMode = 'overview' }
<
DonutChart
data=
{
summary
.
qualityRatingDistribution
}
centerLabel=
"评级记录"
colorForItem=
{
(
item
)
=>
(
item
.
name
===
'高'
?
XINYUAN_CHART_SEMANTIC
.
positive
:
item
.
name
===
'中'
?
XINYUAN_CHART_SEMANTIC
.
warning
:
XINYUAN_CHART_SEMANTIC
.
risk
)
}
/>
)
:
(
<
EmptyBlock
title=
"暂无综合整改质效结果数据"
/>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment