| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- #!/usr/bin/env node
- /**
- * D37: 模板区组件属性色值(switch color / slider activeColor / 图表 fillColor 等)
- * 绑定到 BrandColors,消除模板里编译期不可达的硬编码 hex。
- *
- * 做法:
- * 1) 模板属性 `attr=#HEX` → `:attr="<key>Color"`
- * 2) script 内补 `import { BrandColors } from '@/utils/brand-colors.js'`(已存在则跳过)
- * 3) 在 data() 的 return 对象首部(无 data 则 computed,再否则新建 computed)注入
- * `<key>Color: BrandColors.<key>,`
- *
- * 用法: node scripts/d37-template-color-bind.js [--dry]
- */
- const fs = require('fs');
- const HEXT_TO_KEY = {
- '#FF8C42': 'body',
- '#F97316': 'primary',
- '#10B981': 'success',
- '#6366F1': 'wisdom',
- '#F59E0B': 'wealth',
- '#FF6B9D': 'mind',
- };
- const FILES = [
- 'pages/action-detail/index.vue',
- 'pages/body-detail/index.vue',
- 'pages/child/rewards.vue',
- 'pages/discover/create-circle.vue',
- 'pages/discover-detail/product-detail/product-detail.vue',
- 'pages/family/add-member.vue',
- 'pages/health/components/ComprehensiveSurvey.vue',
- 'pages/health/daily-checkin.vue',
- 'pages/health/sleep-index.vue',
- 'pages/health-main/index.vue',
- 'pages/member-detail/components/member-detail-sections/body-section.vue',
- 'pages/mind-detail/index.vue',
- 'pages/profile/components/ProfileMenu.vue',
- 'pages/promotion/index.vue',
- 'pages/rewards/rewards.vue',
- 'pages/wealth/components/WealthEnergyRing.vue',
- 'pages/wealth/index.vue',
- 'pages/wisdom-detail/index.vue',
- ];
- const DRY = process.argv.includes('--dry');
- const IMPORT_LINE = "import { BrandColors } from '@/utils/brand-colors.js'";
- let totalBind = 0;
- for (const f of FILES) {
- let src = fs.readFileSync(f, 'utf8');
- const eol = src.includes('\r\n') ? '\r\n' : '\n';
- // 只在模板区(第一个行首 <script 之前)替换属性色值
- const scriptStart = src.search(/^<script/m);
- if (scriptStart === -1) { console.log('SKIP(no script): ' + f); continue; }
- let head = src.slice(0, scriptStart);
- const tail = src.slice(scriptStart);
- const keys = new Set();
- let changes = 0;
- head = head.replace(/([A-Za-z-]+)=(#[0-9a-fA-F]{3,8})\b/g, (m, attr, hex) => {
- const key = HEXT_TO_KEY[hex.toUpperCase()];
- if (!key) return m;
- changes++;
- // SVG 内部属性(<stop stop-color>):小程序对内联 SVG 支持有限,
- // 不做响应式绑定,仅补引号保持合法字面量
- if (attr === 'stop-color') return attr + '="' + hex + '"';
- keys.add(key);
- totalBind++;
- return ':' + attr + '="' + key + 'Color"';
- });
- if (!changes) { console.log('SKIP(no binding): ' + f); continue; }
- let body = tail;
- // 1) import(仅有响应式绑定才需要引入)
- if (keys.size && !/brand-colors/.test(body)) {
- body = body.replace(/^<script[^>]*>\r?\n/, m => m + IMPORT_LINE + eol);
- }
- // 2) 注入 data/computed 属性
- let injected = false;
- const dataPatterns = [
- /^([ \t]*)data\s*(?:\([^)]*\)|:\s*function\s*\([^)]*\))\s*\{\r?\n([ \t]*)return\s*\{\r?\n/m,
- /^([ \t]*)data\s*:\s*\(\)\s*=>\s*\(\{\r?\n([ \t]*)/m,
- ];
- for (const re of dataPatterns) {
- const m = body.match(re);
- if (!m) continue;
- const idx = body.indexOf(m[0]) + m[0].length;
- const indent = m[2] + ' ';
- const block = [...keys].map(k => indent + k + 'Color: BrandColors.' + k + ',').join(eol) + eol;
- body = body.slice(0, idx) + block + body.slice(idx);
- injected = true;
- break;
- }
- if (!injected && keys.size) {
- const computedMatch = body.match(/^([ \t]*)computed\s*:\s*\{\r?\n([ \t]*)/m);
- if (computedMatch) {
- const idx = body.indexOf(computedMatch[0]) + computedMatch[0].length;
- const indent = computedMatch[2] || (computedMatch[1] + ' ');
- const block = [...keys].map(k => indent + k + 'Color() { return BrandColors.' + k + ' },').join(eol) + eol;
- // computedMatch 已吞掉下一行缩进,需补回,否则首个既有属性会丢缩进
- body = body.slice(0, idx) + block + indent + body.slice(idx);
- injected = true;
- }
- }
- if (!injected && keys.size) {
- const exp = body.match(/^([ \t]*)export\s+default\s*\{\r?\n/m);
- if (exp) {
- const idx = body.indexOf(exp[0]) + exp[0].length;
- const indent = exp[1] + ' ';
- const block = indent + 'computed: {' + eol +
- [...keys].map(k => indent + ' ' + k + 'Color() { return BrandColors.' + k + ' },').join(eol) + eol +
- indent + '},' + eol;
- body = body.slice(0, idx) + block + body.slice(idx);
- injected = true;
- }
- }
- if (!injected && keys.size) { console.log('FAIL(inject): ' + f); continue; }
- const out = head + body;
- if (!DRY) fs.writeFileSync(f, out, 'utf8');
- console.log((DRY ? 'DRY ' : 'OK ') + f + ' bindings=' + keys.size);
- }
- console.log('模板属性绑定总数: ' + totalBind);
|