/** * 扫描订单/仓储/渠道页面 hasPermission 与附近可见文案,并与 pageButtonCatalog 对比 * 用法: node scripts/scan-module-buttons.mjs */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.resolve(__dirname, '..'); const catalogPath = path.join(root, 'src/constants/pageButtonCatalog.ts'); const modules = [ { name: 'Order', dirs: ['src/views/order'] }, { name: 'Warehouse', dirs: ['src/views/warehouse'] }, { name: 'Channel', dirs: ['src/views/channel'] }, ]; function walk(dir, out = []) { if (!fs.existsSync(dir)) return out; for (const name of fs.readdirSync(dir)) { const full = path.join(dir, name); const stat = fs.statSync(full); if (stat.isDirectory()) walk(full, out); else if (/\.(vue|ts|tsx|js)$/.test(full)) out.push(full); } return out; } function extractLabelNear(text, index) { // look ahead ~400 chars for button text / t('...') / title= const slice = text.slice(index, index + 500); const tMatch = slice.match(/\{\{\s*t\(\s*['"]([^'"]+)['"]\s*\)/); if (tMatch) return { type: 'i18n', value: tMatch[1] }; const titleMatch = slice.match(/title=["']([^"']+)["']/); if (titleMatch) return { type: 'title', value: titleMatch[1] }; const textMatch = slice.match(/>([^<{][^<]{0,40}) b[1] - a[1])[0][0]; } console.log('# Order / Warehouse / Channel 按钮对比(页面 vs pageButtonCatalog)\n'); const parents = new Set([...catalog.keys(), ...foundByParent.keys()]); for (const parent of [...parents].sort()) { const cat = catalog.get(parent) || []; const found = foundByParent.get(parent) || new Map(); const catCodes = new Set(cat.map((b) => b.permissionCode)); const foundCodes = new Set(found.keys()); const missingInCatalog = [...foundCodes].filter((c) => !catCodes.has(c)); const extraInCatalog = [...catCodes].filter((c) => !foundCodes.has(c)); const nameDiffs = []; for (const b of cat) { const f = found.get(b.permissionCode); if (!f) continue; const hint = majority(f.displayHints); if (hint && hint !== b.name && !hint.startsWith('order.') && !hint.startsWith('warehouse.') && !hint.startsWith('channel.') && !hint.startsWith('common.')) { nameDiffs.push({ code: b.permissionCode, catalog: b.name, page: hint }); } } if (!missingInCatalog.length && !extraInCatalog.length && !nameDiffs.length) { console.log(`## ${parent} OK (${cat.length} buttons, ${foundCodes.size} used in pages)`); continue; } console.log(`## ${parent}`); console.log(` catalog=${cat.length} pageUsed=${foundCodes.size}`); if (missingInCatalog.length) { console.log(' [catalog缺少 / 页面有]'); missingInCatalog.forEach((c) => { const f = found.get(c); console.log(` + ${c} pageHint=${majority(f.displayHints) || '-'} @${f.files[0]}`); }); } if (extraInCatalog.length) { console.log(' [catalog多余 / 页面未见 hasPermission]'); extraInCatalog.forEach((c) => { const b = cat.find((x) => x.permissionCode === c); console.log(` - ${c} name=${b?.name}`); }); } if (nameDiffs.length) { console.log(' [名称不一致]'); nameDiffs.forEach((d) => console.log(` ~ ${d.code}: catalog="${d.catalog}" page="${d.page}"`)); } console.log(''); }