// 甄房业务系统 · 三套色彩体系对比页 // 结构:一套 UI 演示结构 + 三套 CSS 变量作用域([data-theme]),保证对比公平 // 对比度由页面运行时真实计算(非手写) // 用法: node _design-test/theme/build-theme-compare.js const fs = require('fs'); const path = require('path'); const HERE = __dirname; const LOGO = path.join(HERE, '..', 'logo'); const FZ = JSON.parse(fs.readFileSync(path.join(LOGO, 'final-z.json'), 'utf8')); const H04 = 'M3 11.99v2.51c0 3.3 0 4.95 1.025 5.975S6.7 21.5 10 21.5h4c3.3 0 4.95 0 5.975-1.025S21 17.8 21 14.5v-2.51c0-1.682 0-2.522-.356-3.25s-1.02-1.244-2.346-2.276l-2-1.555C14.233 3.303 13.2 2.5 12 2.5s-2.233.803-4.298 2.409l-2 1.555C4.375 7.496 3.712 8.012 3.356 8.74S3 10.308 3 11.99'; const FG = 'translate(5.3304 5.3294) scale(2.22222)'; // 品牌标识(框 + Z),c1=框色 c2=Z色 const mark = (c1, c2, size) => '' + '' + ''; // ── 三套色彩令牌 ──────────────────────────────────────────────── // 说明:品牌墨蓝 #0C2340 在三套方案中都用于「品牌/导航区」, // 交互色各自采用该体系的官方主色 —— 即「品牌色与交互色分离」。 const THEMES = [ { id: 'carbon', name: '方案 A · Carbon 严谨派', sys: 'IBM Carbon Design System', score: { dense: 9.5, comfort: 9.5, a11y: 10, brand: 8.5, cost: 7.5, proven: 9.0 }, radius: '2px', font: '"IBM Plex Sans", "PingFang SC", "Microsoft YaHei", sans-serif', shadow: 'none', desc: '以中性灰主导、蓝色只承担「行动色」的经典企业级体系。四套官方主题(White / Gray 10 / Gray 90 / Gray 100)与分层模型,为数据密集型界面而生。', tone: { brand:'#0C2340', primary:'#0F62FE', bg:'#FFFFFF', bg2:'#F4F4F4' }, tags: [ ['品牌 / 导航底', '#0C2340', '甄房墨蓝(品牌区、侧栏、登录页)'], ['交互主色', '#0F62FE', 'Blue 60 · 官方 primary action'], ['主色悬停', '#0043CE', 'Blue 70'], ['主色按下', '#002D9C', 'Blue 80'], ['主色浅底', '#EDF5FF', 'Blue 10 · 高亮/选中浅底'], ['页面底色', '#FFFFFF', '$background(White 主题)'], ['分层底色', '#F4F4F4', 'Gray 10 · $layer-01'], ['悬停底色', '#E8E8E8', 'Gray 10 hover'], ['选中底色', '#E0E0E0', 'Gray 20 · $layer-selected-01'], ['边框', '#E0E0E0', 'Gray 20'], ['边框-强', '#8D8D8D', 'Gray 50'], ['一级文字', '#161616', 'Gray 100'], ['二级文字', '#525252', 'Gray 70'], ['三级文字', '#6F6F6F', 'Gray 60'], ['成功', '#198038', 'Green 60'], ['警告', '#F1C21B', 'Yellow 30'], ['错误', '#DA1E28', 'Red 60'], ['信息', '#0F62FE', 'Blue 60'], ['品牌金(点缀)', '#C6A15B', '仅 logo / 关键数字,占比 ≤5%'] ], pros: ['中性灰主导(官方原文 “Neutral Gray family is dominant”),长时间盯表格最不累', '明确要求「避免使用中间调」,界面层次靠灰阶深浅而非色块,密度最高', '无障碍是一等公民:官方每个令牌都标注对比度要求(如 $layer-selected-inverse 标 4.5:1 AA)', '四套官方主题 + 分层模型,深色模式与高对比模式有现成规范'], cons: ['圆角基本为 0(2px),偏工程感,国内用户可能觉得「硬」', '品牌金几乎没有落点,品牌温度靠墨蓝承担', '与 ElementUI 的默认圆角/间距差异大,改造成本三套中最高'], fit: ['数据密度极高的作业台(房源批量检核、佣金结算)', '需要长时间连续录入/核对的岗位', '对无障碍有硬性要求的场景(政企、大客户交付)'], // 生产落地必改项:[用途, 原值, 建议改为, 理由] fixes: [ ['警告色用法', '#F1C21B 作文字色', '黄只作底色(#F1C21B 底 + #161616 字)', 'Yellow 30 在白底上仅 1.68:1;Carbon 官方本就把黄色用于警报条底色而非文字色'], ['圆角', '2px', '按国内习惯可放宽到 4px', '不是超标项,属外观取舍;Carbon 的 0–2px 方角在国内后台会显「硬」'] ] }, { id: 'fiori', name: '方案 B · Fiori 商务派', sys: 'SAP Fiori · Morning Horizon', final: true, // ✓ 已定稿(2026-09-19) score: { dense: 9.0, comfort: 8.8, a11y: 9.5, brand: 8.0, cost: 7.0, proven: 9.5 }, radius: '8px', font: '"72", "PingFang SC", "Microsoft YaHei", sans-serif', shadow: '0 1px 2px rgba(34,53,72,.08)', desc: 'ERP 领域事实标准。语义令牌分层最完整:表面 S0–S8、文字 T1–T9、边框、语义色、强调色五组,并给出可推导的对比度规则。', tone: { brand:'#0C2340', primary:'#0070F2', bg:'#FFFFFF', bg2:'#F5F6F7' }, tags: [ ['品牌 / Shell Bar', '#0C2340', '甄房墨蓝(Shell Bar、应用头)'], ['交互主色', '#0070F2', 'Blue 7 · S2 Secondary / T4 CTA'], ['主色悬停', '#0057D2', 'Blue 8 · T7 Selected'], ['主色按下', '#0040B0', '同体系推导(Blue 8 再压一档)'], ['主色浅底', '#EBF4FE', 'S5 Selected Surface(官方 rgba(0,112,242,.08) 在白底上的合成值)'], ['页面底色', '#FFFFFF', 'S0 Base Background'], ['次级底色', '#F5F6F7', 'S4 Quaternary · Grey 1'], ['边框', '#D5DADD', 'Grey 3'], ['边框-强', '#A9B4BE', 'Grey 4'], ['一级文字', '#223548', 'T1 · Grey 9'], ['二级文字', '#475E75', 'T2 · Grey 7'], ['三级文字', '#5B738B', 'T3 · Grey 6'], ['成功', '#256F3A', 'Positive(同体系参考值)'], ['警告', '#E76500', 'Critical · Mango(同体系参考值)'], ['错误', '#D20A0A', 'Red 7 · Negative(官方确认)'], ['信息', '#0070F2', 'Blue 7'], ['品牌金(点缀)', '#C6A15B', '仅 logo / 关键数字,占比 ≤5%'] ], pros: ['唯一为 ERP 事务场景设计的体系,表格/表单/Shell 的层级划分最成熟', '「相隔 4 级 = 3:1、相隔 5 级 = 4.5:1」——对比度可推导,选色不用猜', '选中态用 8% 蓝底(而非实色块),长时间使用不刺眼', '品牌化路径最清晰:官方 Quick Theming 就是「替换 logo + 主品牌色」'], cons: ['Shell Bar + 应用头 + 页面头三层结构,垂直空间占用大,密度略低于 Carbon', '蓝白对比偏冷、现代感一般,年轻经纪人可能觉得「像老系统」', '部分语义色官方文档未完整公开,落地前需核对准确值'], fit: ['交易全流程、佣金结算这类强流程、强状态的模块', '与财务/ERP 打通、需要符合企业级规范的场景', '多角色、多层级权限的后台(选中态与层级表达最清楚)'], fixes: [] // 12/12 全部通过,无需改动 }, { id: 'antd', name: '方案 C · AntD 亲和派', sys: 'Ant Design 5', score: { dense: 8.0, comfort: 7.5, a11y: 8.0, brand: 8.5, cost: 9.5, proven: 9.0 }, radius: '6px', font: '-apple-system, "PingFang SC", "Microsoft YaHei", sans-serif', shadow: '0 1px 2px rgba(0,0,0,.06)', desc: '国内中后台事实标准。12 色 × 10 阶色板 + 算法生成,可在品牌色基础上自动产出整条色阶;中文文档与团队熟悉度最高。', tone: { brand:'#0C2340', primary:'#1677FF', bg:'#FFFFFF', bg2:'#F5F5F5' }, tags: [ ['品牌 / 侧栏', '#0C2340', '甄房墨蓝(侧栏、品牌区)'], ['交互主色', '#1677FF', 'colorPrimary · blue-6(官方默认)'], ['主色悬停', '#4096FF', 'blue-5'], ['主色按下', '#0958D9', 'blue-7'], ['主色浅底', '#E6F4FF', 'blue-1 · 选中行浅底'], ['页面底色', '#FFFFFF', '官方容器色'], ['次级底色', '#F5F5F5', '官方中性灰'], ['边框', '#D9D9D9', '官方边框色'], ['一级文字', '#1F1F1F', 'rgba(0,0,0,.88) 合成值'], ['二级文字', '#595959', 'rgba(0,0,0,.65) 合成值'], ['三级文字', '#8C8C8C', 'rgba(0,0,0,.45) 合成值'], ['成功', '#52C41A', 'colorSuccess'], ['警告', '#FAAD14', 'colorWarning'], ['错误', '#FF4D4F', 'colorError'], ['信息', '#1677FF', 'colorInfo'], ['品牌金(点缀)', '#C6A15B', '仅 logo / 关键数字,占比 ≤5%'] ], pros: ['团队最熟、中文文档最全,与现有 Vue2 + ElementUI 生态迁移成本最低', '色板算法可由品牌色直接生成整条色阶(12 阶),扩展与派生最省事', '色彩语义与国内用户预期一致(成功绿/警告黄/错误红),零学习成本', '品牌金在三套中最好安排(圆角卡片 + 金色数字自然)'], cons: ['默认色饱和偏高:#FF4D4F 错误色、#52C41A 成功色长时间看刺激较强', '官方定位偏「轻盈」,高数据密度下需要额外收紧间距与层级', '中性灰层级不如 Carbon/Fiori 严谨,容易出现「灰得不够分明」'], fit: ['经纪人日常高频作业(房源/客源/跟进),要求上手快、认知零负担', '迭代节奏快、需要大量复用生态组件的中后台', '团队以国内前端为主、希望降低长期维护成本的项目'], // 生产落地必改项:[用途, 原值, 建议改为, 理由] fixes: [ ['主色(按钮底 / 链接字)', '#1677FF', '#0958D9(blue-7)', '官方默认主色在白底上实测仅 4.10:1,差 0.4 未达 AA 4.5:1 —— 「第 6 阶满足 4.5:1」只对部分色相成立;下移一阶到 blue-7 即 6.16:1,肉眼几乎无差别'], ['成功色', '#52C41A', '#389E0D(green-7)', '#52C41A 白底仅 2.27:1,连图形类 3:1 都不到;图标与文字改 green-7(3.46:1),浅底仍可保留亮绿'], ['警告色', '#FAAD14', '文字与描边改 #AD6800(gold-8)', '#FAAD14 白底仅 1.90:1;沿用「亮色只作底、深色作字」的做法,gold-8 为 4.41:1,几乎直接满足正文标准'], ['错误色(正文)', '#FF4D4F', '正文与链接改 #CF1322(red-7)', '#FF4D4F 作正文仅 3.27:1;作为图形勉强及格,但表格里的错误提示是正文级文字,改 red-7 后 5.57:1;描边与底色可继续用亮红'], ['三级文字', '#8C8C8C', '#6E6E6E', 'rgba(0,0,0,.45) 的合成值白底 3.36:1,未达 AA;加深到 #6E6E6E 即 5.10:1'], ['灰阶层级', '仅三级文字灰', '补一级 #BFBFBF 专用于占位符 / 禁用', 'AntD 只有三级文字灰,导致占位符被迫复用 #8C8C8C —— 既不够浅(有对比度不达标风险)又会被误读为正文;补一级浅灰后三级文字才能安心加深'] ] } ]; // 加权评分(权重:密度25 舒适20 无障碍15 品牌15 成本15 验证10) const W = { dense: .25, comfort: .20, a11y: .15, brand: .15, cost: .15, proven: .10 }; THEMES.forEach(t => { t.total = Object.keys(W).reduce((s, k) => s + t.score[k] * W[k], 0); }); const CSS = ` *{box-sizing:border-box;margin:0;padding:0} body{background:#0a0d13;color:#e9edf5;font-family:"PingFang SC","Microsoft YaHei",system-ui,sans-serif; padding:40px 24px 90px;line-height:1.6} .wrap{max-width:1500px;margin:0 auto} h1{font-size:27px;letter-spacing:.2px}h1 b{color:#E3CB99} .lead{color:#8b94a7;font-size:13.5px;margin:10px 0 26px;line-height:1.95} h2{font-size:19px;margin:46px 0 6px;padding-left:11px;border-left:4px solid #C6A15B} h2 span{font-size:11px;font-weight:400;color:#8b94a7;margin-left:10px} h3{font-size:15px;margin:26px 0 10px;color:#E3CB99} p.note{font-size:12.5px;color:#8b94a7;margin:8px 0 16px;line-height:1.9} /* ── 调研结论 ── */ table.cmp{width:100%;border-collapse:collapse;font-size:12.5px;margin-top:14px} table.cmp th,table.cmp td{border:1px solid rgba(255,255,255,.09);padding:9px 11px;text-align:left} table.cmp th{background:rgba(255,255,255,.045);font-weight:600;color:#cfd6e4} table.cmp td.num{text-align:center;font-variant-numeric:tabular-nums} table.cmp tr.win td{background:rgba(198,161,91,.10)} table.cmp td.tot{font-weight:700;color:#E3CB99} .badge{display:inline-block;font-size:10px;padding:1px 7px;border-radius:20px;margin-left:7px; border:1px solid rgba(198,161,91,.45);color:#E3CB99} .src{font-size:11px;color:#6d768a;margin-top:8px;line-height:1.9} .src a{color:#8ab4f8;text-decoration:none} /* ── 方案卡 ── */ .plan{border:1px solid rgba(255,255,255,.09);border-radius:16px;padding:22px;margin-top:18px; background:linear-gradient(180deg,rgba(255,255,255,.03),rgba(255,255,255,.012))} .plan .hd{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap} .plan .hd h3{margin:0;font-size:18px;color:#fff} .plan .hd .sys{font-size:11.5px;color:#8b94a7} .plan .hd .sc{margin-left:auto;font-size:13px;color:#E3CB99;font-weight:700} .plan .hd .finalmark{font-size:11px;font-weight:700;padding:2px 9px;border-radius:20px; background:rgba(94,224,138,.13);border:1px solid rgba(94,224,138,.5);color:#5ee08a} .plan.final{border-color:rgba(94,224,138,.42);box-shadow:0 0 0 1px rgba(94,224,138,.16) inset} .plan .d{font-size:12.5px;color:#9aa4b8;margin:9px 0 16px;line-height:1.9} .pc{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:14px} .pc>div{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);border-radius:11px;padding:13px 15px} .pc h4{font-size:12.5px;margin-bottom:8px} .pc ul{list-style:none;font-size:12px;color:#98a1b3;line-height:1.95} .pc ul li{padding-left:15px;position:relative} .pc ul li:before{position:absolute;left:0;top:0} .pc .good li:before{content:'+';color:#5ee08a} .pc .bad li:before{content:'−';color:#f0a55c} .pc .fit li:before{content:'·';color:#8ab4f8} /* ── 生产落地必改项 ── */ table.fix{margin-top:10px;font-size:12px} table.fix th:nth-child(1),table.fix td:nth-child(1){width:132px} table.fix th:nth-child(2),table.fix td:nth-child(2){width:150px} table.fix th:nth-child(3),table.fix td:nth-child(3){width:250px} table.fix code{background:rgba(255,255,255,.06);padding:1px 5px;border-radius:4px;font-size:11px} table.fix td:nth-child(2) code{color:#f0a55c} table.fix td:nth-child(3) code{color:#5ee08a} .fixok{font-size:12.5px;color:#5ee08a;background:rgba(94,224,138,.07); border:1px solid rgba(94,224,138,.28);border-radius:9px;padding:10px 13px;margin-top:10px} .sumline{display:flex;gap:10px;flex-wrap:wrap;margin-top:12px} .sumline div{flex:1;min-width:230px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.08); border-radius:10px;padding:10px 13px;font-size:12px;color:#98a1b3} .sumline div b{color:#e9edf5} .sumline div em{font-style:normal;font-weight:700} .sumline div em.ok{color:#5ee08a}.sumline div em.no{color:#f0a55c} /* ── 令牌表 ── */ .tok{display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));gap:7px;margin-top:12px} .tk{display:flex;align-items:center;gap:9px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.07); border-radius:9px;padding:7px 10px;font-size:11.5px} .tk i{width:22px;height:22px;border-radius:6px;flex:none;border:1px solid rgba(255,255,255,.16)} .tk .n{flex:1;min-width:0} .tk .n b{display:block;font-weight:600;font-size:11.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .tk .n code{color:#7f8899;font-size:10px;letter-spacing:.2px} .tk .r{font-size:9.5px;padding:1px 6px;border-radius:20px;border:1px solid;flex:none;white-space:nowrap} .tk .r.pass{color:#5ee08a;border-color:rgba(94,224,138,.4)} .tk .r.fail{color:#f0a55c;border-color:rgba(240,165,92,.4)} /* ── UI 演示(三套作用域)── */ .demo{border-radius:14px;overflow:hidden;margin-top:14px;border:1px solid rgba(255,255,255,.12);background:var(--bg); font-family:var(--font);color:var(--text1);font-size:12.5px} .demo[data-theme=carbon]{--brand:#0C2340;--primary:#0F62FE;--primary-h:#0043CE;--primary-a:#002D9C;--primary-s:#EDF5FF; --bg:#FFFFFF;--bg2:#F4F4F4;--hover:#E8E8E8;--sel:#E0E0E0;--bd:#E0E0E0;--bd-s:#8D8D8D;--text1:#161616;--text2:#525252;--text3:#6F6F6F; --ok:#198038;--warn:#F1C21B;--err:#DA1E28;--radius:2px;--sh:none;--gold:#C6A15B; --font:"IBM Plex Sans","PingFang SC","Microsoft YaHei",sans-serif} .demo[data-theme=fiori]{--brand:#0C2340;--primary:#0070F2;--primary-h:#0057D2;--primary-a:#0040B0;--primary-s:rgba(0,112,242,.08); --bg:#FFFFFF;--bg2:#F5F6F7;--hover:rgba(0,112,242,.06);--sel:rgba(0,112,242,.10);--bd:#D5DADD;--bd-s:#A9B4BE;--text1:#223548;--text2:#475E75;--text3:#5B738B; --ok:#256F3A;--warn:#E76500;--err:#D20A0A;--radius:8px;--sh:0 1px 2px rgba(34,53,72,.08);--gold:#C6A15B; --font:"72","PingFang SC","Microsoft YaHei",sans-serif} .demo[data-theme=antd]{--brand:#0C2340;--primary:#1677FF;--primary-h:#4096FF;--primary-a:#0958D9;--primary-s:#E6F4FF; --bg:#FFFFFF;--bg2:#F5F5F5;--hover:#F5F5F5;--sel:#E6F4FF;--bd:#D9D9D9;--bd-s:#BFBFBF;--text1:#1F1F1F;--text2:#595959;--text3:#8C8C8C; --ok:#52C41A;--warn:#FAAD14;--err:#FF4D4F;--radius:6px;--sh:0 1px 2px rgba(0,0,0,.06);--gold:#C6A15B; --font:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif} .app{display:grid;grid-template-columns:196px 1fr;min-height:540px} .app .side{background:var(--brand);padding:14px 0;display:flex;flex-direction:column;gap:2px} .app .side .brand{display:flex;align-items:center;gap:9px;padding:2px 14px 16px;color:#fff} .app .side .brand b{font-size:15px;font-weight:600;letter-spacing:1px} .app .side .brand em{font-size:8px;letter-spacing:1.4px;opacity:.62;font-style:normal;display:block} .app .side a{display:flex;align-items:center;gap:9px;color:rgba(255,255,255,.72);text-decoration:none; font-size:12.5px;padding:9px 14px;border-left:3px solid transparent} .app .side a.on{background:rgba(255,255,255,.10);color:#fff;border-left-color:var(--gold);font-weight:600} .app .side a .d{width:6px;height:6px;border-radius:2px;background:currentColor;opacity:.55;flex:none} .app .main{display:flex;flex-direction:column;min-width:0} .app .top{display:flex;align-items:center;gap:14px;padding:11px 18px;border-bottom:1px solid var(--bd);background:var(--bg)} .app .top .crumb{font-size:12.5px;color:var(--text2)} .app .top .crumb b{color:var(--text1);font-weight:600} .app .top .usr{margin-left:auto;font-size:12px;color:var(--text2);display:flex;align-items:center;gap:8px} .app .top .usr .av{width:24px;height:24px;border-radius:50%;background:var(--primary-s);color:var(--primary); display:flex;align-items:center;justify-content:center;font-size:10.5px;font-weight:600} .app .body{padding:14px 18px 20px;background:var(--bg2);flex:1;display:flex;flex-direction:column;gap:12px} .kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:10px} .kpi{background:var(--bg);border:1px solid var(--bd);border-radius:var(--radius);padding:11px 13px;box-shadow:var(--sh)} .kpi .t{font-size:11px;color:var(--text3)} .kpi .v{font-size:20px;font-weight:600;color:var(--text1);margin-top:3px;font-variant-numeric:tabular-nums} .kpi .v em{font-size:11px;font-style:normal;color:var(--text3);font-weight:400;margin-left:3px} .kpi .d{font-size:10.5px;margin-top:2px} .kpi .d.up{color:var(--ok)}.kpi .d.dn{color:var(--err)} .kpi.gold .v{color:var(--gold)} .card{background:var(--bg);border:1px solid var(--bd);border-radius:var(--radius);box-shadow:var(--sh)} .filters{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;padding:13px} .f{display:flex;flex-direction:column;gap:4px} .f label{font-size:11px;color:var(--text3)} .f input,.f select{font-family:inherit;font-size:12.5px;color:var(--text1);background:var(--bg); border:1px solid var(--bd);border-radius:var(--radius);padding:6px 9px;min-width:132px} .f input::placeholder{color:var(--text3)} .f input:focus,.f select:focus{outline:2px solid var(--primary);outline-offset:-1px;border-color:var(--primary)} .f.err input{border-color:var(--err)} .f .msg{font-size:10.5px;color:var(--err)} .btns{display:flex;gap:8px;margin-left:auto} .btn{font-family:inherit;font-size:12.5px;border-radius:var(--radius);padding:6px 15px;cursor:default; border:1px solid var(--bd);background:var(--bg);color:var(--text1);white-space:nowrap} .btn.pri{background:var(--primary);border-color:var(--primary);color:#fff;font-weight:500} .btn.pri.h{background:var(--primary-h);border-color:var(--primary-h)} .btn.pri.a{background:var(--primary-a);border-color:var(--primary-a)} .btn.dan{background:var(--err);border-color:var(--err);color:#fff} .btn.gho{border-color:var(--primary);color:var(--primary);background:transparent} .btn.tx{border-color:transparent;background:transparent;color:var(--primary);padding:6px 6px} .btn.dis{background:var(--bg2);border-color:var(--bd);color:var(--text3);opacity:.75} .tbl{width:100%;border-collapse:collapse;font-size:12.5px;font-variant-numeric:tabular-nums} .tbl th{text-align:left;font-weight:600;font-size:11.5px;color:var(--text2);background:var(--bg2); padding:9px 11px;border-bottom:1px solid var(--bd);white-space:nowrap} .tbl td{padding:9px 11px;border-bottom:1px solid var(--bd);color:var(--text1)} .tbl tbody tr:nth-child(even){background:var(--bg2)} .tbl tbody tr.hov{background:var(--hover)} .tbl tbody tr.sel{background:var(--sel)} .tbl tbody tr.sel td:first-child{box-shadow:inset 2px 0 0 var(--primary)} .tbl .code{color:var(--primary);font-weight:500} .tbl .sub{color:var(--text3);font-size:11px;display:block} .tbl .op a{color:var(--primary);text-decoration:none;margin-right:9px;font-size:12px} .tbl .op a.dan{color:var(--err)} .tag{display:inline-block;font-size:10.5px;padding:1px 8px;border-radius:20px;border:1px solid;white-space:nowrap} .tag.ok{color:var(--ok);border-color:var(--ok);background:color-mix(in srgb,var(--ok) 9%,transparent)} .tag.wn{color:var(--warn);border-color:var(--warn);background:color-mix(in srgb,var(--warn) 12%,transparent)} .tag.er{color:var(--err);border-color:var(--err);background:color-mix(in srgb,var(--err) 9%,transparent)} .tag.pri{color:var(--primary);border-color:var(--primary);background:var(--primary-s)} .tag.nu{color:var(--text2);border-color:var(--bd-s);background:transparent} .tag.gold{color:#8a6a2f;border-color:var(--gold);background:color-mix(in srgb,var(--gold) 14%,transparent)} .pager{display:flex;align-items:center;gap:7px;padding:11px 13px;font-size:12px;color:var(--text2)} .pg{min-width:26px;height:26px;display:flex;align-items:center;justify-content:center;border:1px solid var(--bd); border-radius:var(--radius);background:var(--bg);color:var(--text1)} .pg.on{background:var(--primary);border-color:var(--primary);color:#fff;font-weight:600} .tabs{display:flex;gap:2px;border-bottom:1px solid var(--bd);padding:0 13px} .tabs a{font-size:12.5px;padding:9px 13px;color:var(--text2);text-decoration:none;border-bottom:2px solid transparent} .tabs a.on{color:var(--primary);border-bottom-color:var(--primary);font-weight:600} .alerts{display:grid;grid-template-columns:1fr 1fr;gap:9px} .al{display:flex;gap:8px;align-items:flex-start;font-size:12px;padding:9px 12px;border-radius:var(--radius);border:1px solid} .al b{font-weight:600} .al.i{border-color:var(--primary);background:var(--primary-s);color:var(--text1)} .al.s{border-color:var(--ok);background:color-mix(in srgb,var(--ok) 8%,transparent);color:var(--text1)} .al.w{border-color:var(--warn);background:color-mix(in srgb,var(--warn) 11%,transparent);color:var(--text1)} .al.e{border-color:var(--err);background:color-mix(in srgb,var(--err) 8%,transparent);color:var(--text1)} /* 元素级对照 */ .elcmp{margin-top:12px;border:1px solid rgba(255,255,255,.09);border-radius:12px;overflow:hidden} table.el{width:100%;border-collapse:collapse;font-size:12px} table.el th,table.el td{border-bottom:1px solid rgba(255,255,255,.08);padding:8px 12px;vertical-align:middle} table.el th{background:rgba(255,255,255,.04);color:#cfd6e4;font-size:11.5px;text-align:left} table.el td:first-child{color:#8b94a7;font-size:11.5px;width:118px} table.el td .demo{display:inline-block;margin:0;padding:9px 12px;border:none;border-radius:8px;min-width:0} `; const esc = s => String(s).replace(/&/g, '&').replace(//g, '>'); // ── UI 演示结构(只写一次,三套主题注入同一份)── const demoBody = () => `
房源管理 / 二手房房源
张张伟 · 滨江门店
在售房源
1,284套
↑ 3.2% 较上周
本周新增客源
96位
↑ 12 位
待跟进
23条
↓ 5 条
本月预估佣金
86.4万
↑ 8.6%
请输入完整姓名
房源编码楼盘 / 房号面积售价状态维护人操作
SH-ES-202609-00037 滨江壹号 3幢 1802滨江区 · 钱塘江畔 128.5 ㎡1,285 万有效张伟 查看编辑
SH-ES-202609-00041 钱江新城 7幢 2201上城区 · 万象城 156.0 ㎡1,860 万待检核李敏 查看编辑
SH-ES-202609-00052 滨江壹号 5幢 901滨江区 · 钱塘江畔 98.2 ㎡920 万独家王强 查看编辑
SH-ES-202609-00058 绿城桂语 2幢 1503西湖区 · 文三路 142.6 ㎡1,420 万维护中张伟 查看编辑
SH-ES-202609-00063 万科未来 1幢 608余杭区 · 未来科技城 89.0 ㎡610 万已下架赵磊 查看删除
SH-ES-202609-00071 滨江壹号 2幢 1101滨江区 · 钱塘江畔 110.4 ㎡1,080 万草稿李敏 查看编辑
共 1,284 条 123 …65 每页 20 条
ℹ提示:该房源委托期将于 7 天后到期,请及时续签。
✓成功:房源 SH-ES-202609-00037 已提交检核。
⚠警告:钥匙编号与系统记录不一致,请核对。
✕错误:售价不能低于政府指导价,请修改后重试。
`; // 元素级对照(每行一个元素,三列并排) const EL_ROWS = [ ['主按钮', t => '
'], ['次要按钮', t => '
'], ['危险按钮', t => '
'], ['状态标签', t => '
有效 维护中 已下架 待检核 独家
'], ['表单', t => '
'], ['表单错误态', t => '
请输入完整姓名
'], ['表头 / 行', t => '
房源编码状态
SH-...037有效
SH-...052独家
'], ['分页 / 页签', t => '
全部有效
123
'], ['提示条', t => '
⚠钥匙编号不一致,请核对。
'] ]; const posOf = t => THEMES.findIndex(x => x.id === t) + 2; // 第 1 列是元素名 const html = ` 甄房业务系统 · 色彩体系选型对比(Carbon / Fiori / Ant Design)

