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
190e6b24
Commit
190e6b24
authored
Aug 31, 2026
by
Andy-bubu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: refine dashboard chart palette and headings
parent
b9f397bf
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
138 additions
and
40 deletions
+138
-40
xinyuan-11-app-migration-prompt.md
docs/xinyuan-11-app-migration-prompt.md
+0
-0
xinyuan-design-system.md
docs/xinyuan-design-system.md
+12
-2
xinyuan-ui-audit.md
docs/xinyuan-ui-audit.md
+5
-2
XinyuanEChart.jsx
src/components/XinyuanEChart.jsx
+18
-0
chartPalette.js
src/design-system/chartPalette.js
+46
-10
shell.css
src/design-system/shell.css
+17
-0
theme.css
src/design-system/theme.css
+20
-16
tokens.css
src/design-system/tokens.css
+4
-4
AuditProjectDashboardPage.jsx
src/pages/project_manager_xy/AuditProjectDashboardPage.jsx
+16
-6
No files found.
docs/xinyuan-11-app-migration-prompt.md
View file @
190e6b24
This diff is collapsed.
Click to expand it.
docs/xinyuan-design-system.md
View file @
190e6b24
...
...
@@ -166,14 +166,24 @@ Inter 与系统回退字体之间切换。
-
详情和关联记录中的历史数组字符串必须先解析再展示:数组值使用业务标签以“、”连接;流程图/文档结构等结构化对象展示节点、连线、章节和内容块摘要;无法解析出名称的内部 ID 显示“已关联记录/已关联 N 项”,禁止把 JSON、ObjectId 或存储字段原文直接暴露给业务人员。编辑态 JSON 输入仍保持原数据协议。
-
数字指标使用 Pro KPI;所有图表统一通过共享
`XinyuanEChart`
使用
`echarts`
与
`echarts-for-react`
。系列色、坐标轴、网格、图例、Tooltip Surface/边框/阴影均读取
`theme.css`
的
`--xinyuan-chart-*`
Token。当前主题固定提供 16 个鲜艳且稳定的分类色
(红、粉、橙、黄、绿、青、蓝、靛、紫、品红及中性灰),页面只提供业务数据和图表 option。图标始终使用
`theme.css`
的
`--xinyuan-chart-*`
Token。首轮 8 个色位固定按红→粉→橙→黄→蓝→紫→青→中性灰排列,
让最多 8 个分类拥有清晰且高级的色相差异;第 9-16 位提供对应的明暗补充,页面只提供业务数据和
图表 option。具体色值依次为
`#991b1b`
、
`#db2777`
、
`#ea580c`
、
`#eab308`
、
`#1d4ed8`
、
`#7e22ce`
、
`#0891b2`
、
`#475569`
、
`#dc2626`
、
`#ec4899`
、
`#f97316`
、
`#facc15`
、
`#60a5fa`
、
`#a855f7`
、
`#06b6d4`
、
`#1e293b`
。图标始终使用
`AppIcons`
汇出的 Ant Design Icons,ECharts 不作为图标来源。
-
同一图表的分类色按业务展示顺序连续取用相邻色相;有显式业务顺序时必须传入稳定顺序,
无显式顺序时按稳定 key 确定性分配。禁止在有业务顺序时使用名称哈希打散首轮色板,避免相邻
扇区或相邻图例出现无序跳色色系;筛选、排序和 Tab 切换后已有业务 key 保持原色。
-
环形/饼图需要在图表外展示分类内容时,必须使用 ECharts 原生
`label`
与
`labelLine`
引导线,
使用与标签端点对齐的完整引导线连接对应标签,标签按“分类名称 / 真实数量”分两行显示;分类名称保持
13px/500,数量使用 14px/600;标签位置必须锚定到对应引导线终点,避免自动避让造成错位。
不得把引导线拉到卡片边缘或只保留
底部图例代替内容标注。
-
环形/饼图扇区之间使用统一的紧凑 Surface 色间隙和圆角(
`padAngle: 1`
、
`borderWidth: 2`
、
`borderRadius: 10`
),形成清晰分隔但不改变数据比例;间隙色必须读取
`--xinyuan-chart-sector-gap`
,
圆角值读取
`--xinyuan-chart-sector-radius`
,
随主题 Surface 自动适配。图表外层模块继续使用 24px 共享圆角。
-
严谨业务界面不使用粉色、玫红或洋红危险色;HeroUI
`danger`
/
`danger-soft`
必须继承鑫元红系
`--danger`
Token。图表中的红色序列同样使用红系,不使用粉色替代危险语义。
...
...
docs/xinyuan-ui-audit.md
View file @
190e6b24
...
...
@@ -284,10 +284,13 @@ HeroUI Context。`@heroui/styles@3.2.4` 的两个 pnpm peer 虚拟目录仅分
-
驾驶舱状态饼图和阶段柱图已从 HeroUI Pro/Recharts 切换到 ECharts 6.1.0 与
echarts-for-react 3.0.6。共享
`XinyuanEChart`
统一 ARIA、主题系列色、图例、坐标轴、网格和
body HTML Tooltip;柱宽仍限制为 28px,API、统计口径、项目详情入口和数据结构未改。
-
图表分类色已扩展为
`--xinyuan-chart-1`
至
`--xinyuan-chart-16`
,使用鲜艳固定色值;
通过稳定业务 key 分配颜色,禁止页面写入随机或局部色值。
-
图表分类色已扩展为
`--xinyuan-chart-1`
至
`--xinyuan-chart-16`
,使用鲜艳固定色值,首轮顺序固定为
红→粉→橙→黄→蓝→紫→青→中性灰;
通过稳定业务顺序/业务 key 分配相邻色相,禁止页面写入随机或局部色值;项目状态分布按
计划、执行、归档、整改、待启动、完成的顺序连续取用色板,筛选后 key 颜色保持不变。
-
状态环图已启用 ECharts 外部分类标签和
`labelLine`
引导线,标签显示真实状态与数量,
使用
`labelLayout`
根据
`labelLinePoints`
将标签锚定到各自折线终点,避免文案与折线错位。
-
状态环图扇区统一使用
`padAngle: 1`
、2px Surface 色间隙和
`borderRadius: 10`
,扇区边界紧凑清晰且不改变真实数量比例;图表模块外层使用 24px 圆角。
-
删除、拒绝和危险操作的
`danger-soft`
背景已从默认粉色修正为鑫元红系,活动页面不再出现
粉色危险操作视觉。
-
列表普通业务单元格已关闭 Hover 文案 Tooltip;操作列仍保留查看、编辑、删除等真实操作按钮
...
...
src/components/XinyuanEChart.jsx
View file @
190e6b24
...
...
@@ -59,6 +59,23 @@ export default function XinyuanEChart({
const
themedOption
=
useMemo
(()
=>
{
const
theme
=
getXinyuanEChartsTheme
();
const
applyPieTheme
=
(
series
)
=>
{
if
(
series
?.
type
!==
'pie'
)
return
series
;
return
{
...
series
,
itemStyle
:
{
borderColor
:
theme
.
sectorGap
,
borderRadius
:
theme
.
sectorRadius
,
borderWidth
:
2
,
...
series
.
itemStyle
,
},
padAngle
:
series
.
padAngle
??
1
,
};
};
const
themedSeries
=
Array
.
isArray
(
option
?.
series
)
?
option
.
series
.
map
(
applyPieTheme
)
:
applyPieTheme
(
option
?.
series
);
return
{
animationDuration
:
320
,
...
...
@@ -76,6 +93,7 @@ export default function XinyuanEChart({
...
option
?.
textStyle
,
},
...
option
,
series
:
themedSeries
,
legend
:
option
?.
legend
?
{
icon
:
'circle'
,
itemHeight
:
8
,
...
...
src/design-system/chartPalette.js
View file @
190e6b24
...
...
@@ -37,27 +37,59 @@ function hashChartKey(key) {
);
}
export
function
createXinyuanChartColorMap
(
keys
=
[])
{
const
stableKeys
=
[...
new
Set
(
keys
.
map
(
normalizeChartKey
).
filter
(
Boolean
))]
.
sort
();
export
function
createXinyuanChartColorMap
(
keys
=
[],
options
=
{})
{
const
sourceKeys
=
Array
.
isArray
(
keys
)
?
keys
:
[];
const
order
=
Array
.
isArray
(
options
?.
order
)
?
options
.
order
:
[];
const
orderedKeys
=
[...
new
Set
(
sourceKeys
.
map
(
normalizeChartKey
).
filter
(
Boolean
))];
const
preferredOrder
=
new
Map
(
order
.
map
(
normalizeChartKey
).
filter
(
Boolean
).
map
((
key
,
index
)
=>
[
key
,
index
]),
);
const
stableKeys
=
orderedKeys
.
sort
((
left
,
right
)
=>
{
const
leftOrder
=
preferredOrder
.
has
(
left
)
?
preferredOrder
.
get
(
left
)
:
Number
.
MAX_SAFE_INTEGER
;
const
rightOrder
=
preferredOrder
.
has
(
right
)
?
preferredOrder
.
get
(
right
)
:
Number
.
MAX_SAFE_INTEGER
;
if
(
leftOrder
!==
rightOrder
)
{
return
leftOrder
-
rightOrder
;
}
return
left
.
localeCompare
(
right
,
'zh-CN'
);
});
const
unknownPositions
=
new
Map
(
stableKeys
.
filter
((
key
)
=>
!
preferredOrder
.
has
(
key
))
.
map
((
key
,
index
)
=>
[
key
,
index
]),
);
const
colorMap
=
new
Map
();
const
usedIndexes
=
new
Set
();
stableKeys
.
forEach
((
key
,
keyIndex
)
=>
{
const
preferredIndex
=
hashChartKey
(
key
)
%
XINYUAN_CHART_PALETTE
.
length
;
let
paletteIndex
=
preferredIndex
;
stableKeys
.
forEach
((
key
)
=>
{
const
hasExplicitOrder
=
preferredOrder
.
size
>
0
;
let
paletteIndex
=
hasExplicitOrder
&&
preferredOrder
.
has
(
key
)
?
preferredOrder
.
get
(
key
)
%
XINYUAN_CHART_PALETTE
.
length
:
hasExplicitOrder
?
preferredOrder
.
size
+
unknownPositions
.
get
(
key
)
:
hashChartKey
(
key
)
%
XINYUAN_CHART_PALETTE
.
length
;
if
(
keyIndex
<
XINYUAN_CHART_PALETTE
.
length
)
{
if
(
paletteIndex
>=
XINYUAN_CHART_PALETTE
.
length
||
usedIndexes
.
has
(
paletteIndex
)
)
{
const
preferredIndex
=
hashChartKey
(
key
)
%
XINYUAN_CHART_PALETTE
.
length
;
paletteIndex
=
preferredIndex
;
for
(
let
offset
=
0
;
offset
<
XINYUAN_CHART_PALETTE
.
length
;
offset
+=
1
)
{
const
candidateIndex
=
(
preferredIndex
+
offset
)
%
XINYUAN_CHART_PALETTE
.
length
;
if
(
!
usedIndexes
.
has
(
candidateIndex
))
{
paletteIndex
=
candidateIndex
;
usedIndexes
.
add
(
candidateIndex
);
break
;
}
}
}
usedIndexes
.
add
(
paletteIndex
);
colorMap
.
set
(
key
,
XINYUAN_CHART_PALETTE
[
paletteIndex
]);
});
...
...
@@ -79,9 +111,9 @@ export function resolveXinyuanChartColor(color, fallback = '') {
return
match
?
readRootToken
(
match
[
1
],
fallback
)
:
String
(
color
||
fallback
);
}
export
function
createResolvedXinyuanChartColorMap
(
keys
=
[])
{
export
function
createResolvedXinyuanChartColorMap
(
keys
=
[]
,
options
=
{}
)
{
return
new
Map
(
Array
.
from
(
createXinyuanChartColorMap
(
keys
)).
map
(([
key
,
color
])
=>
[
Array
.
from
(
createXinyuanChartColorMap
(
keys
,
options
)).
map
(([
key
,
color
])
=>
[
key
,
resolveXinyuanChartColor
(
color
),
]),
...
...
@@ -104,6 +136,10 @@ export function getXinyuanEChartsTheme() {
)),
tooltipBackground
:
readRootToken
(
'--xinyuan-chart-tooltip-background'
,
'#ffffff'
),
tooltipBorder
:
readRootToken
(
'--xinyuan-chart-tooltip-border'
,
'#dedede'
),
sectorGap
:
readRootToken
(
'--xinyuan-chart-sector-gap'
,
'#ffffff'
),
sectorRadius
:
Number
.
parseFloat
(
readRootToken
(
'--xinyuan-chart-sector-radius'
,
'10'
),
)
||
10
,
tooltipShadow
:
readRootToken
(
'--xinyuan-chart-tooltip-shadow'
,
'0 16px 40px rgb(15 23 42 / 14%)'
,
...
...
src/design-system/shell.css
View file @
190e6b24
...
...
@@ -435,6 +435,23 @@
gap
:
var
(
--rc-page-module-gap
);
}
.rc-section-title
{
margin
:
0
;
color
:
var
(
--rc-text-primary
);
font-size
:
1.25rem
;
font-weight
:
var
(
--rc-font-semibold
);
line-height
:
1.75rem
;
letter-spacing
:
0
;
}
.rc-module-title
{
color
:
var
(
--rc-text-primary
);
font-size
:
1.125rem
;
font-weight
:
var
(
--rc-font-semibold
);
line-height
:
1.5rem
;
letter-spacing
:
0
;
}
.rc-page-grid
{
min-width
:
0
;
gap
:
var
(
--rc-page-module-gap
);
...
...
src/design-system/theme.css
View file @
190e6b24
...
...
@@ -70,22 +70,26 @@
* Ant Design remains the icon source. Resolve these CSS variables to
* concrete colors before assigning them to an ECharts option object.
*/
--xinyuan-chart-1
:
#d92d20
;
--xinyuan-chart-2
:
#ef4444
;
--xinyuan-chart-3
:
#f97316
;
--xinyuan-chart-4
:
#f59e0b
;
--xinyuan-chart-5
:
#22c55e
;
--xinyuan-chart-6
:
#14b87a
;
--xinyuan-chart-7
:
#06b6d4
;
--xinyuan-chart-8
:
#0ea5e9
;
--xinyuan-chart-9
:
#3b82f6
;
--xinyuan-chart-10
:
#6366f1
;
--xinyuan-chart-11
:
#8b5cf6
;
--xinyuan-chart-12
:
#7c3aed
;
--xinyuan-chart-13
:
#b42318
;
--xinyuan-chart-14
:
#84cc16
;
--xinyuan-chart-15
:
#a855f7
;
--xinyuan-chart-16
:
#64748b
;
/* First pass: red -> pink -> orange -> yellow -> blue -> purple -> cyan -> neutral. */
--xinyuan-chart-1
:
#991b1b
;
--xinyuan-chart-2
:
#db2777
;
--xinyuan-chart-3
:
#ea580c
;
--xinyuan-chart-4
:
#eab308
;
--xinyuan-chart-5
:
#1d4ed8
;
--xinyuan-chart-6
:
#7e22ce
;
--xinyuan-chart-7
:
#0891b2
;
--xinyuan-chart-8
:
#475569
;
/* Second pass: stronger/lighter companions for charts with more series. */
--xinyuan-chart-9
:
#dc2626
;
--xinyuan-chart-10
:
#ec4899
;
--xinyuan-chart-11
:
#f97316
;
--xinyuan-chart-12
:
#facc15
;
--xinyuan-chart-13
:
#60a5fa
;
--xinyuan-chart-14
:
#a855f7
;
--xinyuan-chart-15
:
#06b6d4
;
--xinyuan-chart-16
:
#1e293b
;
--xinyuan-chart-sector-gap
:
var
(
--surface
);
--xinyuan-chart-sector-radius
:
10px
;
--xinyuan-chart-grid
:
#e8e8e8
;
--xinyuan-chart-axis
:
#737373
;
--xinyuan-chart-label
:
#737373
;
...
...
src/design-system/tokens.css
View file @
190e6b24
...
...
@@ -165,10 +165,10 @@
--rc-chart-15
:
var
(
--xinyuan-chart-15
);
--rc-chart-16
:
var
(
--xinyuan-chart-16
);
--rc-chart-primary
:
var
(
--rc-chart-1
);
--rc-chart-risk
:
oklch
(
47%
0.145
27.03
);
--rc-chart-warning
:
var
(
--rc-chart-
6
);
--rc-chart-positive
:
var
(
--rc-chart-
2
);
--rc-chart-comparison
:
var
(
--rc-chart-
4
);
--rc-chart-risk
:
var
(
--rc-chart-1
);
--rc-chart-warning
:
var
(
--rc-chart-
3
);
--rc-chart-positive
:
var
(
--rc-chart-
7
);
--rc-chart-comparison
:
var
(
--rc-chart-
5
);
--rc-chart-neutral
:
var
(
--rc-chart-8
);
--rc-chart-legend-font-size
:
var
(
--xinyuan-chart-legend-font-size
);
--rc-chart-legend-line-height
:
var
(
--xinyuan-chart-legend-line-height
);
...
...
src/pages/project_manager_xy/AuditProjectDashboardPage.jsx
View file @
190e6b24
...
...
@@ -34,6 +34,13 @@ const STATUS_TONES = {
归档
:
'secondary'
,
};
const
STATUS_CHART_ORDER
=
[
'计划'
,
'执行'
,
'归档'
,
'整改'
,
'待启动'
,
'完成'
,
'未设置'
];
function
getStatusChartOrder
(
status
)
{
const
index
=
STATUS_CHART_ORDER
.
indexOf
(
status
);
return
index
===
-
1
?
Number
.
MAX_SAFE_INTEGER
:
index
;
}
function
MetricCard
({
label
,
value
,
icon
:
Icon
,
tone
,
onPress
})
{
const
numericValue
=
Number
.
parseFloat
(
String
(
value
).
replace
(
/
[
% ,
]
/g
,
''
));
const
content
=
(
...
...
@@ -93,7 +100,7 @@ function ProjectPanel({ title, records, emptyText, onOpen }) {
return
(
<
Card
className=
"rc-dashboard-card"
>
<
Card
.
Header
className=
"flex-row items-center gap-3"
>
<
Card
.
Title
className=
"min-w-0 flex-1"
>
{
title
}
</
Card
.
Title
>
<
Card
.
Title
className=
"
rc-module-title
min-w-0 flex-1"
>
{
title
}
</
Card
.
Title
>
</
Card
.
Header
>
<
Card
.
Content
className=
"max-h-80 overflow-y-auto"
>
<
ListView
...
...
@@ -123,6 +130,7 @@ function StatusChart({ data }) {
const
theme
=
getXinyuanEChartsTheme
();
const
colorMap
=
createResolvedXinyuanChartColorMap
(
data
.
map
((
item
)
=>
item
.
name
),
{
order
:
STATUS_CHART_ORDER
},
);
return
{
...
...
@@ -132,7 +140,9 @@ function StatusChart({ data }) {
},
series
:
[{
avoidLabelOverlap
:
true
,
data
:
data
.
map
((
item
)
=>
({
data
:
[...
data
].
sort
(
(
left
,
right
)
=>
getStatusChartOrder
(
left
.
name
)
-
getStatusChartOrder
(
right
.
name
),
).
map
((
item
)
=>
({
...
item
,
itemStyle
:
{
color
:
colorMap
.
get
(
item
.
name
)
},
})),
...
...
@@ -355,21 +365,21 @@ export default function AuditProjectDashboardPage() {
</
section
>
<
section
className=
"rc-page-section"
>
<
h2
className=
"
text-base font-medium text-foreground
"
>
项目进度分析
</
h2
>
<
h2
className=
"
rc-section-title
"
>
项目进度分析
</
h2
>
<
div
className=
"rc-page-grid rc-chart-grid"
>
<
Card
className=
"rc-dashboard-card"
>
<
Card
.
Header
><
Card
.
Title
>
项目状态分布
</
Card
.
Title
></
Card
.
Header
>
<
Card
.
Header
><
Card
.
Title
className=
"rc-module-title"
>
项目状态分布
</
Card
.
Title
></
Card
.
Header
>
<
Card
.
Content
>
{
dashboard
.
statusData
.
length
?
<
StatusChart
data=
{
dashboard
.
statusData
}
/>
:
null
}
</
Card
.
Content
>
</
Card
>
<
Card
className=
"rc-dashboard-card"
>
<
Card
.
Header
><
Card
.
Title
>
开展中项目进度
</
Card
.
Title
></
Card
.
Header
>
<
Card
.
Header
><
Card
.
Title
className=
"rc-module-title"
>
开展中项目进度
</
Card
.
Title
></
Card
.
Header
>
<
Card
.
Content
><
StageChart
data=
{
dashboard
.
stageData
}
/></
Card
.
Content
>
</
Card
>
</
div
>
</
section
>
<
section
className=
"rc-page-section"
>
<
h2
className=
"
text-base font-medium text-foreground
"
>
项目提醒
</
h2
>
<
h2
className=
"
rc-section-title
"
>
项目提醒
</
h2
>
<
div
className=
"grid gap-6 xl:grid-cols-3"
>
<
ProjectPanel
title=
"逾期未完成项目"
records=
{
dashboard
.
overdue
}
emptyText=
"暂无逾期未完成项目"
onOpen=
{
openRecord
}
/>
<
ProjectPanel
title=
"正在开展项目"
records=
{
dashboard
.
active
}
emptyText=
"暂无正在开展项目"
onOpen=
{
openRecord
}
/>
...
...
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