front/scripts/collect-page-buttons.mjs
zzh 7585e0c7b7 feat: 完善三级按钮同步、我的订单与助手等前端能力
新增页面按钮目录与菜单同步三级按钮;增强我的订单(问题件/理赔件、费用、详情抽屉)、Nana 助手、联系客服、渠道问题模板及登录/RBAC 等相关功能。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 12:36:18 +08:00

90 lines
2.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 扫描前端 hasPermission / 路由 permission,输出三级按钮收集结果
* 用法: node scripts/collect-page-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 srcDir = path.join(root, 'src');
const ACTION_LABELS = {
Query: '查询',
Create: '新增',
Update: '修改',
Delete: '删除',
Move: '移动',
AssignRoles: '分配角色',
AssignPermissions: '分配权限',
ResetPassword: '重置密码',
Template: '模板管理',
Audit: '审计',
Use: '使用',
Save: '保存',
Export: '导出',
Import: '导入',
};
function walk(dir, ext, out = []) {
for (const name of fs.readdirSync(dir)) {
const full = path.join(dir, name);
const stat = fs.statSync(full);
if (stat.isDirectory()) {
if (name === 'node_modules') continue;
walk(full, ext, out);
} else if (full.endsWith(ext)) {
out.push(full);
}
}
return out;
}
function inferParent(code) {
const parts = code.split('.');
if (parts.length < 3) return null;
// 资源级:Rbac.Menus.Create → Rbac.Menus
return `${parts[0]}.${parts[1]}`;
}
function inferName(code) {
const action = code.split('.').pop();
return ACTION_LABELS[action] || action;
}
const vueFiles = walk(path.join(srcDir, 'views'), '.vue');
const routeFiles = walk(path.join(srcDir, 'router'), '.ts');
const permissionCodes = new Set();
for (const file of [...vueFiles, ...routeFiles]) {
const text = fs.readFileSync(file, 'utf8');
const hasPermissionRe = /hasPermission\(\s*['"]([^'"]+)['"]\s*\)/g;
const routePermRe = /permission:\s*['"]([^'"]+)['"]/g;
let m;
while ((m = hasPermissionRe.exec(text))) permissionCodes.add(m[1]);
while ((m = routePermRe.exec(text))) permissionCodes.add(m[1]);
}
const grouped = new Map();
for (const code of [...permissionCodes].sort()) {
const parent = inferParent(code);
if (!parent || parent === code) continue;
if (!grouped.has(parent)) grouped.set(parent, []);
grouped.get(parent).push({ permissionCode: code, name: inferName(code) });
}
console.log('# 前端页面按钮收集结果\n');
console.log(`扫描文件: ${vueFiles.length} 个 vue, ${routeFiles.length} 个路由`);
console.log(`权限码总数: ${permissionCodes.size}`);
console.log(`可分组三级按钮父级: ${grouped.size}\n`);
for (const [parent, buttons] of [...grouped.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
console.log(`## ${parent}`);
buttons.forEach((btn, idx) => {
console.log(` ${idx + 1}. ${btn.permissionCode} → ${btn.name}`);
});
console.log('');
}