| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- #!/usr/bin/env node
- /* 全站页面美化审计 v2(2026-10-17)
- * 对齐 UniForge UI 取证阶段:量化每个页面的样式债务
- * 维度:硬编码色 / 旧品牌蓝 / transition:all / getSystemInfoSync / 内联样式 / px 单位 / 页面体量
- */
- const fs = require('fs')
- const path = require('path')
- const ROOT = path.resolve(__dirname, '..')
- const EXCLUDE_DIRS = new Set(['node_modules', 'dist', 'unpackage', '.git', 'dist_bak_1792213156'])
- // 已重制完成的页面(阶段一~四 + 成员标签),不纳入待美化清单
- const DONE_PAGES = new Set([
- 'pages/body-detail/index.vue',
- 'pages/mind-detail/index.vue',
- 'pages/wisdom-detail/index.vue',
- 'pages/wealth/index.vue',
- 'pages/action-detail/index.vue',
- ])
- function walk(dir, out = []) {
- for (const name of fs.readdirSync(dir)) {
- if (EXCLUDE_DIRS.has(name)) continue
- const p = path.join(dir, name)
- const st = fs.statSync(p)
- if (st.isDirectory()) walk(p, out)
- else if (name.endsWith('.vue')) out.push(p)
- }
- return out
- }
- const files = walk(ROOT)
- const HEX = /#[0-9a-fA-F]{3,8}\b/g
- const OLD_BLUE = /#5[Bb]9[Bb][Dd]5/g
- const TRANS_ALL = /transition\s*:\s*all/g
- const SYSINFO = /getSystemInfoSync/g
- const INLINE_STYLE = /style="/g
- const PX_UNIT = /(?<![\w-])\d+px(?![\w-])/g
- const rows = []
- for (const abs of files) {
- const rel = path.relative(ROOT, abs).split(path.sep).join('/')
- if (rel.startsWith('components/')) continue // 组件单独一轮
- const src = fs.readFileSync(abs, 'utf8')
- const styleStart = src.search(/<style/)
- const styleBlock = styleStart === -1 ? '' : src.slice(styleStart)
- const hexInStyle = (styleBlock.match(HEX) || []).length
- const hexAll = (src.match(HEX) || []).length
- rows.push({
- file: rel,
- lines: src.split('\n').length,
- hex: hexAll,
- hexStyle: hexInStyle,
- oldBlue: (styleBlock.match(OLD_BLUE) || []).length,
- transAll: (styleBlock.match(TRANS_ALL) || []).length,
- sysInfo: (styleBlock.match(SYSINFO) || []).length,
- inlineStyle: (src.match(INLINE_STYLE) || []).length,
- px: (styleBlock.match(PX_UNIT) || []).length,
- done: DONE_PAGES.has(rel),
- })
- }
- // 债务分:硬编码色权重最高(与品牌令牌脱钩的直接证据),旧品牌蓝次之
- for (const r of rows) {
- r.debt = r.hex * 3 + r.oldBlue * 5 + r.transAll * 2 + r.sysInfo * 2 + r.px * 0.1
- }
- const pagesCfg = JSON.parse(fs.readFileSync(path.join(ROOT, 'pages.json'), 'utf8'))
- const mainPaths = pagesCfg.pages.map((p) => p.path + '.vue')
- console.log('========== 汇总 ==========')
- console.log('页面文件总数(不含 components):', rows.length)
- console.log('硬编码色总数:', rows.reduce((s, r) => s + r.hex, 0))
- console.log('旧品牌蓝 #5B9BD5 总数:', rows.reduce((s, r) => s + r.oldBlue, 0))
- console.log('transition:all 总数:', rows.reduce((s, r) => s + r.transAll, 0))
- console.log('getSystemInfoSync 总数:', rows.reduce((s, r) => s + r.sysInfo, 0))
- console.log('\n========== 主包 7 门面页 ==========')
- console.log('file|lines|hex|oldBlue|transAll|px|inlineStyle|debt')
- for (const p of mainPaths) {
- const r = rows.find((x) => x.file === p)
- if (r) console.log(`${r.file}|${r.lines}|${r.hex}|${r.oldBlue}|${r.transAll}|${r.px}|${r.inlineStyle}|${r.debt.toFixed(1)}`)
- }
- console.log('\n========== 旧品牌蓝 TOP25(品牌残留,必须收敛) ==========')
- console.log('file|oldBlue|hex|lines')
- rows
- .filter((r) => r.oldBlue > 0)
- .sort((a, b) => b.oldBlue - a.oldBlue || b.hex - a.hex)
- .slice(0, 25)
- .forEach((r) => console.log(`${r.file}|${r.oldBlue}|${r.hex}|${r.lines}`))
- console.log('\n========== 硬编码色 TOP30(样式债务) ==========')
- console.log('file|hex|hexStyle|oldBlue|transAll|px|lines|debt|done')
- rows
- .filter((r) => !r.done)
- .sort((a, b) => b.debt - a.debt)
- .slice(0, 30)
- .forEach((r) => console.log(`${r.file}|${r.hex}|${r.hexStyle}|${r.oldBlue}|${r.transAll}|${r.px}|${r.lines}|${r.debt.toFixed(1)}|${r.done}`))
- console.log('\n========== transition:all 分布 ==========')
- rows
- .filter((r) => r.transAll > 0)
- .sort((a, b) => b.transAll - a.transAll)
- .forEach((r) => console.log(`${r.file}|${r.transAll}`))
- console.log('\n========== getSystemInfoSync 分布 ==========')
- rows
- .filter((r) => r.sysInfo > 0)
- .forEach((r) => console.log(`${r.file}|${r.sysInfo}`))
- // 债务分层
- const alive = rows.filter((r) => !r.done)
- const buckets = { P0: [], P1: [], P2: [], P3: [] }
- for (const r of alive) {
- if (r.debt >= 120) buckets.P0.push(r)
- else if (r.debt >= 60) buckets.P1.push(r)
- else if (r.debt >= 25) buckets.P2.push(r)
- else buckets.P3.push(r)
- }
- console.log('\n========== 债务分层(已重制 5 页除外) ==========')
- for (const k of ['P0', 'P1', 'P2', 'P3']) {
- console.log(`${k}: ${buckets[k].length} 页`)
- buckets[k]
- .sort((a, b) => b.debt - a.debt)
- .slice(0, 12)
- .forEach((r) => console.log(` ${r.file} (debt ${r.debt.toFixed(0)})`))
- }
|