甄房业务系统 · 色彩体系选型对比

面向「经纪人高频长时使用」的业务平台,从全球主流 SaaS / ERP 设计系统中筛选出综合评分最高的三套, 各自结合已定稿的 LOGO(框内 Forte Z + 甄房墨蓝 #0C2340)开发完整界面色彩方案。
三套方案的界面结构完全相同,只替换色彩令牌 —— 保证对比公平。所有对比度数值由页面运行时真实计算,非人工填写。

一、调研结论数据来源:三家官方设计系统文档

筛选维度与权重:数据密度适配 25% | 长时使用舒适度 20% | 无障碍可推导性 15% | 品牌融合度 15% | 落地迁移成本 15% | 行业验证广度 10%。
注意:「无障碍」一列评的是体系提供了多少可推导的对比度规则,与默认色盘的实际通过率并不同向 —— Carbon 规则最硬,但它把 Yellow 30 定位成警报条底色,被当文字色用就会不达标;AntD 规则最少,默认色盘实测反而是问题最多的。所以下面每一套都给出实测通过率与生产落地必改项。

${THEMES.map(t => ` ${['dense','comfort','a11y','brand','cost','proven'].map(k => '').join('')} `).join('')}
体系定位与代表产品密度25%舒适20%无障碍15%品牌15%成本15%验证10%加权总分
${t.sys}${ t.id === 'carbon' ? 'IBM 全线产品 · 数据密集型企业级标杆' : t.id === 'fiori' ? 'SAP S/4HANA、Ariba 等 ERP 全线' : '阿里/蚂蚁中后台生态 · 国内事实标准'}' + t.score[k].toFixed(1) + '${t.total.toFixed(2)}
依据:Carbon 官方明确「Neutral Gray family is dominant」并规定「避免使用中间调」,Blue 60 #0f62fe 为全局 primary action,四套官方主题(White / Gray 10 / Gray 90 / Gray 100);
Fiori Horizon 提供 S0–S8 表面、T1–T9 文字、边框、语义、强调五组语义令牌,并给出「相隔 4 级 = 3:1、相隔 5 级 = 4.5:1」的可推导对比度规则;
Ant Design 5 以 colorPrimary #1677ff 为种子令牌,色板由算法生成 12 色 × 10 阶,第 6 阶满足 WCAG 4.5:1。
参考:Carbon Color Tokens · SAP Fiori Theming · Ant Design 色彩

二、三套方案的完整界面

核心策略:品牌色与交互色分离 —— 三套方案都把甄房墨蓝 #0C2340 固定在品牌/导航区(侧栏、Shell Bar、登录页), 交互色各自沿用体系的官方主色(#0F62FE / #0070F2 / #1677FF)。 这样既保住品牌识别,又拿到该体系已验证的交互反馈与无障碍保障。

${THEMES.map(t => `

${t.name}

${t.sys}${t.final ? '✓ 已定稿' : ''}加权 ${t.total.toFixed(2)} / 10
${t.desc}
${demoBody()}

色彩令牌

${t.tags.map(([n, v, r]) => '
' + n + '' + v + '' + r + '
').join('')}

对比度实测(WCAG,AA 正文 4.5:1 / AA 大字与图形 3:1)

优点

    ${t.pros.map(x => '
  • ' + x + '
  • ').join('')}

代价与不足

    ${t.cons.map(x => '
  • ' + x + '
  • ').join('')}

适用场景

    ${t.fit.map(x => '
  • ' + x + '
  • ').join('')}

生产落地必改项依据上表实测结果

${t.fixes.length ? '' + t.fixes.map(f => '').join('') + '
用途原值建议改为理由
' + f[0] + '' + f[1] + '' + f[2] + '' + f[3] + '
' : '
本方案 12 项对比度检查全部达标,无需为无障碍做任何色值改动 —— 三套中唯一「开箱即合规」的方案。
'}
`).join('')}

三、主要界面元素横向对照同一元素 · 三套色彩并排

${THEMES.map(t => '').join('')} ${EL_ROWS.map(([name, fn]) => '' + THEMES.map(t => '').join('') + '').join('')}
元素' + t.name.split('·')[1].trim() + '
' + name + '' + fn(t.id) + '

四、高频长时使用的统一优化层三套方案共用

这是针对「经纪人每天连续使用 6-8 小时」这一前提,在三套体系之上叠加的四条约束。三套方案均已落实。

优化项做法解决什么
深色品牌侧栏侧栏统一用 #0C2340 而非浅灰减少整屏亮度、形成稳定视觉锚点;浅灰侧栏在长时间使用中会与内容区「糊在一起」,找不到导航边界
斑马纹极低对比条纹底色与白底差值控制在 2–3%(#F4F4F4/#F5F6F7/#F5F5F5)帮助横向追行不串行,但不会产生条纹眩光;这是数据表最高频的疲劳源
语义色降饱和状态标签用「描边+浅底」而非实心色块,浅底透明度压到 8–12%一屏内可能有几十个状态标签,实心高饱和色块是最大的刺眼来源
金色只做点品牌金 #C6A15B 只用于 logo、关键数字、选中指示条,占比 ≤5%保住品牌质感,同时避免金色大面积铺陈显廉价

五、选型建议

本次结论:已定稿 方案 B · Fiori 商务派(2026-09-19)。定稿理由:三套中唯一 12/12 对比度开箱合规、语义令牌分层最完整、选中态用 8% 蓝底而非实色块(长时使用最不刺眼),且官方 Quick Theming 就是「替换 logo + 主品牌色」,与已定稿的甄房墨蓝最契合。下表保留完整选型逻辑,供后续复盘与其他产品线参考。

如果……建议理由
团队人少、迭代快,希望最快落地并降低长期维护成本方案 C · AntD 亲和派迁移成本最低、中文生态最全;代价是要按「生产落地必改项」把 6 处色值各下移一阶(改完视觉几乎无差别,但差别在无障碍上)
需要严谨的层级与无障碍,且能接受工程感外观方案 A · Carbon 严谨派灰阶分层最严谨、对比度有官方硬约束;数据密度最高。仅「黄色被当文字色」与「圆角偏硬」两处需按建议调整
交易与佣金等强流程模块、或需与财务/ERP 打通方案 B · Fiori 商务派状态与选中态表达最清楚,对比度规则可推导,符合企业级规范;三套中唯一 12/12 开箱合规
想要「一次到位」但不想赌混合:Carbon 骨架 + AntD 语义色用 Carbon 的灰阶分层与密度打底,语义色采用国内用户熟悉的 AntD 色相并降饱和(沿用 C 的必改项色值);这是我推荐的落地路径
`; fs.writeFileSync(path.join(HERE, 'index.html'), html); console.log('已生成 theme/index.html ·', THEMES.length, '套方案'); THEMES.forEach(t => console.log(' ', t.name, '加权', t.total.toFixed(2)));