Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
R
risk_case_library
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
risk_case_library
Commits
dedde183
Commit
dedde183
authored
Sep 01, 2026
by
Andy-bubu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: finalize Xinyuan UI alignment
parent
2078e013
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
2707 additions
and
511 deletions
+2707
-511
application-switcher-open.yml
application-switcher-open.yml
+337
-0
dashboard-sidebar-snapshot.yml
dashboard-sidebar-snapshot.yml
+209
-0
ui-architecture.md
docs/ui-architecture.md
+3
-2
ui-migration-checklist.md
docs/ui-migration-checklist.md
+10
-9
xinyuan-design-system.md
docs/xinyuan-design-system.md
+7
-5
xinyuan-ui-audit.md
docs/xinyuan-ui-audit.md
+17
-13
page-size-open.yml
page-size-open.yml
+470
-0
resource-booking-table.yml
resource-booking-table.yml
+470
-0
resource-booking.yml
resource-booking.yml
+285
-0
DataTable.jsx
src/components/DataTable.jsx
+167
-50
XinyuanAutocomplete.jsx
src/components/XinyuanAutocomplete.jsx
+23
-9
chartPalette.js
src/design-system/chartPalette.js
+15
-0
shell.css
src/design-system/shell.css
+266
-44
theme.css
src/design-system/theme.css
+104
-57
tokens.css
src/design-system/tokens.css
+35
-37
ApplicationSwitcherDialog.jsx
src/pages/project_manager_xy/ApplicationSwitcherDialog.jsx
+215
-179
Layout.jsx
src/pages/project_manager_xy/Layout.jsx
+2
-51
audit-material-center.business-flow.md
...project_manager_xy/audit-material-center.business-flow.md
+1
-1
index.jsx
src/pages/project_manager_xy/resource-booking/index.jsx
+62
-53
index.jsx
src/pages/project_manager_xy/resource-stat/index.jsx
+9
-1
No files found.
application-switcher-open.yml
0 → 100644
View file @
dedde183
-
generic
:
-
generic [ref=f10e1]
:
-
complementary [ref=f10e3]
:
-
generic [ref=f10e5]
:
-
img "鑫元" [ref=f10e7]
-
generic [ref=f10e8]
:
警示案例库
-
button "切换应用" [ref=f10e11] [cursor=pointer]
-
treegrid "案例库工作台" [ref=f10e15]
:
-
row "驾驶舱" [level=1] [ref=f10e16] [cursor=pointer]
:
-
gridcell "驾驶舱" [ref=f10e17]
-
row "案例管理" [level=1] [ref=f10e23] [cursor=pointer]
:
-
gridcell "案例管理" [ref=f10e24]
-
button "打开用户菜单" [ref=f10e31] [cursor=pointer]
:
-
generic [ref=f10e32]
:
A
-
generic [ref=f10e34]
:
-
generic [ref=f10e35]
:
Andy
-
generic [ref=f10e36]
:
andy
-
main [ref=f10e37]
:
-
navigation [ref=f10e38]
:
-
generic [ref=f10e39]
:
-
button "收起侧栏" [ref=f10e40] [cursor=pointer]
-
heading "驾驶舱" [level=1] [ref=f10e41]
-
button "通知消息" [ref=f10e44] [cursor=pointer]
-
generic [ref=f10e47]
:
-
heading "驾驶舱" [level=1] [ref=f10e48]
-
generic [ref=f10e49]
:
-
generic [ref=f10e51]
:
-
generic [ref=f10e52]
:
-
term [ref=f10e54]
:
案例总数
-
generic [ref=f10e55]
:
-
definition [ref=f10e56]
:
"
15"
-
generic "案例总数" [ref=f10e57]
-
generic [ref=f10e61]
:
-
term [ref=f10e63]
:
分类数量
-
generic [ref=f10e64]
:
-
definition [ref=f10e65]
:
"
10"
-
generic "分类数量" [ref=f10e66]
-
generic [ref=f10e70]
:
-
term [ref=f10e72]
:
标签数量
-
generic [ref=f10e73]
:
-
definition [ref=f10e74]
:
"
15"
-
generic "标签数量" [ref=f10e75]
-
generic [ref=f10e79]
:
-
term [ref=f10e81]
:
案例类型
-
generic [ref=f10e82]
:
-
definition [ref=f10e83]
:
"
15"
-
generic "案例类型" [ref=f10e84]
-
generic [ref=f10e88]
:
-
term [ref=f10e90]
:
待整理
-
generic [ref=f10e91]
:
-
definition [ref=f10e92]
:
"
0"
-
generic "待整理" [ref=f10e93]
-
generic [ref=f10e97]
:
-
generic [ref=f10e98]
:
-
generic [ref=f10e99]
:
-
heading "分类结构" [level=3] [ref=f10e101]
-
generic [ref=f10e105]
:
-
list [ref=f10e107]
:
-
listitem [ref=f10e108]
:
-
img "信息安全风险 legend icon" [ref=f10e109]
-
text
:
信息安全风险
-
listitem [ref=f10e111]
:
-
img "信息技术风险 legend icon" [ref=f10e112]
-
text
:
信息技术风险
-
listitem [ref=f10e114]
:
-
img "反洗钱风险 legend icon" [ref=f10e115]
-
text
:
反洗钱风险
-
listitem [ref=f10e117]
:
-
img "服务风险 legend icon" [ref=f10e118]
-
text
:
服务风险
-
listitem [ref=f10e120]
:
-
img "未分类 legend icon" [ref=f10e121]
-
text
:
未分类
-
listitem [ref=f10e123]
:
-
img "治理风险 legend icon" [ref=f10e124]
-
text
:
治理风险
-
listitem [ref=f10e126]
:
-
img "知识管理风险 legend icon" [ref=f10e127]
-
text
:
知识管理风险
-
listitem [ref=f10e129]
:
-
img "财务风险 legend icon" [ref=f10e130]
-
text
:
财务风险
-
application [ref=f10e132]
:
-
generic [ref=f10e133]
:
13案例记录
-
generic [ref=f10e161]
:
-
heading "案例类型排行" [level=3] [ref=f10e163]
-
application [ref=f10e168]
:
-
generic [ref=f10e207]
:
-
generic [ref=f10e208]
:
-
generic [ref=f10e209]
:
"
0"
-
generic [ref=f10e211]
:
"
1"
-
generic [ref=f10e213]
:
"
2"
-
generic [ref=f10e215]
:
"
3"
-
generic [ref=f10e217]
:
"
4"
-
generic [ref=f10e219]
:
-
generic [ref=f10e220]
:
形式审查疏漏
-
generic [ref=f10e222]
:
评价机制失效
-
generic [ref=f10e224]
:
主数据同步异常
-
generic [ref=f10e226]
:
提醒失效
-
generic [ref=f10e228]
:
经验未复用
-
generic [ref=f10e230]
:
幂等控制缺失
-
generic [ref=f10e232]
:
权限超期
-
generic [ref=f10e234]
:
标签误分类
-
generic [ref=f10e236]
:
-
generic [ref=f10e237]
:
-
heading "案例来源分布" [level=3] [ref=f10e239]
-
application [ref=f10e244]
:
-
generic [ref=f10e283]
:
-
generic [ref=f10e284]
:
-
generic [ref=f10e285]
:
招标复核
-
generic [ref=f10e287]
:
采购复盘
-
generic [ref=f10e289]
:
运营巡检
-
generic [ref=f10e291]
:
运营复盘
-
generic [ref=f10e293]
:
技术事故复盘
-
generic [ref=f10e295]
:
消保月度复盘
-
generic [ref=f10e297]
:
-
generic [ref=f10e298]
:
"
0"
-
generic [ref=f10e300]
:
"
1"
-
generic [ref=f10e302]
:
"
2"
-
generic [ref=f10e304]
:
"
3"
-
generic [ref=f10e306]
:
"
4"
-
generic [ref=f10e308]
:
-
heading "关键字频次" [level=3] [ref=f10e310]
-
application [ref=f10e315]
:
-
generic [ref=f10e354]
:
-
generic [ref=f10e355]
:
-
generic [ref=f10e356]
:
"
0"
-
generic [ref=f10e358]
:
"
1"
-
generic [ref=f10e360]
:
"
2"
-
generic [ref=f10e362]
:
"
3"
-
generic [ref=f10e364]
:
"
4"
-
generic [ref=f10e366]
:
-
generic [ref=f10e367]
:
投标文件
-
generic [ref=f10e369]
:
盖章页
-
generic [ref=f10e371]
:
形式审查
-
generic [ref=f10e373]
:
供应商评价
-
generic [ref=f10e375]
:
履约管理
-
generic [ref=f10e377]
:
续签决策
-
generic [ref=f10e379]
:
印鉴卡
-
generic [ref=f10e381]
:
主数据同步
-
generic [ref=f10e383]
:
-
heading "最近更新" [level=2] [ref=f10e384]
-
grid "最近更新案例" [ref=f10e390]
:
-
rowgroup [ref=f10e391]
:
-
row [ref=f10e392]
:
-
columnheader [ref=f10e393]
:
-
text
:
案例标题
-
slider "尺寸调整器 案例标题" [ref=f10e394]
:
"
460"
-
columnheader [ref=f10e395]
:
-
text
:
分类
-
slider "尺寸调整器 分类" [ref=f10e396]
:
"
160"
-
columnheader [ref=f10e397]
:
-
text
:
标签
-
slider "尺寸调整器 标签" [ref=f10e398]
:
"
260"
-
columnheader [ref=f10e399]
:
-
text
:
更新时间
-
slider "尺寸调整器 更新时间" [ref=f10e400]
:
"
180"
-
rowgroup [ref=f10e401]
:
-
row [ref=f10e402]
:
-
rowheader [ref=f10e403]
:
-
button "2026测试-投标文件盖章页缺页未识别案例 某投标文件扫描件在上传过程中遗漏了一页盖章页,但评审小组仅核对目录和总页数,未逐项检查关键签章位置,导致带缺页材料进入评审流程。虽然最终未中标,但事件反映出形式审查清单执行不严。" [ref=f10e404]
:
-
generic [ref=f10e405]
:
2026测试-投标文件盖章页缺页未识别案例
-
generic [ref=f10e406]
:
某投标文件扫描件在上传过程中遗漏了一页盖章页,但评审小组仅核对目录和总页数,未逐项检查关键签章位置,导致带缺页材料进入评审流程。虽然最终未中标,但事件反映出形式审查清单执行不严。
-
gridcell "未分类" [ref=f10e407]
-
gridcell "招投标,AI批量造数-20260521-续批" [ref=f10e410]
-
gridcell "2026-08-13 15:52" [ref=f10e413]
-
row [ref=f10e415]
:
-
rowheader [ref=f10e416]
:
-
button "2026测试-供应商履约评价长期未更新案例 部分长期合作供应商的履约评价停留在上一年度,项目交付中的延期、返工和服务投诉未及时沉淀到评价结果,导致续签和复购决策缺少真实依据。问题说明履约反馈回路未真正嵌入采购管理。" [ref=f10e417]
:
-
generic [ref=f10e418]
:
2026测试-供应商履约评价长期未更新案例
-
generic [ref=f10e419]
:
部分长期合作供应商的履约评价停留在上一年度,项目交付中的延期、返工和服务投诉未及时沉淀到评价结果,导致续签和复购决策缺少真实依据。问题说明履约反馈回路未真正嵌入采购管理。
-
gridcell "治理风险" [ref=f10e420]
-
gridcell "供应商评价,AI批量造数-20260521-续批" [ref=f10e423]
-
gridcell "2026-06-15 12:19" [ref=f10e426]
-
row [ref=f10e428]
:
-
rowheader [ref=f10e429]
:
-
button "2026测试-印鉴卡更新后柜面未同步案例 客户完成印鉴变更后,核心主数据已更新,但部分柜面终端缓存未及时刷新,导致经办人员按旧印鉴卡进行人工复核并误判异常。事件虽经复核纠正,但暴露出关键主数据变更后的前端同步机制不足。" [ref=f10e430]
:
-
generic [ref=f10e431]
:
2026测试-印鉴卡更新后柜面未同步案例
-
generic [ref=f10e432]
:
客户完成印鉴变更后,核心主数据已更新,但部分柜面终端缓存未及时刷新,导致经办人员按旧印鉴卡进行人工复核并误判异常。事件虽经复核纠正,但暴露出关键主数据变更后的前端同步机制不足。
-
gridcell "治理风险" [ref=f10e433]
-
gridcell "印鉴管理,AI批量造数-20260521-续批" [ref=f10e436]
-
gridcell "2026-06-15 12:19" [ref=f10e439]
-
row [ref=f10e441]
:
-
rowheader [ref=f10e442]
:
-
button "2026测试-客户证照到期提醒触达失败案例 系统已配置客户证照到期前提醒,但由于短信渠道签名异常和邮件地址长期未维护,多个客户未收到续补资料通知。后续业务办理时集中出现证照过期问题,影响交易时效。该问题暴露出提醒机制仅关注“发出”,未验证“送达”和“处理”。" [ref=f10e443]
:
-
generic [ref=f10e444]
:
2026测试-客户证照到期提醒触达失败案例
-
generic [ref=f10e445]
:
系统已配置客户证照到期前提醒,但由于短信渠道签名异常和邮件地址长期未维护,多个客户未收到续补资料通知。后续业务办理时集中出现证照过期问题,影响交易时效。该问题暴露出提醒机制仅关注“发出”,未验证“送达”和“处理”。
-
gridcell "治理风险" [ref=f10e446]
-
gridcell "证照管理,AI批量造数-20260521-续批" [ref=f10e449]
-
gridcell "2026-06-15 12:19" [ref=f10e452]
-
row [ref=f10e454]
:
-
rowheader [ref=f10e455]
:
-
button "2026测试-项目复盘结论未进入知识库案例 某重点项目在结束后形成了较完整的问题复盘和改进建议,但材料仅保存在项目群和个人网盘中,未录入统一知识库。后续相似项目重复踩坑,团队才发现既有经验未被有效复用。说明复盘机制缺乏沉淀动作和责任要求。" [ref=f10e456]
:
-
generic [ref=f10e457]
:
2026测试-项目复盘结论未进入知识库案例
-
generic [ref=f10e458]
:
某重点项目在结束后形成了较完整的问题复盘和改进建议,但材料仅保存在项目群和个人网盘中,未录入统一知识库。后续相似项目重复踩坑,团队才发现既有经验未被有效复用。说明复盘机制缺乏沉淀动作和责任要求。
-
gridcell "知识管理风险" [ref=f10e459]
-
gridcell "知识沉淀,AI批量造数-20260521-续批" [ref=f10e462]
-
gridcell "2026-05-21 16:58" [ref=f10e465]
-
row [ref=f10e467]
:
-
rowheader [ref=f10e468]
:
-
button "2026测试-系统批量导入失败重试无幂等案例 某业务系统批量导入任务在超时后被人工重复重试,因缺少批次幂等键,导致部分记录重复写入。后续清理耗费大量人工,也影响了下游报表准确性。问题说明批量任务失败处理设计不足。" [ref=f10e469]
:
-
generic [ref=f10e470]
:
2026测试-系统批量导入失败重试无幂等案例
-
generic [ref=f10e471]
:
某业务系统批量导入任务在超时后被人工重复重试,因缺少批次幂等键,导致部分记录重复写入。后续清理耗费大量人工,也影响了下游报表准确性。问题说明批量任务失败处理设计不足。
-
gridcell "信息技术风险" [ref=f10e472]
-
gridcell "批量导入,AI批量造数-20260521-续批" [ref=f10e475]
-
gridcell "2026-05-21 16:58" [ref=f10e478]
-
button [ref=f10e480] [cursor=pointer]
-
generic [ref=f10e481]
:
投标文件
-
generic [ref=f10e482]
:
-
generic
:
-
generic
:
-
button "取消"
-
dialog "应用切换" [active] [ref=f10e483]
:
-
button "关闭应用切换" [ref=f10e484] [cursor=pointer]
-
generic [ref=f10e486]
:
-
heading "应用切换" [level=2] [ref=f10e487]
-
group [ref=f10e489]
:
-
searchbox "搜索应用" [ref=f10e490]
-
button "清空应用搜索"
-
generic [ref=f10e493]
:
-
group [ref=f10e494]
:
-
generic [ref=f10e495]
:
-
heading "演示" [level=3] [ref=f10e496]
-
paragraph [ref=f10e497]
:
12 个应用
-
link "监察稽核管理【新】" [ref=f10e498] [cursor=pointer]
:
-
/url
:
/audit_manager_new
-
generic "监察稽核管理【新】" [ref=f10e503]
-
link "廉洁从业管理【新】" [ref=f10e504] [cursor=pointer]
:
-
/url
:
/integrity_practice_management_new
-
generic "廉洁从业管理【新】" [ref=f10e509]
-
link "廉洁风险防控【新】" [ref=f10e510] [cursor=pointer]
:
-
/url
:
/integrity_risk_prevention_new
-
generic "廉洁风险防控【新】" [ref=f10e515]
-
link "oa流程监控【新】" [ref=f10e516] [cursor=pointer]
:
-
/url
:
/oa_process_monitor_new
-
generic "oa流程监控【新】" [ref=f10e521]
-
link "模型分析" [ref=f10e522] [cursor=pointer]
:
-
/url
:
/model_analysis
-
generic "模型分析" [ref=f10e527]
-
link "AI应用" [ref=f10e528] [cursor=pointer]
:
-
/url
:
/ai_chat
-
generic "AI应用" [ref=f10e533]
-
link "问题整改" [ref=f10e534] [cursor=pointer]
:
-
/url
:
/problem_rectification_new
-
generic "问题整改" [ref=f10e539]
-
link "审计项目管理[新]" [ref=f10e540] [cursor=pointer]
:
-
/url
:
/audit_project_manager_new
-
generic "审计项目管理[新]" [ref=f10e545]
-
link "党风廉政建设" [ref=f10e546] [cursor=pointer]
:
-
/url
:
/incorrupt_party_build
-
generic "党风廉政建设" [ref=f10e551]
-
link "部门任务管理" [ref=f10e552] [cursor=pointer]
:
-
/url
:
/project_manager_xy
-
generic "部门任务管理" [ref=f10e557]
-
link "审计材料中心" [ref=f10e558] [cursor=pointer]
:
-
/url
:
/audit_material_manager
-
generic "审计材料中心" [ref=f10e563]
-
link "警示案例库 案例库工作台 当前" [ref=f10e564] [cursor=pointer]
:
-
/url
:
/risk_case_library
-
generic [ref=f10e568]
:
-
generic "警示案例库" [ref=f10e569]
-
generic "案例库工作台" [ref=f10e570]
-
generic [ref=f10e571]
:
当前
-
group [ref=f10e574]
:
-
generic [ref=f10e575]
:
-
heading "审计应用" [level=3] [ref=f10e576]
-
paragraph [ref=f10e577]
:
6 个应用
-
link "审计项目管理" [ref=f10e578] [cursor=pointer]
:
-
/url
:
/audit_project_manager
-
generic "审计项目管理" [ref=f10e583]
-
link "问题整改" [ref=f10e584] [cursor=pointer]
:
-
/url
:
/problem_rectification
-
generic "问题整改" [ref=f10e589]
-
link "流程管理" [ref=f10e590] [cursor=pointer]
:
-
/url
:
/process_management
-
generic "流程管理" [ref=f10e595]
-
link "公告管理" [ref=f10e596] [cursor=pointer]
:
-
/url
:
/disclosure_management
-
generic "公告管理" [ref=f10e601]
-
link "OA流程监控" [ref=f10e602] [cursor=pointer]
:
-
/url
:
/oa_process_monitor
-
generic "OA流程监控" [ref=f10e607]
-
link "量化指标 量化指标的描述信息" [ref=f10e608] [cursor=pointer]
:
-
/url
:
/indicator_manager
-
generic [ref=f10e612]
:
-
generic "量化指标" [ref=f10e613]
-
generic "量化指标的描述信息" [ref=f10e614]
-
group [ref=f10e615]
:
-
generic [ref=f10e616]
:
-
heading "法规制度管理" [level=3] [ref=f10e617]
-
paragraph [ref=f10e618]
:
3 个应用
-
link "内部制度" [ref=f10e619] [cursor=pointer]
:
-
/url
:
/internal_regulation
-
generic "内部制度" [ref=f10e624]
-
link "内部制度管理" [ref=f10e625] [cursor=pointer]
:
-
/url
:
/internal_regulation_manager
-
generic "内部制度管理" [ref=f10e630]
-
link "外部法规" [ref=f10e631] [cursor=pointer]
:
-
/url
:
/law_reference
-
generic "外部法规" [ref=f10e636]
-
group [ref=f10e637]
:
-
generic [ref=f10e638]
:
-
heading "廉洁风险管理" [level=3] [ref=f10e639]
-
paragraph [ref=f10e640]
:
2 个应用
-
link "廉洁风险防控" [ref=f10e641] [cursor=pointer]
:
-
/url
:
/integrity_risk_prevention
-
generic "廉洁风险防控" [ref=f10e646]
-
link "廉洁从业管理" [ref=f10e647] [cursor=pointer]
:
-
/url
:
/integrity_practice_management
-
generic "廉洁从业管理" [ref=f10e652]
-
group [ref=f10e653]
:
-
generic [ref=f10e654]
:
-
heading "其他相关应用" [level=3] [ref=f10e655]
-
paragraph [ref=f10e656]
:
3 个应用
-
link "监察稽核管理" [ref=f10e657] [cursor=pointer]
:
-
/url
:
/audit_manager
-
generic "监察稽核管理" [ref=f10e662]
-
link "规章制度管理" [ref=f10e663] [cursor=pointer]
:
-
/url
:
/policy_manager
-
generic "规章制度管理" [ref=f10e668]
-
link "风险管理应用" [ref=f10e669] [cursor=pointer]
:
-
/url
:
/risk_manager
-
generic "风险管理应用" [ref=f10e674]
-
group [ref=f10e675]
:
-
generic [ref=f10e676]
:
-
heading "其他应用" [level=3] [ref=f10e677]
-
paragraph [ref=f10e678]
:
3 个应用
-
link "测试量化" [ref=f10e679] [cursor=pointer]
:
-
/url
:
/test
-
generic "测试量化" [ref=f10e684]
-
link "廉洁风险管理" [ref=f10e685] [cursor=pointer]
:
-
/url
:
/integrity_risk_manager
-
generic "廉洁风险管理" [ref=f10e690]
-
link "手机监测" [ref=f10e691] [cursor=pointer]
:
-
/url
:
/phone_detect
-
generic "手机监测" [ref=f10e696]
\ No newline at end of file
dashboard-sidebar-snapshot.yml
0 → 100644
View file @
dedde183
-
generic [active]
:
-
generic [ref=f8e1]
:
-
complementary [ref=f8e3]
:
-
generic [ref=f8e5]
:
-
img "鑫元" [ref=f8e7]
-
generic [ref=f8e8]
:
警示案例库
-
button "切换应用" [ref=f8e11] [cursor=pointer]
-
treegrid "案例库工作台" [ref=f8e15]
:
-
row "驾驶舱" [level=1] [ref=f8e16] [cursor=pointer]
:
-
gridcell "驾驶舱" [ref=f8e17]
-
row "案例管理" [level=1] [ref=f8e23] [cursor=pointer]
:
-
gridcell "案例管理" [ref=f8e24]
-
button "打开用户菜单" [ref=f8e31] [cursor=pointer]
:
-
generic [ref=f8e32]
:
A
-
generic [ref=f8e34]
:
-
generic [ref=f8e35]
:
Andy
-
generic [ref=f8e36]
:
andy
-
main [ref=f8e37]
:
-
navigation [ref=f8e38]
:
-
generic [ref=f8e39]
:
-
button "收起侧栏" [ref=f8e40] [cursor=pointer]
-
heading "驾驶舱" [level=1] [ref=f8e41]
-
button "通知消息" [ref=f8e44] [cursor=pointer]
-
generic [ref=f8e47]
:
-
heading "驾驶舱" [level=1] [ref=f8e48]
-
generic [ref=f8e49]
:
-
generic [ref=f8e51]
:
-
generic [ref=f8e52]
:
-
term [ref=f8e54]
:
案例总数
-
generic [ref=f8e55]
:
-
definition [ref=f8e56]
:
"
15"
-
generic "案例总数" [ref=f8e57]
-
generic [ref=f8e61]
:
-
term [ref=f8e63]
:
分类数量
-
generic [ref=f8e64]
:
-
definition [ref=f8e65]
:
"
10"
-
generic "分类数量" [ref=f8e66]
-
generic [ref=f8e70]
:
-
term [ref=f8e72]
:
标签数量
-
generic [ref=f8e73]
:
-
definition [ref=f8e74]
:
"
15"
-
generic "标签数量" [ref=f8e75]
-
generic [ref=f8e79]
:
-
term [ref=f8e81]
:
案例类型
-
generic [ref=f8e82]
:
-
definition [ref=f8e83]
:
"
15"
-
generic "案例类型" [ref=f8e84]
-
generic [ref=f8e88]
:
-
term [ref=f8e90]
:
待整理
-
generic [ref=f8e91]
:
-
definition [ref=f8e92]
:
"
0"
-
generic "待整理" [ref=f8e93]
-
generic [ref=f8e97]
:
-
generic [ref=f8e98]
:
-
generic [ref=f8e99]
:
-
heading "分类结构" [level=3] [ref=f8e101]
-
generic [ref=f8e105]
:
-
list [ref=f8e107]
:
-
listitem [ref=f8e108]
:
-
img "信息安全风险 legend icon" [ref=f8e109]
-
text
:
信息安全风险
-
listitem [ref=f8e111]
:
-
img "信息技术风险 legend icon" [ref=f8e112]
-
text
:
信息技术风险
-
listitem [ref=f8e114]
:
-
img "反洗钱风险 legend icon" [ref=f8e115]
-
text
:
反洗钱风险
-
listitem [ref=f8e117]
:
-
img "服务风险 legend icon" [ref=f8e118]
-
text
:
服务风险
-
listitem [ref=f8e120]
:
-
img "未分类 legend icon" [ref=f8e121]
-
text
:
未分类
-
listitem [ref=f8e123]
:
-
img "治理风险 legend icon" [ref=f8e124]
-
text
:
治理风险
-
listitem [ref=f8e126]
:
-
img "知识管理风险 legend icon" [ref=f8e127]
-
text
:
知识管理风险
-
listitem [ref=f8e129]
:
-
img "财务风险 legend icon" [ref=f8e130]
-
text
:
财务风险
-
application [ref=f8e132]
:
-
generic [ref=f8e133]
:
13案例记录
-
generic [ref=f8e161]
:
-
heading "案例类型排行" [level=3] [ref=f8e163]
-
application [ref=f8e168]
:
-
generic [ref=f8e207]
:
-
generic [ref=f8e208]
:
-
generic [ref=f8e209]
:
"
0"
-
generic [ref=f8e211]
:
"
1"
-
generic [ref=f8e213]
:
"
2"
-
generic [ref=f8e215]
:
"
3"
-
generic [ref=f8e217]
:
"
4"
-
generic [ref=f8e219]
:
-
generic [ref=f8e220]
:
形式审查疏漏
-
generic [ref=f8e222]
:
评价机制失效
-
generic [ref=f8e224]
:
主数据同步异常
-
generic [ref=f8e226]
:
提醒失效
-
generic [ref=f8e228]
:
经验未复用
-
generic [ref=f8e230]
:
幂等控制缺失
-
generic [ref=f8e232]
:
权限超期
-
generic [ref=f8e234]
:
标签误分类
-
generic [ref=f8e236]
:
-
generic [ref=f8e237]
:
-
heading "案例来源分布" [level=3] [ref=f8e239]
-
application [ref=f8e244]
:
-
generic [ref=f8e283]
:
-
generic [ref=f8e284]
:
-
generic [ref=f8e285]
:
招标复核
-
generic [ref=f8e287]
:
采购复盘
-
generic [ref=f8e289]
:
运营巡检
-
generic [ref=f8e291]
:
运营复盘
-
generic [ref=f8e293]
:
技术事故复盘
-
generic [ref=f8e295]
:
消保月度复盘
-
generic [ref=f8e297]
:
-
generic [ref=f8e298]
:
"
0"
-
generic [ref=f8e300]
:
"
1"
-
generic [ref=f8e302]
:
"
2"
-
generic [ref=f8e304]
:
"
3"
-
generic [ref=f8e306]
:
"
4"
-
generic [ref=f8e308]
:
-
heading "关键字频次" [level=3] [ref=f8e310]
-
application [ref=f8e315]
:
-
generic [ref=f8e354]
:
-
generic [ref=f8e355]
:
-
generic [ref=f8e356]
:
"
0"
-
generic [ref=f8e358]
:
"
1"
-
generic [ref=f8e360]
:
"
2"
-
generic [ref=f8e362]
:
"
3"
-
generic [ref=f8e364]
:
"
4"
-
generic [ref=f8e366]
:
-
generic [ref=f8e367]
:
投标文件
-
generic [ref=f8e369]
:
盖章页
-
generic [ref=f8e371]
:
形式审查
-
generic [ref=f8e373]
:
供应商评价
-
generic [ref=f8e375]
:
履约管理
-
generic [ref=f8e377]
:
续签决策
-
generic [ref=f8e379]
:
印鉴卡
-
generic [ref=f8e381]
:
主数据同步
-
generic [ref=f8e383]
:
-
heading "最近更新" [level=2] [ref=f8e384]
-
grid "最近更新案例" [ref=f8e390]
:
-
rowgroup [ref=f8e391]
:
-
row [ref=f8e392]
:
-
columnheader [ref=f8e393]
:
-
text
:
案例标题
-
slider "尺寸调整器 案例标题" [ref=f8e394]
:
"
460"
-
columnheader [ref=f8e395]
:
-
text
:
分类
-
slider "尺寸调整器 分类" [ref=f8e396]
:
"
160"
-
columnheader [ref=f8e397]
:
-
text
:
标签
-
slider "尺寸调整器 标签" [ref=f8e398]
:
"
260"
-
columnheader [ref=f8e399]
:
-
text
:
更新时间
-
slider "尺寸调整器 更新时间" [ref=f8e400]
:
"
180"
-
rowgroup [ref=f8e401]
:
-
row [ref=f8e402]
:
-
rowheader [ref=f8e403]
:
-
button "2026测试-投标文件盖章页缺页未识别案例 某投标文件扫描件在上传过程中遗漏了一页盖章页,但评审小组仅核对目录和总页数,未逐项检查关键签章位置,导致带缺页材料进入评审流程。虽然最终未中标,但事件反映出形式审查清单执行不严。" [ref=f8e404]
:
-
generic [ref=f8e405]
:
2026测试-投标文件盖章页缺页未识别案例
-
generic [ref=f8e406]
:
某投标文件扫描件在上传过程中遗漏了一页盖章页,但评审小组仅核对目录和总页数,未逐项检查关键签章位置,导致带缺页材料进入评审流程。虽然最终未中标,但事件反映出形式审查清单执行不严。
-
gridcell "未分类" [ref=f8e407]
-
gridcell "招投标,AI批量造数-20260521-续批" [ref=f8e410]
-
gridcell "2026-08-13 15:52" [ref=f8e413]
-
row [ref=f8e415]
:
-
rowheader [ref=f8e416]
:
-
button "2026测试-供应商履约评价长期未更新案例 部分长期合作供应商的履约评价停留在上一年度,项目交付中的延期、返工和服务投诉未及时沉淀到评价结果,导致续签和复购决策缺少真实依据。问题说明履约反馈回路未真正嵌入采购管理。" [ref=f8e417]
:
-
generic [ref=f8e418]
:
2026测试-供应商履约评价长期未更新案例
-
generic [ref=f8e419]
:
部分长期合作供应商的履约评价停留在上一年度,项目交付中的延期、返工和服务投诉未及时沉淀到评价结果,导致续签和复购决策缺少真实依据。问题说明履约反馈回路未真正嵌入采购管理。
-
gridcell "治理风险" [ref=f8e420]
-
gridcell "供应商评价,AI批量造数-20260521-续批" [ref=f8e423]
-
gridcell "2026-06-15 12:19" [ref=f8e426]
-
row [ref=f8e428]
:
-
rowheader [ref=f8e429]
:
-
button "2026测试-印鉴卡更新后柜面未同步案例 客户完成印鉴变更后,核心主数据已更新,但部分柜面终端缓存未及时刷新,导致经办人员按旧印鉴卡进行人工复核并误判异常。事件虽经复核纠正,但暴露出关键主数据变更后的前端同步机制不足。" [ref=f8e430]
:
-
generic [ref=f8e431]
:
2026测试-印鉴卡更新后柜面未同步案例
-
generic [ref=f8e432]
:
客户完成印鉴变更后,核心主数据已更新,但部分柜面终端缓存未及时刷新,导致经办人员按旧印鉴卡进行人工复核并误判异常。事件虽经复核纠正,但暴露出关键主数据变更后的前端同步机制不足。
-
gridcell "治理风险" [ref=f8e433]
-
gridcell "印鉴管理,AI批量造数-20260521-续批" [ref=f8e436]
-
gridcell "2026-06-15 12:19" [ref=f8e439]
-
row [ref=f8e441]
:
-
rowheader [ref=f8e442]
:
-
button "2026测试-客户证照到期提醒触达失败案例 系统已配置客户证照到期前提醒,但由于短信渠道签名异常和邮件地址长期未维护,多个客户未收到续补资料通知。后续业务办理时集中出现证照过期问题,影响交易时效。该问题暴露出提醒机制仅关注“发出”,未验证“送达”和“处理”。" [ref=f8e443]
:
-
generic [ref=f8e444]
:
2026测试-客户证照到期提醒触达失败案例
-
generic [ref=f8e445]
:
系统已配置客户证照到期前提醒,但由于短信渠道签名异常和邮件地址长期未维护,多个客户未收到续补资料通知。后续业务办理时集中出现证照过期问题,影响交易时效。该问题暴露出提醒机制仅关注“发出”,未验证“送达”和“处理”。
-
gridcell "治理风险" [ref=f8e446]
-
gridcell "证照管理,AI批量造数-20260521-续批" [ref=f8e449]
-
gridcell "2026-06-15 12:19" [ref=f8e452]
-
row [ref=f8e454]
:
-
rowheader [ref=f8e455]
:
-
button "2026测试-项目复盘结论未进入知识库案例 某重点项目在结束后形成了较完整的问题复盘和改进建议,但材料仅保存在项目群和个人网盘中,未录入统一知识库。后续相似项目重复踩坑,团队才发现既有经验未被有效复用。说明复盘机制缺乏沉淀动作和责任要求。" [ref=f8e456]
:
-
generic [ref=f8e457]
:
2026测试-项目复盘结论未进入知识库案例
-
generic [ref=f8e458]
:
某重点项目在结束后形成了较完整的问题复盘和改进建议,但材料仅保存在项目群和个人网盘中,未录入统一知识库。后续相似项目重复踩坑,团队才发现既有经验未被有效复用。说明复盘机制缺乏沉淀动作和责任要求。
-
gridcell "知识管理风险" [ref=f8e459]
-
gridcell "知识沉淀,AI批量造数-20260521-续批" [ref=f8e462]
-
gridcell "2026-05-21 16:58" [ref=f8e465]
-
row [ref=f8e467]
:
-
rowheader [ref=f8e468]
:
-
button "2026测试-系统批量导入失败重试无幂等案例 某业务系统批量导入任务在超时后被人工重复重试,因缺少批次幂等键,导致部分记录重复写入。后续清理耗费大量人工,也影响了下游报表准确性。问题说明批量任务失败处理设计不足。" [ref=f8e469]
:
-
generic [ref=f8e470]
:
2026测试-系统批量导入失败重试无幂等案例
-
generic [ref=f8e471]
:
某业务系统批量导入任务在超时后被人工重复重试,因缺少批次幂等键,导致部分记录重复写入。后续清理耗费大量人工,也影响了下游报表准确性。问题说明批量任务失败处理设计不足。
-
gridcell "信息技术风险" [ref=f8e472]
-
gridcell "批量导入,AI批量造数-20260521-续批" [ref=f8e475]
-
gridcell "2026-05-21 16:58" [ref=f8e478]
-
button [ref=f8e480] [cursor=pointer]
-
generic [ref=f8e481]
:
投标文件
\ No newline at end of file
docs/ui-architecture.md
View file @
dedde183
...
...
@@ -8,7 +8,8 @@
-
UI 原语与 compound component 在页面或业务中立共享组合中直接导入 HeroUI/HeroUI Pro。
-
共享组合只可封装重复布局、可访问性与当前 HeroUI value/selection/event 模型,不得复刻 Catalyst props、DOM event 或旧 CSS hook。
-
DataGrid 组合映射列定义并转发原生 selection/fixed-column/ActionBar/Pagination;案例列表分页容量固定为 20,不展示容量选择器。
-
DataGrid 组合映射列定义并转发原生 selection/fixed-column/ActionBar/Pagination;宽表以共享
`.data-table-scroll`
为唯一滚动容器并固定首业务列和操作列,案例列表分页默认 20 条并提供 20/50/100 条数选择。
-
`DataTablePagination`
的页容量选择只将下一个数值传给页面既有
`onPageSizeChange`
;列表页面负责将页码置零并使用原
`loadCaseList`
请求,组件不持有业务查询状态。
-
文件上传采用 HeroUI Pro DropZone 的
`File[]`
协议;页面边界继续只取既有单文件值。
-
应用切换直接组合 HeroUI Modal/Card/Surface/Avatar 与 HeroUI Pro EmptyState;分组只读取现有
`SiteContext`
的
`/info -> site.group`
,应用仍合并
`/application/info`
与当前用户权限数据,不引入硬编码分组或应用。
...
...
@@ -18,7 +19,7 @@
## 样式所有权
`src/design-system/theme.css`
是不可变主题权威;
`tokens.css`
、
`shell.css`
、
`tailwind.css`
与
`global.less`
使用“问题整改”实际副本
。项目不再加载
`portable.css`
,页面没有私有 CSS 外观层。
`src/design-system/theme.css`
与
`tokens.css`
直接同步审计项目管理当前版本;
`shell.css`
、
`tailwind.css`
与
`global.less`
使用同一共享结构规则,并保留当前项目的 Sidebar 状态、固定列和 Autocomplete 受控值扩展
。项目不再加载
`portable.css`
,页面没有私有 CSS 外观层。
本地浏览器验收产物目录
`.playwright-cli/`
与
`test-results/`
必须保持在
`.gitignore`
中。Tailwind CSS v4 会自动探测未忽略的项目文件;若验收日志进入扫描范围,CSS HMR 日志写入会反向触发持续编译,造成样式表和字体反复重载。
...
...
docs/ui-migration-checklist.md
View file @
dedde183
...
...
@@ -10,10 +10,10 @@
## 页面迁移
-
[
x
]
Login:
问题整改几何、字段节奏、验证码、filled/autofill、浅/深色
-
[
x
]
Shell:Sidebar/Navbar/退出/应用切换/
主题/offcanvas(代码
结构)
-
[
x
]
应用切换:同步
`model_analysis`
Modal 几何、动态分组/计数、其他/全部回退、两列 Card 与 identity well(代码结构)
-
[
x
]
驾驶舱:Pro KPI、PieChart、BarChart、ComposedChart、ChartTooltip(代码结构)
-
[
x
]
Login:
审计项目管理几何、字段节奏、验证码和 filled/autofill
-
[
x
]
Shell:Sidebar/Navbar/退出/应用切换/
offcanvas(审计项目管理
结构)
-
[
x
]
应用切换:同步
审计项目管理
Modal 几何、动态分组/计数、其他/全部回退、两列 Card 与 identity well(代码结构)
-
[
x
]
驾驶舱:Pro KPI、PieChart、BarChart、ComposedChart、ChartTooltip
;环形扇区间隙/10px 扇区圆角、柱图 8px 末端圆角与 24px 分析模块圆角
(代码结构)
-
[
x
]
案例管理:Tabs、筛选工具栏、DataGrid、Pagination、ActionBar、EmptyState(代码结构)
-
[
x
]
弹窗:新建、查看/编辑、批量修改、导入、删除确认(代码结构)
-
[
x
]
清除 Catalyst/Headless/ECharts/原生可见控件与页面私有视觉 CSS
...
...
@@ -34,16 +34,17 @@
-
[
x
]
Login 768x1024 light/dark
-
[
x
]
Login 390x844 light/dark
-
[
x
]
应用切换 1440x900、1280x720、768x1024、390x844 light/dark:尺寸、两列/单列、动态分组、全部应用回退、唯一当前态、Body 滚动、Escape 与焦点回归
-
[
]
打开 Sidebar、用户退出菜单、应用切换真实动态分组、Dropdown/ListBox/DatePicker
-
[
x
]
打开 Sidebar、用户退出菜单、应用切换真实动态分组、分页 ListBox
-
[
]
验证 KPI 动态状态与图表 Tooltip
-
[
x
]
目标与问题整改在 1440x900、1280x720、768x1024、390x844 完成浅/深色成对
截图,页面无 viewport 级横向溢出
-
[
x
]
历史目标验收在 1440x900、1280x720、768x1024、390x844 完成
截图,页面无 viewport 级横向溢出
-
[
x
]
真实验证 Tabs、三项折叠筛选、Checkbox、DataGrid、Pagination、ActionBar、详情查看/编辑/取消和移动端通知空态
-
[
x
]
目标与基准最终控制台 0 error / 0 warning
-
[
x
]
宽表外层滚动、首业务列/操作列固定、分页 20/50/100 条数选择、详情画布同色与导入默认 Surface
-
[
x
]
目标最终控制台 0 error / 0 warning
-
[
]
验证 shortTitle true/false、单/多值附件、材料选择、本地上传、取消、失败与重试
-
[
]
验证通知未读 0/1/99/100、单条/全部已读、动态操作与站内外跳转
未勾选原因:真实 View 接口返回空数组,Subject 没有 file 字段,通知接口返回 0 条;未伪造后台配置、通知或业务数据。
-
[
x
]
Login 控制台 0 runtime/slot/provider/accessibility 错误或警告
-
[
]
本次主题同步后重新验证受鉴权页面:当前被服务端验证码拒绝,未伪造登录会话
## 最终门槛
...
...
@@ -56,4 +57,4 @@
-
[
x
]
HeroUI/React Aria 依赖树唯一
-
[
x
]
HeroUI Pro 必需子路径通过 ESM 实际导入
应用切换
目标页打开态已验收;
`model_analysis`
隔离会话未登录,无法对其打开态做同屏像素测量。其余未勾选项仍需使用真实业务状态验收
,不得使用伪造 token、网络数据或测试数字替代。
应用切换
与数据表目标页打开态已在历史真实会话中验收;本次主题同步后的受鉴权复核仍需有效验证码
,不得使用伪造 token、网络数据或测试数字替代。
docs/xinyuan-design-system.md
View file @
dedde183
# 鑫元设计系统
本项目直接采用 HeroUI/HeroUI Pro 与鑫元权威主题。“
问题整改”应用是结构、尺寸、间距、组件层级与交互的唯一实现
基准。
本项目直接采用 HeroUI/HeroUI Pro 与鑫元权威主题。“
审计项目管理”应用是当前 Shell、列表、弹窗与应用切换的结构、尺寸、间距、组件层级与交互
基准。
## 依赖与样式
-
基础控件直接从
`@heroui/react`
导入。
-
Sidebar、Navbar、DataGrid、ActionBar、EmptyState、DropZone、KPI 与图表从
`@heroui-pro/react`
对应子路径直接导入。
-
样式顺序固定为 Tailwind、HeroUI、HeroUI Pro、
`theme.css`
、
`tokens.css`
、批准的共享布局层。
-
`theme.css`
必须与 Skill 权威副
本逐字节一致;页面不得覆盖 HeroUI 外观、slot、BEM 类或交互状态。
-
本轮
`theme.css`
与
`tokens.css`
必须与审计项目管理当前版
本逐字节一致;页面不得覆盖 HeroUI 外观、slot、BEM 类或交互状态。
-
页面
`className`
只承担 flex、grid、宽度、间距、对齐和响应式布局。
## 页面规则
-
Shell 使用 HeroUI Pro offcanvas Sidebar 与 Navbar;页面和 Navbar 使用 48/32/16px 对称 gutter。
-
应用切换严格使用
`model_analysis`
的 HeroUI Modal 基准:46rem 宽、42rem 最大高度、28px inset、Body 内滚动,小屏单列且
`sm`
及以上固定两列;分组读取
`/info -> site.group`
,按真实配置顺序展示分组与数量,未分组应用进入“其他应用”,无分组配置时使用“全部应用”。
-
应用切换严格使用
审计项目管理
的 HeroUI Modal 基准:46rem 宽、42rem 最大高度、28px inset、Body 内滚动,小屏单列且
`sm`
及以上固定两列;分组读取
`/info -> site.group`
,按真实配置顺序展示分组与数量,未分组应用进入“其他应用”,无分组配置时使用“全部应用”。
-
结构化筛选使用 HeroUI Surface,内部控件使用
`secondary`
;关键词搜索位于列表工具栏左侧。
-
列表使用 HeroUI Pro DataGrid 原生选择、排序、固定列、ActionBar、EmptyState 与 compound Pagination。
-
详情和内容型表单使用鑫元 4xl(1088px)Modal:HeroUI
`Modal.Container size="lg"`
保留官方交互与响应式结构,
`Modal.Dialog`
通过共享布局类限制为
`sm:max-w-[68rem]`
;不得把 HeroUI 不支持的
`4xl`
直接传给 Container。查看态只有一个
`Surface secondary`
字段组,编辑态直接放置
`secondary`
控件。
-
列表使用 HeroUI Pro DataGrid 原生选择、排序、固定列、ActionBar、EmptyState 与 Pagination;宽表由共享
`.data-table-scroll`
承担横纵向滚动,首业务列及操作列固定,分页保留真实 20/50/100 条数选择。
-
页容量选择使用现有 HeroUI Autocomplete 的受控 value 合同;切换容量只重置列表页码并复用既有查询回调,不改变筛选、排序或服务请求协议。
-
详情和内容型表单使用鑫元 4xl(1088px)Modal:HeroUI
`Modal.Container size="lg"`
保留官方交互与响应式结构,
`Modal.Dialog`
通过共享布局类限制为
`bg-background p-7 sm:max-w-[68rem]`
,使详情背景与页面画布一致;查看态不添加灰色嵌套 Surface,编辑态直接放置
`secondary`
控件。
-
指标必须使用 HeroUI Pro KPI compound API;图表必须使用 Pro PieChart、BarChart、ComposedChart 与 ChartTooltipContent。
-
图表业务序列只使用
`--rc-chart-*`
Token,不使用 UI 状态色或页面硬编码颜色。
-
环形图通过共享
`--xinyuan-chart-sector-gap`
、
`--xinyuan-chart-sector-radius`
呈现扇区间隙与 10px 扇区圆角;柱图通过共享图表几何统一使用 8px 末端圆角。分析卡片沿用
`.rc-dashboard-card`
的 24px 外圆角。
## 跨应用能力
...
...
docs/xinyuan-ui-audit.md
View file @
dedde183
# 鑫元 UI 审计
审计日期:2026-0
8-27。目标仓库为 Umi 4 / React 19,部署基路径为
`/risk_case_library/`
。整体实现基准为“问题整改”,应用切换专项基准为
`model_analysis`
当前 HeroUI 实现
。
审计日期:2026-0
9-01。目标仓库为 Umi 4 / React 19,部署基路径为
`/risk_case_library/`
。本轮 Shell、列表、弹窗和应用切换的唯一实现基准为“审计项目管理”
。
## 安装与运行基线
-
官方安装命令已在项目根目录执行成功,
`@heroui-pro/react`
解析为本项目
`node_modules`
内的
`1.0.0-beta.8`
,不是符号链接或跨项目
`file:`
依赖。
-
项目使用 npm,保留
`package-lock.json`
;旧
`pnpm-lock.yaml`
已删除。
-
当前迁移实例运行于
`http://localhost:8000/risk_case_library/`
。
-
“问题整改”基准运行于
`http://localhost:8001/problem_rectification_new/`
。
-
应用切换专项基准
`model_analysis`
运行于
`http://localhost:8002/model_analysis/`
;该隔离浏览器会话停留在登录页,因此没有把未登录状态冒充为打开态对照。
-
当前迁移实例运行于
`http://localhost:8003/risk_case_library/`
。
-
“审计项目管理”源代码基准位于
`/Users/liuchenlong/Desktop/job/audit_project_manager`
。
## 业务契约
...
...
@@ -22,14 +21,14 @@
| 范围 | 当前结果 | 迁移要求 |
| --- | --- | --- |
| Login | HeroUI;结构、尺寸、字段节奏与
问题整改一致;可访问性告警已消除 | 四视口浅/深色对照通过
|
| Login | HeroUI;结构、尺寸、字段节奏与
审计项目管理一致;可访问性告警已消除 | 固定浅色 Shell;本次复核受服务端验证码阻塞
|
| Shell | HeroUI Pro Sidebar/Navbar,offcanvas、主题、用户退出与应用切换保留既有业务回调 | 代码与共享基准一致;登录后浏览器状态待真实凭证 |
| 应用切换 | HeroUI Modal compound API;46rem/42rem/28px 几何;读取
`/info -> site.group`
动态分组、真实计数与未分组回退;两列 Card、Surface + Avatar identity、唯一当前态 |
目标应用已在四视口浅/深色真实打开验收;动态分组和无配置回退均由真实会话覆盖,专项基准打开态因未登录仍不可复测
|
| 驾驶舱 | HeroUI Pro KPI、PieChart、BarChart、ComposedChart、ChartTooltipContent |
构建通过;
真实图表 Tooltip 待登录验收 |
| 应用切换 | HeroUI Modal compound API;46rem/42rem/28px 几何;读取
`/info -> site.group`
动态分组、真实计数与未分组回退;两列 Card、Surface + Avatar identity、唯一当前态 |
与审计项目管理的两列/移动端单列结构一致
|
| 驾驶舱 | HeroUI Pro KPI、PieChart、BarChart、ComposedChart、ChartTooltipContent |
环形图使用共享扇区间隙与 10px 圆角;两类柱图使用 8px 末端圆角;外层分析 Card 为 24px。
真实图表 Tooltip 待登录验收 |
| 案例管理 | HeroUI Surface/Tabs/TextField/Autocomplete、Pro DataGrid/ActionBar/EmptyState | 默认 20、20/50/100 分页口径已保护;交互待登录验收 |
| 弹窗 | HeroUI Modal/Alert/DropZone,Container 使用官方
`lg`
、Dialog 映射鑫元 4xl/1088px,
查看态使用单一 secondary Surface | 新建弹窗宽度已按问题整改基准修正;其他打开态待登录验收
|
| 旧组件 | Catalyst、Headless、
ECharts、旧 Select/Date/Table 兼容层已移除 | 活动源码扫描为零
|
| 样式 |
`theme.css`
匹配 Skill;
`tokens.css`
、
`shell.css`
、
`tailwind.css`
、
`global.less`
匹配问题整改
|
`portable.css`
与页面私有视觉 CSS 已移除 |
| 弹窗 | HeroUI Modal/Alert/DropZone,Container 使用官方
`lg`
、Dialog 映射鑫元 4xl/1088px,
详情 Dialog 使用
`bg-background`
并取消查看态灰色嵌套 Surface | 真实打开态已确认 Dialog 与页面画布同色
|
| 旧组件 | Catalyst、Headless、
旧 Select/Date/Table 兼容层已移除 | 活动源码无旧 UI 导入;图表沿用审计项目管理的 HeroUI Pro + Recharts primitive 组合
|
| 样式 |
`theme.css`
、
`tokens.css`
已与审计项目管理逐字节一致;
`shell.css`
保留当前项目所需的 Sidebar 状态、固定列和 Autocomplete 交互扩展
|
`portable.css`
与页面私有视觉 CSS 已移除 |
## 四项功能审计
...
...
@@ -48,13 +47,18 @@ View 配置审计确认本次没有实现权限功能:活动代码不读取 `v
-
不在源码或浏览器中加入测试记录、伪造 token 或统计数字。
-
应用切换已使用现有真实登录会话完成浅/深色四视口验收;未注入 token、应用、分组或业务数据。其他模块的既有未验收项仍按迁移清单保留,不能据此标记全量应用迁移完成。
## 2026-0
8-27
浏览器验收
## 2026-0
9-01
浏览器验收
-
使用账号 Andy 经真实验证码登录目标应用和“问题整改”
,未注入 token、通知、View、附件或测试记录。
-
历史验收使用账号 Andy 经真实验证码登录目标应用
,未注入 token、通知、View、附件或测试记录。
-
在 1440x900、1280x720、768x1024、390x844 分别采集两应用浅色和深色截图。各视口目标页面
`body.scrollWidth === innerWidth`
,宽表滚动限制在 DataGrid 内;桌面 Sidebar/Navbar 为 240px/64px,移动端 Main 与 Navbar 均占满视口。
-
对照确认
两应用在相同视口下的 Sidebar、Navbar、Trigger、Bell、主题按钮
、页面内容边界、Tabs、筛选 Surface 与工具栏层级一致。
-
对照确认
目标与当时的基准在相同视口下的 Sidebar、Navbar、Trigger、Bell
、页面内容边界、Tabs、筛选 Surface 与工具栏层级一致。
-
目标真实验证:筛选默认三项与 Disclosure、卡片/表格 Tabs、DataGrid 固定操作列、Checkbox、ActionBar、固定 20 条 Pagination、详情 4xl Modal、查看态单一 secondary Surface、编辑 dirty、取消恢复且无 PUT、移动端通知空态、移动端 Sidebar、应用切换分组/计数/当前态。
-
目标与基准最终控制台均为 0 error、0 warning(忽略开发模式 React DevTools info)。
-
当前真实接口
`GET /subject/risk_case_library/view`
返回空数组,因此没有 shortTitle true/false 配置可触发;代码路径与配置失败完整字段回退已审计,但未把 Mock 当作浏览器通过。
-
当前 Subject 的 11 个字段均非
`file`
,无法在真实页面触发单值/多值、材料选择、本地上传、取消、失败与重试。
-
当前通知列表和未读数均为 0,可验证空态与 Badge 隐藏;1/99/100、单条/全部已读、动态 actions 和站内外目标没有真实通知数据可操作。
-
宽表在 1440x900 下通过真实
`scrollLeft=700`
验证:外层
`.data-table-scroll`
为唯一横向滚动容器(1096px viewport / 2056px content),首业务列和右侧操作列保持固定且固定单元格背景不透明。
-
详情 Modal 与页面主画布曾计算为同一背景;Excel 导入初始上传区为单一
`Surface default`
,未出现灰色嵌套预览 Surface。
-
审计项目管理基准的应用切换在 1440x900 实测为 736px Dialog、两列 332px 卡片;目标同一会话得到相同 46rem 两列结构。目标移动端 768x1024/390x844 的 Sidebar 是 500px/312px HeroUI offcanvas Dialog,均无 viewport 级横向溢出。
-
页容量选择器在真实列表中从 20 条/页切换到 50 条/页,页面回到第一页并保持原查询数据;1280x720、768x1024 视口均满足
`body.scrollWidth === innerWidth`
。本轮控制台检查为 0 error、0 warning。
-
本次以审计项目管理为唯一基准同步主题后,
`--background`
、
`--field-background`
和
`--accent`
已在目标登录页与基准登录页计算为相同值。目标服务端连续返回“验证码错误”,故没有伪造会话来重新打开受鉴权页面;登录失败产生的 3 条请求错误不计入 UI 控制台验收。
page-size-open.yml
0 → 100644
View file @
dedde183
-
generic [ref=f11e1]
:
-
complementary [ref=f11e3]
:
-
generic [ref=f11e5]
:
-
img "鑫元" [ref=f11e7]
-
generic [ref=f11e8]
:
警示案例库
-
button "切换应用" [ref=f11e11] [cursor=pointer]
-
treegrid "案例库工作台" [ref=f11e15]
:
-
row "驾驶舱" [level=1] [ref=f11e16] [cursor=pointer]
:
-
gridcell "驾驶舱" [ref=f11e17]
-
row "案例管理" [level=1] [ref=f11e23] [cursor=pointer]
:
-
gridcell "案例管理" [ref=f11e24]
-
button "打开用户菜单" [ref=f11e31] [cursor=pointer]
:
-
generic [ref=f11e32]
:
A
-
generic [ref=f11e34]
:
-
generic [ref=f11e35]
:
Andy
-
generic [ref=f11e36]
:
andy
-
main [ref=f11e37]
:
-
navigation [ref=f11e38]
:
-
generic [ref=f11e39]
:
-
button "收起侧栏" [ref=f11e40] [cursor=pointer]
-
heading "案例管理" [level=1] [ref=f11e41]
-
button "通知消息" [ref=f11e44] [cursor=pointer]
-
generic [ref=f11e47]
:
-
heading "案例管理" [level=1] [ref=f11e48]
-
generic [ref=f11e49]
:
-
tablist "案例风险分类" [ref=f11e54]
:
-
tab "全部案例" [selected] [ref=f11e55] [cursor=pointer]
-
tab "治理风险" [ref=f11e58] [cursor=pointer]
-
tab "信息安全风险" [ref=f11e60] [cursor=pointer]
-
tab "服务风险" [ref=f11e62] [cursor=pointer]
-
tab "财务风险" [ref=f11e64] [cursor=pointer]
-
tab "未分类" [ref=f11e66] [cursor=pointer]
-
tab "知识管理风险" [ref=f11e68] [cursor=pointer]
-
tab "信息技术风险" [ref=f11e70] [cursor=pointer]
-
tab "反洗钱风险" [ref=f11e72] [cursor=pointer]
-
tab "合同风险" [ref=f11e74] [cursor=pointer]
-
tab "合规风险" [ref=f11e76] [cursor=pointer]
-
generic [ref=f11e80]
:
-
generic [ref=f11e81]
:
-
generic [ref=f11e82]
:
-
generic [ref=f11e83]
:
案例来源
-
textbox "案例来源" [ref=f11e84]
:
-
/placeholder
:
例如:监管通报
-
generic [ref=f11e85]
:
-
generic [ref=f11e86]
:
案例类型
-
textbox "案例类型" [ref=f11e87]
:
-
/placeholder
:
例如:审批绕行
-
generic [ref=f11e88]
:
-
generic [ref=f11e89]
:
问题属性
-
textbox "问题属性" [ref=f11e90]
:
-
/placeholder
:
例如:流程绕行
-
button "展开" [ref=f11e92] [cursor=pointer]
-
generic [ref=f11e95]
:
-
generic [ref=f11e96]
:
-
group [ref=f11e99]
:
-
searchbox "关键词搜索" [ref=f11e100]
-
button "清空关键词"
-
tablist "案例展示方式" [ref=f11e104]
:
-
tab "卡片视图" [ref=f11e105] [cursor=pointer]
-
tab "表格视图" [active] [selected] [ref=f11e107] [cursor=pointer]
-
generic [ref=f11e108]
:
-
button "Excel 批量导出" [ref=f11e109] [cursor=pointer]
-
button "Excel 批量导入" [ref=f11e110] [cursor=pointer]
-
button "手动新建案例" [ref=f11e111] [cursor=pointer]
-
generic [ref=f11e112]
:
-
grid "全部案例" [ref=f11e386]
:
-
rowgroup [ref=f11e387]
:
-
row [ref=f11e388]
:
-
columnheader [ref=f11e389]
:
-
checkbox "Select all" [ref=f11e393]
-
columnheader [ref=f11e398]
:
-
text
:
标题
-
slider "尺寸调整器 标题" [ref=f11e399]
:
"
380"
-
columnheader [ref=f11e400]
:
-
text
:
分类
-
slider "尺寸调整器 分类" [ref=f11e401]
:
"
180"
-
columnheader [ref=f11e402]
:
-
text
:
标签
-
slider "尺寸调整器 标签" [ref=f11e403]
:
"
240"
-
columnheader [ref=f11e404]
:
-
text
:
案例类型
-
slider "尺寸调整器 案例类型" [ref=f11e405]
:
"
220"
-
columnheader [ref=f11e406]
:
-
text
:
关键字
-
slider "尺寸调整器 关键字" [ref=f11e407]
:
"
280"
-
columnheader [ref=f11e408]
:
-
text
:
案例来源
-
slider "尺寸调整器 案例来源" [ref=f11e409]
:
"
180"
-
columnheader [ref=f11e410]
:
-
text
:
案例等级
-
slider "尺寸调整器 案例等级" [ref=f11e411]
:
"
140"
-
columnheader [ref=f11e412]
:
-
text
:
案例年份
-
slider "尺寸调整器 案例年份" [ref=f11e413]
:
"
140"
-
columnheader [ref=f11e414]
:
-
text
:
更新时间
-
slider "尺寸调整器 更新时间" [ref=f11e415]
:
"
160"
-
columnheader [ref=f11e416]
:
-
text
:
操作
-
slider "尺寸调整器 操作" [ref=f11e417]
:
"
96"
-
rowgroup [ref=f11e418]
:
-
row [ref=f11e419]
:
-
gridcell [ref=f11e420]
:
-
checkbox "Select row 2026测试-投标文件盖章页缺页未识别案例" [ref=f11e424]
-
rowheader [ref=f11e429]
:
-
button "2026测试-投标文件盖章页缺页未识别案例" [ref=f11e430]
-
gridcell [ref=f11e431]
:
-
button "-" [ref=f11e432]
-
gridcell [ref=f11e433]
:
-
button "招投标,AI批量造数-20260521-续批" [ref=f11e434]
-
gridcell [ref=f11e435]
:
-
button "形式审查疏漏" [ref=f11e436]
-
gridcell [ref=f11e437]
:
-
button "投标文件、盖章页、形式审查" [ref=f11e438]
-
gridcell [ref=f11e439]
:
-
button "招标复核" [ref=f11e440]
-
gridcell [ref=f11e441]
:
-
button "中" [ref=f11e442]
-
gridcell [ref=f11e443]
:
-
button "2025" [ref=f11e444]
-
gridcell [ref=f11e445]
:
-
button "2026-08-13 15:52" [ref=f11e446]
-
gridcell [ref=f11e447]
:
-
button "查看详情" [ref=f11e449] [cursor=pointer]
-
row [ref=f11e451]
:
-
gridcell [ref=f11e452]
:
-
checkbox "Select row 2026测试-供应商履约评价长期未更新案例" [ref=f11e456]
-
rowheader [ref=f11e461]
:
-
button "2026测试-供应商履约评价长期未更新案例" [ref=f11e462]
-
gridcell [ref=f11e463]
:
-
button "治理风险" [ref=f11e464]
-
gridcell [ref=f11e465]
:
-
button "供应商评价,AI批量造数-20260521-续批" [ref=f11e466]
-
gridcell [ref=f11e467]
:
-
button "评价机制失效" [ref=f11e468]
-
gridcell [ref=f11e469]
:
-
button "供应商评价、履约管理、续签决策" [ref=f11e470]
-
gridcell [ref=f11e471]
:
-
button "采购复盘" [ref=f11e472]
-
gridcell [ref=f11e473]
:
-
button "中" [ref=f11e474]
-
gridcell [ref=f11e475]
:
-
button "2025" [ref=f11e476]
-
gridcell [ref=f11e477]
:
-
button "2026-06-15 12:19" [ref=f11e478]
-
gridcell [ref=f11e479]
:
-
button "查看详情" [ref=f11e481] [cursor=pointer]
-
row [ref=f11e483]
:
-
gridcell [ref=f11e484]
:
-
checkbox "Select row 2026测试-印鉴卡更新后柜面未同步案例" [ref=f11e488]
-
rowheader [ref=f11e493]
:
-
button "2026测试-印鉴卡更新后柜面未同步案例" [ref=f11e494]
-
gridcell [ref=f11e495]
:
-
button "治理风险" [ref=f11e496]
-
gridcell [ref=f11e497]
:
-
button "印鉴管理,AI批量造数-20260521-续批" [ref=f11e498]
-
gridcell [ref=f11e499]
:
-
button "主数据同步异常" [ref=f11e500]
-
gridcell [ref=f11e501]
:
-
button "印鉴卡、主数据同步、柜面核验" [ref=f11e502]
-
gridcell [ref=f11e503]
:
-
button "运营巡检" [ref=f11e504]
-
gridcell [ref=f11e505]
:
-
button "中" [ref=f11e506]
-
gridcell [ref=f11e507]
:
-
button "2026" [ref=f11e508]
-
gridcell [ref=f11e509]
:
-
button "2026-06-15 12:19" [ref=f11e510]
-
gridcell [ref=f11e511]
:
-
button "查看详情" [ref=f11e513] [cursor=pointer]
-
row [ref=f11e515]
:
-
gridcell [ref=f11e516]
:
-
checkbox "Select row 2026测试-客户证照到期提醒触达失败案例" [ref=f11e520]
-
rowheader [ref=f11e525]
:
-
button "2026测试-客户证照到期提醒触达失败案例" [ref=f11e526]
-
gridcell [ref=f11e527]
:
-
button "治理风险" [ref=f11e528]
-
gridcell [ref=f11e529]
:
-
button "证照管理,AI批量造数-20260521-续批" [ref=f11e530]
-
gridcell [ref=f11e531]
:
-
button "提醒失效" [ref=f11e532]
-
gridcell [ref=f11e533]
:
-
button "证照到期、提醒触达、送达率" [ref=f11e534]
-
gridcell [ref=f11e535]
:
-
button "运营复盘" [ref=f11e536]
-
gridcell [ref=f11e537]
:
-
button "中" [ref=f11e538]
-
gridcell [ref=f11e539]
:
-
button "2025" [ref=f11e540]
-
gridcell [ref=f11e541]
:
-
button "2026-06-15 12:19" [ref=f11e542]
-
gridcell [ref=f11e543]
:
-
button "查看详情" [ref=f11e545] [cursor=pointer]
-
row [ref=f11e547]
:
-
gridcell [ref=f11e548]
:
-
checkbox "Select row 2026测试-项目复盘结论未进入知识库案例" [ref=f11e552]
-
rowheader [ref=f11e557]
:
-
button "2026测试-项目复盘结论未进入知识库案例" [ref=f11e558]
-
gridcell [ref=f11e559]
:
-
button "知识管理风险" [ref=f11e560]
-
gridcell [ref=f11e561]
:
-
button "知识沉淀,AI批量造数-20260521-续批" [ref=f11e562]
-
gridcell [ref=f11e563]
:
-
button "经验未复用" [ref=f11e564]
-
gridcell [ref=f11e565]
:
-
button "项目复盘,知识库,经验复用" [ref=f11e566]
-
gridcell [ref=f11e567]
:
-
button "项目后评估" [ref=f11e568]
-
gridcell [ref=f11e569]
:
-
button "中" [ref=f11e570]
-
gridcell [ref=f11e571]
:
-
button "2024" [ref=f11e572]
-
gridcell [ref=f11e573]
:
-
button "2026-05-21 16:58" [ref=f11e574]
-
gridcell [ref=f11e575]
:
-
button "查看详情" [ref=f11e577] [cursor=pointer]
-
row [ref=f11e579]
:
-
gridcell [ref=f11e580]
:
-
checkbox "Select row 2026测试-系统批量导入失败重试无幂等案例" [ref=f11e584]
-
rowheader [ref=f11e589]
:
-
button "2026测试-系统批量导入失败重试无幂等案例" [ref=f11e590]
-
gridcell [ref=f11e591]
:
-
button "信息技术风险" [ref=f11e592]
-
gridcell [ref=f11e593]
:
-
button "批量导入,AI批量造数-20260521-续批" [ref=f11e594]
-
gridcell [ref=f11e595]
:
-
button "幂等控制缺失" [ref=f11e596]
-
gridcell [ref=f11e597]
:
-
button "批量导入,幂等控制,重复写入" [ref=f11e598]
-
gridcell [ref=f11e599]
:
-
button "技术事故复盘" [ref=f11e600]
-
gridcell [ref=f11e601]
:
-
button "高" [ref=f11e602]
-
gridcell [ref=f11e603]
:
-
button "2026" [ref=f11e604]
-
gridcell [ref=f11e605]
:
-
button "2026-05-21 16:58" [ref=f11e606]
-
gridcell [ref=f11e607]
:
-
button "查看详情" [ref=f11e609] [cursor=pointer]
-
row [ref=f11e611]
:
-
gridcell [ref=f11e612]
:
-
checkbox "Select row 2026测试-访客门禁权限超过申请时长案例" [ref=f11e616]
-
rowheader [ref=f11e621]
:
-
button "2026测试-访客门禁权限超过申请时长案例" [ref=f11e622]
-
gridcell [ref=f11e623]
:
-
button "信息安全风险" [ref=f11e624]
-
gridcell [ref=f11e625]
:
-
button "门禁管理,AI批量造数-20260521-续批" [ref=f11e626]
-
gridcell [ref=f11e627]
:
-
button "权限超期" [ref=f11e628]
-
gridcell [ref=f11e629]
:
-
button "访客门禁,权限超期,物理安全" [ref=f11e630]
-
gridcell [ref=f11e631]
:
-
button "安保检查" [ref=f11e632]
-
gridcell [ref=f11e633]
:
-
button "中" [ref=f11e634]
-
gridcell [ref=f11e635]
:
-
button "2026" [ref=f11e636]
-
gridcell [ref=f11e637]
:
-
button "2026-05-21 16:58" [ref=f11e638]
-
gridcell [ref=f11e639]
:
-
button "查看详情" [ref=f11e641] [cursor=pointer]
-
row [ref=f11e643]
:
-
gridcell [ref=f11e644]
:
-
checkbox "Select row 2026测试-客户投诉分类标签误用案例" [ref=f11e648]
-
rowheader [ref=f11e653]
:
-
button "2026测试-客户投诉分类标签误用案例" [ref=f11e654]
-
gridcell [ref=f11e655]
:
-
button "服务风险" [ref=f11e656]
-
gridcell [ref=f11e657]
:
-
button "客户投诉,AI批量造数-20260521-续批" [ref=f11e658]
-
gridcell [ref=f11e659]
:
-
button "标签误分类" [ref=f11e660]
-
gridcell [ref=f11e661]
:
-
button "客户投诉,标签分类,统计失真" [ref=f11e662]
-
gridcell [ref=f11e663]
:
-
button "消保月度复盘" [ref=f11e664]
-
gridcell [ref=f11e665]
:
-
button "中" [ref=f11e666]
-
gridcell [ref=f11e667]
:
-
button "2024" [ref=f11e668]
-
gridcell [ref=f11e669]
:
-
button "2026-05-21 16:57" [ref=f11e670]
-
gridcell [ref=f11e671]
:
-
button "查看详情" [ref=f11e673] [cursor=pointer]
-
row [ref=f11e675]
:
-
gridcell [ref=f11e676]
:
-
checkbox "Select row 2026测试-异常交易预警处置备注模板化案例" [ref=f11e680]
-
rowheader [ref=f11e685]
:
-
button "2026测试-异常交易预警处置备注模板化案例" [ref=f11e686]
-
gridcell [ref=f11e687]
:
-
button "反洗钱风险" [ref=f11e688]
-
gridcell [ref=f11e689]
:
-
button "异常交易,AI批量造数-20260521-续批" [ref=f11e690]
-
gridcell [ref=f11e691]
:
-
button "处置质量不足" [ref=f11e692]
-
gridcell [ref=f11e693]
:
-
button "异常交易,模板备注,复核质量" [ref=f11e694]
-
gridcell [ref=f11e695]
:
-
button "反洗钱抽样检查" [ref=f11e696]
-
gridcell [ref=f11e697]
:
-
button "高" [ref=f11e698]
-
gridcell [ref=f11e699]
:
-
button "2026" [ref=f11e700]
-
gridcell [ref=f11e701]
:
-
button "2026-05-21 16:57" [ref=f11e702]
-
gridcell [ref=f11e703]
:
-
button "查看详情" [ref=f11e705] [cursor=pointer]
-
row [ref=f11e707]
:
-
gridcell [ref=f11e708]
:
-
checkbox "Select row 2026测试-质保金释放条件核验不充分案例" [ref=f11e712]
-
rowheader [ref=f11e717]
:
-
button "2026测试-质保金释放条件核验不充分案例" [ref=f11e718]
-
gridcell [ref=f11e719]
:
-
button "合同风险" [ref=f11e720]
-
gridcell [ref=f11e721]
:
-
button "质保金管理,AI批量造数-20260521-续批" [ref=f11e722]
-
gridcell [ref=f11e723]
:
-
button "付款条件核验不足" [ref=f11e724]
-
gridcell [ref=f11e725]
:
-
button "质保金,尾款释放,验收状态" [ref=f11e726]
-
gridcell [ref=f11e727]
:
-
button "合同履约抽查" [ref=f11e728]
-
gridcell [ref=f11e729]
:
-
button "高" [ref=f11e730]
-
gridcell [ref=f11e731]
:
-
button "2025" [ref=f11e732]
-
gridcell [ref=f11e733]
:
-
button "2026-05-21 16:57" [ref=f11e734]
-
gridcell [ref=f11e735]
:
-
button "查看详情" [ref=f11e737] [cursor=pointer]
-
row [ref=f11e739]
:
-
gridcell [ref=f11e740]
:
-
checkbox "Select row 2026测试-客服录音抽检覆盖率不足案例" [ref=f11e744]
-
rowheader [ref=f11e749]
:
-
button "2026测试-客服录音抽检覆盖率不足案例" [ref=f11e750]
-
gridcell [ref=f11e751]
:
-
button "服务风险" [ref=f11e752]
-
gridcell [ref=f11e753]
:
-
button "客服质检,AI批量造数-20260521-续批" [ref=f11e754]
-
gridcell [ref=f11e755]
:
-
button "抽检不足" [ref=f11e756]
-
gridcell [ref=f11e757]
:
-
button "客服录音,抽检覆盖率,不规范承诺" [ref=f11e758]
-
gridcell [ref=f11e759]
:
-
button "服务管理复盘" [ref=f11e760]
-
gridcell [ref=f11e761]
:
-
button "中" [ref=f11e762]
-
gridcell [ref=f11e763]
:
-
button "2024" [ref=f11e764]
-
gridcell [ref=f11e765]
:
-
button "2026-05-21 16:57" [ref=f11e766]
-
gridcell [ref=f11e767]
:
-
button "查看详情" [ref=f11e769] [cursor=pointer]
-
row [ref=f11e771]
:
-
gridcell [ref=f11e772]
:
-
checkbox "Select row 2026测试-预算追加审批事后补录案例" [ref=f11e776]
-
rowheader [ref=f11e781]
:
-
button "2026测试-预算追加审批事后补录案例" [ref=f11e782]
-
gridcell [ref=f11e783]
:
-
button "财务风险" [ref=f11e784]
-
gridcell [ref=f11e785]
:
-
button "预算控制,AI批量造数-20260521-续批" [ref=f11e786]
-
gridcell [ref=f11e787]
:
-
button "事后审批" [ref=f11e788]
-
gridcell [ref=f11e789]
:
-
button "预算追加,事后审批,支出承诺" [ref=f11e790]
-
gridcell [ref=f11e791]
:
-
button "预算执行检查" [ref=f11e792]
-
gridcell [ref=f11e793]
:
-
button "中" [ref=f11e794]
-
gridcell [ref=f11e795]
:
-
button "2025" [ref=f11e796]
-
gridcell [ref=f11e797]
:
-
button "2026-05-21 16:57" [ref=f11e798]
-
gridcell [ref=f11e799]
:
-
button "查看详情" [ref=f11e801] [cursor=pointer]
-
row [ref=f11e803]
:
-
gridcell [ref=f11e804]
:
-
checkbox "Select row 2026测试-供应商黑名单同步延迟案例" [ref=f11e808]
-
rowheader [ref=f11e813]
:
-
button "2026测试-供应商黑名单同步延迟案例" [ref=f11e814]
-
gridcell [ref=f11e815]
:
-
button "合规风险" [ref=f11e816]
-
gridcell [ref=f11e817]
:
-
button "供应商管理,AI批量造数-20260521-续批" [ref=f11e818]
-
gridcell [ref=f11e819]
:
-
button "名单同步缺失" [ref=f11e820]
-
gridcell [ref=f11e821]
:
-
button "供应商黑名单,系统同步,下单校验" [ref=f11e822]
-
gridcell [ref=f11e823]
:
-
button "采购合规检查" [ref=f11e824]
-
gridcell [ref=f11e825]
:
-
button "高" [ref=f11e826]
-
gridcell [ref=f11e827]
:
-
button "2026" [ref=f11e828]
-
gridcell [ref=f11e829]
:
-
button "2026-05-21 16:57" [ref=f11e830]
-
gridcell [ref=f11e831]
:
-
button "查看详情" [ref=f11e833] [cursor=pointer]
-
row [ref=f11e835]
:
-
gridcell [ref=f11e836]
:
-
checkbox "Select row 2026测试-外包人员离场账号停用滞后案例" [ref=f11e840]
-
rowheader [ref=f11e845]
:
-
button "2026测试-外包人员离场账号停用滞后案例" [ref=f11e846]
-
gridcell [ref=f11e847]
:
-
button "信息安全风险" [ref=f11e848]
-
gridcell [ref=f11e849]
:
-
button "账号停用,AI批量造数-20260521" [ref=f11e850]
-
gridcell [ref=f11e851]
:
-
button "权限残留" [ref=f11e852]
-
gridcell [ref=f11e853]
:
-
button "外包离场,账号停用,权限回收" [ref=f11e854]
-
gridcell [ref=f11e855]
:
-
button "IT审计" [ref=f11e856]
-
gridcell [ref=f11e857]
:
-
button "高" [ref=f11e858]
-
gridcell [ref=f11e859]
:
-
button "2026" [ref=f11e860]
-
gridcell [ref=f11e861]
:
-
button "2026-05-21 16:38" [ref=f11e862]
-
gridcell [ref=f11e863]
:
-
button "查看详情" [ref=f11e865] [cursor=pointer]
-
row [ref=f11e867]
:
-
gridcell [ref=f11e868]
:
-
checkbox "Select row 2026测试-营销活动费用分摊依据不清案例" [ref=f11e872]
-
rowheader [ref=f11e877]
:
-
button "2026测试-营销活动费用分摊依据不清案例" [ref=f11e878]
-
gridcell [ref=f11e879]
:
-
button "财务风险" [ref=f11e880]
-
gridcell [ref=f11e881]
:
-
button "费用分摊,AI批量造数-20260521" [ref=f11e882]
-
gridcell [ref=f11e883]
:
-
button "核算口径偏差" [ref=f11e884]
-
gridcell [ref=f11e885]
:
-
button "费用分摊,管理会计,利润真实性" [ref=f11e886]
-
gridcell [ref=f11e887]
:
-
button "经营分析复核" [ref=f11e888]
-
gridcell [ref=f11e889]
:
-
button "中" [ref=f11e890]
-
gridcell [ref=f11e891]
:
-
button "2024" [ref=f11e892]
-
gridcell [ref=f11e893]
:
-
button "2026-05-21 16:38" [ref=f11e894]
-
gridcell [ref=f11e895]
:
-
button "查看详情" [ref=f11e897] [cursor=pointer]
-
generic [ref=f11e354]
:
-
generic [ref=f11e355]
:
-
generic [ref=f11e356]
:
共 15 条,第 1 / 1 页
-
generic [ref=f11e357]
:
-
group [ref=f11e358] [cursor=pointer]
:
-
generic "20 条/页" [ref=f11e360]
-
button "20 条/页 每页条数"
-
combobox [ref=f11e366]
-
navigation "pagination" [ref=f11e367]
:
-
list [ref=f11e368]
:
-
listitem [ref=f11e369]
:
-
button "上一页" [disabled]
-
listitem [ref=f11e370]
:
-
button "1" [ref=f11e371] [cursor=pointer]
-
listitem [ref=f11e372]
:
-
button "下一页" [disabled]
-
button [ref=f11e373] [cursor=pointer]
-
button [ref=f11e374] [cursor=pointer]
-
button [ref=f11e375] [cursor=pointer]
-
button [ref=f11e376] [cursor=pointer]
-
button [ref=f11e377] [cursor=pointer]
-
button [ref=f11e378] [cursor=pointer]
-
button [ref=f11e379] [cursor=pointer]
\ No newline at end of file
resource-booking-table.yml
0 → 100644
View file @
dedde183
-
generic [ref=f11e1]
:
-
complementary [ref=f11e3]
:
-
generic [ref=f11e5]
:
-
img "鑫元" [ref=f11e7]
-
generic [ref=f11e8]
:
警示案例库
-
button "切换应用" [ref=f11e11] [cursor=pointer]
-
treegrid "案例库工作台" [ref=f11e15]
:
-
row "驾驶舱" [level=1] [ref=f11e16] [cursor=pointer]
:
-
gridcell "驾驶舱" [ref=f11e17]
-
row "案例管理" [level=1] [ref=f11e23] [cursor=pointer]
:
-
gridcell "案例管理" [ref=f11e24]
-
button "打开用户菜单" [ref=f11e31] [cursor=pointer]
:
-
generic [ref=f11e32]
:
A
-
generic [ref=f11e34]
:
-
generic [ref=f11e35]
:
Andy
-
generic [ref=f11e36]
:
andy
-
main [ref=f11e37]
:
-
navigation [ref=f11e38]
:
-
generic [ref=f11e39]
:
-
button "收起侧栏" [ref=f11e40] [cursor=pointer]
-
heading "案例管理" [level=1] [ref=f11e41]
-
button "通知消息" [ref=f11e44] [cursor=pointer]
-
generic [ref=f11e47]
:
-
heading "案例管理" [level=1] [ref=f11e48]
-
generic [ref=f11e49]
:
-
tablist "案例风险分类" [ref=f11e54]
:
-
tab "全部案例" [selected] [ref=f11e55] [cursor=pointer]
-
tab "治理风险" [ref=f11e58] [cursor=pointer]
-
tab "信息安全风险" [ref=f11e60] [cursor=pointer]
-
tab "服务风险" [ref=f11e62] [cursor=pointer]
-
tab "财务风险" [ref=f11e64] [cursor=pointer]
-
tab "未分类" [ref=f11e66] [cursor=pointer]
-
tab "知识管理风险" [ref=f11e68] [cursor=pointer]
-
tab "信息技术风险" [ref=f11e70] [cursor=pointer]
-
tab "反洗钱风险" [ref=f11e72] [cursor=pointer]
-
tab "合同风险" [ref=f11e74] [cursor=pointer]
-
tab "合规风险" [ref=f11e76] [cursor=pointer]
-
generic [ref=f11e80]
:
-
generic [ref=f11e81]
:
-
generic [ref=f11e82]
:
-
generic [ref=f11e83]
:
案例来源
-
textbox "案例来源" [ref=f11e84]
:
-
/placeholder
:
例如:监管通报
-
generic [ref=f11e85]
:
-
generic [ref=f11e86]
:
案例类型
-
textbox "案例类型" [ref=f11e87]
:
-
/placeholder
:
例如:审批绕行
-
generic [ref=f11e88]
:
-
generic [ref=f11e89]
:
问题属性
-
textbox "问题属性" [ref=f11e90]
:
-
/placeholder
:
例如:流程绕行
-
button "展开" [ref=f11e92] [cursor=pointer]
-
generic [ref=f11e95]
:
-
generic [ref=f11e96]
:
-
group [ref=f11e99]
:
-
searchbox "关键词搜索" [ref=f11e100]
-
button "清空关键词"
-
tablist "案例展示方式" [ref=f11e104]
:
-
tab "卡片视图" [ref=f11e105] [cursor=pointer]
-
tab "表格视图" [active] [selected] [ref=f11e107] [cursor=pointer]
-
generic [ref=f11e108]
:
-
button "Excel 批量导出" [ref=f11e109] [cursor=pointer]
-
button "Excel 批量导入" [ref=f11e110] [cursor=pointer]
-
button "手动新建案例" [ref=f11e111] [cursor=pointer]
-
generic [ref=f11e112]
:
-
grid "全部案例" [ref=f11e386]
:
-
rowgroup [ref=f11e387]
:
-
row [ref=f11e388]
:
-
columnheader [ref=f11e389]
:
-
checkbox "Select all" [ref=f11e393]
-
columnheader [ref=f11e398]
:
-
text
:
标题
-
slider "尺寸调整器 标题" [ref=f11e399]
:
"
380"
-
columnheader [ref=f11e400]
:
-
text
:
分类
-
slider "尺寸调整器 分类" [ref=f11e401]
:
"
180"
-
columnheader [ref=f11e402]
:
-
text
:
标签
-
slider "尺寸调整器 标签" [ref=f11e403]
:
"
240"
-
columnheader [ref=f11e404]
:
-
text
:
案例类型
-
slider "尺寸调整器 案例类型" [ref=f11e405]
:
"
220"
-
columnheader [ref=f11e406]
:
-
text
:
关键字
-
slider "尺寸调整器 关键字" [ref=f11e407]
:
"
280"
-
columnheader [ref=f11e408]
:
-
text
:
案例来源
-
slider "尺寸调整器 案例来源" [ref=f11e409]
:
"
180"
-
columnheader [ref=f11e410]
:
-
text
:
案例等级
-
slider "尺寸调整器 案例等级" [ref=f11e411]
:
"
140"
-
columnheader [ref=f11e412]
:
-
text
:
案例年份
-
slider "尺寸调整器 案例年份" [ref=f11e413]
:
"
140"
-
columnheader [ref=f11e414]
:
-
text
:
更新时间
-
slider "尺寸调整器 更新时间" [ref=f11e415]
:
"
160"
-
columnheader [ref=f11e416]
:
-
text
:
操作
-
slider "尺寸调整器 操作" [ref=f11e417]
:
"
96"
-
rowgroup [ref=f11e418]
:
-
row [ref=f11e419]
:
-
gridcell [ref=f11e420]
:
-
checkbox "Select row 2026测试-投标文件盖章页缺页未识别案例" [ref=f11e424]
-
rowheader [ref=f11e429]
:
-
button "2026测试-投标文件盖章页缺页未识别案例" [ref=f11e430]
-
gridcell [ref=f11e431]
:
-
button "-" [ref=f11e432]
-
gridcell [ref=f11e433]
:
-
button "招投标,AI批量造数-20260521-续批" [ref=f11e434]
-
gridcell [ref=f11e435]
:
-
button "形式审查疏漏" [ref=f11e436]
-
gridcell [ref=f11e437]
:
-
button "投标文件、盖章页、形式审查" [ref=f11e438]
-
gridcell [ref=f11e439]
:
-
button "招标复核" [ref=f11e440]
-
gridcell [ref=f11e441]
:
-
button "中" [ref=f11e442]
-
gridcell [ref=f11e443]
:
-
button "2025" [ref=f11e444]
-
gridcell [ref=f11e445]
:
-
button "2026-08-13 15:52" [ref=f11e446]
-
gridcell [ref=f11e447]
:
-
button "查看详情" [ref=f11e449] [cursor=pointer]
-
row [ref=f11e451]
:
-
gridcell [ref=f11e452]
:
-
checkbox "Select row 2026测试-供应商履约评价长期未更新案例" [ref=f11e456]
-
rowheader [ref=f11e461]
:
-
button "2026测试-供应商履约评价长期未更新案例" [ref=f11e462]
-
gridcell [ref=f11e463]
:
-
button "治理风险" [ref=f11e464]
-
gridcell [ref=f11e465]
:
-
button "供应商评价,AI批量造数-20260521-续批" [ref=f11e466]
-
gridcell [ref=f11e467]
:
-
button "评价机制失效" [ref=f11e468]
-
gridcell [ref=f11e469]
:
-
button "供应商评价、履约管理、续签决策" [ref=f11e470]
-
gridcell [ref=f11e471]
:
-
button "采购复盘" [ref=f11e472]
-
gridcell [ref=f11e473]
:
-
button "中" [ref=f11e474]
-
gridcell [ref=f11e475]
:
-
button "2025" [ref=f11e476]
-
gridcell [ref=f11e477]
:
-
button "2026-06-15 12:19" [ref=f11e478]
-
gridcell [ref=f11e479]
:
-
button "查看详情" [ref=f11e481] [cursor=pointer]
-
row [ref=f11e483]
:
-
gridcell [ref=f11e484]
:
-
checkbox "Select row 2026测试-印鉴卡更新后柜面未同步案例" [ref=f11e488]
-
rowheader [ref=f11e493]
:
-
button "2026测试-印鉴卡更新后柜面未同步案例" [ref=f11e494]
-
gridcell [ref=f11e495]
:
-
button "治理风险" [ref=f11e496]
-
gridcell [ref=f11e497]
:
-
button "印鉴管理,AI批量造数-20260521-续批" [ref=f11e498]
-
gridcell [ref=f11e499]
:
-
button "主数据同步异常" [ref=f11e500]
-
gridcell [ref=f11e501]
:
-
button "印鉴卡、主数据同步、柜面核验" [ref=f11e502]
-
gridcell [ref=f11e503]
:
-
button "运营巡检" [ref=f11e504]
-
gridcell [ref=f11e505]
:
-
button "中" [ref=f11e506]
-
gridcell [ref=f11e507]
:
-
button "2026" [ref=f11e508]
-
gridcell [ref=f11e509]
:
-
button "2026-06-15 12:19" [ref=f11e510]
-
gridcell [ref=f11e511]
:
-
button "查看详情" [ref=f11e513] [cursor=pointer]
-
row [ref=f11e515]
:
-
gridcell [ref=f11e516]
:
-
checkbox "Select row 2026测试-客户证照到期提醒触达失败案例" [ref=f11e520]
-
rowheader [ref=f11e525]
:
-
button "2026测试-客户证照到期提醒触达失败案例" [ref=f11e526]
-
gridcell [ref=f11e527]
:
-
button "治理风险" [ref=f11e528]
-
gridcell [ref=f11e529]
:
-
button "证照管理,AI批量造数-20260521-续批" [ref=f11e530]
-
gridcell [ref=f11e531]
:
-
button "提醒失效" [ref=f11e532]
-
gridcell [ref=f11e533]
:
-
button "证照到期、提醒触达、送达率" [ref=f11e534]
-
gridcell [ref=f11e535]
:
-
button "运营复盘" [ref=f11e536]
-
gridcell [ref=f11e537]
:
-
button "中" [ref=f11e538]
-
gridcell [ref=f11e539]
:
-
button "2025" [ref=f11e540]
-
gridcell [ref=f11e541]
:
-
button "2026-06-15 12:19" [ref=f11e542]
-
gridcell [ref=f11e543]
:
-
button "查看详情" [ref=f11e545] [cursor=pointer]
-
row [ref=f11e547]
:
-
gridcell [ref=f11e548]
:
-
checkbox "Select row 2026测试-项目复盘结论未进入知识库案例" [ref=f11e552]
-
rowheader [ref=f11e557]
:
-
button "2026测试-项目复盘结论未进入知识库案例" [ref=f11e558]
-
gridcell [ref=f11e559]
:
-
button "知识管理风险" [ref=f11e560]
-
gridcell [ref=f11e561]
:
-
button "知识沉淀,AI批量造数-20260521-续批" [ref=f11e562]
-
gridcell [ref=f11e563]
:
-
button "经验未复用" [ref=f11e564]
-
gridcell [ref=f11e565]
:
-
button "项目复盘,知识库,经验复用" [ref=f11e566]
-
gridcell [ref=f11e567]
:
-
button "项目后评估" [ref=f11e568]
-
gridcell [ref=f11e569]
:
-
button "中" [ref=f11e570]
-
gridcell [ref=f11e571]
:
-
button "2024" [ref=f11e572]
-
gridcell [ref=f11e573]
:
-
button "2026-05-21 16:58" [ref=f11e574]
-
gridcell [ref=f11e575]
:
-
button "查看详情" [ref=f11e577] [cursor=pointer]
-
row [ref=f11e579]
:
-
gridcell [ref=f11e580]
:
-
checkbox "Select row 2026测试-系统批量导入失败重试无幂等案例" [ref=f11e584]
-
rowheader [ref=f11e589]
:
-
button "2026测试-系统批量导入失败重试无幂等案例" [ref=f11e590]
-
gridcell [ref=f11e591]
:
-
button "信息技术风险" [ref=f11e592]
-
gridcell [ref=f11e593]
:
-
button "批量导入,AI批量造数-20260521-续批" [ref=f11e594]
-
gridcell [ref=f11e595]
:
-
button "幂等控制缺失" [ref=f11e596]
-
gridcell [ref=f11e597]
:
-
button "批量导入,幂等控制,重复写入" [ref=f11e598]
-
gridcell [ref=f11e599]
:
-
button "技术事故复盘" [ref=f11e600]
-
gridcell [ref=f11e601]
:
-
button "高" [ref=f11e602]
-
gridcell [ref=f11e603]
:
-
button "2026" [ref=f11e604]
-
gridcell [ref=f11e605]
:
-
button "2026-05-21 16:58" [ref=f11e606]
-
gridcell [ref=f11e607]
:
-
button "查看详情" [ref=f11e609] [cursor=pointer]
-
row [ref=f11e611]
:
-
gridcell [ref=f11e612]
:
-
checkbox "Select row 2026测试-访客门禁权限超过申请时长案例" [ref=f11e616]
-
rowheader [ref=f11e621]
:
-
button "2026测试-访客门禁权限超过申请时长案例" [ref=f11e622]
-
gridcell [ref=f11e623]
:
-
button "信息安全风险" [ref=f11e624]
-
gridcell [ref=f11e625]
:
-
button "门禁管理,AI批量造数-20260521-续批" [ref=f11e626]
-
gridcell [ref=f11e627]
:
-
button "权限超期" [ref=f11e628]
-
gridcell [ref=f11e629]
:
-
button "访客门禁,权限超期,物理安全" [ref=f11e630]
-
gridcell [ref=f11e631]
:
-
button "安保检查" [ref=f11e632]
-
gridcell [ref=f11e633]
:
-
button "中" [ref=f11e634]
-
gridcell [ref=f11e635]
:
-
button "2026" [ref=f11e636]
-
gridcell [ref=f11e637]
:
-
button "2026-05-21 16:58" [ref=f11e638]
-
gridcell [ref=f11e639]
:
-
button "查看详情" [ref=f11e641] [cursor=pointer]
-
row [ref=f11e643]
:
-
gridcell [ref=f11e644]
:
-
checkbox "Select row 2026测试-客户投诉分类标签误用案例" [ref=f11e648]
-
rowheader [ref=f11e653]
:
-
button "2026测试-客户投诉分类标签误用案例" [ref=f11e654]
-
gridcell [ref=f11e655]
:
-
button "服务风险" [ref=f11e656]
-
gridcell [ref=f11e657]
:
-
button "客户投诉,AI批量造数-20260521-续批" [ref=f11e658]
-
gridcell [ref=f11e659]
:
-
button "标签误分类" [ref=f11e660]
-
gridcell [ref=f11e661]
:
-
button "客户投诉,标签分类,统计失真" [ref=f11e662]
-
gridcell [ref=f11e663]
:
-
button "消保月度复盘" [ref=f11e664]
-
gridcell [ref=f11e665]
:
-
button "中" [ref=f11e666]
-
gridcell [ref=f11e667]
:
-
button "2024" [ref=f11e668]
-
gridcell [ref=f11e669]
:
-
button "2026-05-21 16:57" [ref=f11e670]
-
gridcell [ref=f11e671]
:
-
button "查看详情" [ref=f11e673] [cursor=pointer]
-
row [ref=f11e675]
:
-
gridcell [ref=f11e676]
:
-
checkbox "Select row 2026测试-异常交易预警处置备注模板化案例" [ref=f11e680]
-
rowheader [ref=f11e685]
:
-
button "2026测试-异常交易预警处置备注模板化案例" [ref=f11e686]
-
gridcell [ref=f11e687]
:
-
button "反洗钱风险" [ref=f11e688]
-
gridcell [ref=f11e689]
:
-
button "异常交易,AI批量造数-20260521-续批" [ref=f11e690]
-
gridcell [ref=f11e691]
:
-
button "处置质量不足" [ref=f11e692]
-
gridcell [ref=f11e693]
:
-
button "异常交易,模板备注,复核质量" [ref=f11e694]
-
gridcell [ref=f11e695]
:
-
button "反洗钱抽样检查" [ref=f11e696]
-
gridcell [ref=f11e697]
:
-
button "高" [ref=f11e698]
-
gridcell [ref=f11e699]
:
-
button "2026" [ref=f11e700]
-
gridcell [ref=f11e701]
:
-
button "2026-05-21 16:57" [ref=f11e702]
-
gridcell [ref=f11e703]
:
-
button "查看详情" [ref=f11e705] [cursor=pointer]
-
row [ref=f11e707]
:
-
gridcell [ref=f11e708]
:
-
checkbox "Select row 2026测试-质保金释放条件核验不充分案例" [ref=f11e712]
-
rowheader [ref=f11e717]
:
-
button "2026测试-质保金释放条件核验不充分案例" [ref=f11e718]
-
gridcell [ref=f11e719]
:
-
button "合同风险" [ref=f11e720]
-
gridcell [ref=f11e721]
:
-
button "质保金管理,AI批量造数-20260521-续批" [ref=f11e722]
-
gridcell [ref=f11e723]
:
-
button "付款条件核验不足" [ref=f11e724]
-
gridcell [ref=f11e725]
:
-
button "质保金,尾款释放,验收状态" [ref=f11e726]
-
gridcell [ref=f11e727]
:
-
button "合同履约抽查" [ref=f11e728]
-
gridcell [ref=f11e729]
:
-
button "高" [ref=f11e730]
-
gridcell [ref=f11e731]
:
-
button "2025" [ref=f11e732]
-
gridcell [ref=f11e733]
:
-
button "2026-05-21 16:57" [ref=f11e734]
-
gridcell [ref=f11e735]
:
-
button "查看详情" [ref=f11e737] [cursor=pointer]
-
row [ref=f11e739]
:
-
gridcell [ref=f11e740]
:
-
checkbox "Select row 2026测试-客服录音抽检覆盖率不足案例" [ref=f11e744]
-
rowheader [ref=f11e749]
:
-
button "2026测试-客服录音抽检覆盖率不足案例" [ref=f11e750]
-
gridcell [ref=f11e751]
:
-
button "服务风险" [ref=f11e752]
-
gridcell [ref=f11e753]
:
-
button "客服质检,AI批量造数-20260521-续批" [ref=f11e754]
-
gridcell [ref=f11e755]
:
-
button "抽检不足" [ref=f11e756]
-
gridcell [ref=f11e757]
:
-
button "客服录音,抽检覆盖率,不规范承诺" [ref=f11e758]
-
gridcell [ref=f11e759]
:
-
button "服务管理复盘" [ref=f11e760]
-
gridcell [ref=f11e761]
:
-
button "中" [ref=f11e762]
-
gridcell [ref=f11e763]
:
-
button "2024" [ref=f11e764]
-
gridcell [ref=f11e765]
:
-
button "2026-05-21 16:57" [ref=f11e766]
-
gridcell [ref=f11e767]
:
-
button "查看详情" [ref=f11e769] [cursor=pointer]
-
row [ref=f11e771]
:
-
gridcell [ref=f11e772]
:
-
checkbox "Select row 2026测试-预算追加审批事后补录案例" [ref=f11e776]
-
rowheader [ref=f11e781]
:
-
button "2026测试-预算追加审批事后补录案例" [ref=f11e782]
-
gridcell [ref=f11e783]
:
-
button "财务风险" [ref=f11e784]
-
gridcell [ref=f11e785]
:
-
button "预算控制,AI批量造数-20260521-续批" [ref=f11e786]
-
gridcell [ref=f11e787]
:
-
button "事后审批" [ref=f11e788]
-
gridcell [ref=f11e789]
:
-
button "预算追加,事后审批,支出承诺" [ref=f11e790]
-
gridcell [ref=f11e791]
:
-
button "预算执行检查" [ref=f11e792]
-
gridcell [ref=f11e793]
:
-
button "中" [ref=f11e794]
-
gridcell [ref=f11e795]
:
-
button "2025" [ref=f11e796]
-
gridcell [ref=f11e797]
:
-
button "2026-05-21 16:57" [ref=f11e798]
-
gridcell [ref=f11e799]
:
-
button "查看详情" [ref=f11e801] [cursor=pointer]
-
row [ref=f11e803]
:
-
gridcell [ref=f11e804]
:
-
checkbox "Select row 2026测试-供应商黑名单同步延迟案例" [ref=f11e808]
-
rowheader [ref=f11e813]
:
-
button "2026测试-供应商黑名单同步延迟案例" [ref=f11e814]
-
gridcell [ref=f11e815]
:
-
button "合规风险" [ref=f11e816]
-
gridcell [ref=f11e817]
:
-
button "供应商管理,AI批量造数-20260521-续批" [ref=f11e818]
-
gridcell [ref=f11e819]
:
-
button "名单同步缺失" [ref=f11e820]
-
gridcell [ref=f11e821]
:
-
button "供应商黑名单,系统同步,下单校验" [ref=f11e822]
-
gridcell [ref=f11e823]
:
-
button "采购合规检查" [ref=f11e824]
-
gridcell [ref=f11e825]
:
-
button "高" [ref=f11e826]
-
gridcell [ref=f11e827]
:
-
button "2026" [ref=f11e828]
-
gridcell [ref=f11e829]
:
-
button "2026-05-21 16:57" [ref=f11e830]
-
gridcell [ref=f11e831]
:
-
button "查看详情" [ref=f11e833] [cursor=pointer]
-
row [ref=f11e835]
:
-
gridcell [ref=f11e836]
:
-
checkbox "Select row 2026测试-外包人员离场账号停用滞后案例" [ref=f11e840]
-
rowheader [ref=f11e845]
:
-
button "2026测试-外包人员离场账号停用滞后案例" [ref=f11e846]
-
gridcell [ref=f11e847]
:
-
button "信息安全风险" [ref=f11e848]
-
gridcell [ref=f11e849]
:
-
button "账号停用,AI批量造数-20260521" [ref=f11e850]
-
gridcell [ref=f11e851]
:
-
button "权限残留" [ref=f11e852]
-
gridcell [ref=f11e853]
:
-
button "外包离场,账号停用,权限回收" [ref=f11e854]
-
gridcell [ref=f11e855]
:
-
button "IT审计" [ref=f11e856]
-
gridcell [ref=f11e857]
:
-
button "高" [ref=f11e858]
-
gridcell [ref=f11e859]
:
-
button "2026" [ref=f11e860]
-
gridcell [ref=f11e861]
:
-
button "2026-05-21 16:38" [ref=f11e862]
-
gridcell [ref=f11e863]
:
-
button "查看详情" [ref=f11e865] [cursor=pointer]
-
row [ref=f11e867]
:
-
gridcell [ref=f11e868]
:
-
checkbox "Select row 2026测试-营销活动费用分摊依据不清案例" [ref=f11e872]
-
rowheader [ref=f11e877]
:
-
button "2026测试-营销活动费用分摊依据不清案例" [ref=f11e878]
-
gridcell [ref=f11e879]
:
-
button "财务风险" [ref=f11e880]
-
gridcell [ref=f11e881]
:
-
button "费用分摊,AI批量造数-20260521" [ref=f11e882]
-
gridcell [ref=f11e883]
:
-
button "核算口径偏差" [ref=f11e884]
-
gridcell [ref=f11e885]
:
-
button "费用分摊,管理会计,利润真实性" [ref=f11e886]
-
gridcell [ref=f11e887]
:
-
button "经营分析复核" [ref=f11e888]
-
gridcell [ref=f11e889]
:
-
button "中" [ref=f11e890]
-
gridcell [ref=f11e891]
:
-
button "2024" [ref=f11e892]
-
gridcell [ref=f11e893]
:
-
button "2026-05-21 16:38" [ref=f11e894]
-
gridcell [ref=f11e895]
:
-
button "查看详情" [ref=f11e897] [cursor=pointer]
-
generic [ref=f11e354]
:
-
generic [ref=f11e355]
:
-
generic [ref=f11e356]
:
共 15 条,第 1 / 1 页
-
generic [ref=f11e357]
:
-
group [ref=f11e358] [cursor=pointer]
:
-
generic "20 条/页" [ref=f11e360]
-
button "20 条/页 每页条数"
-
combobox [ref=f11e366]
-
navigation "pagination" [ref=f11e367]
:
-
list [ref=f11e368]
:
-
listitem [ref=f11e369]
:
-
button "上一页" [disabled]
-
listitem [ref=f11e370]
:
-
button "1" [ref=f11e371] [cursor=pointer]
-
listitem [ref=f11e372]
:
-
button "下一页" [disabled]
-
button [ref=f11e373] [cursor=pointer]
-
button [ref=f11e374] [cursor=pointer]
-
button [ref=f11e375] [cursor=pointer]
-
button [ref=f11e376] [cursor=pointer]
-
button [ref=f11e377] [cursor=pointer]
-
button [ref=f11e378] [cursor=pointer]
-
button [ref=f11e379] [cursor=pointer]
\ No newline at end of file
resource-booking.yml
0 → 100644
View file @
dedde183
-
generic [ref=f11e1]
:
-
complementary [ref=f11e3]
:
-
generic [ref=f11e5]
:
-
img "鑫元" [ref=f11e7]
-
generic [ref=f11e8]
:
警示案例库
-
button "切换应用" [ref=f11e11] [cursor=pointer]
-
treegrid "案例库工作台" [ref=f11e15]
:
-
row "驾驶舱" [level=1] [ref=f11e16] [cursor=pointer]
:
-
gridcell "驾驶舱" [ref=f11e17]
-
row "案例管理" [level=1] [ref=f11e23] [cursor=pointer]
:
-
gridcell "案例管理" [ref=f11e24]
-
button "打开用户菜单" [ref=f11e31] [cursor=pointer]
:
-
generic [ref=f11e32]
:
A
-
generic [ref=f11e34]
:
-
generic [ref=f11e35]
:
Andy
-
generic [ref=f11e36]
:
andy
-
main [ref=f11e37]
:
-
navigation [ref=f11e38]
:
-
generic [ref=f11e39]
:
-
button "收起侧栏" [ref=f11e40] [cursor=pointer]
-
heading "案例管理" [level=1] [ref=f11e41]
-
button "通知消息" [ref=f11e44] [cursor=pointer]
-
generic [ref=f11e47]
:
-
heading "案例管理" [level=1] [ref=f11e48]
-
generic [ref=f11e49]
:
-
tablist "案例风险分类" [ref=f11e54]
:
-
tab "全部案例" [selected] [ref=f11e55] [cursor=pointer]
-
tab "治理风险" [ref=f11e58] [cursor=pointer]
-
tab "信息安全风险" [ref=f11e60] [cursor=pointer]
-
tab "服务风险" [ref=f11e62] [cursor=pointer]
-
tab "财务风险" [ref=f11e64] [cursor=pointer]
-
tab "未分类" [ref=f11e66] [cursor=pointer]
-
tab "知识管理风险" [ref=f11e68] [cursor=pointer]
-
tab "信息技术风险" [ref=f11e70] [cursor=pointer]
-
tab "反洗钱风险" [ref=f11e72] [cursor=pointer]
-
tab "合同风险" [ref=f11e74] [cursor=pointer]
-
tab "合规风险" [ref=f11e76] [cursor=pointer]
-
generic [ref=f11e80]
:
-
generic [ref=f11e81]
:
-
generic [ref=f11e82]
:
-
generic [ref=f11e83]
:
案例来源
-
textbox "案例来源" [ref=f11e84]
:
-
/placeholder
:
例如:监管通报
-
generic [ref=f11e85]
:
-
generic [ref=f11e86]
:
案例类型
-
textbox "案例类型" [ref=f11e87]
:
-
/placeholder
:
例如:审批绕行
-
generic [ref=f11e88]
:
-
generic [ref=f11e89]
:
问题属性
-
textbox "问题属性" [ref=f11e90]
:
-
/placeholder
:
例如:流程绕行
-
button "展开" [ref=f11e92] [cursor=pointer]
-
generic [ref=f11e95]
:
-
generic [ref=f11e96]
:
-
group [ref=f11e99]
:
-
searchbox "关键词搜索" [ref=f11e100]
-
button "清空关键词"
-
tablist "案例展示方式" [ref=f11e104]
:
-
tab "卡片视图" [selected] [ref=f11e105] [cursor=pointer]
-
tab "表格视图" [ref=f11e107] [cursor=pointer]
-
generic [ref=f11e108]
:
-
button "Excel 批量导出" [ref=f11e109] [cursor=pointer]
-
button "Excel 批量导入" [ref=f11e110] [cursor=pointer]
-
button "手动新建案例" [ref=f11e111] [cursor=pointer]
-
generic [ref=f11e112]
:
-
generic [ref=f11e113]
:
-
button "查看案例:2026测试-投标文件盖章页缺页未识别案例" [ref=f11e114] [cursor=pointer]
:
-
generic [ref=f11e115]
:
-
generic [ref=f11e116]
:
-
generic [ref=f11e117]
:
-
generic [ref=f11e118]
:
未分类
-
generic [ref=f11e120]
:
中
-
generic [ref=f11e122]
:
招投标,AI批量造数-20260521-续批
-
generic [ref=f11e124]
:
2026测试-投标文件盖章页缺页未识别案例
-
generic [ref=f11e125]
:
形式审查疏漏
-
generic [ref=f11e126]
:
某投标文件扫描件在上传过程中遗漏了一页盖章页,但评审小组仅核对目录和总页数,未逐项检查关键签章位置,导致带缺页材料进入评审流程。虽然最终未中标,但事件反映出形式审查清单执行不严。
-
generic [ref=f11e127]
:
-
generic [ref=f11e128]
:
2025 年
-
generic [ref=f11e129]
:
更新于 2026-08-13 15:52
-
button "查看案例:2026测试-供应商履约评价长期未更新案例" [ref=f11e130] [cursor=pointer]
:
-
generic [ref=f11e131]
:
-
generic [ref=f11e132]
:
-
generic [ref=f11e133]
:
-
generic [ref=f11e134]
:
治理风险
-
generic [ref=f11e136]
:
中
-
generic [ref=f11e138]
:
供应商评价,AI批量造数-20260521-续批
-
generic [ref=f11e140]
:
2026测试-供应商履约评价长期未更新案例
-
generic [ref=f11e141]
:
评价机制失效
-
generic [ref=f11e142]
:
部分长期合作供应商的履约评价停留在上一年度,项目交付中的延期、返工和服务投诉未及时沉淀到评价结果,导致续签和复购决策缺少真实依据。问题说明履约反馈回路未真正嵌入采购管理。
-
generic [ref=f11e143]
:
-
generic [ref=f11e144]
:
2025 年
-
generic [ref=f11e145]
:
更新于 2026-06-15 12:19
-
button "查看案例:2026测试-印鉴卡更新后柜面未同步案例" [ref=f11e146] [cursor=pointer]
:
-
generic [ref=f11e147]
:
-
generic [ref=f11e148]
:
-
generic [ref=f11e149]
:
-
generic [ref=f11e150]
:
治理风险
-
generic [ref=f11e152]
:
中
-
generic [ref=f11e154]
:
印鉴管理,AI批量造数-20260521-续批
-
generic [ref=f11e156]
:
2026测试-印鉴卡更新后柜面未同步案例
-
generic [ref=f11e157]
:
主数据同步异常
-
generic [ref=f11e158]
:
客户完成印鉴变更后,核心主数据已更新,但部分柜面终端缓存未及时刷新,导致经办人员按旧印鉴卡进行人工复核并误判异常。事件虽经复核纠正,但暴露出关键主数据变更后的前端同步机制不足。
-
generic [ref=f11e159]
:
-
generic [ref=f11e160]
:
2026 年
-
generic [ref=f11e161]
:
更新于 2026-06-15 12:19
-
button "查看案例:2026测试-客户证照到期提醒触达失败案例" [ref=f11e162] [cursor=pointer]
:
-
generic [ref=f11e163]
:
-
generic [ref=f11e164]
:
-
generic [ref=f11e165]
:
-
generic [ref=f11e166]
:
治理风险
-
generic [ref=f11e168]
:
中
-
generic [ref=f11e170]
:
证照管理,AI批量造数-20260521-续批
-
generic [ref=f11e172]
:
2026测试-客户证照到期提醒触达失败案例
-
generic [ref=f11e173]
:
提醒失效
-
generic [ref=f11e174]
:
系统已配置客户证照到期前提醒,但由于短信渠道签名异常和邮件地址长期未维护,多个客户未收到续补资料通知。后续业务办理时集中出现证照过期问题,影响交易时效。该问题暴露出提醒机制仅关注“发出”,未验证“送达”和“处理”。
-
generic [ref=f11e175]
:
-
generic [ref=f11e176]
:
2025 年
-
generic [ref=f11e177]
:
更新于 2026-06-15 12:19
-
button "查看案例:2026测试-项目复盘结论未进入知识库案例" [ref=f11e178] [cursor=pointer]
:
-
generic [ref=f11e179]
:
-
generic [ref=f11e180]
:
-
generic [ref=f11e181]
:
-
generic [ref=f11e182]
:
知识管理风险
-
generic [ref=f11e184]
:
中
-
generic [ref=f11e186]
:
知识沉淀,AI批量造数-20260521-续批
-
generic [ref=f11e188]
:
2026测试-项目复盘结论未进入知识库案例
-
generic [ref=f11e189]
:
经验未复用
-
generic [ref=f11e190]
:
某重点项目在结束后形成了较完整的问题复盘和改进建议,但材料仅保存在项目群和个人网盘中,未录入统一知识库。后续相似项目重复踩坑,团队才发现既有经验未被有效复用。说明复盘机制缺乏沉淀动作和责任要求。
-
generic [ref=f11e191]
:
-
generic [ref=f11e192]
:
2024 年
-
generic [ref=f11e193]
:
更新于 2026-05-21 16:58
-
button "查看案例:2026测试-系统批量导入失败重试无幂等案例" [ref=f11e194] [cursor=pointer]
:
-
generic [ref=f11e195]
:
-
generic [ref=f11e196]
:
-
generic [ref=f11e197]
:
-
generic [ref=f11e198]
:
信息技术风险
-
generic [ref=f11e200]
:
高
-
generic [ref=f11e202]
:
批量导入,AI批量造数-20260521-续批
-
generic [ref=f11e204]
:
2026测试-系统批量导入失败重试无幂等案例
-
generic [ref=f11e205]
:
幂等控制缺失
-
generic [ref=f11e206]
:
某业务系统批量导入任务在超时后被人工重复重试,因缺少批次幂等键,导致部分记录重复写入。后续清理耗费大量人工,也影响了下游报表准确性。问题说明批量任务失败处理设计不足。
-
generic [ref=f11e207]
:
-
generic [ref=f11e208]
:
2026 年
-
generic [ref=f11e209]
:
更新于 2026-05-21 16:58
-
button "查看案例:2026测试-访客门禁权限超过申请时长案例" [ref=f11e210] [cursor=pointer]
:
-
generic [ref=f11e211]
:
-
generic [ref=f11e212]
:
-
generic [ref=f11e213]
:
-
generic [ref=f11e214]
:
信息安全风险
-
generic [ref=f11e216]
:
中
-
generic [ref=f11e218]
:
门禁管理,AI批量造数-20260521-续批
-
generic [ref=f11e220]
:
2026测试-访客门禁权限超过申请时长案例
-
generic [ref=f11e221]
:
权限超期
-
generic [ref=f11e222]
:
某外部访客因现场培训申请了两小时门禁权限,但系统默认授权至当日24点,且未要求访客离场后回收。虽然未发生异常出入,但该设置与最小必要原则不符,增加了物理安全暴露面。
-
generic [ref=f11e223]
:
-
generic [ref=f11e224]
:
2026 年
-
generic [ref=f11e225]
:
更新于 2026-05-21 16:58
-
button "查看案例:2026测试-客户投诉分类标签误用案例" [ref=f11e226] [cursor=pointer]
:
-
generic [ref=f11e227]
:
-
generic [ref=f11e228]
:
-
generic [ref=f11e229]
:
-
generic [ref=f11e230]
:
服务风险
-
generic [ref=f11e232]
:
中
-
generic [ref=f11e234]
:
客户投诉,AI批量造数-20260521-续批
-
generic [ref=f11e236]
:
2026测试-客户投诉分类标签误用案例
-
generic [ref=f11e237]
:
标签误分类
-
generic [ref=f11e238]
:
某批客户投诉被统一标记为“咨询建议”,未按实际情形区分收费争议、服务态度和系统故障等类型,导致后续统计分析失真,也影响整改责任部门定位。说明一线录入标准不清、复核机制不足。
-
generic [ref=f11e239]
:
-
generic [ref=f11e240]
:
2024 年
-
generic [ref=f11e241]
:
更新于 2026-05-21 16:57
-
button "查看案例:2026测试-异常交易预警处置备注模板化案例" [ref=f11e242] [cursor=pointer]
:
-
generic [ref=f11e243]
:
-
generic [ref=f11e244]
:
-
generic [ref=f11e245]
:
-
generic [ref=f11e246]
:
反洗钱风险
-
generic [ref=f11e248]
:
高
-
generic [ref=f11e250]
:
异常交易,AI批量造数-20260521-续批
-
generic [ref=f11e252]
:
2026测试-异常交易预警处置备注模板化案例
-
generic [ref=f11e253]
:
处置质量不足
-
generic [ref=f11e254]
:
部分异常交易预警在人工复核后直接套用“客户交易正常、风险可接受”等模板备注,缺少对交易背景、资金去向和客户行业特征的具体分析。抽查时难以判断复核结论是否充分。问题反映处置过程重关闭数量、轻分析质量。
-
generic [ref=f11e255]
:
-
generic [ref=f11e256]
:
2026 年
-
generic [ref=f11e257]
:
更新于 2026-05-21 16:57
-
button "查看案例:2026测试-质保金释放条件核验不充分案例" [ref=f11e258] [cursor=pointer]
:
-
generic [ref=f11e259]
:
-
generic [ref=f11e260]
:
-
generic [ref=f11e261]
:
-
generic [ref=f11e262]
:
合同风险
-
generic [ref=f11e264]
:
高
-
generic [ref=f11e266]
:
质保金管理,AI批量造数-20260521-续批
-
generic [ref=f11e268]
:
2026测试-质保金释放条件核验不充分案例
-
generic [ref=f11e269]
:
付款条件核验不足
-
generic [ref=f11e270]
:
某项目在质保期未完全届满的情况下,根据供应商邮件说明提前释放质保金,但未完整核验缺陷整改记录、维保工单和质保期限。后续设备再次出现故障时,追偿空间明显受限。说明尾款和质保金支付控制较弱。
-
generic [ref=f11e271]
:
-
generic [ref=f11e272]
:
2025 年
-
generic [ref=f11e273]
:
更新于 2026-05-21 16:57
-
button "查看案例:2026测试-客服录音抽检覆盖率不足案例" [ref=f11e274] [cursor=pointer]
:
-
generic [ref=f11e275]
:
-
generic [ref=f11e276]
:
-
generic [ref=f11e277]
:
-
generic [ref=f11e278]
:
服务风险
-
generic [ref=f11e280]
:
中
-
generic [ref=f11e282]
:
客服质检,AI批量造数-20260521-续批
-
generic [ref=f11e284]
:
2026测试-客服录音抽检覆盖率不足案例
-
generic [ref=f11e285]
:
抽检不足
-
generic [ref=f11e286]
:
某客服中心仅对投诉工单关联通话进行录音抽检,普通咨询类通话长期未纳入质量检查范围。后续在外部回访中发现,部分坐席存在不规范承诺和用语不当问题。说明现有抽检模型偏重事后投诉,未能覆盖高频风险场景。
-
generic [ref=f11e287]
:
-
generic [ref=f11e288]
:
2024 年
-
generic [ref=f11e289]
:
更新于 2026-05-21 16:57
-
button "查看案例:2026测试-预算追加审批事后补录案例" [ref=f11e290] [cursor=pointer]
:
-
generic [ref=f11e291]
:
-
generic [ref=f11e292]
:
-
generic [ref=f11e293]
:
-
generic [ref=f11e294]
:
财务风险
-
generic [ref=f11e296]
:
中
-
generic [ref=f11e298]
:
预算控制,AI批量造数-20260521-续批
-
generic [ref=f11e300]
:
2026测试-预算追加审批事后补录案例
-
generic [ref=f11e301]
:
事后审批
-
generic [ref=f11e302]
:
某项目因营销节点临近,先行发生推广支出后再补提预算追加流程。系统中虽然最终形成审批记录,但审批时间晚于合同签订和付款申请。该做法削弱了预算前置控制作用,容易使审批流沦为形式性补录。
-
generic [ref=f11e303]
:
-
generic [ref=f11e304]
:
2025 年
-
generic [ref=f11e305]
:
更新于 2026-05-21 16:57
-
button "查看案例:2026测试-供应商黑名单同步延迟案例" [ref=f11e306] [cursor=pointer]
:
-
generic [ref=f11e307]
:
-
generic [ref=f11e308]
:
-
generic [ref=f11e309]
:
-
generic [ref=f11e310]
:
合规风险
-
generic [ref=f11e312]
:
高
-
generic [ref=f11e314]
:
供应商管理,AI批量造数-20260521-续批
-
generic [ref=f11e316]
:
2026测试-供应商黑名单同步延迟案例
-
generic [ref=f11e317]
:
名单同步缺失
-
generic [ref=f11e318]
:
某采购平台中的供应商黑名单由法务部门按月维护,但业务下单系统每季度才同步一次,导致已被限制合作的供应商仍可在短期内继续参与询价。后续抽查发现一笔订单正是在同步窗口期发起。问题说明黑名单治理缺少系统间联动和实时校验。
-
generic [ref=f11e319]
:
-
generic [ref=f11e320]
:
2026 年
-
generic [ref=f11e321]
:
更新于 2026-05-21 16:57
-
button "查看案例:2026测试-外包人员离场账号停用滞后案例" [ref=f11e322] [cursor=pointer]
:
-
generic [ref=f11e323]
:
-
generic [ref=f11e324]
:
-
generic [ref=f11e325]
:
-
generic [ref=f11e326]
:
信息安全风险
-
generic [ref=f11e328]
:
高
-
generic [ref=f11e330]
:
账号停用,AI批量造数-20260521
-
generic [ref=f11e332]
:
2026测试-外包人员离场账号停用滞后案例
-
generic [ref=f11e333]
:
权限残留
-
generic [ref=f11e334]
:
某外包开发人员项目结束后已办理门禁注销,但其测试环境和日志平台账号仍保留一周以上。例行审计发现,该账号期间仍存在登录记录,虽未检测到异常操作,但说明离场流程未实现资产、权限和物理通行的统一回收。该问题一旦叠加高权限配置,可能形成明显安全暴露面。
-
generic [ref=f11e335]
:
-
generic [ref=f11e336]
:
2026 年
-
generic [ref=f11e337]
:
更新于 2026-05-21 16:38
-
button "查看案例:2026测试-营销活动费用分摊依据不清案例" [ref=f11e338] [cursor=pointer]
:
-
generic [ref=f11e339]
:
-
generic [ref=f11e340]
:
-
generic [ref=f11e341]
:
-
generic [ref=f11e342]
:
财务风险
-
generic [ref=f11e344]
:
中
-
generic [ref=f11e346]
:
费用分摊,AI批量造数-20260521
-
generic [ref=f11e348]
:
2026测试-营销活动费用分摊依据不清案例
-
generic [ref=f11e349]
:
核算口径偏差
-
generic [ref=f11e350]
:
某跨区域营销活动结束后,相关场地、物料和外包费用按经验比例分摊至多个项目,但缺少事先约定的分摊规则和可追溯依据。月底复盘时,不同部门对费用承担范围产生争议,影响项目利润真实性。问题在于活动立项时未同步明确费用归集与分摊口径。
-
generic [ref=f11e351]
:
-
generic [ref=f11e352]
:
2024 年
-
generic [ref=f11e353]
:
更新于 2026-05-21 16:38
-
generic [ref=f11e354]
:
-
generic [ref=f11e355]
:
-
generic [ref=f11e356]
:
共 15 条,第 1 / 1 页
-
generic [ref=f11e357]
:
-
group [ref=f11e358] [cursor=pointer]
:
-
generic "20 条/页" [ref=f11e360]
-
button "20 条/页 每页条数"
-
combobox [ref=f11e366]
-
navigation "pagination" [ref=f11e367]
:
-
list [ref=f11e368]
:
-
listitem [ref=f11e369]
:
-
button "上一页" [disabled]
-
listitem [ref=f11e370]
:
-
button "1" [ref=f11e371] [cursor=pointer]
-
listitem [ref=f11e372]
:
-
button "下一页" [disabled]
-
button [ref=f11e373] [cursor=pointer]
-
button [ref=f11e374] [cursor=pointer]
-
button [ref=f11e375] [cursor=pointer]
-
button [ref=f11e376] [cursor=pointer]
-
button [ref=f11e377] [cursor=pointer]
-
button [ref=f11e378] [cursor=pointer]
-
button [ref=f11e379] [cursor=pointer]
\ No newline at end of file
src/components/DataTable.jsx
View file @
dedde183
import
{
Close
}
from
'@/components/AppIcons'
;
import
XinyuanAutocomplete
from
'@/components/XinyuanAutocomplete'
;
import
{
ActionBar
}
from
'@heroui-pro/react/action-bar'
;
import
{
DataGrid
}
from
'@heroui-pro/react/data-grid'
;
import
{
EmptyState
}
from
'@heroui-pro/react/empty-state'
;
...
...
@@ -8,8 +9,28 @@ import {
Pagination
,
Separator
,
Spinner
,
Surface
,
}
from
'@heroui/react'
;
import
{
Tooltip
}
from
'@heroui/react'
;
import
{
useLayoutEffect
,
useRef
,
useState
}
from
'react'
;
const
DATA_TABLE_LONG_TEXT_TYPES
=
new
Set
([
'long_text'
,
'rich_text'
,
'object'
,
'form'
,
'subject'
,
]);
const
DATA_TABLE_LONG_TEXT_LABEL_PATTERN
=
/需求明细|需求内容|材料清单|工作内容|检查内容|审计范围|整改情况|问题简述|整改措施|问题描述|具体问题|整改要求|描述|措施|原因|意见|备注|说明/
;
export
function
isDataTableLongTextField
(
fieldDef
,
label
=
''
)
{
return
DATA_TABLE_LONG_TEXT_TYPES
.
has
(
String
(
fieldDef
?.
type
||
''
).
toLowerCase
(),
)
||
DATA_TABLE_LONG_TEXT_LABEL_PATTERN
.
test
(
String
(
label
||
fieldDef
?.
label
||
''
),
);
}
function
normalizeDataTableEmptyTitle
(
title
)
{
const
text
=
String
(
title
||
''
).
trim
();
...
...
@@ -24,23 +45,38 @@ function normalizeDataTableEmptyTitle(title) {
return
title
;
}
export
function
DataTableEmptyState
({
title
=
'暂无数据'
})
{
export
function
EmptyStateSurface
({
children
,
className
=
''
})
{
return
(
<
Surface
className=
{
[
'w-full min-w-0 rounded-2xl p-4'
,
className
]
.
filter
(
Boolean
)
.
join
(
' '
)
}
variant=
"secondary"
>
{
children
}
</
Surface
>
);
}
export
function
DataTableEmptyState
({
title
=
'暂无数据'
,
size
=
'md'
})
{
const
resolvedTitle
=
normalizeDataTableEmptyTitle
(
title
);
return
(
<
EmptyState
size=
"md"
>
<
EmptyState
.
Header
>
<
EmptyState
.
Title
>
{
resolvedTitle
}
</
EmptyState
.
Title
>
</
EmptyState
.
Header
>
</
EmptyState
>
<
EmptyStateSurface
>
<
EmptyState
size=
{
size
}
>
<
EmptyState
.
Header
>
<
EmptyState
.
Description
className=
"rc-empty-state-message"
>
{
resolvedTitle
}
</
EmptyState
.
Description
>
</
EmptyState
.
Header
>
</
EmptyState
>
</
EmptyStateSurface
>
);
}
function
DataTableLoadingState
()
{
return
(
<
div
className=
"flex min-h-48 items-center justify-center"
aria
-
label=
"数据加载中"
>
<
Spinner
size=
"sm"
/>
</
div
>
<
div
className=
"min-h-48 w-full"
aria
-
hidden=
"true"
/>
);
}
...
...
@@ -87,6 +123,7 @@ export function DataTable({
selectedRowKeys
,
onSelectedRowKeysChange
,
emptyTitle
=
'暂无数据'
,
emptyStateSize
=
'md'
,
className
=
''
,
isWide
,
contentMinWidth
,
...
...
@@ -95,18 +132,53 @@ export function DataTable({
sortDescriptor
,
onSortChange
,
renderSelectionActions
,
allowsColumnResize
=
true
,
})
{
const
selectionEnabled
=
Boolean
(
selectedRowKeys
&&
onSelectedRowKeysChange
);
const
selectedKeys
=
selectionEnabled
?
new
Set
(
selectedRowKeys
)
:
undefined
;
const
wide
=
isWide
??
columns
.
length
>
6
;
const
gridViewportRef
=
useRef
(
null
);
const
[
hasHorizontalOverflow
,
setHasHorizontalOverflow
]
=
useState
(
false
);
const
resolvedContentMinWidth
=
wide
&&
Number
(
contentMinWidth
)
>
0
?
Number
(
contentMinWidth
)
:
0
;
Number
(
contentMinWidth
)
>
0
?
Number
(
contentMinWidth
)
:
0
;
const
firstBusinessColumnIndex
=
columns
.
findIndex
(
(
column
)
=>
!
column
.
isSelection
&&
!
column
.
isActions
&&
column
.
key
!==
'actions'
,
);
useLayoutEffect
(()
=>
{
if
(
typeof
isWide
===
'boolean'
)
{
setHasHorizontalOverflow
(
isWide
);
return
undefined
;
}
const
viewport
=
gridViewportRef
.
current
;
if
(
!
viewport
)
return
undefined
;
const
updateOverflow
=
()
=>
{
const
scrollContainer
=
viewport
.
querySelector
(
'.table__resizable-container'
)
||
viewport
.
querySelector
(
'.data-table-scroll'
);
if
(
!
scrollContainer
)
return
;
setHasHorizontalOverflow
(
scrollContainer
.
scrollWidth
>
scrollContainer
.
clientWidth
+
1
,
);
};
updateOverflow
();
if
(
typeof
ResizeObserver
===
'undefined'
)
return
undefined
;
const
resizeObserver
=
new
ResizeObserver
(
updateOverflow
);
resizeObserver
.
observe
(
viewport
);
const
scrollContainer
=
viewport
.
querySelector
(
'.table__resizable-container'
)
||
viewport
.
querySelector
(
'.data-table-scroll'
);
if
(
scrollContainer
)
resizeObserver
.
observe
(
scrollContainer
);
return
()
=>
resizeObserver
.
disconnect
();
},
[
columns
,
isWide
,
rows
]);
const
wide
=
typeof
isWide
===
'boolean'
?
isWide
:
hasHorizontalOverflow
;
const
gridColumns
=
columns
.
map
((
column
,
index
)
=>
{
const
isActions
=
column
.
isActions
||
column
.
key
===
'actions'
;
const
isPrimary
=
...
...
@@ -115,7 +187,7 @@ export function DataTable({
const
numericWidth
=
typeof
configuredWidth
===
'number'
?
configuredWidth
:
undefined
;
const
pinned
=
wide
?
column
.
pin
||
(
isActions
?
'end'
:
undefined
)
?
column
.
pin
||
(
isActions
?
'end'
:
isPrimary
?
'start'
:
undefined
)
:
undefined
;
const
minWidth
=
column
.
minWidth
||
...
...
@@ -148,13 +220,20 @@ export function DataTable({
/>
);
}
return
value
;
return
isActions
?
value
:
(
<
div
className=
"min-w-0 max-w-full"
>
{
value
}
</
div
>
);
},
isRowHeader
:
index
===
0
,
isRowHeader
:
index
===
firstBusinessColumnIndex
,
allowsSorting
:
column
.
sortable
===
true
,
headerClassName
:
column
.
headerClassName
,
cellClassName
:
column
.
cellClassName
,
allowsResizing
:
true
,
cellClassName
:
[
'min-w-0'
,
column
.
cellClassName
,
].
filter
(
Boolean
).
join
(
' '
),
allowsResizing
:
allowsColumnResize
,
minWidth
,
width
,
pinned
,
...
...
@@ -182,15 +261,21 @@ export function DataTable({
return
(
<
div
className=
{
[
'relative min-w-0 w-full'
,
className
].
filter
(
Boolean
).
join
(
' '
)
}
className=
{
[
'relative min-w-0 w-full'
,
wide
&&
'rc-data-table-sticky'
,
className
,
]
.
filter
(
Boolean
)
.
join
(
' '
)
}
style=
{
resolvedContentMinWidth
?
{
'--data-table-content-min-width'
:
`${resolvedContentMinWidth}px`
}
:
undefined
}
>
<
div
className=
"relative min-w-0 w-full"
>
{
isLoading
&&
rows
.
length
>
0
?
(
<
div
ref=
{
gridViewportRef
}
className=
"relative min-w-0 w-full"
>
{
isLoading
?
(
<
div
className=
"absolute inset-0 z-10 grid place-items-center"
aria
-
label=
"数据加载中"
...
...
@@ -200,11 +285,11 @@ export function DataTable({
)
:
null
}
<
DataGrid
showSelectionCheckboxes=
{
selectionEnabled
}
allowsColumnResize
allowsColumnResize
=
{
allowsColumnResize
}
aria
-
label=
{
ariaLabel
}
columns=
{
gridColumns
}
contentClassName=
{
resolvedContentMinWidth
resolvedContentMinWidth
&&
wide
?
'min-w-full [min-width:var(--data-table-content-min-width)]'
:
wide
?
'min-w-max'
...
...
@@ -214,7 +299,11 @@ export function DataTable({
disabledKeys=
{
disabledRowKeys
}
getRowId=
{
(
row
)
=>
resolveRowKey
(
row
,
rows
.
indexOf
(
row
),
getRowKey
)
}
renderEmptyState=
{
()
=>
isLoading
?
<
DataTableLoadingState
/>
:
<
DataTableEmptyState
title=
{
emptyTitle
}
/>
isLoading
?
(
<
DataTableLoadingState
/>
)
:
(
<
DataTableEmptyState
title=
{
emptyTitle
}
size=
{
emptyStateSize
}
/>
)
}
scrollContainerClassName=
"data-table-scroll"
selectedKeys=
{
selectedKeys
}
...
...
@@ -289,8 +378,10 @@ export function DataTableText({
})
{
const
fullText
=
String
(
text
??
children
??
''
).
trim
();
const
multilineClassName
=
maxLines
===
2
?
'overflow-hidden whitespace-normal break-words [display:-webkit-box] [-webkit-box-orient:vertical] [-webkit-line-clamp:2] [overflow-wrap:anywhere]'
maxLines
>
1
?
`overflow-hidden whitespace-normal break-words [display:-webkit-box] [-webkit-box-orient:vertical]
${
maxLines
===
3
?
'[-webkit-line-clamp:3]'
:
'[-webkit-line-clamp:2]'
}
[overflow-wrap:anywhere]`
:
''
;
const
trigger
=
showTooltip
?
(
<
DataTableTextTooltipTrigger
...
...
@@ -301,16 +392,16 @@ export function DataTableText({
{
children
??
(
fullText
||
'-'
)
}
</
DataTableTextTooltipTrigger
>
)
:
(
<
span
className=
{
[
'block min-w-0 truncate'
,
className
].
filter
(
Boolean
).
join
(
' '
)
}
>
<
span
className=
{
[
maxLines
>
1
?
multilineClassName
:
'truncate'
,
'block min-w-0 max-w-full overflow-hidden'
,
className
,
].
filter
(
Boolean
).
join
(
' '
)
}
>
{
children
??
(
fullText
||
'-'
)
}
</
span
>
);
if
(
!
fullText
||
fullText
===
'-'
||
(
!
showTooltip
&&
fullText
.
length
<=
18
)
)
{
if
(
!
showTooltip
||
!
fullText
||
fullText
===
'-'
)
{
return
trigger
;
}
...
...
@@ -335,14 +426,18 @@ function DataTableTextTooltipTrigger({
<
Tooltip
>
<
Tooltip
.
Trigger
className=
{
[
'block
w-full
max-w-full overflow-hidden'
,
'block
min-w-0
max-w-full overflow-hidden'
,
truncate
?
'min-w-0 truncate'
:
''
,
className
,
].
filter
(
Boolean
).
join
(
' '
)
}
>
{
children
}
</
Tooltip
.
Trigger
>
<
Tooltip
.
Content
>
{
text
}
</
Tooltip
.
Content
>
<
Tooltip
.
Content
>
<
span
className=
"block max-w-[min(32rem,calc(100vw-2rem))] whitespace-pre-wrap break-words"
>
{
text
}
</
span
>
</
Tooltip
.
Content
>
</
Tooltip
>
);
}
...
...
@@ -444,22 +539,9 @@ export function DataTableChip({
children
,
text
,
className
=
''
,
color
,
variant
,
})
{
const
fullText
=
String
(
text
??
children
??
''
).
trim
();
const
trigger
=
(
<
Chip
className=
{
[
'max-w-full overflow-hidden'
,
className
]
.
filter
(
Boolean
)
.
join
(
' '
)
}
color=
{
color
}
size=
"sm"
variant=
{
variant
}
>
{
children
??
(
fullText
||
'-'
)
}
</
Chip
>
);
const
trigger
=
<
Chip
className=
{
className
}
size=
"sm"
>
{
children
??
(
fullText
||
'-'
)
}
</
Chip
>;
if
(
!
fullText
||
fullText
===
'-'
||
fullText
.
length
<=
18
)
{
return
trigger
;
...
...
@@ -476,8 +558,10 @@ export function DataTableChip({
export
function
DataTablePagination
({
page
,
pageSize
,
pageSizeOptions
=
[],
total
,
onPageChange
,
onPageSizeChange
,
isDisabled
=
false
,
itemLabel
=
'条'
,
})
{
...
...
@@ -486,13 +570,46 @@ export function DataTablePagination({
const
pageWindowEnd
=
total
?
Math
.
min
(
totalPages
,
pageWindowStart
+
5
)
:
pageWindowStart
;
const
normalizedPageSizeOptions
=
pageSizeOptions
.
map
((
value
)
=>
Number
(
value
))
.
filter
((
value
,
index
,
values
)
=>
(
Number
.
isFinite
(
value
)
&&
value
>
0
&&
values
.
indexOf
(
value
)
===
index
));
const
pageSizeOptionsForPicker
=
normalizedPageSizeOptions
.
map
((
value
)
=>
({
id
:
String
(
value
),
label
:
`
${
value
}
条/页`
,
}));
const
hasPageSizePicker
=
pageSizeOptionsForPicker
.
length
>
0
&&
typeof
onPageSizeChange
===
'function'
;
return
(
<
div
className=
"flex flex-wrap items-center justify-between gap-3"
>
<
Pagination
size=
"sm
"
>
<
Pagination
.
Summary
>
<
div
className=
"flex
min-w-0
flex-wrap items-center justify-between gap-3"
>
<
div
className=
"flex min-w-0 shrink-0 items-center gap-3
"
>
<
span
className=
"min-w-0 text-sm text-muted"
>
共
{
total
}
{
itemLabel
}
,第
{
' '
}
{
total
?
page
+
1
:
0
}
/
{
total
?
totalPages
:
0
}
页
</
Pagination
.
Summary
>
</
span
>
{
hasPageSizePicker
?
(
<
XinyuanAutocomplete
aria
-
label=
"每页条数"
className=
"w-24 shrink-0"
isDisabled=
{
isDisabled
}
options=
{
pageSizeOptionsForPicker
}
placeholder=
"每页条数"
selectedKey=
{
String
(
pageSize
)
}
showSearch=
{
false
}
valueText=
{
`${pageSize} 条/页`
}
variant=
"secondary"
onSelectionChange=
{
(
key
)
=>
{
const
nextPageSize
=
Number
(
key
);
if
(
normalizedPageSizeOptions
.
includes
(
nextPageSize
))
{
onPageSizeChange
(
nextPageSize
);
}
}
}
/>
)
:
null
}
</
div
>
<
Pagination
className=
"ml-auto w-auto shrink-0"
size=
{
hasPageSizePicker
?
'lg'
:
'sm'
}
>
<
Pagination
.
Content
>
<
Pagination
.
Item
>
<
Pagination
.
Previous
...
...
src/components/XinyuanAutocomplete.jsx
View file @
dedde183
...
...
@@ -30,6 +30,7 @@ const toSelectionKey = (value) => String(value ?? '');
export
default
function
XinyuanAutocomplete
({
'aria-label'
:
ariaLabel
,
allowsEmptyCollection
=
false
,
className
,
emptyText
=
'暂无可选项'
,
errorText
=
''
,
...
...
@@ -37,23 +38,28 @@ export default function XinyuanAutocomplete({
fullWidth
=
true
,
inputValue
,
isDisabled
=
false
,
isClearable
=
false
,
isLoading
=
false
,
isOpen
,
isRequired
=
false
,
name
,
onInputChange
,
onClear
,
onOpenChange
,
onSelectionChange
,
options
=
[],
placeholder
=
'请选择'
,
placement
=
'bottom start'
,
popoverHeader
,
renderOption
,
searchPlaceholder
=
'输入关键词搜索'
,
showSearch
=
true
,
selectedKey
,
selectedKeys
,
selectionMode
=
'single'
,
shouldFilter
=
true
,
valueText
,
variant
=
'secondary'
,
variant
,
})
{
const
selectedKeySet
=
getSelectedKeySet
(
selectionMode
,
selectedKey
,
selectedKeys
);
const
selectedKeyStringSet
=
new
Set
(
...
...
@@ -87,12 +93,14 @@ export default function XinyuanAutocomplete({
<
Autocomplete
{
...
rootSelectionProps
}
aria
-
label=
{
ariaLabel
||
placeholder
}
allowsEmptyCollection=
{
allowsEmptyCollection
}
className=
{
className
}
fullWidth=
{
fullWidth
}
isDisabled=
{
isDisabled
}
isOpen=
{
isOpen
}
isRequired=
{
isRequired
}
name=
{
name
}
onClear=
{
onClear
}
selectionMode=
{
selectionMode
}
variant=
{
variant
}
onOpenChange=
{
onOpenChange
}
...
...
@@ -104,23 +112,29 @@ export default function XinyuanAutocomplete({
{
resolvedValue
}
</
span
>
</
Autocomplete
.
Value
>
{
isClearable
?
(
<
Autocomplete
.
ClearButton
aria
-
label=
{
`清除${ariaLabel || placeholder}`
}
/>
)
:
null
}
<
Autocomplete
.
Indicator
>
<
ChevronDown
size=
{
16
}
style=
{
FIELD_SUFFIX_ICON_STYLE
}
/>
</
Autocomplete
.
Indicator
>
</
Autocomplete
.
Trigger
>
<
Autocomplete
.
Popover
placement=
"bottom start"
>
<
Autocomplete
.
Popover
placement=
{
placement
}
>
<
Autocomplete
.
Filter
filter=
{
shouldFilter
?
filter
||
defaultFilter
:
()
=>
true
}
inputValue=
{
inputValue
}
onInputChange=
{
onInputChange
}
>
<
SearchField
aria
-
label=
{
searchPlaceholder
}
fullWidth
variant=
"secondary"
>
<
SearchField
.
Group
>
<
SearchField
.
SearchIcon
><
Search
size=
{
16
}
/></
SearchField
.
SearchIcon
>
<
SearchField
.
Input
autoFocus
placeholder=
{
searchPlaceholder
}
/>
<
SearchField
.
ClearButton
aria
-
label=
"清空搜索"
><
Close
size=
{
14
}
/></
SearchField
.
ClearButton
>
</
SearchField
.
Group
>
</
SearchField
>
{
popoverHeader
}
{
showSearch
?
(
<
SearchField
aria
-
label=
{
searchPlaceholder
}
fullWidth
variant=
{
variant
}
>
<
SearchField
.
Group
>
<
SearchField
.
SearchIcon
><
Search
size=
{
16
}
/></
SearchField
.
SearchIcon
>
<
SearchField
.
Input
autoFocus
placeholder=
{
searchPlaceholder
}
/>
<
SearchField
.
ClearButton
aria
-
label=
"清空搜索"
><
Close
size=
{
14
}
/></
SearchField
.
ClearButton
>
</
SearchField
.
Group
>
</
SearchField
>
)
:
null
}
<
ListBox
aria
-
label=
{
ariaLabel
||
placeholder
}
items=
{
visibleOptions
}
...
...
src/design-system/chartPalette.js
View file @
dedde183
...
...
@@ -18,6 +18,21 @@ export const XINYUAN_CHART_SEMANTIC = Object.freeze({
neutral
:
'var(--rc-chart-neutral)'
,
});
export
function
getXinyuanChartGeometry
()
{
const
rootStyles
=
typeof
document
===
'undefined'
?
null
:
window
.
getComputedStyle
(
document
.
documentElement
);
const
readToken
=
(
token
,
fallback
)
=>
rootStyles
?.
getPropertyValue
(
token
).
trim
()
||
fallback
;
return
{
barRadius
:
8
,
sectorGap
:
readToken
(
'--xinyuan-chart-sector-gap'
,
'#ffffff'
),
sectorRadius
:
Number
.
parseFloat
(
readToken
(
'--xinyuan-chart-sector-radius'
,
'10'
),
)
||
10
,
};
}
function
normalizeChartKey
(
key
)
{
return
String
(
key
??
''
).
trim
();
}
...
...
src/design-system/shell.css
View file @
dedde183
...
...
@@ -381,18 +381,20 @@
gap
:
var
(
--rc-space-2
);
}
.xy-app-switcher-dialog
{
width
:
min
(
46rem
,
calc
(
100vw
-
2rem
));
max-width
:
min
(
46rem
,
calc
(
100vw
-
2rem
));
max-height
:
min
(
42rem
,
calc
(
100
dvh
-
2rem
));
}
.xy-app-identity-well
,
.xy-app-identity-avatar
{
.rc-application-identity-well
{
display
:
grid
;
width
:
2.25rem
;
height
:
2.25rem
;
flex
:
none
;
place-items
:
center
;
overflow
:
hidden
;
border-radius
:
var
(
--rc-radius-control
);
}
.xy-app-identity-avatar
{
.rc-application-identity-avatar
{
width
:
100%
;
height
:
100%
;
border-radius
:
var
(
--rc-radius-control
);
background
:
transparent
;
}
...
...
@@ -670,6 +672,85 @@
gap
:
var
(
--rc-list-stack-gap
);
}
/* Keep wide DataGrid headings and pinned cells aligned to one shared scroll
container, matching the audit-project list behavior. */
.rc-data-table-sticky
.data-table-scroll
{
position
:
relative
;
max-height
:
min
(
60
dvh
,
42rem
);
overflow-x
:
auto
;
overflow-y
:
auto
;
overscroll-behavior
:
contain
;
}
.rc-data-table-sticky
.table__resizable-container
{
overflow
:
visible
;
}
.rc-data-table-sticky
[
data-slot
=
'table-header'
]
.table__column
{
position
:
sticky
;
top
:
0
;
inset-block-start
:
0
;
z-index
:
4
;
display
:
table-cell
;
color
:
var
(
--muted
);
background
:
var
(
--surface-secondary
);
box-shadow
:
inset
0
-1px
0
var
(
--separator
);
}
.rc-data-table-sticky
[
data-slot
=
'table-header'
]
.table__column.bg-accent-soft
{
color
:
var
(
--accent-soft-foreground
);
background
:
var
(
--accent-soft
);
}
.rc-data-table-sticky
[
data-pinned
=
'start'
]
{
position
:
sticky
;
z-index
:
6
;
background
:
var
(
--surface
);
}
.rc-data-table-sticky
[
data-slot
=
'table-header'
]
[
data-pinned
=
'start'
]
{
z-index
:
8
;
background
:
var
(
--surface-secondary
);
}
@media
(
hover
:
hover
)
{
.rc-data-table-sticky
[
data-slot
=
'data-grid'
]
.table__row
:hover
[
data-pinned
=
'start'
]
.table__cell
,
.rc-data-table-sticky
[
data-slot
=
'data-grid'
]
.table__row
[
data-hovered
=
'true'
]
[
data-pinned
=
'start'
]
.table__cell
{
background-color
:
var
(
--color-surface-secondary
);
}
@supports
(
color
:
color-mix
(
in
oklab
,
white
,
black
))
{
.rc-data-table-sticky
[
data-slot
=
'data-grid'
]
.table__row
:hover
[
data-pinned
=
'start'
]
.table__cell
,
.rc-data-table-sticky
[
data-slot
=
'data-grid'
]
.table__row
[
data-hovered
=
'true'
]
[
data-pinned
=
'start'
]
.table__cell
{
background-color
:
color-mix
(
in
oklab
,
var
(
--color-surface
)
40%
,
var
(
--color-surface-secondary
)
);
}
}
}
.rc-data-table-sticky
[
data-slot
=
'data-grid'
]
.table__row
[
data-selected
=
'true'
]
[
data-pinned
=
'start'
]
.table__cell
{
background-color
:
var
(
--color-surface-secondary
);
}
@supports
(
color
:
color-mix
(
in
oklab
,
white
,
black
))
{
.rc-data-table-sticky
[
data-slot
=
'data-grid'
]
.table__row
[
data-selected
=
'true'
]
[
data-pinned
=
'start'
]
.table__cell
{
background-color
:
color-mix
(
in
oklab
,
var
(
--color-surface
)
10%
,
var
(
--color-surface-secondary
)
);
}
}
@media
(
max-width
:
48rem
)
{
.rc-data-table-sticky
.data-table-scroll
{
max-height
:
min
(
54
dvh
,
32rem
);
}
}
@media
(
min-width
:
80rem
)
{
.rc-chart-grid
{
grid-template-columns
:
repeat
(
2
,
minmax
(
0
,
1
fr
));
...
...
@@ -820,22 +901,23 @@
color
:
var
(
--foreground
);
background
:
transparent
;
line-height
:
1
;
--button-bg-hover
:
var
(
--accent-soft
);
--button-bg-pressed
:
var
(
--accent-soft
);
--button-bg-hover
:
var
(
--rc-sidebar-item-selected
);
--button-bg-pressed
:
var
(
--rc-sidebar-item-selected
);
--button-fg
:
var
(
--rc-sidebar-item-selected-foreground
);
}
@media
(
hover
:
hover
)
{
.rc-sidebar-app-trigger.button
:hover
,
.rc-sidebar-app-trigger.button
[
data-hovered
=
'true'
]
{
color
:
var
(
--foreground
);
background
:
var
(
--
accent-soft
);
color
:
var
(
--
rc-sidebar-item-selected-
foreground
);
background
:
var
(
--
rc-sidebar-item-selected
);
}
}
.rc-sidebar-app-trigger.button
[
data-pressed
=
'true'
],
.rc-sidebar-app-trigger.button
[
aria-expanded
=
'true'
]
{
color
:
var
(
--foreground
);
background
:
var
(
--
accent-soft
);
color
:
var
(
--
rc-sidebar-item-selected-
foreground
);
background
:
var
(
--
rc-sidebar-item-selected
);
}
.rc-sidebar-app-popover
{
...
...
@@ -1121,16 +1203,22 @@
margin
:
0
;
overflow
:
hidden
;
border
:
0
;
border-right
:
1px
solid
var
(
--rc-
border-default
);
border-right
:
1px
solid
var
(
--rc-
sidebar-border
);
border-radius
:
0
;
color
:
var
(
--foreground
);
background
:
var
(
--rc-
bg-canvas
);
color
:
var
(
--
rc-sidebar-
foreground
);
background
:
var
(
--rc-
sidebar-background
);
box-shadow
:
none
;
font-synthesis
:
none
;
text-rendering
:
optimizeLegibility
;
-webkit-font-smoothing
:
antialiased
;
}
/* Match the Pro Sidebar root selector so its canvas default cannot win the cascade. */
.template-dashboard-shell
.app-sidebar-glass.sidebar
{
border-right-color
:
var
(
--rc-sidebar-border
);
background-color
:
var
(
--rc-sidebar-background
);
}
.template-dashboard-shell
.sidebar__header
,
.rc-mobile-sidebar
.sidebar__header
{
padding
:
6px
1rem
0.5rem
;
...
...
@@ -1174,7 +1262,7 @@
.app-sidebar-title
{
min-width
:
0
;
overflow
:
hidden
;
color
:
var
(
--foreground
);
color
:
var
(
--
rc-sidebar-
foreground
);
font-size
:
0.95rem
;
font-weight
:
650
;
line-height
:
1.2rem
;
...
...
@@ -1203,9 +1291,9 @@
color
:
var
(
--foreground
);
background-color
:
transparent
;
line-height
:
1
;
--button-bg-hover
:
var
(
--
accent-soft
);
--button-bg-pressed
:
var
(
--
accent-soft
);
--button-fg
:
var
(
--foreground
);
--button-bg-hover
:
var
(
--
rc-sidebar-item-selected
);
--button-bg-pressed
:
var
(
--
rc-sidebar-item-selected
);
--button-fg
:
var
(
--
rc-sidebar-item-selected-
foreground
);
}
.app-sidebar-app-trigger
.anticon
{
...
...
@@ -1225,16 +1313,16 @@
@media
(
hover
:
hover
)
{
.app-sidebar-app-trigger.button
:hover
,
.app-sidebar-app-trigger.button
[
data-hovered
=
'true'
]
{
color
:
var
(
--foreground
);
background-color
:
var
(
--
accent-soft
);
color
:
var
(
--
rc-sidebar-item-selected-
foreground
);
background-color
:
var
(
--
rc-sidebar-item-selected
);
}
}
.app-sidebar-app-trigger.button
:active
,
.app-sidebar-app-trigger.button
[
data-pressed
=
'true'
],
.app-sidebar-app-trigger.button
[
aria-expanded
=
'true'
]
{
color
:
var
(
--foreground
);
background-color
:
var
(
--
accent-soft
);
color
:
var
(
--
rc-sidebar-item-selected-
foreground
);
background-color
:
var
(
--
rc-sidebar-item-selected
);
}
.app-sidebar-app-dialog
{
...
...
@@ -1267,7 +1355,7 @@
.template-dashboard-shell
.sidebar__group-label
,
.rc-mobile-sidebar
.sidebar__group-label
{
padding
:
0.375rem
0.5rem
;
color
:
var
(
--rc-
text
-muted
);
color
:
var
(
--rc-
sidebar
-muted
);
font-size
:
0.75rem
;
font-weight
:
500
;
line-height
:
1rem
;
...
...
@@ -1280,7 +1368,7 @@
gap
:
0.75rem
;
padding
:
0.375rem
0.5rem
;
border-radius
:
calc
(
var
(
--radius
)
*
2
);
color
:
var
(
--
foregroun
d
);
color
:
var
(
--
rc-sidebar-mute
d
);
font-weight
:
400
;
}
...
...
@@ -1288,7 +1376,7 @@
.rc-mobile-sidebar
.sidebar__menu-icon
{
width
:
1.25rem
;
height
:
1.25rem
;
color
:
var
(
--muted
);
color
:
var
(
--
rc-sidebar-
muted
);
}
.template-dashboard-shell
.sidebar__menu-icon
svg
,
...
...
@@ -1299,7 +1387,7 @@
.template-dashboard-shell
.sidebar__menu-label
,
.rc-mobile-sidebar
.sidebar__menu-label
{
color
:
var
(
--foreground
)
;
color
:
inherit
;
font-size
:
0.875rem
;
font-weight
:
inherit
;
line-height
:
1.25rem
;
...
...
@@ -1308,19 +1396,23 @@
.template-dashboard-shell
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
,
.rc-mobile-sidebar
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
{
color
:
var
(
--rc-sidebar-item-selected-foreground
);
background
:
var
(
--rc-sidebar-item-selected
);
}
.template-dashboard-shell
.sidebar__menu-item
:hover
.sidebar__menu-item-content
,
.template-dashboard-shell
.sidebar__menu-item
[
data-hovered
=
'true'
]
.sidebar__menu-item-content
,
.rc-mobile-sidebar
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
,
.rc-mobile-sidebar
.sidebar__menu-item
:hover
.sidebar__menu-item-content
,
.rc-mobile-sidebar
.sidebar__menu-item
[
data-hovered
=
'true'
]
.sidebar__menu-item-content
{
color
:
var
(
--foreground
);
background
:
var
(
--
default
);
color
:
var
(
--
rc-sidebar-
foreground
);
background
:
var
(
--
rc-sidebar-item-hover
);
}
.template-dashboard-shell
...
...
@@ -1338,7 +1430,7 @@
.rc-mobile-sidebar
.sidebar__menu-item
[
data-current
=
'true'
]
:is
(
.sidebar__menu-icon
,
.sidebar__menu-label
)
{
color
:
var
(
--foreground
);
color
:
var
(
--
rc-sidebar-item-selected-
foreground
);
}
.template-dashboard-shell
.sidebar__footer
,
...
...
@@ -1356,20 +1448,20 @@
gap
:
0.625rem
;
padding
:
0.375rem
0.5rem
!important
;
border-radius
:
calc
(
var
(
--radius
)
*
2
)
!important
;
color
:
var
(
--foreground
);
color
:
var
(
--
rc-sidebar-
foreground
);
background
:
transparent
;
text-align
:
left
;
}
@media
(
hover
:
hover
)
{
.app-sidebar-user-trigger.dropdown__trigger
:hover
,
.app-sidebar-user-trigger.dropdown__trigger
[
data-hovered
=
'true'
]
{
background
:
var
(
--
default
);
.app-sidebar-user-trigger.dropdown__trigger
:hover
,
.app-sidebar-user-trigger.dropdown__trigger
[
data-hovered
=
'true'
]
{
background
:
var
(
--
rc-sidebar-item-hover
);
}
}
.app-sidebar-user-trigger.dropdown__trigger
[
aria-expanded
=
'true'
]
{
background
:
var
(
--
default
);
background
:
var
(
--
rc-sidebar-item-selected
);
}
.app-sidebar-user-avatar
{
...
...
@@ -1377,7 +1469,7 @@
height
:
2rem
;
min-width
:
2rem
;
flex
:
0
0
2rem
;
background
:
var
(
--
default
);
background
:
var
(
--
rc-sidebar-item-selected
);
}
.app-sidebar-user-copy
{
...
...
@@ -1399,14 +1491,14 @@
}
.app-sidebar-user-name
{
color
:
var
(
--foreground
);
color
:
var
(
--
rc-sidebar-
foreground
);
font-size
:
0.875rem
;
font-weight
:
500
;
line-height
:
1.125rem
;
}
.app-sidebar-user-description
{
color
:
var
(
--muted
);
color
:
var
(
--
rc-sidebar-
muted
);
font-size
:
0.75rem
;
font-weight
:
500
;
line-height
:
1rem
;
...
...
@@ -1418,3 +1510,133 @@
border-right
:
0
;
}
}
/* Fixed split-shell theme: dark navigation, permanently light content canvas. */
.template-dashboard-shell
.app-sidebar-glass
,
.rc-mobile-sidebar.app-sidebar-glass
{
color
:
var
(
--rc-sidebar-foreground
);
border-color
:
var
(
--rc-sidebar-border
)
!important
;
background-color
:
var
(
--rc-sidebar-background
)
!important
;
}
.template-dashboard-shell
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
,
.rc-mobile-sidebar
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
{
background
:
var
(
--rc-sidebar-item-selected
);
}
.template-dashboard-shell
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
,
.template-dashboard-shell
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
*,
.rc-mobile-sidebar
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
,
.rc-mobile-sidebar
.sidebar__menu-item
[
data-current
=
'true'
]
.sidebar__menu-item-content
*
{
color
:
var
(
--rc-sidebar-item-selected-foreground
)
!important
;
}
.template-dashboard-shell
.sidebar__menu-item
:hover
.sidebar__menu-item-content
,
.template-dashboard-shell
.sidebar__menu-item
[
data-hovered
=
'true'
]
.sidebar__menu-item-content
,
.rc-mobile-sidebar
.sidebar__menu-item
:hover
.sidebar__menu-item-content
,
.rc-mobile-sidebar
.sidebar__menu-item
[
data-hovered
=
'true'
]
.sidebar__menu-item-content
,
.app-sidebar-user-trigger.dropdown__trigger
:hover
,
.app-sidebar-user-trigger.dropdown__trigger
[
data-hovered
=
'true'
]
{
background
:
var
(
--rc-sidebar-item-hover
);
}
.app-sidebar-user-trigger.dropdown__trigger
[
aria-expanded
=
'true'
],
.app-sidebar-app-trigger.button
[
data-pressed
=
'true'
],
.app-sidebar-app-trigger.button
[
aria-expanded
=
'true'
]
{
background
:
var
(
--rc-sidebar-item-selected
);
}
.app-sidebar-user-avatar
,
.app-sidebar-app-trigger.button
,
.app-sidebar-user-trigger.dropdown__trigger
{
color
:
var
(
--rc-sidebar-foreground
);
background
:
transparent
;
}
.app-sidebar-user-avatar
{
background
:
var
(
--rc-sidebar-item-hover
);
}
.template-dashboard-shell
.sidebar__group-label
,
.rc-mobile-sidebar
.sidebar__group-label
,
.template-dashboard-shell
.sidebar__menu-icon
,
.rc-mobile-sidebar
.sidebar__menu-icon
,
.app-sidebar-user-description
{
color
:
var
(
--rc-sidebar-muted
);
}
.template-dashboard-shell
.sidebar__menu-label
,
.rc-mobile-sidebar
.sidebar__menu-label
,
.app-sidebar-title
,
.app-sidebar-user-name
,
.app-sidebar-app-trigger.button
{
color
:
var
(
--rc-sidebar-foreground
);
}
.app-sidebar-user-trigger.dropdown__trigger
:focus-visible
,
.app-sidebar-app-trigger.button
:focus-visible
,
.template-dashboard-shell
.sidebar__menu-item
:focus-visible
,
.rc-mobile-sidebar
.sidebar__menu-item
:focus-visible
{
outline
:
2px
solid
var
(
--rc-sidebar-focus
);
outline-offset
:
2px
;
}
/* Keep the app switcher icon in reverse contrast whenever its state surface is active. */
@media
(
hover
:
hover
)
{
.app-sidebar-app-trigger.button
:hover
,
.app-sidebar-app-trigger.button
[
data-hovered
=
'true'
],
.rc-sidebar-app-trigger.button
:hover
,
.rc-sidebar-app-trigger.button
[
data-hovered
=
'true'
]
{
color
:
var
(
--rc-sidebar-item-selected-foreground
);
background-color
:
var
(
--rc-sidebar-item-selected
);
}
}
.app-sidebar-app-trigger.button
:active
,
.app-sidebar-app-trigger.button
[
data-pressed
=
'true'
],
.app-sidebar-app-trigger.button
[
aria-expanded
=
'true'
],
.rc-sidebar-app-trigger.button
:active
,
.rc-sidebar-app-trigger.button
[
data-pressed
=
'true'
],
.rc-sidebar-app-trigger.button
[
aria-expanded
=
'true'
]
{
color
:
var
(
--rc-sidebar-item-selected-foreground
);
background-color
:
var
(
--rc-sidebar-item-selected
);
}
.app-sidebar-app-trigger.button
:is
(
:hover
,
:active
)
.anticon
,
.app-sidebar-app-trigger.button
[
data-hovered
=
'true'
]
.anticon
,
.app-sidebar-app-trigger.button
[
data-pressed
=
'true'
]
.anticon
,
.app-sidebar-app-trigger.button
[
aria-expanded
=
'true'
]
.anticon
,
.rc-sidebar-app-trigger.button
:is
(
:hover
,
:active
)
.anticon
,
.rc-sidebar-app-trigger.button
[
data-hovered
=
'true'
]
.anticon
,
.rc-sidebar-app-trigger.button
[
data-pressed
=
'true'
]
.anticon
,
.rc-sidebar-app-trigger.button
[
aria-expanded
=
'true'
]
.anticon
{
color
:
inherit
;
}
/* HeroUI renders the Autocomplete indicator inside a bare React Aria button.
Reserve its control slot so the icon has a real pointer and keyboard target. */
.autocomplete__trigger
>
.react-aria-Button
{
display
:
inline-flex
;
width
:
1.5rem
;
min-width
:
1.5rem
;
min-height
:
100%
;
flex
:
0
0
1.5rem
;
align-items
:
center
;
justify-content
:
center
;
}
src/design-system/theme.css
View file @
dedde183
...
...
@@ -11,38 +11,37 @@
[
data-theme
=
"light"
],
[
data-theme
=
"default"
]
{
/* Theme Colors (Light Mode) */
--accent
:
oklch
(
45.32%
0.1661
27.03
);
--accent
:
oklch
(
57.14%
0.2156
29.20
);
--accent-foreground
:
oklch
(
99.11%
0
0
);
--background
:
oklch
(
97.02%
0.00
45
27.03
);
--border
:
oklch
(
90.00%
0.00
45
27.03
);
--background
:
oklch
(
97.02%
0.00
00
29.20
);
--border
:
oklch
(
90.00%
0.00
00
29.20
);
--danger
:
oklch
(
45.32%
0.1661
27.03
);
--danger-foreground
:
oklch
(
99.11%
0
0
);
/* HeroUI control aliases: primary = L1, secondary = L2. */
--default
:
var
(
--surface-secondary
);
--default-foreground
:
var
(
--surface-secondary-foreground
);
--field-background
:
var
(
--surface
);
--default
:
oklch
(
94.00%
0.0000
29.20
);
--default-foreground
:
oklch
(
21.03%
0.0059
29.20
);
--field-background
:
oklch
(
100.00%
0.0000
29.20
);
--field-border
:
transparent
;
--field-foreground
:
var
(
--surface-foreground
);
--field-placeholder
:
oklch
(
55.17%
0.00
90
27.03
);
--focus
:
oklch
(
45.32%
0.1661
27.03
);
--foreground
:
oklch
(
21.03%
0.00
45
27.03
);
--muted
:
oklch
(
55.17%
0.00
90
27.03
);
--overlay
:
var
(
--surface
);
--overlay-foreground
:
var
(
--surface-foreground
);
--scrollbar
:
oklch
(
87.10%
0.00
45
27.03
);
--segment
:
oklch
(
100.00%
0.00
45
27.03
);
--segment-foreground
:
oklch
(
21.03%
0.00
45
27.03
);
--separator
:
oklch
(
92.00%
0.00
45
27.03
);
--success
:
oklch
(
73.29%
0.19
52
123.59
);
--success-foreground
:
oklch
(
21.03%
0.0059
123.
59
);
--surface
:
oklch
(
100.00%
0.00
22
27.03
);
--surface-foreground
:
oklch
(
21.03%
0.00
45
27.03
);
--surface-secondary
:
oklch
(
95.24%
0.00
36
27.03
);
--surface-secondary-foreground
:
oklch
(
21.03%
0.00
45
27.03
);
--surface-tertiary
:
oklch
(
93.73%
0.00
36
27.03
);
--surface-tertiary-foreground
:
oklch
(
21.03%
0.00
45
27.03
);
--warning
:
oklch
(
78.19%
0.15
99
45.11
);
--warning-foreground
:
oklch
(
21.03%
0.0059
45.
11
);
--field-foreground
:
oklch
(
21.03%
0.0000
29.20
);
--field-placeholder
:
oklch
(
55.17%
0.00
00
29.20
);
--focus
:
oklch
(
57.14%
0.2156
29.20
);
--foreground
:
oklch
(
21.03%
0.00
00
29.20
);
--muted
:
oklch
(
55.17%
0.00
00
29.20
);
--overlay
:
oklch
(
100.00%
0.0000
29.20
);
--overlay-foreground
:
oklch
(
21.03%
0.0000
29.20
);
--scrollbar
:
oklch
(
87.10%
0.00
00
29.20
);
--segment
:
oklch
(
100.00%
0.00
00
29.20
);
--segment-foreground
:
oklch
(
21.03%
0.00
00
29.20
);
--separator
:
oklch
(
92.00%
0.00
00
29.20
);
--success
:
oklch
(
73.29%
0.19
35
123.85
);
--success-foreground
:
oklch
(
21.03%
0.0059
123.
85
);
--surface
:
oklch
(
100.00%
0.00
00
29.20
);
--surface-foreground
:
oklch
(
21.03%
0.00
00
29.20
);
--surface-secondary
:
oklch
(
95.24%
0.00
00
29.20
);
--surface-secondary-foreground
:
oklch
(
21.03%
0.00
00
29.20
);
--surface-tertiary
:
oklch
(
93.73%
0.00
00
29.20
);
--surface-tertiary-foreground
:
oklch
(
21.03%
0.00
00
29.20
);
--warning
:
oklch
(
78.19%
0.15
85
45.37
);
--warning-foreground
:
oklch
(
21.03%
0.0059
45.
37
);
/* Border Radius */
--radius
:
0.5rem
;
...
...
@@ -51,42 +50,90 @@
/* Font Family */
/* Make sure to load Inter font in your app */
--font-sans
:
var
(
--font-inter
);
/*
* Xinyuan fixed split-shell contract.
* The application canvas always uses this light theme. The Sidebar is the
* only persistent dark plane and must consume these semantic tokens.
*/
--xinyuan-sidebar-background
:
oklch
(
16%
0.020
255
);
--xinyuan-sidebar-border
:
oklch
(
26%
0.022
255
);
--xinyuan-sidebar-foreground
:
oklch
(
97%
0.004
255
);
--xinyuan-sidebar-muted
:
oklch
(
76%
0.018
255
);
--xinyuan-sidebar-item-hover
:
oklch
(
22%
0.026
255
);
--xinyuan-sidebar-item-selected
:
oklch
(
28%
0.016
255
);
--xinyuan-sidebar-item-selected-foreground
:
oklch
(
99%
0.002
255
);
--xinyuan-sidebar-focus
:
oklch
(
78%
0.105
29.20
);
/*
* ECharts is the required chart renderer for Xinyuan applications.
* Ant Design remains the icon source. Resolve these CSS variables to
* concrete colors before assigning them to an ECharts option object.
*/
/* 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
;
--xinyuan-chart-tooltip-background
:
#ffffff
;
--xinyuan-chart-tooltip-border
:
#dedede
;
--xinyuan-chart-tooltip-foreground
:
#222222
;
--xinyuan-chart-tooltip-shadow
:
0
16px
40px
rgb
(
15
23
42
/
14%
);
--xinyuan-chart-legend-font-size
:
0.75rem
;
--xinyuan-chart-legend-line-height
:
1rem
;
}
.dark
,
[
data-theme
=
"dark"
]
{
color-scheme
:
dark
;
/* Theme Colors (Dark Mode) */
--accent
:
oklch
(
45.32%
0.1661
27.03
);
--accent
:
oklch
(
57.14%
0.2156
29.20
);
--accent-foreground
:
oklch
(
99.11%
0
0
);
--background
:
oklch
(
12.00%
0.00
45
27.03
);
--border
:
oklch
(
28.00%
0.00
45
27.03
);
--danger
:
oklch
(
62
.00%
0.1600
27.03
);
--background
:
oklch
(
12.00%
0.00
00
29.20
);
--border
:
oklch
(
28.00%
0.00
00
29.20
);
--danger
:
oklch
(
62
%
0.16
27.03
);
--danger-foreground
:
oklch
(
99.11%
0
0
);
/* HeroUI control aliases: primary = L1, secondary = L2. */
--default
:
var
(
--surface-secondary
);
--default-foreground
:
var
(
--surface-secondary-foreground
);
--field-background
:
var
(
--surface
);
--default
:
oklch
(
27.40%
0.0000
29.20
);
--default-foreground
:
oklch
(
99.11%
0
0
);
--field-background
:
oklch
(
21.03%
0.0000
29.20
);
--field-border
:
transparent
;
--field-foreground
:
var
(
--surface-foreground
);
--field-placeholder
:
oklch
(
70.50%
0.00
90
27.03
);
--focus
:
oklch
(
45.32%
0.1661
27.03
);
--foreground
:
oklch
(
99.11%
0.00
45
27.03
);
--muted
:
oklch
(
70.50%
0.00
90
27.03
);
--overlay
:
var
(
--surface
);
--overlay-foreground
:
var
(
--surface-foreground
);
--scrollbar
:
oklch
(
70.50%
0.00
45
27.03
);
--segment
:
oklch
(
39.64%
0.00
45
27.03
);
--segment-foreground
:
oklch
(
99.11%
0.00
45
27.03
);
--separator
:
oklch
(
25.00%
0.00
45
27.03
);
--success
:
oklch
(
73.29%
0.19
52
123.59
);
--success-foreground
:
oklch
(
21.03%
0.0059
123.
59
);
--surface
:
oklch
(
21.03%
0.00
90
27.03
);
--surface-foreground
:
oklch
(
99.11%
0.00
45
27.03
);
--surface-secondary
:
oklch
(
25.70%
0.00
67
27.03
);
--surface-secondary-foreground
:
oklch
(
99.11%
0.00
45
27.03
);
--surface-tertiary
:
oklch
(
27.21%
0.00
67
27.03
);
--surface-tertiary-foreground
:
oklch
(
99.11%
0.00
45
27.03
);
--warning
:
oklch
(
82.03%
0.1
400
49.12
);
--warning-foreground
:
oklch
(
21.03%
0.0059
49.
12
);
--field-foreground
:
oklch
(
99.11%
0.0000
29.20
);
--field-placeholder
:
oklch
(
70.50%
0.00
00
29.20
);
--focus
:
oklch
(
57.14%
0.2156
29.20
);
--foreground
:
oklch
(
99.11%
0.00
00
29.20
);
--muted
:
oklch
(
70.50%
0.00
00
29.20
);
--overlay
:
oklch
(
21.03%
0.0000
29.20
);
--overlay-foreground
:
oklch
(
99.11%
0.0000
29.20
);
--scrollbar
:
oklch
(
70.50%
0.00
00
29.20
);
--segment
:
oklch
(
39.64%
0.00
00
29.20
);
--segment-foreground
:
oklch
(
99.11%
0.00
00
29.20
);
--separator
:
oklch
(
25.00%
0.00
00
29.20
);
--success
:
oklch
(
73.29%
0.19
35
123.85
);
--success-foreground
:
oklch
(
21.03%
0.0059
123.
85
);
--surface
:
oklch
(
21.03%
0.00
00
29.20
);
--surface-foreground
:
oklch
(
99.11%
0.00
00
29.20
);
--surface-secondary
:
oklch
(
25.70%
0.00
00
29.20
);
--surface-secondary-foreground
:
oklch
(
99.11%
0.00
00
29.20
);
--surface-tertiary
:
oklch
(
27.21%
0.00
00
29.20
);
--surface-tertiary-foreground
:
oklch
(
99.11%
0.00
00
29.20
);
--warning
:
oklch
(
82.03%
0.1
388
49.38
);
--warning-foreground
:
oklch
(
21.03%
0.0059
49.
38
);
}
src/design-system/tokens.css
View file @
dedde183
...
...
@@ -2,9 +2,7 @@
.light
,
.default
,
[
data-theme
=
'light'
],
[
data-theme
=
'default'
],
.dark
,
[
data-theme
=
'dark'
]
{
[
data-theme
=
'default'
]
{
/* Xinyuan aliases and extension tokens. HeroUI core tokens are owned by theme.css. */
--rc-brand-primary
:
var
(
--accent
);
--rc-brand-light
:
color-mix
(
in
oklab
,
var
(
--accent
)
84%
,
white
);
...
...
@@ -27,6 +25,17 @@
var
(
--background
)
);
--rc-text-muted
:
var
(
--muted
);
--rc-text-empty
:
color-mix
(
in
oklab
,
var
(
--muted
)
72%
,
var
(
--background
));
/* Fixed dark Sidebar aliases. The content canvas remains light. */
--rc-sidebar-background
:
var
(
--xinyuan-sidebar-background
);
--rc-sidebar-border
:
var
(
--xinyuan-sidebar-border
);
--rc-sidebar-foreground
:
var
(
--xinyuan-sidebar-foreground
);
--rc-sidebar-muted
:
var
(
--xinyuan-sidebar-muted
);
--rc-sidebar-item-hover
:
var
(
--xinyuan-sidebar-item-hover
);
--rc-sidebar-item-selected
:
var
(
--xinyuan-sidebar-item-selected
);
--rc-sidebar-item-selected-foreground
:
var
(
--xinyuan-sidebar-item-selected-foreground
);
--rc-sidebar-focus
:
var
(
--xinyuan-sidebar-focus
);
--rc-font-sans
:
var
(
--font-sans
);
--rc-font-medium
:
500
;
...
...
@@ -45,6 +54,7 @@
/* Page geometry only. Component appearance remains owned by HeroUI. */
--rc-shell-inline-gutter
:
3rem
;
--rc-sidebar-width
:
15rem
;
--rc-navbar-inline-padding
:
var
(
--rc-shell-inline-gutter
);
--rc-page-inline-start
:
var
(
--rc-shell-inline-gutter
);
--rc-page-inline-end
:
var
(
--rc-shell-inline-gutter
);
...
...
@@ -138,40 +148,28 @@
);
/* Data visualization only. Do not reuse UI status colors as a series palette. */
--rc-chart-1
:
oklch
(
45.32%
0.1661
27.03
);
--rc-chart-2
:
oklch
(
56%
0.135
255
);
--rc-chart-3
:
oklch
(
64%
0.11
220
);
--rc-chart-4
:
oklch
(
56%
0.115
280
);
--rc-chart-5
:
oklch
(
60%
0.095
200
);
--rc-chart-6
:
oklch
(
70%
0.12
80
);
--rc-chart-7
:
oklch
(
60%
0.1
295
);
--rc-chart-8
:
oklch
(
57%
0.035
250
);
--rc-chart-1
:
var
(
--xinyuan-chart-1
);
--rc-chart-2
:
var
(
--xinyuan-chart-2
);
--rc-chart-3
:
var
(
--xinyuan-chart-3
);
--rc-chart-4
:
var
(
--xinyuan-chart-4
);
--rc-chart-5
:
var
(
--xinyuan-chart-5
);
--rc-chart-6
:
var
(
--xinyuan-chart-6
);
--rc-chart-7
:
var
(
--xinyuan-chart-7
);
--rc-chart-8
:
var
(
--xinyuan-chart-8
);
--rc-chart-9
:
var
(
--xinyuan-chart-9
);
--rc-chart-10
:
var
(
--xinyuan-chart-10
);
--rc-chart-11
:
var
(
--xinyuan-chart-11
);
--rc-chart-12
:
var
(
--xinyuan-chart-12
);
--rc-chart-13
:
var
(
--xinyuan-chart-13
);
--rc-chart-14
:
var
(
--xinyuan-chart-14
);
--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
:
0.75rem
;
--rc-chart-legend-line-height
:
1rem
;
}
.dark
,
[
data-theme
=
'dark'
]
{
color-scheme
:
dark
;
--rc-text-secondary
:
color-mix
(
in
oklab
,
var
(
--foreground
)
76%
,
var
(
--background
)
);
--rc-button-secondary-border
:
color-mix
(
in
oklab
,
var
(
--foreground
)
18%
,
transparent
);
--rc-shadow-raised
:
0
1px
2px
rgb
(
0
0
0
/
24%
),
0
8px
24px
rgb
(
0
0
0
/
24%
);
--rc-shadow-overlay
:
0
16px
40px
rgb
(
0
0
0
/
38%
);
--rc-shadow-card
:
0
1px
2px
rgb
(
0
0
0
/
20%
),
0
4px
12px
rgb
(
0
0
0
/
18%
);
--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/ApplicationSwitcherDialog.jsx
View file @
dedde183
import
{
getApplications
}
from
'@/services/DataService'
;
import
xinyuanLogo
from
'@/assets/identity/xy-logo-red.png'
;
import
{
EmptyStateSurface
}
from
'@/components/DataTable'
;
import
{
ExclamationTriangleIcon
,
MagnifyingGlassIcon
}
from
'@/components/AppIcons'
;
import
{
APP_DISPLAY_NAME
,
APP_LOGIN_SUBTITLE
,
APP_ROUTE_NAME
,
}
from
'@/constants/app'
;
import
WarningOutlined
from
'@ant-design/icons/WarningOutlined
'
;
import
{
getApplications
}
from
'@/services/DataService
'
;
import
{
EmptyState
}
from
'@heroui-pro/react/empty-state'
;
import
{
Alert
,
Avatar
,
Card
,
Chip
,
Description
,
Modal
,
Skeleton
,
Surface
}
from
'@heroui/react'
;
import
{
ItemCard
}
from
'@heroui-pro/react/item-card'
;
import
{
ItemCardGroup
}
from
'@heroui-pro/react/item-card-group'
;
import
{
Alert
,
Avatar
,
Chip
,
Modal
,
ScrollShadow
,
SearchField
,
Skeleton
,
Surface
,
}
from
'@heroui/react'
;
import
{
useEffect
,
useMemo
,
useState
}
from
'react'
;
const
CURRENT_APPLICATION_NAME
=
APP_ROUTE_NAME
;
function
normalizeLabelList
(
items
)
{
if
(
!
Array
.
isArray
(
items
))
{
return
[];
...
...
@@ -36,28 +50,26 @@ function resolveApplicationUrl(application) {
function
normalizeApplication
(
application
,
fallback
=
{})
{
const
name
=
application
?.
name
||
fallback
?.
name
||
''
;
const
isCurrentSystem
=
name
===
APP_ROUTE
_NAME
;
const
current
=
name
===
CURRENT_APPLICATION
_NAME
;
return
{
name
,
title
:
isCurrentSystem
title
:
current
?
APP_DISPLAY_NAME
:
application
?.
title
||
fallback
?.
title
||
application
?.
name
||
fallback
?.
name
||
'未命名应用'
,
description
:
isCurrentSystem
description
:
current
?
APP_LOGIN_SUBTITLE
:
application
?.
description
||
fallback
?.
description
||
fallback
?.
title
||
application
?.
title
||
'进入对应应用继续处理业务'
,
logo
:
application
?.
logo
||
fallback
?.
logo
||
null
,
url
:
resolveApplicationUrl
({
name
,
}),
logo
:
xinyuanLogo
,
url
:
resolveApplicationUrl
({
name
}),
access
:
application
?.
access
||
fallback
?.
access
||
''
,
status
:
application
?.
status
||
fallback
?.
status
||
''
,
type
:
application
?.
type
||
fallback
?.
type
||
''
,
...
...
@@ -75,13 +87,11 @@ function mergeApplications(fetchedApplications, fallbackApplications) {
const
fallbackMap
=
new
Map
(
fallbackApplications
.
map
((
application
)
=>
[
application
.
name
,
application
]),
);
const
normalizedFetched
=
fetchedApplications
.
map
((
application
)
=>
normalizeApplication
(
application
,
fallbackMap
.
get
(
application
?.
name
)),
)
.
filter
((
application
)
=>
application
.
name
);
const
mergedMap
=
new
Map
(
normalizedFetched
.
map
((
application
)
=>
[
application
.
name
,
application
]),
);
...
...
@@ -91,23 +101,20 @@ function mergeApplications(fetchedApplications, fallbackApplications) {
return
;
}
mergedMap
.
set
(
application
.
name
,
normalizeApplication
(
application
,
application
),
);
mergedMap
.
set
(
application
.
name
,
normalizeApplication
(
application
,
application
));
});
if
(
!
mergedMap
.
has
(
APP_ROUTE_NAME
))
{
mergedMap
.
set
(
APP_ROUTE_NAME
,
normalizeApplication
({
name
:
APP_ROUTE_NAME
,
const
currentApplication
=
mergedMap
.
get
(
CURRENT_APPLICATION_NAME
);
mergedMap
.
set
(
CURRENT_APPLICATION_NAME
,
normalizeApplication
(
{
name
:
CURRENT_APPLICATION_NAME
},
currentApplication
||
{
title
:
APP_DISPLAY_NAME
,
description
:
APP_LOGIN_SUBTITLE
,
logo
:
xinyuanLogo
,
}),
);
}
},
),
);
return
Array
.
from
(
mergedMap
.
values
()).
sort
((
left
,
right
)
=>
{
const
orderDifference
=
Number
(
left
.
order
||
0
)
-
Number
(
right
.
order
||
0
);
...
...
@@ -120,23 +127,18 @@ function mergeApplications(fetchedApplications, fallbackApplications) {
}
function
unwrapApplications
(
response
)
{
if
(
Array
.
isArray
(
response
))
{
return
response
;
}
if
(
Array
.
isArray
(
response
?.
data
))
{
return
response
.
data
;
}
if
(
Array
.
isArray
(
response
?.
applications
))
{
return
response
.
applications
;
}
if
(
Array
.
isArray
(
response
?.
result
))
{
return
response
.
result
;
}
return
[];
const
candidates
=
[
response
,
response
?.
data
,
response
?.
applications
,
response
?.
apps
,
response
?.
records
,
response
?.
rows
,
response
?.
list
,
response
?.
result
,
];
return
candidates
.
find
(
Array
.
isArray
)
||
[];
}
function
buildApplicationSections
(
applications
,
applicationGroups
)
{
...
...
@@ -191,6 +193,10 @@ function buildApplicationSections(applications, applicationGroups) {
}
function
isCurrentApplication
(
application
)
{
if
(
application
?.
name
===
CURRENT_APPLICATION_NAME
)
{
return
true
;
}
if
(
typeof
window
===
'undefined'
)
{
return
false
;
}
...
...
@@ -200,9 +206,8 @@ function isCurrentApplication(application) {
if
(
application
?.
url
?.
startsWith
(
'http'
))
{
try
{
const
url
=
new
URL
(
application
.
url
);
return
url
.
pathname
===
currentPath
;
}
catch
(
error
)
{
return
new
URL
(
application
.
url
).
pathname
===
currentPath
;
}
catch
{
return
false
;
}
}
...
...
@@ -215,82 +220,78 @@ function isCurrentApplication(application) {
);
}
function
markCurrentApplication
(
applications
)
{
if
(
!
Array
.
isArray
(
applications
)
||
applications
.
length
===
0
)
{
return
[];
}
const
currentApplication
=
applications
.
find
(
isCurrentApplication
);
return
applications
.
map
((
application
)
=>
({
...
application
,
current
:
currentApplication
?.
name
===
application
.
name
,
}));
}
function
ApplicationCard
({
application
})
{
const
current
=
application
.
current
===
true
;
function
ApplicationCard
({
application
,
current
})
{
const
target
=
application
?.
url
?.
startsWith
(
'http'
)
?
'_blank'
:
undefined
;
const
description
=
application
.
description
?.
trim
();
const
hasDistinctDescription
=
description
&&
description
!==
application
.
title
.
trim
();
return
(
<
Card
className=
"h-full"
variant=
"secondary"
>
<
Card
.
Content
>
<
a
aria
-
current=
{
current
?
'page'
:
undefined
}
className=
"flex min-w-0 items-center gap-3 no-underline"
href=
{
application
.
url
}
rel=
{
target
?
'noreferrer'
:
undefined
}
target=
{
target
}
>
<
Surface
aria
-
hidden=
"true"
className=
"xy-app-identity-well grid size-9 shrink-0 place-items-center overflow-hidden"
variant=
"tertiary"
<
ItemCard
aria
-
current=
{
current
?
'page'
:
undefined
}
className=
"h-full min-w-0 no-underline"
href=
{
application
.
url
}
rel=
{
target
?
'noreferrer'
:
undefined
}
render=
{
(
props
)
=>
<
a
{
...
props
}
/>
}
target=
{
target
}
variant=
{
current
?
'outline'
:
'default'
}
>
<
ItemCard
.
Icon
aria
-
hidden=
"true"
>
<
Surface
className=
"rc-application-identity-well"
variant=
"default"
>
<
Avatar
className=
"rc-application-identity-avatar"
color=
"accent"
variant=
"soft"
>
<
Avatar
className=
"xy-app-identity-avatar size-full"
color=
"accent"
variant=
"soft"
>
{
application
.
logo
?
(
<
Avatar
.
Image
alt=
""
className=
"object-contain p-1.5"
src=
{
application
.
logo
}
/>
)
:
null
}
<
Avatar
.
Fallback
>
{
application
.
title
.
trim
().
slice
(
0
,
1
)
||
'应'
}
</
Avatar
.
Fallback
>
</
Avatar
>
</
Surface
>
<
span
className=
"min-w-0 flex-1"
>
<
span
className=
"block truncate text-sm font-medium leading-5"
title=
{
application
.
title
}
>
{
application
.
title
}
</
span
>
<
Description
className=
"mt-0.5 block truncate leading-4"
title=
{
application
.
description
}
>
{
application
.
description
||
application
.
name
||
'应用'
}
</
Description
>
</
span
>
{
current
?
<
Chip
color=
"accent"
size=
"sm"
>
当前
</
Chip
>
:
null
}
</
a
>
</
Card
.
Content
>
</
Card
>
<
Avatar
.
Image
alt=
""
className=
"object-contain p-1.5"
src=
{
application
.
logo
||
xinyuanLogo
}
/>
<
Avatar
.
Fallback
>
<
img
alt=
""
className=
"object-contain p-1.5"
src=
{
xinyuanLogo
}
/>
</
Avatar
.
Fallback
>
</
Avatar
>
</
Surface
>
</
ItemCard
.
Icon
>
<
ItemCard
.
Content
>
<
ItemCard
.
Title
title=
{
application
.
title
}
>
{
application
.
title
}
</
ItemCard
.
Title
>
{
hasDistinctDescription
?
(
<
ItemCard
.
Description
title=
{
description
}
>
{
description
}
</
ItemCard
.
Description
>
)
:
null
}
</
ItemCard
.
Content
>
{
current
?
(
<
ItemCard
.
Action
>
<
Chip
color=
"accent"
size=
"sm"
>
当前
</
Chip
>
</
ItemCard
.
Action
>
)
:
null
}
</
ItemCard
>
);
}
function
LoadingSkeleton
()
{
return
(
<
div
className=
"grid gap-3 sm:grid-cols-2"
aria
-
label=
"正在加载应用"
>
<
ItemCardGroup
aria
-
label=
"正在加载应用"
className=
"grid-cols-1 sm:grid-cols-2"
columns=
{
2
}
layout=
"grid"
variant=
"transparent"
>
{
Array
.
from
({
length
:
6
}).
map
((
_
,
index
)
=>
(
<
Card
key=
{
index
}
variant=
"secondary"
>
<
Card
.
Content
className=
"flex items-center gap-3"
>
<
Skeleton
className=
"size-9 shrink-0"
/>
<
div
className=
"min-w-0 flex-1 space-y-2"
>
<
Skeleton
className=
"h-4 w-2/3"
/>
<
Skeleton
className=
"h-3 w-full"
/>
</
div
>
</
Card
.
Content
>
</
Card
>
<
ItemCard
key=
{
index
}
variant=
"default"
>
<
ItemCard
.
Icon
><
Skeleton
className=
"size-9"
/></
ItemCard
.
Icon
>
<
ItemCard
.
Content
className=
"gap-2"
>
<
Skeleton
className=
"h-4 w-2/3"
/>
<
Skeleton
className=
"h-3 w-full"
/>
</
ItemCard
.
Content
>
</
ItemCard
>
))
}
</
div
>
</
ItemCardGroup
>
);
}
...
...
@@ -314,72 +315,84 @@ export default function ApplicationSwitcherDialog({
)
.
filter
((
application
)
=>
application
.
name
);
},
[
userInfo
]);
const
[
applications
,
setApplications
]
=
useState
(()
=>
m
arkCurrentApplication
(
fallbackApplications
),
m
ergeApplications
([],
fallbackApplications
),
);
const
[
status
,
setStatus
]
=
useState
(
fallbackApplications
.
length
>
0
?
'success'
:
'idle'
,
);
const
[
errorMessage
,
setErrorMessage
]
=
useState
(
''
);
const
[
applicationSearch
,
setApplicationSearch
]
=
useState
(
''
);
const
currentApplicationName
=
useMemo
(
()
=>
applications
.
find
(
isCurrentApplication
)?.
name
||
CURRENT_APPLICATION_NAME
,
[
applications
],
);
const
applicationSections
=
useMemo
(
()
=>
buildApplicationSections
(
applications
,
siteInfo
?.
group
),
[
applications
,
siteInfo
?.
group
],
);
const
filteredApplicationSections
=
useMemo
(()
=>
{
const
keyword
=
applicationSearch
.
trim
().
toLocaleLowerCase
();
if
(
!
keyword
)
{
return
applicationSections
;
}
return
applicationSections
.
map
((
section
)
=>
({
...
section
,
applications
:
section
.
applications
.
filter
((
application
)
=>
[
application
.
title
,
application
.
description
,
application
.
name
,
].
some
((
value
)
=>
String
(
value
||
''
).
toLocaleLowerCase
().
includes
(
keyword
))),
}))
.
filter
((
section
)
=>
section
.
applications
.
length
>
0
);
},
[
applicationSearch
,
applicationSections
]);
useEffect
(()
=>
{
if
(
fallbackApplications
.
length
===
0
)
{
return
;
}
setApplications
((
previous
)
=>
{
if
(
previous
.
length
>
0
)
{
return
previous
;
}
return
markCurrentApplication
(
fallbackApplications
);
});
setApplications
((
previous
)
=>
mergeApplications
(
previous
,
fallbackApplications
),
);
},
[
fallbackApplications
]);
useEffect
(()
=>
{
if
(
!
open
)
{
setApplicationSearch
(
''
);
return
;
}
let
cancelled
=
false
;
const
loadApplications
=
async
()
=>
{
setStatus
((
previous
)
=>
(
previous
===
'success'
?
previous
:
'loading'
));
setErrorMessage
(
''
);
try
{
const
response
=
await
getApplications
();
const
fetchedApplications
=
unwrapApplications
(
response
);
if
(
cancelled
)
{
return
;
}
const
mergedApplications
=
mergeApplications
(
fetchedApplications
,
fallbackApplications
,
setApplications
(
mergeApplications
(
unwrapApplications
(
response
),
fallbackApplications
),
);
setApplications
(
markCurrentApplication
(
mergedApplications
));
setStatus
(
'success'
);
}
catch
(
error
)
{
}
catch
{
if
(
cancelled
)
{
return
;
}
setApplications
(
m
arkCurrentApplication
(
fallbackApplications
));
setApplications
(
m
ergeApplications
([],
fallbackApplications
));
setStatus
(
'error'
);
setErrorMessage
(
'应用列表加载失败,已回退展示当前账号上的应用信息。'
);
}
};
loadApplications
();
return
()
=>
{
cancelled
=
true
;
};
...
...
@@ -395,70 +408,93 @@ export default function ApplicationSwitcherDialog({
if
(
!
isOpen
)
onClose
?.();
}
}
>
<
Modal
.
Container
className=
"w-full p-4 sm:w-full sm:p-4"
placement=
"center"
scroll=
"inside"
size=
"lg"
>
<
Modal
.
Container
placement=
"center"
scroll=
"inside"
size=
"lg"
>
<
Modal
.
Dialog
aria
-
label=
"应用切换"
className=
"xy-app-switcher-dialog p-7"
className=
"max-h-[min(42rem,calc(100dvh-2rem))] bg-background p-7"
style=
{
{
maxWidth
:
'min(46rem, calc(100vw - 2rem))'
,
width
:
'min(46rem, calc(100vw - 2rem))'
,
}
}
>
<
Modal
.
CloseTrigger
aria
-
label=
"关闭应用切换"
style=
{
{
insetInlineEnd
:
'1.75rem'
,
top
:
'1.75rem'
}
}
/>
<
Modal
.
Header
>
<
Modal
.
Heading
>
应用切换
</
Modal
.
Heading
>
<
Description
>
选择你可访问的应用
</
Description
>
<
SearchField
aria
-
label=
"搜索应用"
className=
"w-full min-w-0"
fullWidth
value=
{
applicationSearch
}
variant=
"secondary"
onChange=
{
setApplicationSearch
}
>
<
SearchField
.
Group
>
<
SearchField
.
SearchIcon
>
<
MagnifyingGlassIcon
size=
{
16
}
/>
</
SearchField
.
SearchIcon
>
<
SearchField
.
Input
placeholder=
"搜索应用"
/>
<
SearchField
.
ClearButton
aria
-
label=
"清空应用搜索"
/>
</
SearchField
.
Group
>
</
SearchField
>
</
Modal
.
Header
>
<
Modal
.
Body
style=
{
{
marginTop
:
'0.75rem'
}
}
>
{
status
===
'error'
&&
errorMessage
?
(
<
Alert
status=
"warning"
>
<
Alert
.
Indicator
>
<
WarningOutlined
className=
"size-5"
/>
</
Alert
.
Indicator
>
<
Alert
.
Content
>
<
Alert
.
Description
>
{
errorMessage
}
</
Alert
.
Description
>
</
Alert
.
Content
>
</
Alert
>
)
:
null
}
{
status
===
'loading'
&&
applications
.
length
===
0
?
(
<
LoadingSkeleton
/>
)
:
applications
.
length
>
0
?
(
<
div
className=
"space-y-6"
>
{
applicationSections
.
map
((
section
)
=>
(
<
section
key=
{
section
.
key
}
>
<
div
className=
"mb-3 flex items-center justify-between gap-3"
>
<
h3
className=
"text-sm font-medium leading-5"
>
{
section
.
label
}
</
h3
>
<
Description
>
{
section
.
applications
.
length
}
个应用
</
Description
>
</
div
>
<
div
className=
"grid gap-3 sm:grid-cols-2"
>
<
Modal
.
Body
className=
"flex min-h-0 overflow-hidden"
style=
{
{
marginTop
:
'0.75rem'
}
}
>
<
ScrollShadow
className=
"h-full min-h-0 flex-1 overflow-y-auto pe-1 pb-1"
hideScrollBar=
{
false
}
offset=
{
12
}
size=
{
24
}
>
{
status
===
'error'
&&
errorMessage
?
(
<
Alert
className=
"mb-4"
status=
"warning"
>
<
Alert
.
Indicator
><
ExclamationTriangleIcon
size=
{
20
}
/></
Alert
.
Indicator
>
<
Alert
.
Content
><
Alert
.
Description
>
{
errorMessage
}
</
Alert
.
Description
></
Alert
.
Content
>
</
Alert
>
)
:
null
}
{
status
===
'loading'
&&
applications
.
length
===
0
?
(
<
LoadingSkeleton
/>
)
:
filteredApplicationSections
.
length
>
0
?
(
<
div
className=
"space-y-6"
>
{
filteredApplicationSections
.
map
((
section
)
=>
(
<
ItemCardGroup
key=
{
section
.
key
}
className=
"grid-cols-1 sm:grid-cols-2"
columns=
{
2
}
layout=
"grid"
variant=
"transparent"
>
<
ItemCardGroup
.
Header
>
<
ItemCardGroup
.
Title
>
{
section
.
label
}
</
ItemCardGroup
.
Title
>
</
ItemCardGroup
.
Header
>
{
section
.
applications
.
map
((
application
)
=>
(
<
ApplicationCard
key=
{
application
.
name
}
application=
{
application
}
current=
{
application
.
name
===
currentApplicationName
}
/>
))
}
</
div
>
</
section
>
))
}
</
div
>
)
:
(
<
EmptyState
size=
"md"
>
<
EmptyState
.
Header
>
<
EmptyState
.
Title
>
暂无可切换应用
</
EmptyState
.
Title
>
<
EmptyState
.
Description
>
当前账号还没有分配其它应用权限。
</
EmptyState
.
Description
>
</
EmptyState
.
Header
>
</
EmptyState
>
)
}
</
ItemCardGroup
>
))
}
</
div
>
)
:
(
<
EmptyStateSurface
>
<
EmptyState
size=
"md"
>
<
EmptyState
.
Header
>
<
EmptyState
.
Title
>
{
applicationSearch
.
trim
()
?
'没有找到匹配的应用'
:
'暂无可切换应用'
}
</
EmptyState
.
Title
>
<
EmptyState
.
Description
>
{
applicationSearch
.
trim
()
?
'请尝试其他关键词。'
:
'当前账号还没有分配其它应用权限。'
}
</
EmptyState
.
Description
>
</
EmptyState
.
Header
>
</
EmptyState
>
</
EmptyStateSurface
>
)
}
</
ScrollShadow
>
</
Modal
.
Body
>
</
Modal
.
Dialog
>
</
Modal
.
Container
>
...
...
src/pages/project_manager_xy/Layout.jsx
View file @
dedde183
...
...
@@ -14,10 +14,9 @@ import { Avatar, Badge, Button, Dropdown, Label, Toast, Tooltip } from '@heroui/
import
{
useContext
,
useEffect
,
useLayoutEffect
,
useMemo
,
useState
}
from
'react'
;
import
{
Heading
}
from
'react-aria-components/Dialog'
;
import
{
history
,
Outlet
,
useLocation
}
from
'umi'
;
import
{
AppGrid
,
Archive
,
BarChart
,
Bell
,
LogOut
,
Moon
,
Sun
}
from
'./AppIcons'
;
import
{
AppGrid
,
Archive
,
BarChart
,
Bell
,
LogOut
}
from
'./AppIcons'
;
const
APP_NAME
=
APP_DISPLAY_NAME
;
const
THEME_STORAGE_KEY
=
'xinyuan_color_theme'
;
const
menuGroups
=
[
{
...
...
@@ -29,18 +28,6 @@ const menuGroups = [
},
];
function
getInitialTheme
()
{
if
(
typeof
window
===
'undefined'
)
return
'light'
;
try
{
return
window
.
localStorage
.
getItem
(
THEME_STORAGE_KEY
)
===
'dark'
?
'dark'
:
'light'
;
}
catch
{
return
'light'
;
}
}
function
getRouteTitle
(
pathname
)
{
if
(
pathname
===
'/notifications'
)
return
'通知消息'
;
for
(
const
group
of
menuGroups
)
{
...
...
@@ -223,13 +210,9 @@ function SidebarContents({ onOpenApplicationSwitcher, pathname, userInfo }) {
function
DashboardNavbar
({
notificationCount
,
onOpenNotifications
,
onThemeChange
,
theme
,
title
,
})
{
const
{
isMobile
,
isOpen
}
=
useSidebar
();
const
isDark
=
theme
===
'dark'
;
const
themeLabel
=
isDark
?
'切换至浅色模式'
:
'切换至深色模式'
;
const
unreadCount
=
Math
.
max
(
0
,
Number
(
notificationCount
)
||
0
);
return
(
...
...
@@ -262,20 +245,6 @@ function DashboardNavbar({
</
Badge
.
Anchor
>
<
Tooltip
.
Content
>
通知消息
</
Tooltip
.
Content
>
</
Tooltip
>
<
Tooltip
delay=
{
0
}
>
<
Button
isIconOnly
aria
-
label=
{
themeLabel
}
aria
-
pressed=
{
isDark
}
className=
"app-navbar-icon-button rc-navbar-icon-button"
size=
"sm"
variant=
"ghost"
onPress=
{
()
=>
onThemeChange
(
isDark
?
'light'
:
'dark'
)
}
>
{
isDark
?
<
Sun
size=
{
16
}
/>
:
<
Moon
size=
{
16
}
/>
}
</
Button
>
<
Tooltip
.
Content
>
{
themeLabel
}
</
Tooltip
.
Content
>
</
Tooltip
>
</
div
>
</
Navbar
.
Header
>
</
Navbar
>
...
...
@@ -286,25 +255,10 @@ export default function ProjectManagerLayout() {
const
location
=
useLocation
();
const
[
isApplicationDialogOpen
,
setIsApplicationDialogOpen
]
=
useState
(
false
);
const
[
sidebarOpen
,
setSidebarOpen
]
=
useState
(
true
);
const
[
theme
,
setTheme
]
=
useState
(
getInitialTheme
);
const
[
unreadNotificationCount
,
setUnreadNotificationCount
]
=
useState
(
0
);
const
userInfo
=
useContext
(
UserContext
);
const
siteInfo
=
useContext
(
SiteContext
);
useLayoutEffect
(()
=>
{
const
root
=
document
.
documentElement
;
root
.
classList
.
toggle
(
'dark'
,
theme
===
'dark'
);
root
.
classList
.
toggle
(
'light'
,
theme
===
'light'
);
root
.
dataset
.
theme
=
theme
;
root
.
style
.
colorScheme
=
theme
;
try
{
window
.
localStorage
.
setItem
(
THEME_STORAGE_KEY
,
theme
);
}
catch
{
// Theme still applies for this session when storage is unavailable.
}
},
[
theme
]);
useLayoutEffect
(()
=>
{
document
.
title
=
APP_NAME
;
},
[]);
...
...
@@ -336,7 +290,7 @@ export default function ProjectManagerLayout() {
return
(
<
Sidebar
.
Provider
className=
{
`template-dashboard-shell rc-app-shell ${theme}`
}
className=
"template-dashboard-shell rc-app-shell light"
collapsible=
"offcanvas"
navigate=
{
(
href
)
=>
history
.
push
(
href
)
}
open=
{
sidebarOpen
}
...
...
@@ -351,13 +305,10 @@ export default function ProjectManagerLayout() {
<
DashboardNavbar
notificationCount=
{
unreadNotificationCount
}
onOpenNotifications=
{
()
=>
history
.
push
(
'/notifications'
)
}
theme=
{
theme
}
title=
{
routeTitle
}
onThemeChange=
{
setTheme
}
/>
<
div
className=
"app-shell-content-scroll min-h-0 flex-1 overflow-y-auto"
>
<
div
className=
"rc-app-page"
>
<
h1
className=
"sr-only"
>
{
routeTitle
}
</
h1
>
<
Outlet
/>
</
div
>
</
div
>
...
...
src/pages/project_manager_xy/audit-material-center.business-flow.md
View file @
dedde183
...
...
@@ -139,7 +139,7 @@
9.
页面顶部提供
`Segment`
视图切换,可在“卡片视图 / 表格视图”之间切换
10.
卡片视图用于浏览案例摘要;每条案例使用独立一级业务卡片,分类/等级/标签、标题、类型、摘要和时间信息按层级展示,点击卡片后以弹框方式查看并编辑详情
11.
表格视图展示标题、分类、标签、案例类型、关键字、来源、等级、年份、更新时间等字段;宽表在 DataGrid 内部横向滚动,各字段只在所属单元格内单行截断,完整值通过 HeroUI Tooltip 查看,不允许跨列覆盖;首列标题仅用于展示,不承担点击操作,查看详情统一从右侧操作列进入
12.
表格视图按页加载案例,
固定每页 20 条,并显示页码、当前页和匹配总数;分页栏不提供每页条数选择器
12.
表格视图按页加载案例,
默认每页 20 条,并显示页码、当前页和匹配总数;分页栏提供 20、50、100 条/页选择器,切换后回到第一页并按原查询条件重新加载
13.
表格视图支持单选、全选当前页案例,并显示当前勾选数量
### 4.5 编辑案例
...
...
src/pages/project_manager_xy/resource-booking/index.jsx
View file @
dedde183
...
...
@@ -19,6 +19,7 @@ import {
Button
,
Checkbox
,
Chip
,
CloseButton
,
Disclosure
,
Input
,
Label
,
...
...
@@ -77,7 +78,7 @@ import {
const
ALL_CATEGORY_KEY
=
'__all__'
;
const
CARD_VIEW_MODE
=
'card'
;
const
TABLE_VIEW_MODE
=
'table'
;
const
XINYUAN_4XL_DIALOG_CLASS
=
'
sm:max-w-[68rem] p-7
'
;
const
XINYUAN_4XL_DIALOG_CLASS
=
'
bg-background p-7 sm:max-w-[68rem]
'
;
const
FALLBACK_CASE_FIELDS
=
[
{
name
:
'label'
,
label
:
'标题'
,
type
:
'text'
},
{
name
:
'category'
,
label
:
'分类'
,
type
:
'text'
},
...
...
@@ -273,19 +274,16 @@ function KeywordEditor({
<
div
className=
"flex flex-wrap gap-2"
>
{
keywords
.
map
((
keyword
)
=>
(
<
Chip
key=
{
keyword
}
color=
"accent"
size=
"sm"
variant=
"soft"
>
<
span
className=
"truncate"
>
{
keyword
}
</
span
>
<
Tooltip
delay=
{
0
}
>
<
Button
isIconOnly
aria
-
label=
{
`删除关键字 ${keyword}`
}
size=
"sm"
variant=
"tertiary"
onPress=
{
()
=>
removeKeyword
(
keyword
)
}
>
<
XMarkIcon
className=
"size-3"
/>
</
Button
>
<
Tooltip
.
Content
>
删除关键字
</
Tooltip
.
Content
>
</
Tooltip
>
<
Chip
.
Label
className=
"max-w-48 truncate"
>
{
keyword
}
</
Chip
.
Label
>
<
Tooltip
delay=
{
0
}
>
<
CloseButton
aria
-
label=
{
`删除关键字 ${keyword}`
}
onPress=
{
()
=>
removeKeyword
(
keyword
)
}
>
<
XMarkIcon
size=
{
14
}
/>
</
CloseButton
>
<
Tooltip
.
Content
>
删除关键字
</
Tooltip
.
Content
>
</
Tooltip
>
</
Chip
>
))
}
</
div
>
...
...
@@ -525,7 +523,7 @@ export default function ResourceBookingPage() {
const
[
activeCaseId
,
setActiveCaseId
]
=
useState
(
''
);
const
[
selectedCaseIds
,
setSelectedCaseIds
]
=
useState
([]);
const
[
page
,
setPage
]
=
useState
(
0
);
const
pageSize
=
DEFAULT_PAGE_SIZE
;
const
[
pageSize
,
setPageSize
]
=
useState
(
DEFAULT_PAGE_SIZE
)
;
const
[
total
,
setTotal
]
=
useState
(
0
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
saving
,
setSaving
]
=
useState
(
false
);
...
...
@@ -837,6 +835,15 @@ export default function ResourceBookingPage() {
loadCaseList
(
nextPage
,
pageSize
);
}
function
handlePageSizeChange
(
nextPageSize
)
{
if
(
nextPageSize
===
pageSize
)
{
return
;
}
setPage
(
0
);
setPageSize
(
nextPageSize
);
}
function
resetCreateForm
()
{
setCreateForm
(
buildEmptyCaseForm
());
}
...
...
@@ -1348,7 +1355,6 @@ export default function ResourceBookingPage() {
</div>
) : null}
<h2>案例列表</h2>
<div className="rc-page-toolbar">
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-2">
<div className="w-full min-w-0 flex-none sm:w-80 lg:w-96">
...
...
@@ -1493,10 +1499,12 @@ export default function ResourceBookingPage() {
)}
<DataTablePagination
pageSizeOptions={[20, 50, 100]}
page={page}
pageSize={pageSize}
total={total}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
isDisabled={loading}
/>
</div>
...
...
@@ -1719,39 +1727,43 @@ export default function ResourceBookingPage() {
<Modal.Dialog className={XINYUAN_4XL_DIALOG_CLASS}>
<Modal.CloseTrigger aria-label="关闭案例详情" />
<Modal.Header>
<Modal.Heading>
<span className="inline-flex min-w-0 items-center gap-2">
<span>案例详情</span>
{activeCase ? (
<Tooltip delay={0}>
<Button
isIconOnly
aria-label="复制详情"
className="size-6"
isDisabled={saving}
size="sm"
variant="tertiary"
onPress={handleCopyCaseContent}
>
<CopyDetailIcon size={14} />
</Button>
<Tooltip.Content>复制详情</Tooltip.Content>
</Tooltip>
) : null}
</span>
</Modal.Heading>
<div className="flex w-full min-w-0 items-start gap-3 pr-16">
<div className="min-w-0 flex-1">
<div className="flex min-w-0 items-center gap-2">
<Modal.Heading className="min-w-0">
{!hideDetailTitle && activeCase
? activeCase.title || activeCase.label || '案例详情'
: '案例详情'}
</Modal.Heading>
{activeCase ? (
<Tooltip delay={0}>
<Button
isIconOnly
aria-label="复制详情"
className="size-6 shrink-0"
isDisabled={saving}
size="sm"
variant="tertiary"
onPress={handleCopyCaseContent}
>
<CopyDetailIcon size={14} />
</Button>
<Tooltip.Content>复制详情</Tooltip.Content>
</Tooltip>
) : null}
</div>
{activeCase && !hideDetailTitle ? (
<div className="mt-1 flex flex-wrap gap-x-4 gap-y-1 text-sm text-muted">
<span>创建于 {activeCase.createdAtLabel}</span>
<span>最近更新 {activeCase.updatedAtLabel}</span>
</div>
) : null}
</div>
</div>
</Modal.Header>
<Modal.Body>
{activeCase ? (
<div className="space-y-6">
{!hideDetailTitle ? (
<h2 className="break-words text-lg font-semibold">{activeCase.title || activeCase.label}</h2>
) : null}
<div className="flex flex-wrap gap-x-4 gap-y-2">
<span>创建于 {activeCase.createdAtLabel}</span>
<span>最近更新 {activeCase.updatedAtLabel}</span>
</div>
{visibleDetailViews.length > 1 ? (
<Tabs selectedKey={activeDetailView?.name} onSelectionChange={(key) => setActiveDetailViewName(String(key))}>
<Tabs.ListContainer className="w-fit max-w-full min-w-0">
...
...
@@ -1767,10 +1779,7 @@ export default function ResourceBookingPage() {
) : null}
{!isDetailEditing ? (
<Surface
className="px-5 py-2 sm:px-6 sm:py-3"
variant="secondary"
>
<div className="px-1 py-2 sm:px-2 sm:py-3">
<div className="grid min-w-0 grid-cols-1 gap-x-8 gap-y-2 md:grid-cols-2">
{activeDetailColumns.map((column) => {
const value = getFieldValue(activeCase.rawRecord, column.field);
...
...
@@ -1783,7 +1792,7 @@ export default function ResourceBookingPage() {
);
})}
</div>
</
Surface
>
</
div
>
) : (
<div className="grid min-w-0 gap-4 md:grid-cols-2">
{activeDetailColumns.map((column) => {
...
...
@@ -2051,7 +2060,7 @@ export default function ResourceBookingPage() {
<Modal.Trigger aria-hidden="true" className="sr-only" tabIndex={-1} />
<Modal.Backdrop>
<Modal.Container placement="center" size="md">
<Modal.Dialog>
<Modal.Dialog
className="bg-background"
>
<Modal.CloseTrigger aria-label="关闭批量删除确认" />
<Modal.Header>
<Modal.Heading>确认批量删除案例</Modal.Heading>
...
...
@@ -2092,7 +2101,7 @@ export default function ResourceBookingPage() {
<Modal.Trigger aria-hidden="true" className="sr-only" tabIndex={-1} />
<Modal.Backdrop>
<Modal.Container placement="center" size="md">
<Modal.Dialog>
<Modal.Dialog
className="bg-background"
>
<Modal.CloseTrigger aria-label="关闭删除确认" />
<Modal.Header>
<Modal.Heading>确认删除案例</Modal.Heading>
...
...
@@ -2140,7 +2149,7 @@ export default function ResourceBookingPage() {
</Modal.Header>
<Modal.Body>
<div className="space-y-6">
<Surface className="p-4" variant="
secondary
">
<Surface className="p-4" variant="
default
">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="min-w-0 flex-1">
<span>{importFile ? importFile.name : '还没有选择 Excel 文件'}</span>
...
...
src/pages/project_manager_xy/resource-stat/index.jsx
View file @
dedde183
...
...
@@ -7,6 +7,7 @@ import {
import
{
colorizeXinyuanChartTooltipPayload
,
createXinyuanChartColorMap
,
getXinyuanChartGeometry
,
XINYUAN_CHART_SEMANTIC
,
}
from
'@/design-system/chartPalette'
;
import
{
Alert
,
Card
,
Spinner
}
from
'@heroui/react'
;
...
...
@@ -111,6 +112,7 @@ function DonutChart({ groups }) {
const
data
=
normalizeChartData
(
groups
);
const
total
=
data
.
reduce
((
sum
,
item
)
=>
sum
+
item
.
value
,
0
);
const
colorMap
=
createXinyuanChartColorMap
(
data
.
map
((
item
)
=>
item
.
name
));
const
geometry
=
getXinyuanChartGeometry
();
return
(
<
PieChart
className=
"rc-chart-interaction"
height=
{
320
}
>
...
...
@@ -121,6 +123,9 @@ function DonutChart({ groups }) {
nameKey=
"name"
outerRadius=
"72%"
paddingAngle=
{
2
}
cornerRadius=
{
geometry
.
sectorRadius
}
stroke=
{
geometry
.
sectorGap
}
strokeWidth=
{
3
}
>
{
data
.
map
((
item
)
=>
(
<
PieChart
.
Cell
...
...
@@ -163,6 +168,7 @@ function DonutChart({ groups }) {
function
HorizontalBarChart
({
groups
})
{
const
data
=
normalizeChartData
(
groups
);
const
geometry
=
getXinyuanChartGeometry
();
return
(
<
BarChart
...
...
@@ -187,7 +193,7 @@ function HorizontalBarChart({ groups }) {
fill=
{
XINYUAN_CHART_SEMANTIC
.
primary
}
maxBarSize=
{
28
}
name=
"数量"
radius=
{
[
0
,
4
,
4
,
0
]
}
radius=
{
[
0
,
geometry
.
barRadius
,
geometry
.
barRadius
,
0
]
}
/>
</
BarChart
>
);
...
...
@@ -196,6 +202,7 @@ function HorizontalBarChart({ groups }) {
function
SourceComposedChart
({
groups
})
{
const
data
=
normalizeChartData
(
groups
);
const
colorMap
=
createXinyuanChartColorMap
(
data
.
map
((
item
)
=>
item
.
name
));
const
geometry
=
getXinyuanChartGeometry
();
return
(
<
ComposedChart
...
...
@@ -219,6 +226,7 @@ function SourceComposedChart({ groups }) {
fill=
{
XINYUAN_CHART_SEMANTIC
.
comparison
}
maxBarSize=
{
28
}
name=
"数量"
radius=
{
[
geometry
.
barRadius
,
geometry
.
barRadius
,
0
,
0
]
}
>
{
data
.
map
((
item
)
=>
(
<
Cell
key=
{
item
.
key
||
item
.
name
}
fill=
{
colorMap
.
get
(
item
.
name
)
}
/>
...
...
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