701 lines
17 KiB
JavaScript
701 lines
17 KiB
JavaScript
// 生成菜单配置 Excel 文件的脚本
|
||
import fs from 'fs';
|
||
import path from 'path';
|
||
|
||
const VERBOSE_SCRIPT_LOGS = process.env.DEBUG_SCRIPT_LOGS === 'true';
|
||
if (!VERBOSE_SCRIPT_LOGS) {
|
||
console.log = () => {};
|
||
console.warn = () => {};
|
||
}
|
||
|
||
// 菜单配置数据(从路由配置中提取)
|
||
const menuData = [
|
||
// 一级菜单 - 首页
|
||
{
|
||
level: 1,
|
||
name: '首页',
|
||
permissionCode: 'Home',
|
||
route: '/home',
|
||
component: null,
|
||
icon: 'icon-dashboard',
|
||
order: 0,
|
||
hideInMenu: false,
|
||
},
|
||
// 二级菜单 - 首页
|
||
{
|
||
level: 2,
|
||
name: '工作台',
|
||
permissionCode: 'Home.Workplace',
|
||
route: '/home/workplace',
|
||
component: 'views/home/workplace/index',
|
||
icon: null,
|
||
order: 1,
|
||
hideInMenu: false,
|
||
parentRoute: '/home',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '实时监控',
|
||
permissionCode: 'Home.Monitor',
|
||
route: '/home/monitor',
|
||
component: 'views/home/monitor/index',
|
||
icon: null,
|
||
order: 2,
|
||
hideInMenu: false,
|
||
parentRoute: '/home',
|
||
},
|
||
|
||
// 一级菜单 - API
|
||
{
|
||
level: 1,
|
||
name: 'API',
|
||
permissionCode: 'Api',
|
||
route: '/api',
|
||
component: null,
|
||
icon: 'icon-computer',
|
||
order: 1,
|
||
hideInMenu: false,
|
||
},
|
||
// 二级菜单 - API
|
||
{
|
||
level: 2,
|
||
name: '上游接口',
|
||
permissionCode: 'Api.UpstreamInterface',
|
||
route: '/api/upstreamInterface',
|
||
component: 'views/api/upstreamInterface/index',
|
||
icon: 'icon-code',
|
||
order: 1,
|
||
hideInMenu: false,
|
||
parentRoute: '/api',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '下游接口',
|
||
permissionCode: 'Api.DownstreamInterface',
|
||
route: '/api/downstreamInterface',
|
||
component: 'views/api/downstreamInterface/index',
|
||
icon: 'icon-code',
|
||
order: 2,
|
||
hideInMenu: false,
|
||
parentRoute: '/api',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: 'App列表',
|
||
permissionCode: 'Api.AppList',
|
||
route: '/api/appList',
|
||
component: 'views/api/appList/index',
|
||
icon: 'icon-apps',
|
||
order: 3,
|
||
hideInMenu: false,
|
||
parentRoute: '/api',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '请求日志',
|
||
permissionCode: 'Api.RequestLog',
|
||
route: '/api/requestLog',
|
||
component: 'views/api/requestLog/index',
|
||
icon: 'icon-file-text',
|
||
order: 4,
|
||
hideInMenu: false,
|
||
parentRoute: '/api',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '调用统计',
|
||
permissionCode: 'Api.CallStatistics',
|
||
route: '/api/callStatistics',
|
||
component: 'views/api/callStatistics/index',
|
||
icon: 'icon-bar-chart',
|
||
order: 5,
|
||
hideInMenu: false,
|
||
parentRoute: '/api',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: 'API调试',
|
||
permissionCode: 'Api.ApiDebugger',
|
||
route: '/api/apiDebugger',
|
||
component: 'views/api/apiDebugger/index',
|
||
icon: 'icon-bug',
|
||
order: 6,
|
||
hideInMenu: false,
|
||
parentRoute: '/api',
|
||
},
|
||
// 隐藏页面 - API
|
||
{
|
||
level: 2,
|
||
name: '创建接口',
|
||
permissionCode: 'Api.UpstreamInterfaceConfig',
|
||
route: '/api/upstreamInterfaceConfig',
|
||
component: 'views/api/upstreamInterface/interfaceConfig',
|
||
icon: null,
|
||
order: null,
|
||
hideInMenu: true,
|
||
parentRoute: '/api',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '修改接口',
|
||
permissionCode: 'Api.UpstreamInterfaceConfigEdit',
|
||
route: '/api/upstreamInterfaceConfigEdit',
|
||
component: 'views/api/upstreamInterface/interfaceConfig',
|
||
icon: null,
|
||
order: null,
|
||
hideInMenu: true,
|
||
parentRoute: '/api',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '创建应用',
|
||
permissionCode: 'Api.AppList.CreateApplication',
|
||
route: '/api/appList/createApplication',
|
||
component: 'views/api/appList/components/createApplication',
|
||
icon: null,
|
||
order: null,
|
||
hideInMenu: true,
|
||
parentRoute: '/api',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '新建端口',
|
||
permissionCode: 'Api.DownstreamInterface.NewPort',
|
||
route: '/api/downstreamInterface/newPort',
|
||
component: 'views/api/downstreamInterface/newPort',
|
||
icon: null,
|
||
order: null,
|
||
hideInMenu: true,
|
||
parentRoute: '/api',
|
||
},
|
||
|
||
// 一级菜单 - 轨迹
|
||
{
|
||
level: 1,
|
||
name: '轨迹',
|
||
permissionCode: 'Track',
|
||
route: '/track',
|
||
component: null,
|
||
icon: 'icon-computer',
|
||
order: 2,
|
||
hideInMenu: false,
|
||
},
|
||
// 二级菜单 - 轨迹
|
||
{
|
||
level: 2,
|
||
name: '轨迹订单',
|
||
permissionCode: 'Track.Order',
|
||
route: '/track/order',
|
||
component: 'views/track/trackOrder/index',
|
||
icon: null,
|
||
order: 1,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '预警订单',
|
||
permissionCode: 'Track.WarningOrder',
|
||
route: '/track/warningOrder',
|
||
component: 'views/track/trackWarningOrder/index',
|
||
icon: null,
|
||
order: 2,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '轨迹状态',
|
||
permissionCode: 'Track.State',
|
||
route: '/track/state',
|
||
component: 'views/track/trackState/index',
|
||
icon: null,
|
||
order: 3,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '轨迹标记',
|
||
permissionCode: 'Track.Flag',
|
||
route: '/track/flag',
|
||
component: 'views/track/trackFlag/index',
|
||
icon: null,
|
||
order: 4,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '轨迹内容',
|
||
permissionCode: 'Track.Content',
|
||
route: '/track/content',
|
||
component: 'views/track/trackContent/index',
|
||
icon: null,
|
||
order: 5,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '轨迹拉取',
|
||
permissionCode: 'Track.Pull',
|
||
route: '/track/pull',
|
||
component: 'views/track/trackMapping/Pull',
|
||
icon: null,
|
||
order: 6,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '轨迹推送',
|
||
permissionCode: 'Track.Push',
|
||
route: '/track/push',
|
||
component: 'views/track/trackMapping/Push',
|
||
icon: null,
|
||
order: 7,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '预警分区',
|
||
permissionCode: 'Track.KpiRegion',
|
||
route: '/track/kpiRegion',
|
||
component: 'views/track/kpiRegion/index',
|
||
icon: null,
|
||
order: 8,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '预警配置',
|
||
permissionCode: 'Track.KpiEarlyWarningConfig',
|
||
route: '/track/kpiEarlyWarningConfig',
|
||
component: 'views/track/kpiEarlyWarningConfig/index',
|
||
icon: null,
|
||
order: 9,
|
||
hideInMenu: false,
|
||
parentRoute: '/track',
|
||
},
|
||
|
||
// 一级菜单 - 订单
|
||
{
|
||
level: 1,
|
||
name: '订单',
|
||
permissionCode: 'Order',
|
||
route: '/order',
|
||
component: null,
|
||
icon: 'icon-file',
|
||
order: 3,
|
||
hideInMenu: false,
|
||
},
|
||
// 二级菜单 - 订单
|
||
{
|
||
level: 2,
|
||
name: '我要查价',
|
||
permissionCode: 'Order.PriceCheck',
|
||
route: '/order/priceCheck',
|
||
component: 'views/order/priceCheck/index',
|
||
icon: null,
|
||
order: 1,
|
||
hideInMenu: false,
|
||
parentRoute: '/order',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '我要下单',
|
||
permissionCode: 'Order.PlaceOrder',
|
||
route: '/order/placeOrder',
|
||
component: 'views/order/placeOrder/index',
|
||
icon: null,
|
||
order: 2,
|
||
hideInMenu: false,
|
||
parentRoute: '/order',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '我的订单',
|
||
permissionCode: 'Order.MyOrders',
|
||
route: '/order/myOrders',
|
||
component: 'views/order/myOrders/index',
|
||
icon: null,
|
||
order: 3,
|
||
hideInMenu: false,
|
||
parentRoute: '/order',
|
||
},
|
||
|
||
// 一级菜单 - 渠道
|
||
{
|
||
level: 1,
|
||
name: '渠道',
|
||
permissionCode: 'Channel',
|
||
route: '/channel',
|
||
component: null,
|
||
icon: 'icon-link',
|
||
order: 4,
|
||
hideInMenu: false,
|
||
},
|
||
// 二级菜单 - 渠道
|
||
{
|
||
level: 2,
|
||
name: '销售产品',
|
||
permissionCode: 'Channel.SalesProducts',
|
||
route: '/channel/salesProducts',
|
||
component: 'views/channel/salesProducts/index',
|
||
icon: null,
|
||
order: 1,
|
||
hideInMenu: false,
|
||
parentRoute: '/channel',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '服务渠道',
|
||
permissionCode: 'Channel.ServiceChannels',
|
||
route: '/channel/serviceChannels',
|
||
component: 'views/channel/serviceChannels/index',
|
||
icon: null,
|
||
order: 2,
|
||
hideInMenu: false,
|
||
parentRoute: '/channel',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '价格分区',
|
||
permissionCode: 'Channel.PriceArea',
|
||
route: '/channel/priceArea',
|
||
component: 'views/channel/priceArea/index',
|
||
icon: null,
|
||
order: 3,
|
||
hideInMenu: false,
|
||
parentRoute: '/channel',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '黑名单区',
|
||
permissionCode: 'Channel.Blacklist',
|
||
route: '/channel/blacklist',
|
||
component: 'views/channel/blacklist/index',
|
||
icon: null,
|
||
order: 4,
|
||
hideInMenu: false,
|
||
parentRoute: '/channel',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '偏远分区',
|
||
permissionCode: 'Channel.RemoteZones',
|
||
route: '/channel/remoteZones',
|
||
component: 'views/channel/remoteZones/index',
|
||
icon: null,
|
||
order: 5,
|
||
hideInMenu: false,
|
||
parentRoute: '/channel',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '计重规则',
|
||
permissionCode: 'Channel.WeightRules',
|
||
route: '/channel/weightRules',
|
||
component: 'views/channel/weightRules/index',
|
||
icon: null,
|
||
order: 6,
|
||
hideInMenu: false,
|
||
parentRoute: '/channel',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '计重进位',
|
||
permissionCode: 'Channel.WeightRounding',
|
||
route: '/channel/weightRounding',
|
||
component: 'views/channel/weightRounding/index',
|
||
icon: null,
|
||
order: 7,
|
||
hideInMenu: false,
|
||
parentRoute: '/channel',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '自动触发',
|
||
permissionCode: 'Channel.AutoTrigger',
|
||
route: '/channel/autoTrigger',
|
||
component: 'views/channel/autoTrigger/index',
|
||
icon: null,
|
||
order: 8,
|
||
hideInMenu: false,
|
||
parentRoute: '/channel',
|
||
},
|
||
|
||
// 一级菜单 - 操作
|
||
{
|
||
level: 1,
|
||
name: '操作',
|
||
permissionCode: 'Operation',
|
||
route: '/operation',
|
||
component: null,
|
||
icon: 'icon-settings',
|
||
order: 5,
|
||
hideInMenu: false,
|
||
},
|
||
// 二级菜单 - 操作
|
||
{
|
||
level: 2,
|
||
name: '入库扫描',
|
||
permissionCode: 'Operation.InboundScan',
|
||
route: '/operation/inboundScan',
|
||
component: 'views/operation/inboundScan/index',
|
||
icon: null,
|
||
order: 1,
|
||
hideInMenu: false,
|
||
parentRoute: '/operation',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '出库扫描',
|
||
permissionCode: 'Operation.OutboundScan',
|
||
route: '/operation/outboundScan',
|
||
component: 'views/operation/outboundScan/index',
|
||
icon: null,
|
||
order: 2,
|
||
hideInMenu: false,
|
||
parentRoute: '/operation',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '打印标签',
|
||
permissionCode: 'Operation.PrintLabel',
|
||
route: '/operation/printLabel',
|
||
component: 'views/operation/printLabel/index',
|
||
icon: null,
|
||
order: 3,
|
||
hideInMenu: false,
|
||
parentRoute: '/operation',
|
||
},
|
||
|
||
// 一级菜单 - 客户
|
||
{
|
||
level: 1,
|
||
name: '客户',
|
||
permissionCode: 'User',
|
||
route: '/user',
|
||
component: null,
|
||
icon: 'icon-user',
|
||
order: 7,
|
||
hideInMenu: false,
|
||
},
|
||
// 二级菜单 - 客户
|
||
{
|
||
level: 2,
|
||
name: '资金充值',
|
||
permissionCode: 'User.Recharge',
|
||
route: '/user/recharge',
|
||
component: 'views/user/recharge/deposit',
|
||
icon: null,
|
||
order: 1,
|
||
hideInMenu: true,
|
||
parentRoute: '/user',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '充值记录',
|
||
permissionCode: 'User.RechargeRecord',
|
||
route: '/user/rechargeRecord',
|
||
component: 'views/user/recharge/rechargeRecord',
|
||
icon: null,
|
||
order: 2,
|
||
hideInMenu: false,
|
||
parentRoute: '/user',
|
||
},
|
||
|
||
// 一级菜单 - 系统
|
||
{
|
||
level: 1,
|
||
name: '系统',
|
||
permissionCode: 'Rbac',
|
||
route: '/system',
|
||
component: null,
|
||
icon: 'icon-settings',
|
||
order: 999,
|
||
hideInMenu: false,
|
||
},
|
||
// 二级菜单 - 系统
|
||
{
|
||
level: 2,
|
||
name: '用户管理',
|
||
permissionCode: 'Rbac.Users',
|
||
route: '/system/users',
|
||
component: 'views/system/organization/user/index',
|
||
icon: null,
|
||
order: 1,
|
||
hideInMenu: false,
|
||
parentRoute: '/system',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '角色管理',
|
||
permissionCode: 'Rbac.Roles',
|
||
route: '/system/roles',
|
||
component: 'views/system/permission/role/index',
|
||
icon: null,
|
||
order: 2,
|
||
hideInMenu: false,
|
||
parentRoute: '/system',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '菜单管理',
|
||
permissionCode: 'Rbac.Menus',
|
||
route: '/system/menus',
|
||
component: 'views/system/permission/menu/index',
|
||
icon: null,
|
||
order: 3,
|
||
hideInMenu: false,
|
||
parentRoute: '/system',
|
||
},
|
||
{
|
||
level: 2,
|
||
name: '组织管理',
|
||
permissionCode: 'Rbac.Organizations',
|
||
route: '/system/organizations',
|
||
component: 'views/system/organization/index',
|
||
icon: null,
|
||
order: 4,
|
||
hideInMenu: false,
|
||
parentRoute: '/system',
|
||
},
|
||
];
|
||
|
||
// 生成 CSV 内容(UTF-8 BOM 以便 Excel 正确识别中文)
|
||
function generateCSV() {
|
||
const headers = ['菜单级别', '父级菜单', '菜单名称', '权限编码', '路由路径', '组件路径', '图标', '排序', '是否隐藏'];
|
||
const rows = [headers];
|
||
|
||
menuData.forEach(menu => {
|
||
// 获取父级菜单名称
|
||
let parentName = '';
|
||
if (menu.parentRoute) {
|
||
const parentMenu = menuData.find(m => m.route === menu.parentRoute);
|
||
parentName = parentMenu ? parentMenu.name : '';
|
||
}
|
||
|
||
const row = [
|
||
menu.level === 1 ? '一级菜单' : '二级菜单',
|
||
parentName,
|
||
menu.name,
|
||
menu.permissionCode || '',
|
||
menu.route,
|
||
menu.component || '',
|
||
menu.icon || '',
|
||
menu.order !== null && menu.order !== undefined ? menu.order : '',
|
||
menu.hideInMenu ? '是' : '否',
|
||
];
|
||
rows.push(row);
|
||
});
|
||
|
||
// 添加 UTF-8 BOM 以便 Excel 正确识别中文
|
||
const csvContent = rows.map(row => row.map(cell => {
|
||
// 处理包含逗号或引号的单元格
|
||
if (typeof cell === 'string' && (cell.includes(',') || cell.includes('"') || cell.includes('\n'))) {
|
||
return `"${cell.replace(/"/g, '""')}"`;
|
||
}
|
||
return cell;
|
||
}).join(',')).join('\n');
|
||
|
||
return '\ufeff' + csvContent; // UTF-8 BOM
|
||
}
|
||
|
||
// 生成 Excel XML 格式(更兼容)
|
||
function generateExcelXML() {
|
||
const xmlHeader = `<?xml version="1.0"?>
|
||
<?mso-application progid="Excel.Sheet"?>
|
||
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
|
||
xmlns:o="urn:schemas-microsoft-com:office:office"
|
||
xmlns:x="urn:schemas-microsoft-com:office:excel"
|
||
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
|
||
xmlns:html="http://www.w3.org/TR/REC-html40">
|
||
<Worksheet ss:Name="菜单配置">
|
||
<Table>`;
|
||
|
||
const xmlRows = menuData.map((menu, index) => {
|
||
// 获取父级菜单名称
|
||
let parentName = '';
|
||
if (menu.parentRoute) {
|
||
const parentMenu = menuData.find(m => m.route === menu.parentRoute);
|
||
parentName = parentMenu ? parentMenu.name : '';
|
||
}
|
||
|
||
if (index === 0) {
|
||
// 表头
|
||
return ` <Row>
|
||
<Cell><Data ss:Type="String">菜单级别</Data></Cell>
|
||
<Cell><Data ss:Type="String">父级菜单</Data></Cell>
|
||
<Cell><Data ss:Type="String">菜单名称</Data></Cell>
|
||
<Cell><Data ss:Type="String">权限编码</Data></Cell>
|
||
<Cell><Data ss:Type="String">路由路径</Data></Cell>
|
||
<Cell><Data ss:Type="String">组件路径</Data></Cell>
|
||
<Cell><Data ss:Type="String">图标</Data></Cell>
|
||
<Cell><Data ss:Type="String">排序</Data></Cell>
|
||
<Cell><Data ss:Type="String">是否隐藏</Data></Cell>
|
||
</Row>`;
|
||
}
|
||
return ` <Row>
|
||
<Cell><Data ss:Type="String">${menu.level === 1 ? '一级菜单' : '二级菜单'}</Data></Cell>
|
||
<Cell><Data ss:Type="String">${parentName}</Data></Cell>
|
||
<Cell><Data ss:Type="String">${menu.name}</Data></Cell>
|
||
<Cell><Data ss:Type="String">${menu.permissionCode || ''}</Data></Cell>
|
||
<Cell><Data ss:Type="String">${menu.route}</Data></Cell>
|
||
<Cell><Data ss:Type="String">${menu.component || ''}</Data></Cell>
|
||
<Cell><Data ss:Type="String">${menu.icon || ''}</Data></Cell>
|
||
<Cell><Data ss:Type="Number">${menu.order !== null && menu.order !== undefined ? menu.order : ''}</Data></Cell>
|
||
<Cell><Data ss:Type="String">${menu.hideInMenu ? '是' : '否'}</Data></Cell>
|
||
</Row>`;
|
||
}).join('\n');
|
||
|
||
const xmlFooter = ` </Table>
|
||
</Worksheet>
|
||
</Workbook>`;
|
||
|
||
return xmlHeader + '\n' + xmlRows + '\n' + xmlFooter;
|
||
}
|
||
|
||
// 生成文件
|
||
const csvContent = generateCSV();
|
||
const xmlContent = generateExcelXML();
|
||
|
||
// 写入 CSV 文件(带 BOM)
|
||
fs.writeFileSync('菜单配置.csv', csvContent, 'utf-8');
|
||
console.log('✅ CSV 文件已生成: 菜单配置.csv (可直接用 Excel 打开)');
|
||
|
||
// 写入 Excel XML 文件(先写入临时文件,然后重命名)
|
||
const tempFileName = '菜单配置_temp.xls';
|
||
const finalFileName = '菜单配置.xls';
|
||
try {
|
||
// 先删除旧文件(如果存在且未被锁定)
|
||
if (fs.existsSync(finalFileName)) {
|
||
try {
|
||
fs.unlinkSync(finalFileName);
|
||
} catch (e) {
|
||
console.log('⚠️ 无法删除旧文件,可能正在被 Excel 打开,请先关闭 Excel 文件');
|
||
}
|
||
}
|
||
// 写入新文件
|
||
fs.writeFileSync(tempFileName, xmlContent, 'utf-8');
|
||
// 重命名为最终文件名
|
||
if (fs.existsSync(finalFileName)) {
|
||
fs.unlinkSync(finalFileName);
|
||
}
|
||
fs.renameSync(tempFileName, finalFileName);
|
||
console.log('✅ Excel XML 文件已生成: 菜单配置.xls');
|
||
} catch (error) {
|
||
if (error.code === 'EBUSY') {
|
||
console.log('⚠️ Excel 文件正在被使用,已生成临时文件: ' + tempFileName);
|
||
console.log(' 请关闭 Excel 后,将临时文件重命名为: 菜单配置.xls');
|
||
} else {
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
console.log(`\n共生成 ${menuData.length} 条菜单记录`);
|