Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
A
audit_manager_new
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_manager_new
Commits
b20234c0
Commit
b20234c0
authored
Sep 08, 2026
by
Andy-bubu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add xinyuan pie chart palette
parent
d0a2a6ab
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
374 additions
and
9 deletions
+374
-9
UI_UX_AUDIT.md
docs/UI_UX_AUDIT.md
+1
-1
XINYUAN_DESIGN_SYSTEM.md
docs/XINYUAN_DESIGN_SYSTEM.md
+2
-2
ui-architecture.md
docs/ui-architecture.md
+1
-1
ui-migration-checklist.md
docs/ui-migration-checklist.md
+2
-2
xinyuan-design-system.md
docs/xinyuan-design-system.md
+3
-2
xinyuan-ui-audit.md
docs/xinyuan-ui-audit.md
+1
-1
XinyuanEChart.jsx
src/components/XinyuanEChart.jsx
+128
-0
chartPalette.js
src/design-system/chartPalette.js
+202
-0
theme.css
src/design-system/theme.css
+17
-0
tokens.css
src/design-system/tokens.css
+17
-0
No files found.
docs/UI_UX_AUDIT.md
View file @
b20234c0
...
@@ -57,7 +57,7 @@
...
@@ -57,7 +57,7 @@
-
源码中存在蓝、sky、rose、pink、渐变和页面私有品牌色;专项优化后主色统一为
`#DA241A`
,历史问题记录保留用于追踪。
-
源码中存在蓝、sky、rose、pink、渐变和页面私有品牌色;专项优化后主色统一为
`#DA241A`
,历史问题记录保留用于追踪。
-
同时存在 Heroicons 和局部自绘图标,缺少 Ant Design Icons 适配层。
-
同时存在 Heroicons 和局部自绘图标,缺少 Ant Design Icons 适配层。
-
没有根级暗色主题,Portal 中的 Dropdown、Popover、Modal、Tooltip 和上传面板无法保证一致暗色表面。
-
没有根级暗色主题,Portal 中的 Dropdown、Popover、Modal、Tooltip 和上传面板无法保证一致暗色表面。
-
当前活动路由未渲染数据图表;图表规范作为后续功能的强制合同写入设计文档。
-
当前活动路由未渲染数据图表;图表规范作为后续功能的强制合同写入设计文档。
共享
`XinyuanEChart`
已预留 Pie/Donut 专用适配,仅允许通过
`--xinyuan-pie-chart-*`
/
`--rc-pie-chart-*`
取色。
## 4. 迁移边界
## 4. 迁移边界
...
...
docs/XINYUAN_DESIGN_SYSTEM.md
View file @
b20234c0
...
@@ -52,7 +52,7 @@
...
@@ -52,7 +52,7 @@
-
多选动作仅显示在当前视口底部居中的 ActionBar,右侧使用关闭图标清除选择。
-
多选动作仅显示在当前视口底部居中的 ActionBar,右侧使用关闭图标清除选择。
-
业务列表沿用各模块既有分页口径;通知列表固定每页 10 条且不显示页容量选择器。Pagination 距离 DataGrid 约 8px。
-
业务列表沿用各模块既有分页口径;通知列表固定每页 10 条且不显示页容量选择器。Pagination 距离 DataGrid 约 8px。
-
成功反馈使用顶部居中的 3-4 秒 Toast,不得用页面流 Alert 推挤筛选或表格。
-
成功反馈使用顶部居中的 3-4 秒 Toast,不得用页面流 Alert 推挤筛选或表格。
-
图表和 KPI 使用独立的低饱和色板:品牌红、暖棕、灰紫、蓝灰依次承载系列,不使用绿色、荧光色或彩虹式相邻颜色;单系列固定使用
`#DA241A`
。
-
普通柱状图、折线图、排名图和组合图继续使用
`--xinyuan-chart-*`
/
`--rc-chart-*`
低饱和色板;单系列固定使用
`#DA241A`
。饼图/环形图是独立例外,仅由共享
`XinyuanEChart`
适配器使用
`--xinyuan-pie-chart-*`
/
`--rc-pie-chart-*`
:前六色依次为
`#DA241A`
、
`#3F5F86`
、
`#8A668F`
、
`#C28A3D`
、
`#8A6253`
、
`#667080`
,扩展色限定为灰蓝、灰紫、暖棕、暖灰和石板灰;禁止绿色、青绿色、荧光色和彩虹色。颜色按完整稳定分类 key 确定性分配并消除冲突,筛选、排序、Tab 切换和主题变化不得改变分类颜色,也不影响页面主色或普通图表色板
。
## 6. 表单、上传与弹窗
## 6. 表单、上传与弹窗
...
@@ -69,7 +69,7 @@
...
@@ -69,7 +69,7 @@
-
KPI 网格移动端 1 列、中屏 2 列;桌面 4 张为 4 列、5 张为 3+2、6 张为 3+3、8 张为 4+4。
-
KPI 网格移动端 1 列、中屏 2 列;桌面 4 张为 4 列、5 张为 3+2、6 张为 3+3、8 张为 4+4。
-
KPI Card 使用不透明中性表面、24px 圆角、细边框与共享轻阴影;不得使用渐变或每卡异色背景。
-
KPI Card 使用不透明中性表面、24px 圆角、细边框与共享轻阴影;不得使用渐变或每卡异色背景。
-
密集 KPI 图标面为 48px、16px 圆角、20px Ant 图标,容器与 SVG 中心偏差不得超过 0.5px。
-
密集 KPI 图标面为 48px、16px 圆角、20px Ant 图标,容器与 SVG 中心偏差不得超过 0.5px。
-
ECharts 必须从共享
主题 Hook 读取浅色/暗色文字、网格、系列和 Tooltip
Token。
-
ECharts 必须从共享
`XinyuanEChart`
/
`chartPalette.js`
读取浅色/暗色文字、网格、系列和 Tooltip Token;未来新增饼图/环形图不得在页面直接写
`itemStyle.color`
,必须使用专用 Pie/Donut
Token。
-
图表 Tooltip 使用根主题 overlay surface,不能成为白色孤岛,也不能遮挡关键数据。
-
图表 Tooltip 使用根主题 overlay surface,不能成为白色孤岛,也不能遮挡关键数据。
## 8. 响应式与交付检查
## 8. 响应式与交付检查
...
...
docs/ui-architecture.md
View file @
b20234c0
...
@@ -32,7 +32,7 @@
...
@@ -32,7 +32,7 @@
-
通知:页面直接组合 HeroUI Tabs/SearchField/Card/Chip/Badge/Tooltip 与 Pro EmptyState;业务 service 复用
`DataService`
的
`/notice/customer/*`
接口,Shell 只持有真实未读计数和 60 秒刷新事件,不在 Sidebar 建立第二入口。
-
通知:页面直接组合 HeroUI Tabs/SearchField/Card/Chip/Badge/Tooltip 与 Pro EmptyState;业务 service 复用
`DataService`
的
`/notice/customer/*`
接口,Shell 只持有真实未读计数和 60 秒刷新事件,不在 Sidebar 建立第二入口。
-
应用切换:共享
`ApplicationSwitcherDialog`
直接组合 HeroUI Modal、Pro ItemCardGroup/ItemCard、Surface/Avatar/Chip 与 Pro EmptyState;Modal 仅保留无障碍名称和搜索字段,不显示重复标题/关闭图标,面板使用页面
`background`
。
`SiteContext.siteInfo.group`
只负责动态分组与顺序并在跨组时去重,
`UserContext.applications`
和
`/application/info`
继续负责访问集合与详情。授权域
`audit_manager`
规范化为唯一当前项,并导航到部署基路径
`/audit_manager_new/`
。
-
应用切换:共享
`ApplicationSwitcherDialog`
直接组合 HeroUI Modal、Pro ItemCardGroup/ItemCard、Surface/Avatar/Chip 与 Pro EmptyState;Modal 仅保留无障碍名称和搜索字段,不显示重复标题/关闭图标,面板使用页面
`background`
。
`SiteContext.siteInfo.group`
只负责动态分组与顺序并在跨组时去重,
`UserContext.applications`
和
`/application/info`
继续负责访问集合与详情。授权域
`audit_manager`
规范化为唯一当前项,并导航到部署基路径
`/audit_manager_new/`
。
-
上传:Pro DropZone,页面边界接收真实 File。
-
上传:Pro DropZone,页面边界接收真实 File。
-
KPI/图表:仅业务存在时使用 Pro KPI
/PieChart/BarChart/ComposedChart/ChartTooltip;当前活动路由无真实图表,后续图表必须使用
`--xinyuan-chart-*`
低饱和 token
。
-
KPI/图表:仅业务存在时使用 Pro KPI
与 HeroUI 图表外壳;实际 ECharts 由共享
`XinyuanEChart`
适配器统一承载,当前活动路由无真实图表。普通柱状图、折线图和组合图必须使用
`--xinyuan-chart-*`
,饼图/环形图必须使用独立
`--xinyuan-pie-chart-*`
,不得在页面写入颜色常量
。
-
报告视图:一级 HeroUI Tabs 切换 current/history,二级内容宽度 Tabs 以稳定项目 ID 切换报告快照;当前项目只挂载工具栏和 DataGrid,不重复 Tabs 已表达的项目标题,整体进度由 KPI 承担。项目与控制点仍携带原数组索引进入现有更新/确认回调。
-
报告视图:一级 HeroUI Tabs 切换 current/history,二级内容宽度 Tabs 以稳定项目 ID 切换报告快照;当前项目只挂载工具栏和 DataGrid,不重复 Tabs 已表达的项目标题,整体进度由 KPI 承担。项目与控制点仍携带原数组索引进入现有更新/确认回调。
-
当前报告搜索由
`AuditReportContent`
放在二级 Tabs 下方,并与当前项目确认、备注保存组成标准响应式列表工具栏;它继续使用父页面原受控 keyword 与保存回调。
-
当前报告搜索由
`AuditReportContent`
放在二级 Tabs 下方,并与当前项目确认、备注保存组成标准响应式列表工具栏;它继续使用父页面原受控 keyword 与保存回调。
-
报告附件单元格由 HeroUI
`Chip/Link`
与 Pro
`DropZone`
直接组合,不创建 Card/Surface 包装层;上传、追加和删除仍进入原
`onUpdate(processIndex, pointIndex, patch)`
协议。
-
报告附件单元格由 HeroUI
`Chip/Link`
与 Pro
`DropZone`
直接组合,不创建 Card/Surface 包装层;上传、追加和删除仍进入原
`onUpdate(processIndex, pointIndex, patch)`
协议。
...
...
docs/ui-migration-checklist.md
View file @
b20234c0
...
@@ -20,7 +20,7 @@
...
@@ -20,7 +20,7 @@
| 报告附件单元格 | 报告控制点 DataGrid | 完成 | 缺有效登录态/真实附件 | HeroUI Chip/Link;无嵌套 Card |
| 报告附件单元格 | 报告控制点 DataGrid | 完成 | 缺有效登录态/真实附件 | HeroUI Chip/Link;无嵌套 Card |
| 历史详情 |
`/audit_report/:reportId`
| 完成 | 缺有效登录态/真实记录 | 代码复核完成 |
| 历史详情 |
`/audit_report/:reportId`
| 完成 | 缺有效登录态/真实记录 | 代码复核完成 |
| 外部填报 |
`/audit_report_fill`
| 完成 | 缺真实 ticket/report | 代码复核完成 |
| 外部填报 |
`/audit_report_fill`
| 完成 | 缺真实 ticket/report | 代码复核完成 |
| KPI/图表 | 报告 KPI;
无图表 | KPI 完成/图表不适用 | 缺有效登录态 | 不制造数据
|
| KPI/图表 | 报告 KPI;
当前无真实图表调用 | KPI 完成;共享 ECharts 适配器与 Pie/Donut 专用色板已就绪 | 无真实图表,保持不适用 | 不制造数据;未来饼图/环图按稳定 key 使用专用 Token
|
## 最终门槛
## 最终门槛
...
@@ -50,7 +50,7 @@
...
@@ -50,7 +50,7 @@
`src/pages/process_monitor`
、
`src/pages/RecordDetailDrawer`
、
`src/components/record-workbench`
不在当前路由树;排除 Catalyst Kit 自身目录后,共有 7 个存量源码文件仍含 Catalyst/Headless 引用。不能把这些目录误报为已迁移。
`src/pages/process_monitor`
、
`src/pages/RecordDetailDrawer`
、
`src/components/record-workbench`
不在当前路由树;排除 Catalyst Kit 自身目录后,共有 7 个存量源码文件仍含 Catalyst/Headless 引用。不能把这些目录误报为已迁移。
# 本轮迁移清单(2026-09-02)
# 本轮迁移清单(2026-09-02)
-
[
x
]
权威 theme.css / tokens.css / shell.css 与审计项目管理同步
-
[
x
]
权威 theme.css / tokens.css / shell.css 与审计项目管理同步
;Pie/Donut 专用 Token 与共享适配器已同步
-
[
x
]
固定深色 Sidebar、浅色内容区、HeroUI Pro Navbar.Trigger、应用切换 Modal
-
[
x
]
固定深色 Sidebar、浅色内容区、HeroUI Pro Navbar.Trigger、应用切换 Modal
-
[
x
]
共享 DataTable 增强固定列、sticky/横向滚动、EmptyState、ActionBar、20/50/100 页容量接口
-
[
x
]
共享 DataTable 增强固定列、sticky/横向滚动、EmptyState、ActionBar、20/50/100 页容量接口
-
[
x
]
`process_monitor`
页面移除 Catalyst、Headless UI、Heroicons 和私有渐变卡片表格
-
[
x
]
`process_monitor`
页面移除 Catalyst、Headless UI、Heroicons 和私有渐变卡片表格
...
...
docs/xinyuan-design-system.md
View file @
b20234c0
...
@@ -61,9 +61,10 @@
...
@@ -61,9 +61,10 @@
## KPI 与图表
## KPI 与图表
-
业务存在数值 KPI 时必须使用 Pro KPI compound API;模块 24px radius、16px grid gap、业务图标和唯一 tone。
-
业务存在数值 KPI 时必须使用 Pro KPI compound API;模块 24px radius、16px grid gap、业务图标和唯一 tone。
-
业务存在图表时
使用 Pro PieChart、BarChart、ComposedChart 与 ChartTooltipContent,并遵循共享 chart Tokens
。
-
业务存在图表时
,HeroUI Pro 负责图表容器与交互外壳;实际图表统一通过共享
`XinyuanEChart`
(
`echarts-for-react`
)适配器渲染,并遵循共享 chart Tokens。页面不得直接创建私有图表主题或颜色常量
。
-
稽核报告的现有业务统计使用 Pro KPI compound API;活动路由没有图表,不新增演示数字或伪造业务数据。
-
稽核报告的现有业务统计使用 Pro KPI compound API;活动路由没有图表,不新增演示数字或伪造业务数据。
-
图表色板独立于 UI 状态色,按“品牌红 -> 暖色 -> 中性灰 -> 蓝灰”顺序使用
`--xinyuan-chart-1..16`
;单系列统一
`#DA241A`
,多系列按稳定业务键分配相近低饱和色,不使用绿色、荧光色或彩虹序列。KPI 图标 tone 复用同一色板,避免蓝/绿/紫高对比跳色。当前活动路由没有真实图表调用,因此只维护 token 合同,不添加伪造图表或数据。
-
图表色板独立于 UI 状态色,普通柱状图、折线图、排名图和组合图继续使用
`--xinyuan-chart-1..16`
/
`--rc-chart-*`
;单系列统一
`#DA241A`
,多系列按稳定业务键分配相近低饱和色,不使用绿色、荧光色或彩虹序列。当前活动路由没有真实图表调用,因此只维护共享适配器和 Token 合同,不添加伪造图表或数据。
-
饼图 / 环形图是普通分类色板之外的专用例外,仅由共享
`XinyuanEChart`
在
`series.type === 'pie'`
时接管,使用
`--xinyuan-pie-chart-1..16`
/
`--rc-pie-chart-*`
;前六个固定为
`#DA241A`
、
`#3F5F86`
、
`#8A668F`
、
`#C28A3D`
、
`#8A6253`
、
`#667080`
,后续使用低饱和灰蓝、灰紫、暖棕、暖灰和石板灰扩展。饼图禁止绿色、青绿色、荧光色和彩虹色;按完整且排序后的稳定分类 key 确定性分配并消除冲突,筛选、排序、Tab 切换和主题变化不得改变分类颜色。该色板不影响页面品牌主色及普通图表 Token。
## 验收
## 验收
...
...
docs/xinyuan-ui-audit.md
View file @
b20234c0
...
@@ -65,7 +65,7 @@
...
@@ -65,7 +65,7 @@
-
活动弹窗使用 HeroUI Modal/AlertDialog;普通辅助按钮使用
`secondary`
,删除确认使用 danger。控制点查看态采用无框单列字段布局,字段标签使用 muted、字段值使用 foreground,避免在 Overlay 上叠加同层级 Surface;控制点查看和编辑在同一 Modal 内切换,编辑入口保留权限与原保存回调。
-
活动弹窗使用 HeroUI Modal/AlertDialog;普通辅助按钮使用
`secondary`
,删除确认使用 danger。控制点查看态采用无框单列字段布局,字段标签使用 muted、字段值使用 foreground,避免在 Overlay 上叠加同层级 Surface;控制点查看和编辑在同一 Modal 内切换,编辑入口保留权限与原保存回调。
-
全应用弹窗已统一到鑫元尺寸阶梯:确认弹窗
`md/480px`
,单列表单与填报链接
`lg/640px`
,控制点详情/表单使用
`4xl/1088px`
;应用切换保持基准
`46rem/736px`
。大尺寸面板通过
`bg-background p-7 sm:max-w-[68rem]`
与 HeroUI
`Modal.Container size="lg"`
组合实现,正文保持独立滚动。
-
全应用弹窗已统一到鑫元尺寸阶梯:确认弹窗
`md/480px`
,单列表单与填报链接
`lg/640px`
,控制点详情/表单使用
`4xl/1088px`
;应用切换保持基准
`46rem/736px`
。大尺寸面板通过
`bg-background p-7 sm:max-w-[68rem]`
与 HeroUI
`Modal.Container size="lg"`
组合实现,正文保持独立滚动。
-
报告页已有真实业务统计改为 Pro KPI compound API;活动路由没有图表,不新增图表或演示数据。主题专项已固定 HeroUI
`--accent`
/
`--focus`
为
`#DA241A`
,并提供完整
`--primary-50..950`
与
`--color-primary-*`
色阶;success 映射为低饱和蓝灰,图表 token 不含绿色。
-
报告页已有真实业务统计改为 Pro KPI compound API;活动路由没有图表,不新增图表或演示数据。主题专项已固定 HeroUI
`--accent`
/
`--focus`
为
`#DA241A`
,并提供完整
`--primary-50..950`
与
`--color-primary-*`
色阶;success 映射为低饱和蓝灰,图表 token 不含绿色。
-
全仓库无 ECharts 源码引用,未使用的
`echarts`
与
`echarts-for-react`
根依赖已移除
。
-
当前活动路由无真实图表调用,不新增伪造图表或演示数据。共享
`src/components/XinyuanEChart.jsx`
与
`src/design-system/chartPalette.js`
已建立统一 ECharts 边界:普通图表使用
`--xinyuan-chart-*`
,饼图/环形图仅使用独立
`--xinyuan-pie-chart-*`
/
`--rc-pie-chart-*`
,并按稳定分类 key 做确定性冲突消解;页面不直接写颜色或私有图表主题
。
-
权威
`theme.css`
逐字节一致;活动页不存在页面级 CSS,私有外观 utility 扫描为零。
-
权威
`theme.css`
逐字节一致;活动页不存在页面级 CSS,私有外观 utility 扫描为零。
## 迁移风险
## 迁移风险
...
...
src/components/XinyuanEChart.jsx
0 → 100644
View file @
b20234c0
import
{
createResolvedXinyuanPieChartColorMap
,
getXinyuanEChartsTheme
,
}
from
'@/design-system/chartPalette'
;
import
ReactECharts
from
'echarts-for-react'
;
import
{
useCallback
,
useEffect
,
useMemo
,
useRef
}
from
'react'
;
function
forceItemStyle
(
data
,
itemStyle
)
{
if
(
!
Array
.
isArray
(
data
))
return
data
;
return
data
.
map
((
item
)
=>
{
if
(
!
item
||
typeof
item
!==
'object'
||
Array
.
isArray
(
item
))
return
item
;
return
{
...
item
,
itemStyle
:
{
...
item
.
itemStyle
,
...
itemStyle
}
};
});
}
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
pieKeys
=
(
Array
.
isArray
(
option
?.
series
)
?
option
.
series
:
[
option
?.
series
])
.
filter
((
series
)
=>
series
?.
type
===
'pie'
)
.
flatMap
((
series
)
=>
(
Array
.
isArray
(
series
.
data
)
?
series
.
data
.
map
((
item
)
=>
item
?.
name
)
:
[]));
const
pieColorMap
=
createResolvedXinyuanPieChartColorMap
(
pieKeys
);
const
applySeriesTheme
=
(
series
)
=>
{
if
(
series
?.
type
!==
'pie'
)
return
series
;
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
,
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
(
pieData
,
{
borderColor
:
theme
.
sectorGap
,
borderRadius
:
theme
.
sectorRadius
,
borderWidth
:
theme
.
sectorGapWidth
,
}),
padAngle
:
1
,
};
};
const
themedSeries
=
Array
.
isArray
(
option
?.
series
)
?
option
.
series
.
map
(
applySeriesTheme
)
:
applySeriesTheme
(
option
?.
series
);
return
{
animationDuration
:
320
,
animationDurationUpdate
:
240
,
animationEasing
:
'cubicOut'
,
aria
:
{
enabled
:
true
,
decal
:
{
show
:
false
},
...
option
?.
aria
},
color
:
option
?.
color
||
theme
.
palette
,
textStyle
:
{
color
:
theme
.
label
,
fontFamily
:
theme
.
fontFamily
,
...
option
?.
textStyle
},
...
option
,
series
:
themedSeries
,
legend
:
option
?.
legend
?
{
icon
:
'circle'
,
itemHeight
:
8
,
itemWidth
:
8
,
...
option
.
legend
,
textStyle
:
{
color
:
theme
.
label
,
fontFamily
:
theme
.
fontFamily
,
fontSize
:
12
,
lineHeight
:
16
,
...
option
.
legend
?.
textStyle
,
},
}
:
undefined
,
tooltip
:
{
appendToBody
:
true
,
backgroundColor
:
theme
.
tooltipBackground
,
borderColor
:
theme
.
tooltipBorder
,
borderWidth
:
1
,
className
:
'rc-echarts-tooltip'
,
confine
:
true
,
enterable
:
false
,
extraCssText
:
`border-radius:
${
theme
.
tooltipRadius
}
; box-shadow:
${
theme
.
tooltipShadow
}
;`
,
padding
:
[
10
,
12
],
renderMode
:
'html'
,
...
option
?.
tooltip
,
textStyle
:
{
color
:
theme
.
foreground
,
fontFamily
:
theme
.
fontFamily
,
fontSize
:
12
,
...
option
?.
tooltip
?.
textStyle
},
},
};
},
[
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
>
);
}
src/design-system/chartPalette.js
0 → 100644
View file @
b20234c0
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
}
)`
),
);
export
const
XINYUAN_CHART_SEMANTIC
=
Object
.
freeze
({
primary
:
'var(--rc-chart-primary)'
,
risk
:
'var(--rc-chart-risk)'
,
warning
:
'var(--rc-chart-warning)'
,
positive
:
'var(--rc-chart-positive)'
,
comparison
:
'var(--rc-chart-comparison)'
,
neutral
:
'var(--rc-chart-neutral)'
,
});
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
getXinyuanChartColor
(
key
,
fallbackIndex
=
0
)
{
const
stableKey
=
normalizeChartKey
(
key
);
if
(
!
stableKey
)
{
return
XINYUAN_CHART_PALETTE
[
fallbackIndex
%
XINYUAN_CHART_PALETTE
.
length
];
}
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
();
const
colorMap
=
new
Map
();
const
usedIndexes
=
new
Set
();
stableKeys
.
forEach
((
key
,
keyIndex
)
=>
{
const
preferredIndex
=
hashChartKey
(
key
)
%
XINYUAN_CHART_PALETTE
.
length
;
let
paletteIndex
=
preferredIndex
;
if
(
keyIndex
<
XINYUAN_CHART_PALETTE
.
length
)
{
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
;
}
}
}
colorMap
.
set
(
key
,
XINYUAN_CHART_PALETTE
[
paletteIndex
]);
});
return
colorMap
;
}
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
createResolvedXinyuanChartColorMap
(
keys
=
[])
{
return
new
Map
(
Array
.
from
(
createXinyuanChartColorMap
(
keys
)).
map
(([
key
,
color
])
=>
[
key
,
resolveXinyuanChartColor
(
color
),
]),
);
}
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'
,
'#222222'
),
grid
:
readRootToken
(
'--xinyuan-chart-grid'
,
'#e8e8e8'
),
label
:
readRootToken
(
'--xinyuan-chart-label'
,
'#737373'
),
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
,
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
,
tooltipRadius
:
readRootToken
(
'--xinyuan-chart-tooltip-radius'
,
'0.75rem'
),
tooltipBackground
:
readConcreteColorToken
(
'--xinyuan-chart-tooltip-background'
,
'#ffffff'
),
tooltipBorder
:
readConcreteColorToken
(
'--xinyuan-chart-tooltip-border'
,
'#dedede'
),
tooltipShadow
:
readRootToken
(
'--xinyuan-chart-tooltip-shadow'
,
'0 16px 40px rgb(15 23 42 / 14%)'
),
};
}
src/design-system/theme.css
View file @
b20234c0
...
@@ -116,6 +116,23 @@
...
@@ -116,6 +116,23 @@
--xinyuan-chart-14
:
#8A6F63
;
--xinyuan-chart-14
:
#8A6F63
;
--xinyuan-chart-15
:
#9A8174
;
--xinyuan-chart-15
:
#9A8174
;
--xinyuan-chart-16
:
#5B5964
;
--xinyuan-chart-16
:
#5B5964
;
/* Pie and donut charts use an independent categorical palette. */
--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
:
var
(
--surface
);
--xinyuan-chart-sector-radius
:
10px
;
--xinyuan-chart-sector-radius
:
10px
;
--xinyuan-chart-grid
:
#e8e8e8
;
--xinyuan-chart-grid
:
#e8e8e8
;
...
...
src/design-system/tokens.css
View file @
b20234c0
...
@@ -219,6 +219,23 @@
...
@@ -219,6 +219,23 @@
--rc-chart-14
:
var
(
--xinyuan-chart-14
);
--rc-chart-14
:
var
(
--xinyuan-chart-14
);
--rc-chart-15
:
var
(
--xinyuan-chart-15
);
--rc-chart-15
:
var
(
--xinyuan-chart-15
);
--rc-chart-16
:
var
(
--xinyuan-chart-16
);
--rc-chart-16
:
var
(
--xinyuan-chart-16
);
/* Pie/donut-only aliases. Never use these for bars, lines or ranking charts. */
--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-primary
:
var
(
--rc-chart-1
);
--rc-chart-risk
:
var
(
--rc-chart-1
);
--rc-chart-risk
:
var
(
--rc-chart-1
);
--rc-chart-warning
:
var
(
--rc-chart-3
);
--rc-chart-warning
:
var
(
--rc-chart-3
);
...
...
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