Commit db38d98a authored by Andy-bubu's avatar Andy-bubu

refine xinyuan visual tokens and filter guidance

parent c2458d09
...@@ -10,6 +10,9 @@ Catalyst props、DOM event、variant、class hook 或旧组件名。活动源码 ...@@ -10,6 +10,9 @@ Catalyst props、DOM event、variant、class hook 或旧组件名。活动源码
权威 `theme.css` 与批准的 `tokens.css`/`shell.css` 持有背景、颜色、描边、圆角、阴影和 权威 `theme.css` 与批准的 `tokens.css`/`shell.css` 持有背景、颜色、描边、圆角、阴影和
Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS。 Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS。
`theme.css` 中的 HeroUI `--accent`、`--primary`、`--focus` 和品牌相关交互 Token 固定使用鑫元基金
品牌红 `#DA241A` 及其深浅阶;图表颜色只从 `--xinyuan-chart-*` 金融低饱和色板读取。
## 共享组合 ## 共享组合
- `AppShell`:固定浅色根主题、Pro Sidebar Provider、Navbar 和 base-aware 页面滚动容器。 - `AppShell`:固定浅色根主题、Pro Sidebar Provider、Navbar 和 base-aware 页面滚动容器。
...@@ -41,6 +44,10 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS ...@@ -41,6 +44,10 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS
- `TableActionButton`:HeroUI Button/Dropdown/Tooltip 与统一 Ant Design 图标。 - `TableActionButton`:HeroUI Button/Dropdown/Tooltip 与统一 Ant Design 图标。
- `XinyuanEChart`:`echarts-for-react` 的共享主题边界,统一解析 `--xinyuan-chart-*`,注入 - `XinyuanEChart`:`echarts-for-react` 的共享主题边界,统一解析 `--xinyuan-chart-*`,注入
ARIA、Tooltip、图例、坐标轴和响应式;业务页面只拥有 series/data 等业务配置。 ARIA、Tooltip、图例、坐标轴和响应式;业务页面只拥有 series/data 等业务配置。
- `FilterSurface`(共享布局合同):三列以上字段使用 HeroUI `Surface default` 承载,字段使用
`secondary`,首行最多三个高频字段,剩余字段通过官方 `Disclosure` 展开。查询按钮与展开触发器沿字段
底部右对齐;触发器是 `sm ghost`,文案“展开/收起”,指示器由 `Disclosure.Indicator` 负责旋转。折叠
只改变显示,不触发查询或重置筛选,内容保持挂载并以布局 `hidden` 收起;移动端退化为单列。
- `DynamicFormValueEditor`:将 Subject `form` 的 `{ form, data, settings }` 协议映射为纵向 - `DynamicFormValueEditor`:将 Subject `form` 的 `{ form, data, settings }` 协议映射为纵向
HeroUI Tabs 分组导航、Disclosure、Pro DataGrid、Autocomplete、Modal 和 AlertDialog;组件只通过 `onChange` 回传 HeroUI Tabs 分组导航、Disclosure、Pro DataGrid、Autocomplete、Modal 和 AlertDialog;组件只通过 `onChange` 回传
草稿,不拥有记录更新接口或提交时机。 草稿,不拥有记录更新接口或提交时机。
...@@ -60,6 +67,19 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS ...@@ -60,6 +67,19 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS
业务标题仍按视图字段在正文中呈现。正文不得从 Tabs 下方穿过,也不得通过给 Tabs 增加私有 业务标题仍按视图字段在正文中呈现。正文不得从 Tabs 下方穿过,也不得通过给 Tabs 增加私有
背景、描边或阴影来遮挡滚动内容。 背景、描边或阴影来遮挡滚动内容。
详情弹窗的状态合同必须同时覆盖预览和编辑:
- 预览态使用页面 `background` 作为 Dialog 面板底色。正文按当前 Detail View 的 `columns` 顺序单列排列,
标签是 16px/400 `muted`,值是 16px/400 `foreground`;空值只显示弱化的“暂无内容”,不渲染输入控件、
JSON、内部字段名或整块灰色占位。普通字段不包额外 Surface;动态表单、关联记录等完整业务板块才使用
透明底、1px 灰色描边的 `rc-workspace-module`。Footer 只提供“编辑”和真实业务操作。
- 编辑态不改变 Header、Tabs、字段顺序或弹窗宽度。可编辑字段使用 HeroUI `secondary` 并通铺整行,
`disableColumns` 和权限只读字段继续以只读控件/文本呈现。修改仅写入详情草稿,dirty 和校验状态即时更新,
输入过程中不发请求。Modal Footer 固定为左侧“取消”、右侧“保存”;取消恢复打开前快照且无接口副作用,
保存按钮在无变更或校验失败时禁用,保存成功后回到预览态。编辑态不增加额外灰色模块层。
- `Modal.Body` 是预览和编辑共同的唯一纵向滚动容器,Header/Tabs/Footer 固定在各自区域;正文滚动不能覆盖、
穿过或推动 Tabs。模式切换只改变字段控件和 Footer,不得建立第二套内容布局。
所有 Modal/AlertDialog Dialog 均使用页面 `background` 作为面板底色;`RecordDetailDrawer` 的字段 所有 Modal/AlertDialog Dialog 均使用页面 `background` 作为面板底色;`RecordDetailDrawer` 的字段
按视图配置顺序单列纵向排列,普通字段直接位于 Dialog 背景。动态表单等带统计、导航的完整业务板块 按视图配置顺序单列纵向排列,普通字段直接位于 Dialog 背景。动态表单等带统计、导航的完整业务板块
使用共享 `rc-workspace-module`(24px 圆角、1px 灰色描边、透明背景)统一承载板块内容;关联记录短列表保持透明摘要,直接使用 Pro DataGrid 自有的 L2 表头和 L1 数据单元格;数据为空时仅在数据区域使用一层紧凑的 `Surface secondary` 灰色背景承载 EmptyState; 使用共享 `rc-workspace-module`(24px 圆角、1px 灰色描边、透明背景)统一承载板块内容;关联记录短列表保持透明摘要,直接使用 Pro DataGrid 自有的 L2 表头和 L1 数据单元格;数据为空时仅在数据区域使用一层紧凑的 `Surface secondary` 灰色背景承载 EmptyState;
...@@ -141,7 +161,7 @@ Umi routes / auth wrappers ...@@ -141,7 +161,7 @@ Umi routes / auth wrappers
-> route page (layout and business state) -> route page (layout and business state)
-> HeroUI base compounds -> HeroUI base compounds
-> HeroUI Pro DataGrid / DropZone / KPI -> HeroUI Pro DataGrid / DropZone / KPI
-> XinyuanEChart (ECharts renderer + shared theme adapter) -> XinyuanEChart (ECharts renderer + shared theme adapter; brand red #DA241A palette)
-> business-neutral shared compositions -> business-neutral shared compositions
-> existing service callbacks and data protocols -> existing service callbacks and data protocols
``` ```
......
...@@ -157,7 +157,7 @@ NODE ...@@ -157,7 +157,7 @@ NODE
### 5.7 ECharts ### 5.7 ECharts
- 所有图表只使用 echarts + echarts-for-react + 共享 XinyuanEChart;HeroUI Pro 只负责外围 Card、布局和 KPI。 - 所有图表只使用 echarts + echarts-for-react + 共享 XinyuanEChart;HeroUI Pro 只负责外围 Card、布局和 KPI。
- 图表颜色使用主题中的 16 个鲜艳且稳定的 --xinyuan-chart-* Token,首轮色固定按红→粉→橙→黄→蓝→紫→青→中性灰排列,第 9-16 位只作明暗补充,和 UI danger/success/warning 分离;红色保持鑫元品牌主色,粉色仅作为图表分类色使用。饼图/环图统一使用 `padAngle: 1`、2px Surface 色间隙和 `borderRadius: 10`,间隙色读取 `--xinyuan-chart-sector-gap`,圆角值读取 `--xinyuan-chart-sector-radius`,图表模块外层遵循 24px 圆角。 - 图表颜色使用主题中的 16 个克制且稳定的 --xinyuan-chart-* Token,按品牌红→暖色→中性→灰蓝的协调顺序排列,禁止绿色、荧光色和彩虹式跳色;单系列优先使用品牌红,多系列优先使用同色相深浅或相邻低冲突色阶,和 UI danger/success/warning 分离。饼图/环图统一使用 `padAngle: 1`、2px Surface 色间隙和 `borderRadius: 10`,间隙色读取 `--xinyuan-chart-sector-gap`,圆角值读取 `--xinyuan-chart-sector-radius`,图表模块外层遵循 24px 圆角。
- 同一图表的相邻分类按业务展示顺序连续使用邻近色相;有显式业务顺序时传入稳定顺序,无显式顺序时按稳定 key 确定性分配。禁止在有业务顺序时使用名称哈希把相邻扇区/图例打散为跳色色系,筛选、排序和 Tab 切换后已有 key 保持原色。 - 同一图表的相邻分类按业务展示顺序连续使用邻近色相;有显式业务顺序时传入稳定顺序,无显式顺序时按稳定 key 确定性分配。禁止在有业务顺序时使用名称哈希把相邻扇区/图例打散为跳色色系,筛选、排序和 Tab 切换后已有 key 保持原色。
- 共享适配器统一负责轴、网格、图例、ARIA、resize、globalout、HTML Tooltip、appendToBody、confine 和主题字体;页面只提供真实业务数据与 option。 - 共享适配器统一负责轴、网格、图例、ARIA、resize、globalout、HTML Tooltip、appendToBody、confine 和主题字体;页面只提供真实业务数据与 option。
- 饼图/环图外部标签必须使用 ECharts 原生 label/labelLine,标签显示“分类名称 / 真实数量”两行,引导线短而完整,标签锚定到对应引导线终点,不得错位或拉到卡片边缘。 - 饼图/环图外部标签必须使用 ECharts 原生 label/labelLine,标签显示“分类名称 / 真实数量”两行,引导线短而完整,标签锚定到对应引导线终点,不得错位或拉到卡片边缘。
......
...@@ -110,6 +110,24 @@ Inter 与系统回退字体之间切换。 ...@@ -110,6 +110,24 @@ Inter 与系统回退字体之间切换。
- 页面首层模块、普通模块、紧密相关列表/KPI 分别使用 32/24/16px 间距;移动端为 24/16px。 - 页面首层模块、普通模块、紧密相关列表/KPI 分别使用 32/24/16px 间距;移动端为 24/16px。
- 列表标题和工具栏透明无框;搜索在左、列分组紧随其后、业务操作在右、主操作最右; - 列表标题和工具栏透明无框;搜索在左、列分组紧随其后、业务操作在右、主操作最右;
手机搜索独占第一行。DataGrid 是列表唯一数据 Surface。 手机搜索独占第一行。DataGrid 是列表唯一数据 Surface。
- 筛选字段已经在控件上方显示业务标签,控件内部占位文案保持简洁:文本/数字使用“请输入”,
枚举/用户/引用使用“请选择”,日期使用“请选择日期”;禁止重复拼接“筛选+字段名”造成视觉噪音。
- 筛选模块的详细结构固定为:三个及以上字段使用 HeroUI `Surface variant="default"`,外层只承担
`rounded-3xl p-6` 的页面布局;字段控件统一使用 HeroUI `secondary`,圆角 12px、标签与控件间距 6px、
同行字段间距 12px。宽屏首行使用等宽三列,字段标签和控件左边界严格对齐,右侧保留操作轨道;不使用
Card、额外描边、阴影或页面私有字段背景。
- 筛选首行只放最常用的前三个字段,其余字段继续挂载在同一个 HeroUI `Disclosure` 中。首行操作按字段
底部对齐:手动提交页面显示品牌红 `primary` 的“查询”,随后显示 `sm` `ghost` 的“展开/收起”;实时
筛选页面不显示重复的查询或刷新按钮。展开/收起只改变字段可见性,不提交查询、不清空已选值、不改变分页。
- “展开/收起”必须使用官方 `Disclosure.Trigger + Disclosure.Indicator`,文字始终为“展开”或“收起”,
指示器使用共享 Ant Design `DownOutlined`,收起时向下、展开时由 HeroUI 状态旋转向上。触发器默认透明,
Hover、focus-visible、pressed 和 expanded 状态由 HeroUI Token 提供;禁止自绘箭头、固定灰色胶囊、分隔线或
自定义高度动画。`Disclosure.Content` 保持挂载,收起时仅使用布局 `hidden`,不能复制一套筛选状态或条件卸载。
- 筛选交互必须可键盘操作并保持 React Aria 语义:触发器有 `aria-expanded`/`aria-controls`,Enter/Space
切换后焦点仍留在触发器;展开内容按原字段顺序插入首行下方,保留 12px 行间距。切换视图时清空当前筛选并
回到首行,查询和展开状态不互相触发。
- 响应式规则固定为:桌面宽屏三列等宽,平板两列,手机单列;手机上字段先完整排列,查询和展开操作放在
字段之后并保持同一行可换行,不产生横向滚动。所有字段填满自己的网格轨道,禁止半列短控件和不均匀孤列。
- 列表区域不重复渲染与页面/Navbar 相同层级的模块标题;页面标题或当前 Tab 已能表达业务语境时, - 列表区域不重复渲染与页面/Navbar 相同层级的模块标题;页面标题或当前 Tab 已能表达业务语境时,
列表直接从工具栏开始,避免重复文案占用垂直空间。 列表直接从工具栏开始,避免重复文案占用垂直空间。
- 列表采用页面整体纵向滚动,不创建固定高度的表格纵向滚动容器,也不产生“页面滚动 + 表格滚动”的 - 列表采用页面整体纵向滚动,不创建固定高度的表格纵向滚动容器,也不产生“页面滚动 + 表格滚动”的
...@@ -157,6 +175,20 @@ Inter 与系统回退字体之间切换。 ...@@ -157,6 +175,20 @@ Inter 与系统回退字体之间切换。
Dialog 页面背景;form 等完整业务板块使用透明底、1px 灰色描边的 `rc-workspace-module` 模块层,关联记录列表不再额外包裹模块 Surface,编辑态直接使用 Dialog 页面背景;form 等完整业务板块使用透明底、1px 灰色描边的 `rc-workspace-module` 模块层,关联记录列表不再额外包裹模块 Surface,编辑态直接使用
secondary 控件,取消恢复原值且不得请求接口。详情 Modal Header 只保留功能标题和阶段 Tabs, secondary 控件,取消恢复原值且不得请求接口。详情 Modal Header 只保留功能标题和阶段 Tabs,
不展示记录 ID、内部名称或冗余小标题。删除使用受控 AlertDialog。 不展示记录 ID、内部名称或冗余小标题。删除使用受控 AlertDialog。
- 详情弹窗必须明确区分两种状态:
- **预览态**:Modal 使用页面 `background`,Header 仅显示功能标题、详情 Tabs 和可用业务操作;正文按
当前 Detail View 的 `columns` 顺序单列向下排列。字段标签使用 `muted` 的 16px/400,真实字段值使用
`foreground` 的 16px/400;空值显示弱化的“暂无内容”,不使用输入框、灰色大块 Surface 或内部字段名。
普通标量字段直接落在 Dialog 背景,只有动态表单、关联记录等完整业务板块才使用透明底灰色描边的
`rc-workspace-module`。底部只显示“编辑”及真实业务操作,不能出现保存/取消。
- **编辑态**:保持与预览态相同的 Header、Tabs、字段顺序和单列布局,切换状态不得重排内容。可编辑字段
直接使用 HeroUI `secondary` 控件并通铺弹窗内容宽度;只读字段保留预览值,`disableColumns` 始终只读。
字段修改先进入详情草稿,dirty/校验状态实时更新,不能在输入时发起保存请求。底部固定使用“取消”在左、
“保存”在右的 HeroUI Modal Footer;取消恢复打开前快照且不请求接口,保存按钮在无变更或校验失败时禁用,
保存成功后回到预览态。编辑态不额外增加灰色模块背景,只有控件本身使用 HeroUI secondary 层级。
- 详情弹窗正文是唯一纵向滚动区域,Header 和 Footer 不随正文内容穿插;Tabs 固定在 Header 内,内容从 Tabs 下方
开始,禁止正文滚动时覆盖或穿过 Tabs。预览与编辑均使用相同 4xl 宽度、24px Modal 圆角、页面背景和字段间距,
仅控件交互状态与 Footer 操作随模式变化。
- Subject `form` 字段保持 `{ form, data, settings }` 存储协议,但业务 UI 只显示表单名称、 - Subject `form` 字段保持 `{ form, data, settings }` 存储协议,但业务 UI 只显示表单名称、
行数和动态字段 DataGrid。多值表单按 `settings.group` 使用 HeroUI `Tabs orientation="vertical"` 行数和动态字段 DataGrid。多值表单按 `settings.group` 使用 HeroUI `Tabs orientation="vertical"`
分组:左侧为纵向分组导航,右侧为当前分组内容;实例使用 Disclosure,新增分组位于左侧导航底部, 分组:左侧为纵向分组导航,右侧为当前分组内容;实例使用 Disclosure,新增分组位于左侧导航底部,
...@@ -184,11 +216,11 @@ Inter 与系统回退字体之间切换。 ...@@ -184,11 +216,11 @@ Inter 与系统回退字体之间切换。
- 详情和关联记录中的历史数组字符串必须先解析再展示:数组值使用业务标签以“、”连接;流程图/文档结构等结构化对象展示节点、连线、章节和内容块摘要;无法解析出名称的内部 ID 显示“已关联记录/已关联 N 项”,禁止把 JSON、ObjectId 或存储字段原文直接暴露给业务人员。编辑态 JSON 输入仍保持原数据协议。 - 详情和关联记录中的历史数组字符串必须先解析再展示:数组值使用业务标签以“、”连接;流程图/文档结构等结构化对象展示节点、连线、章节和内容块摘要;无法解析出名称的内部 ID 显示“已关联记录/已关联 N 项”,禁止把 JSON、ObjectId 或存储字段原文直接暴露给业务人员。编辑态 JSON 输入仍保持原数据协议。
- 数字指标使用 Pro KPI;所有图表统一通过共享 `XinyuanEChart` 使用 `echarts` 与 - 数字指标使用 Pro KPI;所有图表统一通过共享 `XinyuanEChart` 使用 `echarts` 与
`echarts-for-react`。系列色、坐标轴、网格、图例、Tooltip Surface/边框/阴影均读取 `echarts-for-react`。系列色、坐标轴、网格、图例、Tooltip Surface/边框/阴影均读取
`theme.css` 的 `--xinyuan-chart-*` Token。首轮 8 个色位固定按红→粉→橙→黄→蓝→紫→青→中性灰排列, `theme.css` 的 `--xinyuan-chart-*` Token。色板按品牌红→暖色→中性→灰蓝的协调顺序排列,
让最多 8 个分类拥有清晰且高级的色相差异;第 9-16 位提供对应的明暗补充,页面只提供业务数据和 不使用彩虹式跳色、绿色或荧光色。16 个固定色值依次为 `#DA241A`、`#B83A32`、`#C95A52`、
图表 option。具体色值依次为 `#991b1b`、`#db2777`、`#ea580c`、`#eab308`、`#1d4ed8`、 `#D47A70`、`#A65A45`、`#B87958`、`#C99772`、`#6F6674`、`#807783`、`#938B96`、
`#7e22ce`、`#0891b2`、`#475569`、`#dc2626`、`#ec4899`、`#f97316`、`#facc15`、`#60a5fa`、 `#4F6380`、`#61758F`、`#74879A`、`#8A6F63`、`#9A8174`、`#B0A5AD`。单系列优先使用品牌红,
`#a855f7`、`#06b6d4`、`#1e293b`。图标始终使用 同一指标的不同状态优先使用品牌红深浅层级;图标始终使用
`AppIcons` 汇出的 Ant Design Icons,ECharts 不作为图标来源。 `AppIcons` 汇出的 Ant Design Icons,ECharts 不作为图标来源。
- 所有 Autocomplete、引用、用户和材料选择器的下拉选项只展示一行业务标题;不得在第二行 - 所有 Autocomplete、引用、用户和材料选择器的下拉选项只展示一行业务标题;不得在第二行
展示内部 ID、用户名、文件名、年份或其它技术描述。描述字段可以继续保留用于搜索、匹配和 展示内部 ID、用户名、文件名、年份或其它技术描述。描述字段可以继续保留用于搜索、匹配和
...@@ -207,8 +239,9 @@ Inter 与系统回退字体之间切换。 ...@@ -207,8 +239,9 @@ Inter 与系统回退字体之间切换。
`borderRadius: 10`),形成清晰分隔但不改变数据比例;间隙色必须读取 `--xinyuan-chart-sector-gap`, `borderRadius: 10`),形成清晰分隔但不改变数据比例;间隙色必须读取 `--xinyuan-chart-sector-gap`,
圆角值读取 `--xinyuan-chart-sector-radius`, 圆角值读取 `--xinyuan-chart-sector-radius`,
随主题 Surface 自动适配。图表外层模块继续使用 24px 共享圆角。 随主题 Surface 自动适配。图表外层模块继续使用 24px 共享圆角。
- 严谨业务界面不使用粉色、玫红或洋红危险色;HeroUI `danger` / `danger-soft` 必须继承鑫元红系 - 严谨业务界面不使用绿色系、粉色、玫红或洋红危险色;HeroUI 主色固定为鑫元基金品牌红
`--danger` Token。图表中的红色序列同样使用红系,不使用粉色替代危险语义。 `#DA241A`,由 `--accent`/`--primary` 提供,Hover 使用 `#C91F17`,Pressed 使用 `#B51B14`。
`danger` / `danger-soft` 继续继承品牌红深色阶,不允许被 HeroUI 默认蓝色或其它主题色覆盖。
- 详情请求失败、详情动作失败和保存失败使用 HeroUI `toast.danger` 短提醒(约 4 秒自动消失), - 详情请求失败、详情动作失败和保存失败使用 HeroUI `toast.danger` 短提醒(约 4 秒自动消失),
不在弹窗内容流中渲染持续占位的错误 Alert;需要用户持续处理的锁定、字段校验等业务提示仍保留在上下文中。 不在弹窗内容流中渲染持续占位的错误 Alert;需要用户持续处理的锁定、字段校验等业务提示仍保留在上下文中。
......
...@@ -284,9 +284,9 @@ HeroUI Context。`@heroui/styles@3.2.4` 的两个 pnpm peer 虚拟目录仅分 ...@@ -284,9 +284,9 @@ HeroUI Context。`@heroui/styles@3.2.4` 的两个 pnpm peer 虚拟目录仅分
- 驾驶舱状态饼图和阶段柱图已从 HeroUI Pro/Recharts 切换到 ECharts 6.1.0 与 - 驾驶舱状态饼图和阶段柱图已从 HeroUI Pro/Recharts 切换到 ECharts 6.1.0 与
echarts-for-react 3.0.6。共享 `XinyuanEChart` 统一 ARIA、主题系列色、图例、坐标轴、网格和 echarts-for-react 3.0.6。共享 `XinyuanEChart` 统一 ARIA、主题系列色、图例、坐标轴、网格和
body HTML Tooltip;柱宽仍限制为 28px,API、统计口径、项目详情入口和数据结构未改。 body HTML Tooltip;柱宽仍限制为 28px,API、统计口径、项目详情入口和数据结构未改。
- 图表分类色已扩展为 `--xinyuan-chart-1` 至 `--xinyuan-chart-16`,使用鲜艳固定色值,首轮顺序固定为 - 图表分类色已扩展为 `--xinyuan-chart-1` 至 `--xinyuan-chart-16`,改用克制、低冲突的金融色板,
红→粉→橙→黄→蓝→紫→青→中性灰; 按品牌红→暖色→中性→灰蓝的协调顺序取色,不使用绿色、荧光色或彩虹式跳色;
通过稳定业务顺序/业务 key 分配相邻色相,禁止页面写入随机或局部色值;项目状态分布按 通过稳定业务顺序/业务 key 分配相邻色阶,禁止页面写入随机或局部色值;项目状态分布按
计划、执行、归档、整改、待启动、完成的顺序连续取用色板,筛选后 key 颜色保持不变。 计划、执行、归档、整改、待启动、完成的顺序连续取用色板,筛选后 key 颜色保持不变。
- 状态环图已启用 ECharts 外部分类标签和 `labelLine` 引导线,标签显示真实状态与数量, - 状态环图已启用 ECharts 外部分类标签和 `labelLine` 引导线,标签显示真实状态与数量,
使用 `labelLayout` 根据 `labelLinePoints` 将标签锚定到各自折线终点,避免文案与折线错位。 使用 `labelLayout` 根据 `labelLinePoints` 将标签锚定到各自折线终点,避免文案与折线错位。
......
...@@ -31,6 +31,14 @@ function normalizeFilterFieldPath(field) { ...@@ -31,6 +31,14 @@ function normalizeFilterFieldPath(field) {
: `metadata.${value}`; : `metadata.${value}`;
} }
function getFilterPlaceholder(controlType) {
return ['text', 'number'].includes(controlType)
? '请输入'
: controlType === 'date'
? '请选择日期'
: '请选择';
}
function resolveFilterColumns(view, columns = [], subject) { function resolveFilterColumns(view, columns = [], subject) {
const configuredFields = view?.settings?.filters?.fields; const configuredFields = view?.settings?.filters?.fields;
// Preserve the existing visible-column fallback when the setting is absent. // Preserve the existing visible-column fallback when the setting is absent.
...@@ -85,7 +93,7 @@ function buildFilterSchema(columns = []) { ...@@ -85,7 +93,7 @@ function buildFilterSchema(columns = []) {
label: column.label || field, label: column.label || field,
type: controlType, type: controlType,
options, options,
placeholder: `筛选${column.label || field}`, placeholder: getFilterPlaceholder(controlType),
}); });
return fields; return fields;
}, []); }, []);
......
...@@ -311,6 +311,15 @@ function defaultGetRecordLabel(record) { ...@@ -311,6 +311,15 @@ function defaultGetRecordLabel(record) {
return record?.title || record?.name || record?.id || '当前记录'; return record?.title || record?.name || record?.id || '当前记录';
} }
function getFilterPlaceholder(field) {
if (field?.placeholder) return field.placeholder;
if (field?.type === 'text' || field?.type === 'number') return '请输入';
if (field?.type === 'date' || field?.type === 'datetime' || field?.type === 'month') {
return '请选择日期';
}
return '请选择';
}
function defaultRenderCell(record, column) { function defaultRenderCell(record, column) {
const sourceRecord = record?.raw || record; const sourceRecord = record?.raw || record;
const value = getFieldValue(sourceRecord, column.field); const value = getFieldValue(sourceRecord, column.field);
...@@ -969,7 +978,7 @@ export default function RecordWorkbench({ ...@@ -969,7 +978,7 @@ export default function RecordWorkbench({
value={filters[field.key] || ''} value={filters[field.key] || ''}
onChange={(value) => onFiltersChange?.(field.key, value)} onChange={(value) => onFiltersChange?.(field.key, value)}
options={field.options || []} options={field.options || []}
placeholder={field.placeholder || `请选择${field.label}`} placeholder={getFilterPlaceholder(field)}
variant={filterFieldVariant} variant={filterFieldVariant}
/> />
) : field.type === 'date' || field.type === 'datetime' || field.type === 'month' ? ( ) : field.type === 'date' || field.type === 'datetime' || field.type === 'month' ? (
...@@ -985,7 +994,7 @@ export default function RecordWorkbench({ ...@@ -985,7 +994,7 @@ export default function RecordWorkbench({
) : field.type === 'reference' ? ( ) : field.type === 'reference' ? (
<ReferenceSearchSelect <ReferenceSearchSelect
fieldDef={field.fieldDef} fieldDef={field.fieldDef}
placeholder={field.placeholder || `筛选${field.label}`} placeholder={getFilterPlaceholder(field)}
value={filters[field.key] || ''} value={filters[field.key] || ''}
onChange={(value) => onFiltersChange?.(field.key, value)} onChange={(value) => onFiltersChange?.(field.key, value)}
/> />
...@@ -993,7 +1002,7 @@ export default function RecordWorkbench({ ...@@ -993,7 +1002,7 @@ export default function RecordWorkbench({
<UserSearchSelect <UserSearchSelect
ariaLabel={field.label} ariaLabel={field.label}
multiple={Boolean(field.fieldDef?.multiple)} multiple={Boolean(field.fieldDef?.multiple)}
placeholder={field.placeholder || `筛选${field.label}`} placeholder={getFilterPlaceholder(field)}
value={filters[field.key] || ''} value={filters[field.key] || ''}
onChange={(value) => onFiltersChange?.(field.key, value)} onChange={(value) => onFiltersChange?.(field.key, value)}
/> />
...@@ -1003,7 +1012,7 @@ export default function RecordWorkbench({ ...@@ -1003,7 +1012,7 @@ export default function RecordWorkbench({
type={field.type === 'number' ? 'number' : 'text'} type={field.type === 'number' ? 'number' : 'text'}
value={filters[field.key] || ''} value={filters[field.key] || ''}
onChange={(event) => onFiltersChange?.(field.key, event.target.value)} onChange={(event) => onFiltersChange?.(field.key, event.target.value)}
placeholder={field.placeholder} placeholder={getFilterPlaceholder(field)}
variant={filterFieldVariant} variant={filterFieldVariant}
/> />
)} )}
......
...@@ -11,11 +11,30 @@ ...@@ -11,11 +11,30 @@
[data-theme="light"], [data-theme="light"],
[data-theme="default"] { [data-theme="default"] {
/* Theme Colors (Light Mode) */ /* Theme Colors (Light Mode) */
--accent: oklch(57.14% 0.2156 29.20); /* Xinyuan Fund brand red. HeroUI's accent is the primary action color. */
--accent: #DA241A;
--accent-hover: #C91F17;
--accent-pressed: #B51B14;
--accent-foreground: oklch(99.11% 0 0); --accent-foreground: oklch(99.11% 0 0);
--primary: #DA241A;
--primary-hover: #C91F17;
--primary-pressed: #B51B14;
--primary-foreground: #FFFFFF;
--primary-50: #FCE9E7;
--primary-100: #F9D0CC;
--primary-200: #F3A29A;
--primary-300: #EC756D;
--primary-400: #E44A3F;
--primary-500: #DA241A;
--primary-600: #C91F17;
--primary-700: #B51B14;
--primary-800: #941711;
--primary-900: #73120D;
--primary-950: #4A0C08;
--background: oklch(97.02% 0.0000 29.20); --background: oklch(97.02% 0.0000 29.20);
--border: oklch(90.00% 0.0000 29.20); --border: oklch(90.00% 0.0000 29.20);
--danger: oklch(45.32% 0.1661 27.03); --danger: #B51B14;
--danger-hover: #941711;
--danger-foreground: oklch(99.11% 0 0); --danger-foreground: oklch(99.11% 0 0);
--default: oklch(94.00% 0.0000 29.20); --default: oklch(94.00% 0.0000 29.20);
--default-foreground: oklch(21.03% 0.0059 29.20); --default-foreground: oklch(21.03% 0.0059 29.20);
...@@ -23,7 +42,7 @@ ...@@ -23,7 +42,7 @@
--field-border: transparent; --field-border: transparent;
--field-foreground: oklch(21.03% 0.0000 29.20); --field-foreground: oklch(21.03% 0.0000 29.20);
--field-placeholder: oklch(55.17% 0.0000 29.20); --field-placeholder: oklch(55.17% 0.0000 29.20);
--focus: oklch(57.14% 0.2156 29.20); --focus: #DA241A;
--foreground: oklch(21.03% 0.0000 29.20); --foreground: oklch(21.03% 0.0000 29.20);
--muted: oklch(55.17% 0.0000 29.20); --muted: oklch(55.17% 0.0000 29.20);
--overlay: oklch(100.00% 0.0000 29.20); --overlay: oklch(100.00% 0.0000 29.20);
...@@ -70,24 +89,23 @@ ...@@ -70,24 +89,23 @@
* Ant Design remains the icon source. Resolve these CSS variables to * Ant Design remains the icon source. Resolve these CSS variables to
* concrete colors before assigning them to an ECharts option object. * concrete colors before assigning them to an ECharts option object.
*/ */
/* First pass: red -> pink -> orange -> yellow -> blue -> purple -> cyan -> neutral. */ /* Coordinated financial palette: brand reds -> warm neutrals -> gray blues. */
--xinyuan-chart-1: #991b1b; --xinyuan-chart-1: #DA241A;
--xinyuan-chart-2: #db2777; --xinyuan-chart-2: #B83A32;
--xinyuan-chart-3: #ea580c; --xinyuan-chart-3: #C95A52;
--xinyuan-chart-4: #eab308; --xinyuan-chart-4: #D47A70;
--xinyuan-chart-5: #1d4ed8; --xinyuan-chart-5: #A65A45;
--xinyuan-chart-6: #7e22ce; --xinyuan-chart-6: #B87958;
--xinyuan-chart-7: #0891b2; --xinyuan-chart-7: #C99772;
--xinyuan-chart-8: #475569; --xinyuan-chart-8: #6F6674;
/* Second pass: stronger/lighter companions for charts with more series. */ --xinyuan-chart-9: #807783;
--xinyuan-chart-9: #dc2626; --xinyuan-chart-10: #938B96;
--xinyuan-chart-10: #ec4899; --xinyuan-chart-11: #4F6380;
--xinyuan-chart-11: #f97316; --xinyuan-chart-12: #61758F;
--xinyuan-chart-12: #facc15; --xinyuan-chart-13: #74879A;
--xinyuan-chart-13: #60a5fa; --xinyuan-chart-14: #8A6F63;
--xinyuan-chart-14: #a855f7; --xinyuan-chart-15: #9A8174;
--xinyuan-chart-15: #06b6d4; --xinyuan-chart-16: #B0A5AD;
--xinyuan-chart-16: #1e293b;
--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;
...@@ -105,11 +123,29 @@ ...@@ -105,11 +123,29 @@
[data-theme="dark"] { [data-theme="dark"] {
color-scheme: dark; color-scheme: dark;
/* Theme Colors (Dark Mode) */ /* Theme Colors (Dark Mode) */
--accent: oklch(57.14% 0.2156 29.20); --accent: #DA241A;
--accent-hover: #C91F17;
--accent-pressed: #B51B14;
--accent-foreground: oklch(99.11% 0 0); --accent-foreground: oklch(99.11% 0 0);
--primary: #DA241A;
--primary-hover: #C91F17;
--primary-pressed: #B51B14;
--primary-foreground: #FFFFFF;
--primary-50: #FCE9E7;
--primary-100: #F9D0CC;
--primary-200: #F3A29A;
--primary-300: #EC756D;
--primary-400: #E44A3F;
--primary-500: #DA241A;
--primary-600: #C91F17;
--primary-700: #B51B14;
--primary-800: #941711;
--primary-900: #73120D;
--primary-950: #4A0C08;
--background: oklch(12.00% 0.0000 29.20); --background: oklch(12.00% 0.0000 29.20);
--border: oklch(28.00% 0.0000 29.20); --border: oklch(28.00% 0.0000 29.20);
--danger: oklch(62% 0.16 27.03); --danger: #B51B14;
--danger-hover: #941711;
--danger-foreground: oklch(99.11% 0 0); --danger-foreground: oklch(99.11% 0 0);
--default: oklch(27.40% 0.0000 29.20); --default: oklch(27.40% 0.0000 29.20);
--default-foreground: oklch(99.11% 0 0); --default-foreground: oklch(99.11% 0 0);
...@@ -117,7 +153,7 @@ ...@@ -117,7 +153,7 @@
--field-border: transparent; --field-border: transparent;
--field-foreground: oklch(99.11% 0.0000 29.20); --field-foreground: oklch(99.11% 0.0000 29.20);
--field-placeholder: oklch(70.50% 0.0000 29.20); --field-placeholder: oklch(70.50% 0.0000 29.20);
--focus: oklch(57.14% 0.2156 29.20); --focus: #DA241A;
--foreground: oklch(99.11% 0.0000 29.20); --foreground: oklch(99.11% 0.0000 29.20);
--muted: oklch(70.50% 0.0000 29.20); --muted: oklch(70.50% 0.0000 29.20);
--overlay: oklch(21.03% 0.0000 29.20); --overlay: oklch(21.03% 0.0000 29.20);
......
...@@ -92,14 +92,14 @@ ...@@ -92,14 +92,14 @@
0 4px 12px rgb(15 23 42 / 2%); 0 4px 12px rgb(15 23 42 / 2%);
/* KPI icon surfaces. Card bodies remain neutral; only the icon well carries tone. */ /* KPI icon surfaces. Card bodies remain neutral; only the icon well carries tone. */
--rc-metric-tone-info: oklch(55% 0.14 255); --rc-metric-tone-info: #4F6380;
--rc-metric-tone-success: oklch(54% 0.13 145); --rc-metric-tone-success: #61758F;
--rc-metric-tone-teal: oklch(55% 0.1 205); --rc-metric-tone-teal: #74879A;
--rc-metric-tone-danger: oklch(52% 0.17 25); --rc-metric-tone-danger: #DA241A;
--rc-metric-tone-warning: oklch(62% 0.12 75); --rc-metric-tone-warning: #B87958;
--rc-metric-tone-violet: oklch(56% 0.12 300); --rc-metric-tone-violet: #6F6674;
--rc-metric-tone-indigo: oklch(53% 0.13 275); --rc-metric-tone-indigo: #807783;
--rc-metric-tone-coral: oklch(60% 0.12 48); --rc-metric-tone-coral: #C95A52;
--rc-metric-tone-neutral: var(--muted); --rc-metric-tone-neutral: var(--muted);
--rc-metric-tone-info-soft: color-mix( --rc-metric-tone-info-soft: color-mix(
in oklab, in oklab,
......
...@@ -10,6 +10,12 @@ ...@@ -10,6 +10,12 @@
@source "./pages/**/*.{js,jsx,ts,tsx}"; @source "./pages/**/*.{js,jsx,ts,tsx}";
@source "./wrapper/**/*.{js,jsx,ts,tsx}"; @source "./wrapper/**/*.{js,jsx,ts,tsx}";
/* Keep HeroUI primary button pressed feedback on the Xinyuan brand-red scale. */
.button--primary {
--button-bg-hover: var(--primary-hover);
--button-bg-pressed: var(--primary-pressed);
}
@theme { @theme {
--font-inter: --font-inter:
"Inter", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; "Inter", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
......
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