feat: 初始化画个标仓库 - AI驱动设计系统(BYOK模式) - 224源/371504图标+25海报源+130技能

This commit is contained in:
sgp
2026-10-03 21:44:18 +08:00
commit 9e222877ea
497 changed files with 43611 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
// 甄房换肤 · WEB 业务层替换器
// 用法: node _design-test/apply-vue-colors.js (预演,报告每条规则的命中数)
// node _design-test/apply-vue-colors.js --write (真写)
//
// 为什么不能只做单色 1:1 替换:
// 1) ECharts 的 10 色分类色板若逐色映射,会与已有的 #3b82f6 / #ef4444 撞色,序列失去区分度
// 2) 同一个渐变串 linear-gradient(135deg,#f08060,#d9481c) 同时用于「房源等级 A」和「客源热度 H」,
// 但 A 现在要改成品牌金、H 仍是红 —— 必须按选择器区分
// 所以规则分两类:先按「选择器/整串」精确处理,再对剩余单色做通用映射。
const fs = require('fs');
const path = require('path');
const WRITE = process.argv.includes('--write');
// 两个前端:WEB(ruoyi-ui)与 H5(zhenfang-uni)
const ROOTS = [
{ root: 'e:/KF_7/ZhenOS-ZF/ruoyi-ui/src', skip: new Set(['assets/styles']) },
{ root: 'e:/KF_7/ZhenOS-ZF/zhenfang-uni/src', skip: new Set() }
];
const EXT = new Set(['.vue', '.js', '.ts', '.html']);
// ── 新的 10 色分类色板(Fiori 官方 Category/Accent 色 + 品牌金,色相拉开 + 明度错位)──
const PALETTE10 = ['#0057D2', '#E76500', '#188918', '#DF1278', '#07838F', '#D27700', '#7858FF', '#D20A0A', '#5B738B', '#C6A15B'];
const OLD10 = ['#ff7a1a', '#2e9e5b', '#ffb31a', '#f08060', '#0F766E', '#756a5e', '#7c3aed', '#14B8A6', '#ef4444', '#3b82f6'];
const OLD6 = ['#ff7a1a', '#2e9e5b', '#ffb31a', '#f08060', '#0F766E', '#756a5e'];
const arr = a => '[' + a.map(c => "'" + c + "'").join(', ') + ']';
// ── 规则(按顺序执行)──
const RULES = [
// 1. 图表 10 色分类色板(4 处)
['图表色板 · 10 色', arr(OLD10).replace(/'/g, "'"), arr(PALETTE10), 'literal'],
// 2. 图表 6 色(1 处)
['图表色板 · 6 色', arr(OLD6).replace(/'/g, "'"), arr(PALETTE10.slice(0, 6)), 'literal'],
// 3. 房源等级 A → 品牌金
['等级 A 渐变 → 品牌金', /(&\.level-A\s*\{[^}]*?linear-gradient\()135deg,\s*#f08060,\s*#d9481c/g,
'$1135deg, #E3CB99, #C6A15B', 'regex'],
// 4. 客源热度 H(热)→ 保留红系
['热度 H 渐变 → 红', /(&\.level-H\s*\{[^}]*?linear-gradient\()135deg,\s*#f08060,\s*#d9481c/g,
'$1135deg, #E06B6B, #AA0808', 'regex'],
// 5. 等级 B → 警告橙
['等级 B 渐变 → 橙', /(&\.level-B\s*\{[^}]*?linear-gradient\()135deg,\s*#FBBF24,\s*#ffb31a/g,
'$1135deg, #D27700, #E76500', 'regex'],
// 6. 等级 C → 中性
['等级 C 渐变 → 中性', /(&\.level-C\s*\{[^}]*?linear-gradient\()135deg,\s*#9a8e80,\s*#756a5e/g,
'$1135deg, #A9B4BE, #788FA6', 'regex'],
// 7. 统计卡 warning 渐变
['warning 渐变', /linear-gradient\((90|135)deg,\s*#ffb31a,\s*#FBBF24\)/g,
'linear-gradient($1deg, #E76500, #D27700)', 'regex'],
// 8. 统计卡 danger 渐变
['danger 渐变', /linear-gradient\((90|135)deg,\s*#f08060,\s*#ef4444\)/g,
'linear-gradient($1deg, #E76500, #D20A0A)', 'regex'],
// 9. el-rate 星级(单一琥珀色 ×3)
['星级 · 单色', "['#ffb31a', '#ffb31a', '#ffb31a']", "['#D27700', '#D27700', '#D27700']", 'literal'],
['星级 · 同色系', "['#2e9e5b', '#2e9e5b', '#2e9e5b']", "['#188918', '#188918', '#188918']", 'literal'],
// 10. el-rate 五档评价(差→极佳)
['评价五档', "['#d9481c', '#ffb31a', '#FBBF24', '#ff7a1a', '#2e9e5b']",
"['#D20A0A', '#E76500', '#D27700', '#C6A15B', '#188918']", 'literal'],
// 11. ThemePicker 预置色
['主题预置色', "['#ff7a1a', '#ff8a3d', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]",
"['#0070F2', '#4D9BF6', '#0C2340','#131E29','#188918', '#07838F', '#7858FF', '#D20A0A', ]", 'literal'],
// 12. rgb()/rgba() 三元组(保留 rgb/rgba 与后面的 alpha)
['rgb 三元组 · 橙', /(rgba?)\(\s*255,\s*122,\s*26\b/g, '$1(0, 112, 242', 'regex'],
['rgb 三元组 · 浅橙', /(rgba?)\(\s*255,\s*138,\s*61\b/g, '$1(77, 155, 246', 'regex'],
['rgb 三元组 · 琥珀', /(rgba?)\(\s*255,\s*179,\s*26\b/g, '$1(231, 101, 0', 'regex'],
['rgb 三元组 · 亮琥珀', /(rgba?)\(\s*251,\s*191,\s*36\b/g, '$1(210, 119, 0', 'regex'],
['rgb 三元组 · 暖墨', /(rgba?)\(\s*42,\s*33,\s*24\b/g, '$1(19, 30, 41', 'regex']
];
// ── 通用单色映射(处理剩余的所有字面量)──
const GENERIC = [
['#ff7a1a', '#0070F2'], ['#e86a0c', '#0064D9'], ['#d96408', '#0057D2'],
['#ff8a3d', '#4D9BF6'], ['#ffc48a', '#A6CFFF'], ['#ffe8d1', '#EBF8FF'], ['#fff6ec', '#F0F6FE'],
['#f6ead8', '#EAECEE'], ['#faf7f2', '#F5F6F7'], ['#fbf5ea', '#F5F6F7'],
['#e9d3b0', '#758CA4'], ['#efe7dc', '#E5E5E5'],
['#2a2118', '#131E29'], ['#3a322a', '#556B82'], ['#756a5e', '#5F7590'],
['#9a8e80', '#5F7590'], ['#8a7f73', '#758CA4'], ['#b8ac9e', '#A9B4BE'], ['#c9beb0', '#BBC1CA'],
['#ffb31a', '#E76500'], ['#fff6e0', '#FDF1E8'],
['#d9481c', '#AA0808'], ['#fdeee8', '#FBF0F0'],
['#2e9e5b', '#256F3A'], ['#ecf7f0', '#F1F7F3'], ['#1f8a5b', '#256F3A'], ['#ea580c', '#B44F00'],
['#f08060', '#D98A8A'], ['#fbbf24', '#D27700'],
['#2f2820', '#0A1D35'], ['#4a4036', '#1E2A36'], ['#d3dce6', '#223040'], ['#99a9bf', '#6B7F94'],
// 图表/其它残留的 Tailwind 色
['#ef4444', '#D20A0A'], ['#3b82f6', '#0057D2'], ['#0f766e', '#07838F'],
['#14b8a6', '#07838F'], ['#7c3aed', '#7858FF'], ['#f5222d', '#D20A0A'],
// H5 专有
['#36cfc9', '#4DA37A'], ['#ff9a3d', '#4D9BF6'], ['#10b981', '#188918']
];
const files = [];
for (const { root, skip } of ROOTS) {
(function walk(d) {
for (const e of fs.readdirSync(d, { withFileTypes: true })) {
const p = path.join(d, e.name);
const rel = path.relative(root, p).replace(/\\/g, '/');
if (e.isDirectory()) { if (!skip.has(rel)) walk(p); continue; }
if (EXT.has(path.extname(e.name).toLowerCase())) files.push(p);
}
})(root);
}
const tally = new Map();
let changedFiles = 0;
for (const f of files) {
let txt = fs.readFileSync(f, 'utf8');
const orig = txt;
for (const [label, pat, rep, kind] of RULES) {
const n = kind === 'literal'
? (txt.split(pat).length - 1)
: (txt.match(pat) || []).length;
if (!n) continue;
txt = kind === 'literal' ? txt.split(pat).join(rep) : txt.replace(pat, rep);
tally.set(label, (tally.get(label) || 0) + n);
}
for (const [from, to] of GENERIC) {
const n = (txt.match(new RegExp(from, 'gi')) || []).length;
if (!n) continue;
txt = txt.replace(new RegExp(from, 'gi'), to);
const k = '单色 ' + from + ' -> ' + to;
tally.set(k, (tally.get(k) || 0) + n);
}
if (txt !== orig) { changedFiles++; if (WRITE) fs.writeFileSync(f, txt); }
}
console.log('=== 规则命中统计 ===');
[...tally.entries()].sort((a, b) => b[1] - a[1]).forEach(([k, v]) => {
console.log(' ' + String(v).padStart(5) + ' ' + k);
});
const total = [...tally.values()].reduce((a, b) => a + b, 0);
console.log('\n涉及文件 ' + changedFiles + ' 个 / 替换合计 ' + total + ' 处');
console.log(WRITE ? '已写入' : '预演,未写入(加 --write 生效)');