452 lines
16 KiB
JavaScript
452 lines
16 KiB
JavaScript
/**
|
||
* 从路由配置中提取菜单并添加到菜单管理系统
|
||
* 该脚本会读取路由配置,提取所有主菜单和子菜单,然后调用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 };
|
||
|