Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
A
audit_project_manager
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
audit_project_manager
Commits
5ba517d6
Commit
5ba517d6
authored
Sep 08, 2026
by
Andy-bubu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: sync pie chart palette
parent
918096b2
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
158 additions
and
19 deletions
+158
-19
ui-architecture.md
docs/ui-architecture.md
+3
-1
ui-migration-checklist.md
docs/ui-migration-checklist.md
+1
-0
xinyuan-design-system.md
docs/xinyuan-design-system.md
+5
-0
xinyuan-ui-audit.md
docs/xinyuan-ui-audit.md
+3
-0
XinyuanEChart.jsx
src/components/XinyuanEChart.jsx
+39
-2
chartPalette.js
src/design-system/chartPalette.js
+72
-1
theme.css
src/design-system/theme.css
+17
-0
tokens.css
src/design-system/tokens.css
+17
-0
AuditProjectDashboardPage.jsx
src/pages/project_manager_xy/AuditProjectDashboardPage.jsx
+1
-15
No files found.
docs/ui-architecture.md
View file @
5ba517d6
...
...
@@ -49,7 +49,9 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS
-
`XinyuanModal`
:HeroUI Modal compound 结构、批准宽度和 Surface 层级。
-
`TableActionButton`
:HeroUI Button/Dropdown/Tooltip 与统一 Ant Design 图标。
-
`XinyuanEChart`
:
`echarts-for-react`
的共享主题边界,统一解析
`--xinyuan-chart-*`
,注入
ARIA、Tooltip、图例、坐标轴和响应式;业务页面只拥有 series/data 等业务配置。
ARIA、Tooltip、图例、坐标轴和响应式;业务页面只拥有 series/data 等业务配置。
`type: 'pie'`
的饼图/环形图由同一适配器切换到独立
`--xinyuan-pie-chart-*`
色板,普通柱状图、折线图和排名图继续使用
`--xinyuan-chart-*`
,页面不得写入饼图颜色常量或覆盖专用映射。
-
`FilterSurface`
(共享布局合同):三列以上字段使用 HeroUI
`Surface default`
承载,字段使用
`secondary`
,首行最多三个高频字段,剩余字段通过官方
`Disclosure`
展开。操作按字段底部右对齐,顺序
固定为 36px 圆形重置图标、品牌红查询按钮、
`sm ghost`
展开/收起触发器;重置通过共享回调同步清空待提交/已提交
...
...
docs/ui-migration-checklist.md
View file @
5ba517d6
...
...
@@ -41,6 +41,7 @@
## 页面与组件
-
[
x
]
驾驶舱:4 个 Pro KPI;状态饼图和阶段柱图统一使用共享 ECharts 主题适配器
-
[
x
]
饼图/环形图使用独立
`--xinyuan-pie-chart-*`
/
`--rc-pie-chart-*`
色板;普通柱状图、折线图和排名图继续使用
`--xinyuan-chart-*`
-
[
x
]
项目列表/归档/外部填写:Tabs、Surface、Autocomplete、DatePicker、Disclosure
-
[
x
]
全部业务列表:Pro DataGrid、原生选择/排序/固定列、Pagination、ActionBar、EmptyState
-
[
x
]
项目业务列表 Pagination 同行左侧摘要右侧提供 HeroUI 20/50/100 条页容量切换(无搜索框),页码控制靠右,切换后回到第一页
...
...
docs/xinyuan-design-system.md
View file @
5ba517d6
...
...
@@ -228,6 +228,11 @@ Inter 与系统回退字体之间切换。
`#4F6380`
、
`#61758F`
、
`#74879A`
、
`#8A6F63`
、
`#9A8174`
、
`#B0A5AD`
。单系列优先使用品牌红,
同一指标的不同状态优先使用品牌红深浅层级;图标始终使用
`AppIcons`
汇出的 Ant Design Icons,ECharts 不作为图标来源。
-
饼图/环形图是普通图表色板之外的独立例外,仅由共享
`XinyuanEChart`
从
`--xinyuan-pie-chart-*`
/
`--rc-pie-chart-*`
读取颜色。前六个颜色固定为
`#DA241A`
、
`#3F5F86`
、
`#8A668F`
、
`#C28A3D`
、
`#8A6253`
、
`#667080`
,其余为批准的低饱和灰蓝、灰紫、暖棕、暖灰和石板灰扩展色;禁止绿色、青绿色、荧光色和彩虹色。
共享适配器先收集完整分类 key,再按稳定 key 确定性分配并处理冲突;筛选、排序、Tab 切换和主题切换均不得改变同一分类的颜色。
该专用色板不影响柱状图、折线图、排名图、KPI 或页面品牌主色,未来新增饼图/环形图必须直接使用共享适配器,页面不得保留旧
`itemStyle.color`
覆盖。
-
所有 Autocomplete、引用、用户和材料选择器的下拉选项只展示一行业务标题;不得在第二行
展示内部 ID、用户名、文件名、年份或其它技术描述。描述字段可以继续保留用于搜索、匹配和
数据处理,但不能渲染为选项副标题。应用切换卡片的业务描述不属于选择器选项,可按应用切换规范保留。
...
...
docs/xinyuan-ui-audit.md
View file @
5ba517d6
...
...
@@ -288,6 +288,9 @@ HeroUI Context。`@heroui/styles@3.2.4` 的两个 pnpm peer 虚拟目录仅分
按品牌红→暖色→中性→灰蓝的协调顺序取色,不使用绿色、荧光色或彩虹式跳色;
通过稳定业务顺序/业务 key 分配相邻色阶,禁止页面写入随机或局部色值;项目状态分布按
计划、执行、归档、整改、待启动、完成的顺序连续取用色板,筛选后 key 颜色保持不变。
-
饼图/环形图已独立使用
`--xinyuan-pie-chart-*`
/
`--rc-pie-chart-*`
专用色板,前六个颜色为
`#DA241A`
、
`#3F5F86`
、
`#8A668F`
、
`#C28A3D`
、
`#8A6253`
、
`#667080`
;共享适配器按稳定分类 key
确定性分配并处理冲突,过滤、排序、Tab 和主题变化不改色,普通 Bar/Line/Ranking 色板未改变。
-
状态环图已启用 ECharts 外部分类标签和
`labelLine`
引导线,标签显示真实状态与数量,
使用
`labelLayout`
根据
`labelLinePoints`
将标签锚定到各自折线终点,避免文案与折线错位。
-
状态环图扇区统一使用
`padAngle: 1`
、2px Surface 色间隙和
`borderRadius: 10`
,扇区边界紧凑清晰且不改变真实数量比例;图表模块外层使用 24px 圆角。
...
...
src/components/XinyuanEChart.jsx
View file @
5ba517d6
import
{
createResolvedXinyuanPieChartColorMap
,
getXinyuanEChartsTheme
,
}
from
'@/design-system/chartPalette'
;
import
ReactECharts
from
'echarts-for-react'
;
...
...
@@ -62,15 +63,51 @@ 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
();
const
{
color
:
_itemColor
,
...
itemStyle
}
=
item
.
itemStyle
||
{};
const
itemLabelLine
=
item
.
labelLine
;
const
{
color
:
_lineColor
,
...
labelLineStyle
}
=
itemLabelLine
?.
lineStyle
||
{};
return
{
...
item
,
itemStyle
:
{
...
itemStyle
,
color
:
pieColorMap
.
get
(
key
)
||
theme
.
piePalette
[
index
%
theme
.
piePalette
.
length
],
},
...(
itemLabelLine
?
{
labelLine
:
{
...
itemLabelLine
,
...(
itemLabelLine
.
lineStyle
?
{
lineStyle
:
labelLineStyle
}
:
{}),
},
}
:
{}),
};
})
:
series
.
data
;
const
{
color
:
_seriesColor
,
...
seriesItemStyle
}
=
series
.
itemStyle
||
{};
return
{
...
series
,
color
:
theme
.
piePalette
,
itemStyle
:
{
...
seriesItemStyle
,
borderColor
:
theme
.
sectorGap
,
borderRadius
:
theme
.
sectorRadius
,
borderWidth
:
2
,
...
series
.
itemStyle
,
},
padAngle
:
series
.
padAngle
??
1
,
data
:
pieData
,
padAngle
:
1
,
};
};
const
themedSeries
=
Array
.
isArray
(
option
?.
series
)
...
...
src/design-system/chartPalette.js
View file @
5ba517d6
...
...
@@ -17,6 +17,12 @@ export const XINYUAN_CHART_PALETTE = Object.freeze([
'var(--rc-chart-16)'
,
]);
// Pie and donut slices use an independent palette so their adjacent sectors
// remain distinguishable without changing the approved bar/line 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)'
,
...
...
@@ -106,6 +112,37 @@ function readRootToken(tokenName, fallback = '') {
.
trim
()
||
fallback
;
}
const
concreteColorCache
=
new
Map
();
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
=
window
.
getComputedStyle
(
probe
).
color
||
fallback
;
probe
.
remove
();
concreteColorCache
.
set
(
tokenName
,
{
raw
,
value
:
resolved
});
return
resolved
;
}
export
function
getXinyuanPieChartColor
(
index
=
0
)
{
return
XINYUAN_PIE_CHART_PALETTE
[
Math
.
max
(
0
,
Number
(
index
)
||
0
)
%
XINYUAN_PIE_CHART_PALETTE
.
length
];
}
export
function
resolveXinyuanChartColor
(
color
,
fallback
=
''
)
{
const
match
=
String
(
color
||
''
).
trim
().
match
(
/^var
\((
--
[^
)
]
+
)\)
$/
);
return
match
?
readRootToken
(
match
[
1
],
fallback
)
:
String
(
color
||
fallback
);
...
...
@@ -120,6 +157,37 @@ export function createResolvedXinyuanChartColorMap(keys = [], options = {}) {
);
}
export
function
createResolvedXinyuanPieChartColorMap
(
keys
=
[])
{
const
stableKeys
=
[...
new
Set
(
keys
.
map
(
normalizeChartKey
).
filter
(
Boolean
))]
.
sort
((
left
,
right
)
=>
left
.
localeCompare
(
right
,
'zh-CN'
));
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
,
readConcreteColorToken
(
`--rc-pie-chart-
${
paletteIndex
+
1
}
`
,
getXinyuanPieChartColor
(
paletteIndex
),
),
);
});
return
colorMap
;
}
export
function
getXinyuanEChartsTheme
()
{
const
documentFont
=
typeof
document
===
'undefined'
?
'Inter, sans-serif'
...
...
@@ -134,9 +202,12 @@ 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'
)
)),
tooltipBackground
:
readRootToken
(
'--xinyuan-chart-tooltip-background'
,
'#ffffff'
),
tooltipBorder
:
readRootToken
(
'--xinyuan-chart-tooltip-border'
,
'#dedede'
),
sectorGap
:
read
Root
Token
(
'--xinyuan-chart-sector-gap'
,
'#ffffff'
),
sectorGap
:
read
ConcreteColor
Token
(
'--xinyuan-chart-sector-gap'
,
'#ffffff'
),
sectorRadius
:
Number
.
parseFloat
(
readRootToken
(
'--xinyuan-chart-sector-radius'
,
'10'
),
)
||
10
,
...
...
src/design-system/theme.css
View file @
5ba517d6
...
...
@@ -106,6 +106,23 @@
--xinyuan-chart-14
:
#8A6F63
;
--xinyuan-chart-15
:
#9A8174
;
--xinyuan-chart-16
:
#B0A5AD
;
/* Pie / donut only: independent muted palette with higher hue separation. */
--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-radius
:
10px
;
--xinyuan-chart-grid
:
#e8e8e8
;
...
...
src/design-system/tokens.css
View file @
5ba517d6
...
...
@@ -164,6 +164,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 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
);
...
...
src/pages/project_manager_xy/AuditProjectDashboardPage.jsx
View file @
5ba517d6
...
...
@@ -3,7 +3,6 @@ import { EmptyStateSurface } from '@/components/DataTable';
import
XinyuanModal
from
'@/components/XinyuanModal'
;
import
XinyuanEChart
from
'@/components/XinyuanEChart'
;
import
{
createResolvedXinyuanChartColorMap
,
getXinyuanEChartsTheme
,
}
from
'@/design-system/chartPalette'
;
import
{
...
...
@@ -128,10 +127,6 @@ function ProjectPanel({ title, records, emptyText, onOpen }) {
function
StatusChart
({
data
})
{
const
option
=
useMemo
(()
=>
{
const
theme
=
getXinyuanEChartsTheme
();
const
colorMap
=
createResolvedXinyuanChartColorMap
(
data
.
map
((
item
)
=>
item
.
name
),
{
order
:
STATUS_CHART_ORDER
},
);
return
{
legend
:
{
...
...
@@ -142,16 +137,7 @@ function StatusChart({ data }) {
avoidLabelOverlap
:
true
,
data
:
[...
data
].
sort
(
(
left
,
right
)
=>
getStatusChartOrder
(
left
.
name
)
-
getStatusChartOrder
(
right
.
name
),
).
map
((
item
)
=>
({
...
item
,
itemStyle
:
{
color
:
colorMap
.
get
(
item
.
name
)
},
labelLine
:
{
lineStyle
:
{
color
:
colorMap
.
get
(
item
.
name
),
width
:
1.5
,
},
},
})),
),
emphasis
:
{
scaleSize
:
4
,
},
...
...
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