|
|
const { resolve } = require('path') |
|
|
const { readFileSync, existsSync, readdirSync } = require('fs') |
|
|
const fs = require('fs') |
|
|
const parser = require('@babel/parser') |
|
|
const traverse = require('@babel/traverse').default |
|
|
const { parse } = require('@vue/compiler-sfc') |
|
|
|
|
|
// 第一阶段路由映射插件 - 收集直接映射关系 |
|
|
function routeMappingPlugin() { |
|
|
return { |
|
|
name: 'route-mapping-phase1', |
|
|
|
|
|
// 防重复生成机制 |
|
|
_lastGenerationTime: 0, |
|
|
_generationInProgress: false, |
|
|
_generationCooldown: 5000, // 5秒冷却时间 |
|
|
|
|
|
// Webpack插件接口 |
|
|
apply(compiler) { |
|
|
const self = this |
|
|
|
|
|
// 在编译开始前就生成映射文件 |
|
|
compiler.hooks.beforeCompile.tapAsync('RouteMappingPlugin', (params, callback) => { |
|
|
if (self._shouldSkipGeneration()) { |
|
|
console.log('⏭️ 跳过路由映射生成(冷却期内)') |
|
|
callback() |
|
|
return |
|
|
} |
|
|
|
|
|
console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') |
|
|
try { |
|
|
self.collectDirectMappings() |
|
|
callback() |
|
|
} catch (error) { |
|
|
console.error('❌ 路由映射插件执行失败:', error) |
|
|
callback(error) |
|
|
} |
|
|
}) |
|
|
|
|
|
// 备用钩子,确保在beforeRun时也执行 |
|
|
compiler.hooks.beforeRun.tapAsync('RouteMappingPlugin', (compilation, callback) => { |
|
|
if (self._shouldSkipGeneration()) { |
|
|
console.log('⏭️ 跳过路由映射生成(冷却期内)') |
|
|
callback() |
|
|
return |
|
|
} |
|
|
|
|
|
console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') |
|
|
try { |
|
|
self.collectDirectMappings() |
|
|
callback() |
|
|
} catch (error) { |
|
|
console.error('❌ 路由映射插件执行失败:', error) |
|
|
callback(error) |
|
|
} |
|
|
}) |
|
|
}, |
|
|
|
|
|
// Vite插件接口 |
|
|
buildStart() { |
|
|
console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') |
|
|
this.collectDirectMappings() |
|
|
}, |
|
|
|
|
|
// 在开发模式下也执行 |
|
|
configureServer(server) { |
|
|
if (this._shouldSkipGeneration()) { |
|
|
console.log('⏭️ 跳过路由映射生成(冷却期内)') |
|
|
return |
|
|
} |
|
|
console.log('🔧 开发模式下收集直接映射关系...') |
|
|
this.collectDirectMappings() |
|
|
}, |
|
|
|
|
|
// 检查是否应该跳过生成 |
|
|
_shouldSkipGeneration() { |
|
|
const now = Date.now() |
|
|
|
|
|
// 如果正在生成中,跳过 |
|
|
if (this._generationInProgress) { |
|
|
return true |
|
|
} |
|
|
|
|
|
// 如果在冷却期内,跳过 |
|
|
if (now - this._lastGenerationTime < this._generationCooldown) { |
|
|
return true |
|
|
} |
|
|
|
|
|
return false |
|
|
}, |
|
|
|
|
|
// 检查是否需要重新生成文件 |
|
|
_shouldRegenerateFile() { |
|
|
const outputPath = resolve(__dirname, '../src/renderer/modules/route-sync/direct-route-mappings.js') |
|
|
|
|
|
// 如果文件不存在,需要生成 |
|
|
if (!existsSync(outputPath)) { |
|
|
return true |
|
|
} |
|
|
|
|
|
// 检查源文件是否比生成文件更新 |
|
|
const sourceFiles = this._getSourceFiles() |
|
|
const outputStats = fs.statSync(outputPath) |
|
|
|
|
|
for (const sourceFile of sourceFiles) { |
|
|
if (existsSync(sourceFile)) { |
|
|
const sourceStats = fs.statSync(sourceFile) |
|
|
if (sourceStats.mtime > outputStats.mtime) { |
|
|
return true |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return false |
|
|
}, |
|
|
|
|
|
// 获取需要监控的源文件列表 |
|
|
_getSourceFiles() { |
|
|
const sourceFiles = [] |
|
|
|
|
|
// 路由配置文件 |
|
|
sourceFiles.push(resolve(__dirname, '../src/renderer/router/index.js')) |
|
|
|
|
|
// 模块配置文件 |
|
|
sourceFiles.push(resolve(__dirname, '../src/renderer/modules/config.js')) |
|
|
|
|
|
// 页面组件文件 |
|
|
const moduleDirs = this.readModuleNamesFromConfig() |
|
|
moduleDirs.forEach(moduleName => { |
|
|
// 页面组件 |
|
|
const viewsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/views`) |
|
|
if (existsSync(viewsPath)) { |
|
|
const files = readdirSync(viewsPath).filter(f => f.endsWith('.vue')) |
|
|
files.forEach(file => { |
|
|
sourceFiles.push(resolve(viewsPath, file)) |
|
|
}) |
|
|
} |
|
|
|
|
|
// 弹窗组件 |
|
|
const componentsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/components`) |
|
|
if (existsSync(componentsPath)) { |
|
|
const files = readdirSync(componentsPath).filter(f => |
|
|
f.endsWith('.vue') && (f.toLowerCase().includes('modal') || f.toLowerCase().includes('dialog')) |
|
|
) |
|
|
files.forEach(file => { |
|
|
sourceFiles.push(resolve(componentsPath, file)) |
|
|
}) |
|
|
} |
|
|
}) |
|
|
|
|
|
return sourceFiles |
|
|
}, |
|
|
|
|
|
// 收集直接映射关系 |
|
|
collectDirectMappings() { |
|
|
// 设置生成中标志 |
|
|
this._generationInProgress = true |
|
|
|
|
|
try { |
|
|
// 检查文件是否需要重新生成 |
|
|
if (this._shouldRegenerateFile()) { |
|
|
console.log('🔄 检测到需要重新生成路由映射文件') |
|
|
|
|
|
// 1. 分析路由配置 |
|
|
const routes = this.analyzeRoutes() |
|
|
|
|
|
// 2. 分析页面组件(第一层) |
|
|
const pageMappings = this.analyzePageComponents(routes) |
|
|
|
|
|
// 3. 分析弹窗组件(第二层) |
|
|
const modalMappings = this.analyzeModalComponents() |
|
|
|
|
|
// 4. 生成映射文件 |
|
|
this.generateMappingFile(pageMappings, modalMappings) |
|
|
|
|
|
console.log('✅ 第一阶段直接映射关系收集完成') |
|
|
} else { |
|
|
console.log('⏭️ 路由映射文件无需重新生成') |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('❌ 直接映射关系收集失败:', error) |
|
|
throw error |
|
|
} finally { |
|
|
// 重置生成中标志并更新时间戳 |
|
|
this._generationInProgress = false |
|
|
this._lastGenerationTime = Date.now() |
|
|
} |
|
|
}, |
|
|
|
|
|
// 分析路由配置 |
|
|
analyzeRoutes() { |
|
|
const routerPath = resolve(__dirname, '../src/renderer/router/index.js') |
|
|
if (!existsSync(routerPath)) { |
|
|
throw new Error('Router file not found') |
|
|
} |
|
|
|
|
|
const routerContent = readFileSync(routerPath, 'utf-8') |
|
|
const ast = parser.parse(routerContent, { |
|
|
sourceType: 'module', |
|
|
plugins: ['jsx'] |
|
|
}) |
|
|
|
|
|
const routes = [] |
|
|
|
|
|
const self = this |
|
|
traverse(ast, { |
|
|
ObjectExpression(path) { |
|
|
const properties = path.node.properties |
|
|
let route = {} |
|
|
|
|
|
properties.forEach(prop => { |
|
|
if (prop.key && prop.key.name === 'path' && prop.value && prop.value.value) { |
|
|
route.path = prop.value.value |
|
|
} |
|
|
if (prop.key && prop.key.name === 'name' && prop.value && prop.value.value) { |
|
|
route.name = prop.value.value |
|
|
} |
|
|
if (prop.key && prop.key.name === 'component' && prop.value) { |
|
|
route.component = self.extractComponentName(prop.value) |
|
|
} |
|
|
}) |
|
|
|
|
|
if (route.path) { |
|
|
route.module = self.extractModuleFromPath(route.path) |
|
|
routes.push(route) |
|
|
} |
|
|
} |
|
|
}) |
|
|
|
|
|
return routes |
|
|
}, |
|
|
|
|
|
// 分析页面组件(第一层) |
|
|
analyzePageComponents(routes) { |
|
|
const pageMappings = [] |
|
|
|
|
|
routes.forEach(route => { |
|
|
if (route.component) { |
|
|
const componentAnalysis = this.analyzeComponent(route.component, route.path) |
|
|
if (componentAnalysis && componentAnalysis.apiCalls.length > 0) { |
|
|
pageMappings.push({ |
|
|
route: route.path, |
|
|
routeName: route.name, |
|
|
component: route.component, |
|
|
module: route.module, |
|
|
layer: 'page', |
|
|
apiCalls: componentAnalysis.apiCalls, |
|
|
methods: componentAnalysis.methods |
|
|
}) |
|
|
} |
|
|
} |
|
|
}) |
|
|
|
|
|
return pageMappings |
|
|
}, |
|
|
|
|
|
// 分析弹窗组件(第二层) |
|
|
analyzeModalComponents() { |
|
|
const modalMappings = [] |
|
|
const modalFiles = this.findModalFiles() |
|
|
|
|
|
modalFiles.forEach(modalFile => { |
|
|
const analysis = this.analyzeComponent(modalFile.name, modalFile.path) |
|
|
if (analysis && analysis.apiCalls.length > 0) { |
|
|
modalMappings.push({ |
|
|
component: modalFile.name, |
|
|
path: modalFile.path, |
|
|
module: modalFile.module, |
|
|
layer: 'modal', |
|
|
apiCalls: analysis.apiCalls, |
|
|
methods: analysis.methods |
|
|
}) |
|
|
} |
|
|
}) |
|
|
|
|
|
return modalMappings |
|
|
}, |
|
|
|
|
|
// 分析单个组件 |
|
|
analyzeComponent(componentName, componentPath) { |
|
|
try { |
|
|
let filePath = componentPath |
|
|
|
|
|
// 如果是组件名,需要找到对应的文件 |
|
|
if (!filePath || !existsSync(filePath)) { |
|
|
filePath = this.findComponentFile(componentName) |
|
|
} |
|
|
|
|
|
if (!filePath || !existsSync(filePath)) { |
|
|
console.warn(`⚠️ 组件文件未找到: ${componentName}`) |
|
|
return null |
|
|
} |
|
|
|
|
|
const content = readFileSync(filePath, 'utf-8') |
|
|
|
|
|
if (filePath.endsWith('.vue')) { |
|
|
return this.analyzeVueComponent(content, componentName) |
|
|
} else { |
|
|
return this.analyzeJavaScriptComponent(content, componentName) |
|
|
} |
|
|
} catch (error) { |
|
|
console.warn(`⚠️ 分析组件失败: ${componentName}`, error.message) |
|
|
return null |
|
|
} |
|
|
}, |
|
|
|
|
|
// 分析Vue组件 |
|
|
analyzeVueComponent(content, componentName) { |
|
|
const { descriptor } = parse(content) |
|
|
const scriptContent = descriptor.script?.content || '' |
|
|
|
|
|
if (!scriptContent) { |
|
|
return { apiCalls: [], methods: [] } |
|
|
} |
|
|
|
|
|
const ast = parser.parse(scriptContent, { |
|
|
sourceType: 'module', |
|
|
plugins: ['jsx'] |
|
|
}) |
|
|
|
|
|
return this.extractApiCallsFromAST(ast, componentName) |
|
|
}, |
|
|
|
|
|
// 分析JavaScript组件 |
|
|
analyzeJavaScriptComponent(content, componentName) { |
|
|
const ast = parser.parse(content, { |
|
|
sourceType: 'module', |
|
|
plugins: ['jsx'] |
|
|
}) |
|
|
|
|
|
return this.extractApiCallsFromAST(ast, componentName) |
|
|
}, |
|
|
|
|
|
// 从AST中提取API调用 |
|
|
extractApiCallsFromAST(ast, componentName) { |
|
|
const apiCalls = [] |
|
|
const methods = [] |
|
|
|
|
|
const self = this |
|
|
traverse(ast, { |
|
|
// 查找方法定义 |
|
|
FunctionDeclaration(path) { |
|
|
if (path.node.id) { |
|
|
methods.push(path.node.id.name) |
|
|
} |
|
|
}, |
|
|
|
|
|
// 查找箭头函数 |
|
|
ArrowFunctionExpression(path) { |
|
|
if (path.parent && path.parent.type === 'VariableDeclarator' && path.parent.id) { |
|
|
methods.push(path.parent.id.name) |
|
|
} |
|
|
}, |
|
|
|
|
|
// 查找API调用 |
|
|
CallExpression(path) { |
|
|
const callInfo = self.extractApiCall(path) |
|
|
if (callInfo) { |
|
|
apiCalls.push(callInfo) |
|
|
} |
|
|
} |
|
|
}) |
|
|
|
|
|
return { apiCalls, methods } |
|
|
}, |
|
|
|
|
|
// 提取API调用信息 |
|
|
extractApiCall(path) { |
|
|
const node = path.node |
|
|
const self = this |
|
|
|
|
|
// 检查是否是服务调用 (service.method()) |
|
|
if (node.callee && node.callee.type === 'MemberExpression') { |
|
|
const object = node.callee.object |
|
|
const property = node.callee.property |
|
|
|
|
|
if (object && property && object.name && property.name) { |
|
|
// 检查是否是服务调用 |
|
|
if (object.name.endsWith('Service') || object.name === 'api' || object.name === 'axios') { |
|
|
return { |
|
|
type: 'service', |
|
|
service: object.name, |
|
|
method: property.name, |
|
|
arguments: node.arguments.map(arg => self.extractArgumentValue(arg)), |
|
|
line: node.loc?.start?.line || 0 |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// 检查是否是直接的API调用 (api.get, api.post等) |
|
|
if (node.callee && node.callee.type === 'MemberExpression') { |
|
|
const object = node.callee.object |
|
|
const property = node.callee.property |
|
|
|
|
|
if (object && property) { |
|
|
if (object.name === 'api' || object.name === 'axios' || object.name === 'http') { |
|
|
const method = property.name |
|
|
const args = node.arguments |
|
|
|
|
|
if (args.length > 0 && args[0].type === 'StringLiteral') { |
|
|
return { |
|
|
type: 'api', |
|
|
method: method.toUpperCase(), |
|
|
path: args[0].value, |
|
|
arguments: args.slice(1).map(arg => self.extractArgumentValue(arg)), |
|
|
line: node.loc?.start?.line || 0 |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return null |
|
|
}, |
|
|
|
|
|
// 从模块配置中读取模块名称 |
|
|
readModuleNamesFromConfig() { |
|
|
try { |
|
|
const configPath = resolve(__dirname, '../src/renderer/modules/config.js') |
|
|
if (!existsSync(configPath)) { |
|
|
return [] |
|
|
} |
|
|
|
|
|
const code = readFileSync(configPath, 'utf-8') |
|
|
const ast = parser.parse(code, { sourceType: 'module' }) |
|
|
const modules = [] |
|
|
|
|
|
traverse(ast, { |
|
|
ExportNamedDeclaration(path) { |
|
|
const decl = path.node.declaration |
|
|
if (decl && decl.type === 'VariableDeclaration') { |
|
|
decl.declarations.forEach(d => { |
|
|
if ( |
|
|
d.id && d.id.name === 'MODULE_CONFIG' && |
|
|
d.init && d.init.type === 'ObjectExpression' |
|
|
) { |
|
|
d.init.properties.forEach(prop => { |
|
|
if (prop.type === 'ObjectProperty') { |
|
|
if (prop.key.type === 'Identifier') { |
|
|
modules.push(prop.key.name) |
|
|
} else if (prop.key.type === 'StringLiteral') { |
|
|
modules.push(prop.key.value) |
|
|
} |
|
|
} |
|
|
}) |
|
|
} |
|
|
}) |
|
|
} |
|
|
} |
|
|
}) |
|
|
|
|
|
return modules |
|
|
} catch (e) { |
|
|
console.warn('⚠️ 读取 MODULE_CONFIG 失败,使用默认模块集合: ', e.message) |
|
|
return [] |
|
|
} |
|
|
}, |
|
|
|
|
|
// 查找弹窗文件 |
|
|
findModalFiles() { |
|
|
const modalFiles = [] |
|
|
const moduleDirs = this.readModuleNamesFromConfig() |
|
|
|
|
|
moduleDirs.forEach(moduleName => { |
|
|
const componentsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/components`) |
|
|
|
|
|
if (existsSync(componentsPath)) { |
|
|
const files = readdirSync(componentsPath).filter(f => |
|
|
f.endsWith('.vue') && (f.toLowerCase().includes('modal') || f.toLowerCase().includes('dialog')) |
|
|
) |
|
|
files.forEach(file => { |
|
|
modalFiles.push({ |
|
|
name: file.replace('.vue', ''), |
|
|
path: resolve(componentsPath, file), |
|
|
module: moduleName |
|
|
}) |
|
|
}) |
|
|
} |
|
|
}) |
|
|
|
|
|
return modalFiles |
|
|
}, |
|
|
|
|
|
// 查找组件文件 |
|
|
findComponentFile(componentName) { |
|
|
const moduleDirs = this.readModuleNamesFromConfig() |
|
|
|
|
|
for (const moduleName of moduleDirs) { |
|
|
// 查找views目录 |
|
|
const viewsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/views`) |
|
|
if (existsSync(viewsPath)) { |
|
|
const files = readdirSync(viewsPath).filter(f => f.endsWith('.vue')) |
|
|
const matchingFile = files.find(f => f.replace('.vue', '') === componentName) |
|
|
if (matchingFile) { |
|
|
return resolve(viewsPath, matchingFile) |
|
|
} |
|
|
} |
|
|
|
|
|
// 查找components目录 |
|
|
const componentsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/components`) |
|
|
if (existsSync(componentsPath)) { |
|
|
const files = readdirSync(componentsPath).filter(f => f.endsWith('.vue')) |
|
|
const matchingFile = files.find(f => f.replace('.vue', '') === componentName) |
|
|
if (matchingFile) { |
|
|
return resolve(componentsPath, matchingFile) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return null |
|
|
}, |
|
|
|
|
|
// 生成映射文件 |
|
|
generateMappingFile(pageMappings, modalMappings) { |
|
|
const outputPath = resolve(__dirname, '../src/renderer/modules/route-sync/direct-route-mappings.js') |
|
|
|
|
|
// 检查文件内容是否真的需要更新 |
|
|
if (this._isFileContentSame(pageMappings, modalMappings, outputPath)) { |
|
|
console.log('⏭️ 路由映射文件内容未变化,跳过写入') |
|
|
return |
|
|
} |
|
|
|
|
|
const mappingContent = `// 第一阶段:直接路由-API映射关系 |
|
|
// 此文件由 route-mapping-plugin 在构建时生成 |
|
|
// 收集页面和弹窗组件的直接API调用关系 |
|
|
// 请勿手动修改 |
|
|
|
|
|
export const directRouteMappings = { |
|
|
// 第一层:页面组件的数据操作API |
|
|
pageMappings: ${JSON.stringify(pageMappings, null, 2)}, |
|
|
|
|
|
// 第二层:弹窗组件的数据操作API |
|
|
modalMappings: ${JSON.stringify(modalMappings, null, 2)}, |
|
|
|
|
|
// 分析信息 |
|
|
analysisInfo: { |
|
|
phase: 'phase1', |
|
|
description: '第一阶段:收集直接映射关系', |
|
|
timestamp: new Date().toISOString(), |
|
|
pageCount: ${pageMappings.length}, |
|
|
modalCount: ${modalMappings.length}, |
|
|
totalApiCalls: ${pageMappings.reduce((sum, p) => sum + p.apiCalls.length, 0) + modalMappings.reduce((sum, m) => sum + m.apiCalls.length, 0)} |
|
|
} |
|
|
} |
|
|
|
|
|
// 按模块分组的映射 |
|
|
export const moduleMappings = ${JSON.stringify(this.groupMappingsByModule(pageMappings, modalMappings), null, 2)} |
|
|
|
|
|
// 按API路径分组的映射 |
|
|
export const apiPathMappings = ${JSON.stringify(this.groupMappingsByApiPath(pageMappings, modalMappings), null, 2)} |
|
|
|
|
|
export default directRouteMappings |
|
|
` |
|
|
|
|
|
fs.writeFileSync(outputPath, mappingContent, 'utf-8') |
|
|
|
|
|
console.log(`✅ 直接映射关系文件已生成: ${outputPath}`) |
|
|
console.log(`📊 统计信息:`) |
|
|
console.log(` - 页面组件: ${pageMappings.length} 个`) |
|
|
console.log(` - 弹窗组件: ${modalMappings.length} 个`) |
|
|
console.log(` - 总API调用: ${pageMappings.reduce((sum, p) => sum + p.apiCalls.length, 0) + modalMappings.reduce((sum, m) => sum + m.apiCalls.length, 0)} 个`) |
|
|
}, |
|
|
|
|
|
// 检查文件内容是否相同 |
|
|
_isFileContentSame(pageMappings, modalMappings, outputPath) { |
|
|
if (!existsSync(outputPath)) { |
|
|
return false |
|
|
} |
|
|
|
|
|
try { |
|
|
const currentContent = fs.readFileSync(outputPath, 'utf-8') |
|
|
|
|
|
// 提取当前文件中的映射数据 |
|
|
const currentDataMatch = currentContent.match(/export const directRouteMappings = ({[\s\S]*?});/) |
|
|
if (!currentDataMatch) { |
|
|
return false |
|
|
} |
|
|
|
|
|
// 解析当前数据 |
|
|
const currentData = JSON.parse(currentDataMatch[1]) |
|
|
|
|
|
// 比较数据 |
|
|
const newData = { |
|
|
pageMappings, |
|
|
modalMappings, |
|
|
analysisInfo: { |
|
|
phase: 'phase1', |
|
|
description: '第一阶段:收集直接映射关系', |
|
|
timestamp: new Date().toISOString(), |
|
|
pageCount: pageMappings.length, |
|
|
modalCount: modalMappings.length, |
|
|
totalApiCalls: pageMappings.reduce((sum, p) => sum + p.apiCalls.length, 0) + modalMappings.reduce((sum, m) => sum + m.apiCalls.length, 0) |
|
|
} |
|
|
} |
|
|
|
|
|
// 忽略时间戳差异进行比较 |
|
|
const currentDataForCompare = { ...currentData } |
|
|
const newDataForCompare = { ...newData } |
|
|
delete currentDataForCompare.analysisInfo.timestamp |
|
|
delete newDataForCompare.analysisInfo.timestamp |
|
|
|
|
|
return JSON.stringify(currentDataForCompare) === JSON.stringify(newDataForCompare) |
|
|
} catch (error) { |
|
|
console.warn('⚠️ 比较文件内容时出错:', error.message) |
|
|
return false |
|
|
} |
|
|
}, |
|
|
|
|
|
// 按模块分组映射 |
|
|
groupMappingsByModule(pageMappings, modalMappings) { |
|
|
const grouped = {} |
|
|
|
|
|
// 分组页面映射 |
|
|
pageMappings.forEach(mapping => { |
|
|
const module = mapping.module || 'unknown' |
|
|
if (!grouped[module]) { |
|
|
grouped[module] = { pages: [], modals: [] } |
|
|
} |
|
|
grouped[module].pages.push(mapping) |
|
|
}) |
|
|
|
|
|
// 分组弹窗映射 |
|
|
modalMappings.forEach(mapping => { |
|
|
const module = mapping.module || 'unknown' |
|
|
if (!grouped[module]) { |
|
|
grouped[module] = { pages: [], modals: [] } |
|
|
} |
|
|
grouped[module].modals.push(mapping) |
|
|
}) |
|
|
|
|
|
return grouped |
|
|
}, |
|
|
|
|
|
// 按API路径分组映射 |
|
|
groupMappingsByApiPath(pageMappings, modalMappings) { |
|
|
const grouped = {} |
|
|
|
|
|
const addToGroup = (mapping, layer) => { |
|
|
mapping.apiCalls.forEach(apiCall => { |
|
|
let key = '' |
|
|
if (apiCall.type === 'api') { |
|
|
key = `${apiCall.method} ${apiCall.path}` |
|
|
} else if (apiCall.type === 'service') { |
|
|
key = `${apiCall.service}.${apiCall.method}` |
|
|
} |
|
|
|
|
|
if (key) { |
|
|
if (!grouped[key]) { |
|
|
grouped[key] = { pages: [], modals: [] } |
|
|
} |
|
|
grouped[key][layer].push({ |
|
|
component: mapping.component, |
|
|
module: mapping.module, |
|
|
route: mapping.route || mapping.path, |
|
|
apiCall: apiCall |
|
|
}) |
|
|
} |
|
|
}) |
|
|
} |
|
|
|
|
|
pageMappings.forEach(mapping => addToGroup(mapping, 'pages')) |
|
|
modalMappings.forEach(mapping => addToGroup(mapping, 'modals')) |
|
|
|
|
|
return grouped |
|
|
}, |
|
|
|
|
|
// 提取组件名称 |
|
|
extractComponentName(node) { |
|
|
if (node.type === 'Identifier') { |
|
|
return node.name |
|
|
} else if (node.type === 'StringLiteral') { |
|
|
return node.value |
|
|
} |
|
|
return null |
|
|
}, |
|
|
|
|
|
// 提取参数值 |
|
|
extractArgumentValue(node) { |
|
|
if (node.type === 'StringLiteral') { |
|
|
return node.value |
|
|
} else if (node.type === 'NumericLiteral') { |
|
|
return node.value |
|
|
} else if (node.type === 'Identifier') { |
|
|
return node.name |
|
|
} |
|
|
return null |
|
|
}, |
|
|
|
|
|
// 从路径提取模块名 |
|
|
extractModuleFromPath(path) { |
|
|
if (path === '/' || path === '') return 'home' |
|
|
|
|
|
const segments = path.split('/').filter(Boolean) |
|
|
if (segments.length === 0) return 'home' |
|
|
|
|
|
return segments[0] |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
module.exports = { routeMappingPlugin }
|
|
|
|