/** * 从路由配置中提取菜单并添加到菜单管理系统 * 该脚本会读取路由配置,提取所有主菜单和子菜单,然后调用API添加到菜单管理列表 */ const fs = require('fs'); const path = require('path'); const axios = require('axios'); // 配置API基础URL(需要根据实际情况修改) const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:5173'; const API_PREFIX = '/api'; // 标准按钮配置 const STANDARD_BUTTONS = [ { title: '新增', code: 'add', sortCode: 1 }, { title: '编辑', code: 'edit', sortCode: 2 }, { title: '删除', code: 'delete', sortCode: 3 }, { title: '查询', code: 'query', sortCode: 4 }, { title: '导入', code: 'import', sortCode: 5 }, { title: '导出', code: 'export', sortCode: 6 }, ]; // 读取路由配置文件 function readRouteFiles() { const routesDir = path.join(__dirname, '../src/router/routes/newModules'); const files = fs.readdirSync(routesDir); const routes = []; files.forEach(file => { if (file.endsWith('.ts')) { try { const filePath = path.join(routesDir, file); const content = fs.readFileSync(filePath, 'utf-8'); // 简单的解析路由配置(实际应该使用TypeScript解析器) // 这里使用正则表达式提取基本信息 const routeMatch = content.match(/const\s+(\w+):\s*AppRouteRecordRaw\s*=\s*\{([\s\S]*?)\};/); if (routeMatch) { const routeContent = routeMatch[2]; // 提取path const pathMatch = routeContent.match(/path:\s*['"]([^'"]+)['"]/); // 提取name const nameMatch = routeContent.match(/name:\s*['"]([^'"]+)['"]/); // 提取locale const localeMatch = routeContent.match(/locale:\s*['"]([^'"]+)['"]/); // 提取icon const iconMatch = routeContent.match(/icon:\s*['"]([^'"]+)['"]/); // 提取order const orderMatch = routeContent.match(/order:\s*(\d+)/); if (pathMatch && nameMatch) { const mainMenu = { path: pathMatch[1], name: nameMatch[1], title: localeMatch ? localeMatch[1] : nameMatch[1], icon: iconMatch ? iconMatch[1] : '', order: orderMatch ? parseInt(orderMatch[1]) : 999, children: [] }; // 提取children const childrenMatch = routeContent.match(/children:\s*\[([\s\S]*?)\]/); if (childrenMatch) { const childrenContent = childrenMatch[1]; // 使用正则表达式提取每个子路由 const childMatches = childrenContent.matchAll(/path:\s*['"]([^'"]+)['"][\s\S]*?name:\s*['"]([^'"]+)['"][\s\S]*?locale:\s*['"]([^'"]+)['"]/g); for (const childMatch of childMatches) { const childPath = childMatch[1]; const childName = childMatch[2]; const childLocale = childMatch[3]; // 检查是否隐藏 const hideMatch = childrenContent.match(new RegExp(`name:\\s*['"]${childName}['"][\\s\\S]*?hideInMenu:\\s*(true|false)`)); const isHide = hideMatch && hideMatch[1] === 'true'; if (!isHide) { mainMenu.children.push({ path: childPath.startsWith('/') ? childPath : `${mainMenu.path}/${childPath}`, name: childName, title: childLocale }); } } } routes.push(mainMenu); } } } catch (error) { console.error(`解析文件 ${file} 时出错:`, error.message); } } }); return routes.sort((a, b) => a.order - b.order); } // 解析路由配置(更准确的方法) function parseRouteConfig() { const routesDir = path.join(__dirname, '../src/router/routes/newModules'); const routes = []; // 手动定义路由结构(因为TypeScript文件需要编译才能解析) const routeConfig = [ { path: '/system', name: 'system', title: 'API', icon: 'icon-computer', order: 1, children: [ { path: 'interface', name: 'SystemInterface', title: '上游接口' }, { path: 'serviceProvidersList', name: 'ServiceProvidersList', title: '下游接口' }, { path: '/system/newList/adhibition-list', name: 'AdhibitionList', title: 'App列表' }, { path: 'log-record', name: 'LogRecord', title: '请求日志' }, { path: '/system/applicationStatistics', name: 'ApplicationStatistics', title: '调用统计' }, { path: 'api-debugger', name: 'ApiDebugger', title: 'API调试' }, ] }, { path: '/trajectoryCenter', name: 'TrajectoryCenter', title: '轨迹', icon: 'icon-computer', order: 2, children: [ { path: 'trajectoryOrder', name: 'TrajectoryOrder', title: '轨迹订单', order: 1 }, { path: 'trajectoryWarningOrder', name: 'TrajectoryWarningOrder', title: '预警订单', order: 2 }, { path: 'trajectoryState', name: 'TrajectoryState', title: '轨迹状态', order: 3 }, { path: 'trajectoryFlag', name: 'TrajectoryFlag', title: '轨迹标记', order: 4 }, { path: 'trajectoryContent', name: 'TrajectoryContent', title: '轨迹内容', order: 5 }, { path: 'trajectoryPull', name: 'TrajectoryPull', title: '轨迹拉取', order: 6 }, { path: 'trajectoryPush', name: 'TrajectoryPush', title: '轨迹推送', order: 7 }, { path: 'kpiRegion', name: 'KpiRegion', title: '预警分区', order: 8 }, { path: 'kpiEarlyWarningConfig', name: 'KpiEarlyWarningConfig', title: '预警配置', order: 9 }, ] }, { path: '/order', name: 'Order', title: '订单', icon: 'icon-file', order: 3, children: [ { path: 'price-check', name: 'PriceCheck', title: '我要查价' }, { path: 'place-order', name: 'PlaceOrder', title: '我要下单' }, { path: 'my-orders', name: 'MyOrders', title: '我的订单' }, ] }, { path: '/channel', name: 'Channel', title: '渠道', icon: 'icon-link', order: 4, children: [ { path: 'sales-products', name: 'SalesProducts', title: '销售产品' }, { path: 'service-channels', name: 'ServiceChannels', title: '服务渠道' }, { path: 'blacklist', name: 'Blacklist', title: '黑名单区' }, { path: 'remote-zones', name: 'RemoteZones', title: '偏远分区' }, { path: 'weight-rules', name: 'WeightRules', title: '计重规则' }, { path: 'weight-rounding', name: 'WeightRounding', title: '计重进位' }, { path: 'auto-trigger', name: 'AutoTrigger', title: '自动触发' }, ] }, { path: '/organization', name: 'organization', title: '系统', icon: 'icon-settings', order: 999, children: [ { path: 'org', name: 'OrganizationOrg', title: '组织管理' }, { path: 'user', name: 'OrganizationUser', title: '用户管理' }, { path: 'position', name: 'OrganizationPosition', title: '职位管理' }, { path: 'role', name: 'OrganizationRole', title: '角色管理' }, { path: 'module', name: 'OrganizationModule', title: '模块管理' }, { path: 'menu', name: 'OrganizationMenu', title: '菜单管理' }, { path: 'profile', name: 'OrganizationProfile', title: '个人中心' }, ] }, ]; return routeConfig; } // 创建axios实例 function createAxiosInstance(token) { return axios.create({ baseURL: API_BASE_URL + API_PREFIX, headers: { 'Content-Type': 'application/json', 'Authorization': token ? `Bearer ${token}` : '', }, timeout: 30000, }); } // 获取模块列表 async function getModuleList(api) { try { const response = await api.get('/sys/limit/module/list'); if (response.data && response.data.data) { const modules = Array.isArray(response.data.data) ? response.data.data : []; return modules.length > 0 ? modules[0].id : 0; } return 0; } catch (error) { console.error('获取模块列表失败:', error.message); return 0; } } // 获取菜单树(用于检查菜单是否已存在) async function getMenuTree(api, moduleId) { try { const response = await api.get('/sys/limit/menu/tree', { params: { Module: moduleId } }); if (response.data && response.data.data) { return Array.isArray(response.data.data) ? response.data.data : []; } return []; } catch (error) { console.error('获取菜单树失败:', error.message); return []; } } // 检查菜单是否存在 function findMenuInTree(tree, code) { for (const item of tree) { if (item.code === code) { return item.id; } if (item.children && item.children.length > 0) { const found = findMenuInTree(item.children, code); if (found) return found; } } return null; } // 生成菜单编码 function generateMenuCode(path, name) { // 使用路径和名称生成编码 const parts = path.split('/').filter(p => p); if (parts.length > 0) { return parts.join('_').replace(/-/g, '_'); } return name.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, ''); } // 添加菜单 async function addMenu(api, menuData, moduleId) { try { const response = await api.post('/sys/limit/menu/add', menuData); if (response.data && response.data.code === 200) { return response.data.data; } throw new Error(response.data?.message || '添加菜单失败'); } catch (error) { if (error.response && error.response.data) { throw new Error(error.response.data.message || error.message); } throw error; } } // 添加按钮 async function addButton(api, buttonData) { try { const response = await api.post('/sys/limit/button/add', buttonData); if (response.data && response.data.code === 200) { return response.data.data; } throw new Error(response.data?.message || '添加按钮失败'); } catch (error) { if (error.response && error.response.data) { throw new Error(error.response.data.message || error.message); } throw error; } } // 主函数 async function main() { console.log('开始添加菜单和按钮...\n'); // 获取token(如果需要) const token = process.env.AUTH_TOKEN || ''; const api = createAxiosInstance(token); // 获取模块ID console.log('正在获取模块列表...'); const moduleId = await getModuleList(api); if (moduleId === 0) { console.warn('警告: 未找到模块,将使用模块ID 0'); } else { console.log(`使用模块ID: ${moduleId}\n`); } // 获取现有菜单树 console.log('正在获取现有菜单树...'); const existingMenuTree = await getMenuTree(api, moduleId); console.log(`已找到 ${existingMenuTree.length} 个现有菜单\n`); // 解析路由配置 const routes = parseRouteConfig(); // 存储已创建的菜单ID映射 const menuIdMap = new Map(); // 添加主菜单和子菜单 for (const route of routes) { console.log(`处理主菜单: ${route.title} (${route.path})`); // 检查主菜单是否已存在 const mainMenuCode = generateMenuCode(route.path, route.name); let mainMenuId = findMenuInTree(existingMenuTree, mainMenuCode); if (!mainMenuId) { // 添加主菜单 try { const mainMenuData = { title: route.title, code: mainMenuCode, menuType: 'CATALOG', // 目录类型 parentId: 0, path: route.path, component: '', icon: route.icon || '', sortCode: route.order || 999, status: 'ENABLE', isHide: false, description: `主菜单: ${route.title}`, module: moduleId, }; console.log(` 添加主菜单: ${route.title}`); const result = await addMenu(api, mainMenuData, moduleId); mainMenuId = result?.id || result; console.log(` ✓ 主菜单添加成功,ID: ${mainMenuId}`); } catch (error) { console.error(` ✗ 主菜单添加失败: ${error.message}`); continue; } } else { console.log(` 主菜单已存在,ID: ${mainMenuId}`); } menuIdMap.set(route.path, mainMenuId); // 添加子菜单 if (route.children && route.children.length > 0) { // 按order排序子菜单 const sortedChildren = route.children.sort((a, b) => (a.order || 999) - (b.order || 999)); for (const child of sortedChildren) { const childMenuCode = generateMenuCode(child.path, child.name); let childMenuId = findMenuInTree(existingMenuTree, childMenuCode); if (!childMenuId) { try { // 提取组件路径 let component = ''; if (child.path.startsWith('/')) { // 如果路径以/开头,可能是完整路径 component = `views${child.path}/index.vue`; } else { component = `views${route.path}/${child.path}/index.vue`; } const childMenuData = { title: child.title, code: childMenuCode, menuType: 'MENU', // 菜单类型 parentId: mainMenuId, path: child.path.startsWith('/') ? child.path : `${route.path}/${child.path}`, component: component, icon: '', sortCode: child.order || 999, status: 'ENABLE', isHide: false, description: `子菜单: ${child.title}`, module: moduleId, }; console.log(` 添加子菜单: ${child.title}`); const result = await addMenu(api, childMenuData, moduleId); childMenuId = result?.id || result; console.log(` ✓ 子菜单添加成功,ID: ${childMenuId}`); // 重新获取菜单树以包含新添加的菜单 const updatedTree = await getMenuTree(api, moduleId); existingMenuTree.length = 0; existingMenuTree.push(...updatedTree); } catch (error) { console.error(` ✗ 子菜单添加失败: ${error.message}`); continue; } } else { console.log(` 子菜单已存在,ID: ${childMenuId}`); } // 为子菜单添加标准按钮 console.log(` 为子菜单添加标准按钮...`); for (const button of STANDARD_BUTTONS) { try { const buttonCode = `${childMenuCode}_${button.code}`; const buttonData = { title: button.title, code: buttonCode, parentId: childMenuId, sortCode: button.sortCode, description: `${child.title} - ${button.title}`, }; await addButton(api, buttonData); console.log(` ✓ 按钮 "${button.title}" 添加成功`); } catch (error) { // 如果按钮已存在,忽略错误 if (error.message.includes('已存在') || error.message.includes('exist')) { console.log(` - 按钮 "${button.title}" 已存在,跳过`); } else { console.error(` ✗ 按钮 "${button.title}" 添加失败: ${error.message}`); } } } } } console.log(''); } console.log('\n菜单和按钮添加完成!'); } // 运行主函数 if (require.main === module) { main().catch(error => { console.error('执行失败:', error); process.exit(1); }); } module.exports = { main, parseRouteConfig };