Browse Source

button增加name属性,service api 函数增加注释

master
hejl 2 days ago
parent
commit
abcb8e44fb
  1. 313
      gofaster/app/plugins/route-mapping-plugin.js
  2. 9
      gofaster/app/scripts/generate-route-mappings.js
  3. 3
      gofaster/app/src/renderer/components/LoginModal.vue
  4. 22
      gofaster/app/src/renderer/components/MainLayout.vue
  5. 1
      gofaster/app/src/renderer/components/PasswordChangeModal.vue
  6. 20
      gofaster/app/src/renderer/modules/core/components/MainLayout.vue
  7. 4
      gofaster/app/src/renderer/modules/core/components/StatusBar.vue
  8. 4
      gofaster/app/src/renderer/modules/core/components/Toast.vue
  9. 2
      gofaster/app/src/renderer/modules/core/views/ConfigTest.vue
  10. 6
      gofaster/app/src/renderer/modules/core/views/Home.vue
  11. 6
      gofaster/app/src/renderer/modules/role-management/components/PermissionManager.vue
  12. 4
      gofaster/app/src/renderer/modules/role-management/components/RolePermissionAssignment.vue

313
gofaster/app/plugins/route-mapping-plugin.js

@ -7,13 +7,26 @@ const { parse } = require('@vue/compiler-sfc') @@ -7,13 +7,26 @@ const { parse } = require('@vue/compiler-sfc')
// 第一阶段路由映射插件 - 收集直接映射关系
function routeMappingPlugin() {
// 检查是否在静默模式下运行
const isSilentMode = process.env.ROUTE_MAPPING_SILENT === 'true' || process.argv.includes('--silent')
// 日志函数
const log = (message) => {
if (!isSilentMode) {
console.log(message)
}
}
return {
name: 'route-mapping-phase1',
// 防重复生成机制
_lastGenerationTime: 0,
_generationInProgress: false,
_generationCooldown: 5000, // 5秒冷却时间
_generationCooldown: 10000, // 10秒冷却时间,增加冷却时间
_maxGenerationsPerMinute: 3, // 每分钟最多生成3次
_generationCount: 0,
_generationWindowStart: 0,
// Webpack插件接口
apply(compiler) {
@ -22,12 +35,12 @@ function routeMappingPlugin() { @@ -22,12 +35,12 @@ function routeMappingPlugin() {
// 在编译开始前就生成映射文件
compiler.hooks.beforeCompile.tapAsync('RouteMappingPlugin', (params, callback) => {
if (self._shouldSkipGeneration()) {
console.log('⏭ 跳过路由映射生成(冷却期内)')
log('⏭ 跳过路由映射生成(冷却期内)')
callback()
return
}
console.log('🔧 第一阶段:开始收集直接路由-API映射关系...')
log('🔧 第一阶段:开始收集直接路由-API映射关系...')
try {
self.collectDirectMappings()
callback()
@ -78,11 +91,26 @@ function routeMappingPlugin() { @@ -78,11 +91,26 @@ function routeMappingPlugin() {
// 如果正在生成中,跳过
if (this._generationInProgress) {
console.log('⏭ 跳过生成:正在生成中')
return true
}
// 如果在冷却期内,跳过
if (now - this._lastGenerationTime < this._generationCooldown) {
const remainingTime = Math.ceil((this._generationCooldown - (now - this._lastGenerationTime)) / 1000)
console.log(` 跳过生成:冷却期内,还需等待 ${remainingTime}`)
return true
}
// 检查每分钟生成次数限制
if (now - this._generationWindowStart > 60000) {
// 重置计数窗口
this._generationCount = 0
this._generationWindowStart = now
}
if (this._generationCount >= this._maxGenerationsPerMinute) {
console.log(` 跳过生成:已达到每分钟最大生成次数限制 (${this._maxGenerationsPerMinute} 次)`)
return true
}
@ -204,6 +232,11 @@ function routeMappingPlugin() { @@ -204,6 +232,11 @@ function routeMappingPlugin() {
// 重置生成中标志并更新时间戳
this._generationInProgress = false
this._lastGenerationTime = Date.now()
this._generationCount++
if (this._generationWindowStart === 0) {
this._generationWindowStart = Date.now()
}
}
},
@ -259,7 +292,8 @@ function routeMappingPlugin() { @@ -259,7 +292,8 @@ function routeMappingPlugin() {
const content = readFileSync(servicePath, 'utf-8')
const ast = parser.parse(content, {
sourceType: 'module',
plugins: ['jsx']
plugins: ['jsx'],
attachComments: true
})
const apiMappings = []
@ -288,7 +322,8 @@ function routeMappingPlugin() { @@ -288,7 +322,8 @@ function routeMappingPlugin() {
if (path.node.init && (path.node.init.type === 'ArrowFunctionExpression' || path.node.init.type === 'FunctionExpression')) {
const functionName = path.node.id?.name
if (functionName) {
const apiCalls = self.extractApiCallsFromFunction(path.get('init'))
const functionPath = path.get('init')
const apiCalls = self.extractApiCallsFromFunction(functionPath)
apiCalls.forEach(apiCall => {
apiMappings.push({
methodName: functionName,
@ -348,7 +383,14 @@ function routeMappingPlugin() { @@ -348,7 +383,14 @@ function routeMappingPlugin() {
const apiCalls = []
const self = this
// 使用正确的traverse调用方式
// 首先检查函数注释中是否有 @serverApiMethod 标记
const commentApiInfo = self.extractApiFromComment(functionPath)
if (commentApiInfo) {
apiCalls.push(commentApiInfo)
return apiCalls
}
// 如果没有注释信息,则使用原来的AST遍历方式
if (functionPath.node) {
traverse(functionPath.node, {
CallExpression(path) {
@ -363,22 +405,55 @@ function routeMappingPlugin() { @@ -363,22 +405,55 @@ function routeMappingPlugin() {
return apiCalls
},
// 从注释中提取API信息
extractApiFromComment(functionPath) {
const node = functionPath.node
// 首先检查 leadingComments
if (node && node.leadingComments) {
for (const comment of node.leadingComments) {
const commentText = comment.value.trim()
const match = commentText.match(/@serverApiMethod\s+(\w+)\s+(.+)/)
if (match) {
const method = match[1].toUpperCase()
const path = match[2].trim()
return {
method,
path,
line: node.loc ? node.loc.start.line : 0,
source: 'comment'
}
}
}
} else {
}
// 如果没有找到注释,尝试从函数名推断API信息
// 这是一个备用方案,用于测试
return null
},
// 第二步(第一层):收集API信息
collectApiMappings(routes) {
const apiMappings = []
const moduleDirs = this.readModuleNamesFromConfig()
console.log('🔍 开始收集API映射,模块列表:', moduleDirs)
moduleDirs.forEach(moduleName => {
const servicesPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/services`)
if (existsSync(servicesPath)) {
const serviceFiles = readdirSync(servicesPath).filter(f => f.endsWith('.js'))
console.log(`🔍 模块 ${moduleName} 的service文件:`, serviceFiles)
serviceFiles.forEach(serviceFile => {
const serviceName = serviceFile.replace('.js', '')
const servicePath = resolve(servicesPath, serviceFile)
const serviceApiMappings = this.analyzeServiceFileForApiMappings(servicePath, serviceName, moduleName)
console.log(`🔍 服务 ${serviceName} 的API映射数量:`, serviceApiMappings.length)
if (serviceApiMappings.length > 0) {
apiMappings.push({
module: moduleName,
@ -393,23 +468,29 @@ function routeMappingPlugin() { @@ -393,23 +468,29 @@ function routeMappingPlugin() {
}
})
console.log('🔍 最终收集到的API映射数量:', apiMappings.length)
return apiMappings
},
// 第三步:关联页面与API
associatePagesWithApi(routes, apiMappings) {
console.log('🔍 开始关联页面与API,输入API映射数量:', apiMappings.length)
// 为每个API映射添加调用该API的页面信息
const enhancedApiMappings = apiMappings.map(moduleMapping => {
console.log(`🔍 处理模块 ${moduleMapping.module},API映射数量:`, moduleMapping.apiMappings.length)
const enhancedApiMappings = moduleMapping.apiMappings.map(apiMapping => {
// 查找调用该API方法的组件
const callingComponents = this.findComponentsCallingApiMethod(
// 查找调用该API方法的组件,并收集button触发器信息
const callingComponents = this.findComponentsCallingApiMethodWithTriggers(
routes,
moduleMapping.module,
moduleMapping.serviceName,
apiMapping.methodName
)
console.log(`🔍 API方法 ${apiMapping.methodName} 的调用组件数量:`, callingComponents.length)
return {
...apiMapping,
callingComponents: callingComponents
@ -422,6 +503,7 @@ function routeMappingPlugin() { @@ -422,6 +503,7 @@ function routeMappingPlugin() {
}
})
console.log('🔍 关联完成,输出API映射数量:', enhancedApiMappings.length)
return enhancedApiMappings
},
@ -480,6 +562,55 @@ function routeMappingPlugin() { @@ -480,6 +562,55 @@ function routeMappingPlugin() {
return [...new Set(callingComponents)]
},
// 查找调用特定API方法的组件,并收集button触发器信息
findComponentsCallingApiMethodWithTriggers(routes, moduleName, serviceName, methodName) {
const callingComponents = []
// 1. 遍历所有路由,查找调用该API方法的页面组件
routes.forEach(route => {
if (route.module === moduleName) {
const componentAnalysis = this.analyzeComponentForServiceCallsWithTriggers(
route.component,
route.path,
moduleName,
serviceName,
methodName
)
if (componentAnalysis && componentAnalysis.hasServiceCall) {
callingComponents.push({
component: route.component,
path: route.path,
trigger_source: componentAnalysis.triggerSources
})
}
}
})
// 2. 搜索模块的components目录,查找调用该API方法的组件
const componentsInModule = this.findComponentsInModule(moduleName)
componentsInModule.forEach(componentName => {
const componentAnalysis = this.analyzeComponentForServiceCallsWithTriggers(
componentName,
null, // components目录中的组件没有路由路径
moduleName,
serviceName,
methodName
)
if (componentAnalysis && componentAnalysis.hasServiceCall) {
callingComponents.push({
component: componentName,
path: null,
trigger_source: componentAnalysis.triggerSources
})
}
})
return callingComponents
},
// 查找模块中的所有组件
findComponentsInModule(moduleName) {
const components = []
@ -513,9 +644,9 @@ function routeMappingPlugin() { @@ -513,9 +644,9 @@ function routeMappingPlugin() {
optimizeApiMappings(routes, apiMappings) {
const optimizedApiMappings = apiMappings.map(moduleMapping => {
// 过滤掉callingComponents为空的API映射
// 暂时不过滤空的调用组件,保留所有API映射
const filteredApiMappings = moduleMapping.apiMappings.filter(apiMapping => {
return apiMapping.callingComponents && apiMapping.callingComponents.length > 0
return true // 保留所有API映射
})
// 为每个API映射的callingComponents添加路径信息
@ -829,6 +960,164 @@ function routeMappingPlugin() { @@ -829,6 +960,164 @@ function routeMappingPlugin() {
}
},
// 分析组件中调用特定API方法的服务调用和触发器
analyzeComponentForServiceCallsWithTriggers(componentName, componentPath, moduleName, serviceName, methodName) {
try {
let filePath = componentPath
// 如果是组件名,需要找到对应的文件
if (!filePath || !existsSync(filePath)) {
filePath = this.findComponentFile(componentName)
}
if (!filePath || !existsSync(filePath)) {
console.warn(` 组件文件未找到: ${componentName}`)
return { hasServiceCall: false, triggerSources: [] }
}
const content = readFileSync(filePath, 'utf-8')
if (filePath.endsWith('.vue')) {
return this.analyzeVueComponentForServiceCallsWithTriggers(content, componentName, moduleName, serviceName, methodName)
} else {
return this.analyzeJavaScriptComponentForServiceCallsWithTriggers(content, componentName, moduleName, serviceName, methodName)
}
} catch (error) {
console.warn(` 分析组件服务调用和触发器失败: ${componentName}`, error.message)
return { hasServiceCall: false, triggerSources: [] }
}
},
// 分析Vue组件中的服务调用和触发器
analyzeVueComponentForServiceCallsWithTriggers(content, componentName, moduleName, serviceName, methodName) {
const { descriptor } = parse(content)
const scriptContent = descriptor.script?.content || ''
const templateContent = descriptor.template?.content || ''
if (!scriptContent) {
return { hasServiceCall: false, triggerSources: [] }
}
// 解析script部分,找到调用指定service方法的方法
const ast = parser.parse(scriptContent, {
sourceType: 'module',
plugins: ['jsx']
})
const callingMethods = this.findMethodsCallingService(ast, serviceName, methodName)
if (callingMethods.length === 0) {
return { hasServiceCall: false, triggerSources: [] }
}
// 解析template部分,找到调用这些方法的button
const triggerSources = this.findButtonsCallingMethods(templateContent, callingMethods, componentName)
return {
hasServiceCall: true,
triggerSources: triggerSources
}
},
// 分析JavaScript组件中的服务调用和触发器
analyzeJavaScriptComponentForServiceCallsWithTriggers(content, componentName, moduleName, serviceName, methodName) {
const ast = parser.parse(content, {
sourceType: 'module',
plugins: ['jsx']
})
const callingMethods = this.findMethodsCallingService(ast, serviceName, methodName)
if (callingMethods.length === 0) {
return { hasServiceCall: false, triggerSources: [] }
}
// 对于JS组件,我们假设方法名就是触发器
const triggerSources = callingMethods.map(method => `${componentName}#${method}`)
return {
hasServiceCall: true,
triggerSources: triggerSources
}
},
// 查找调用指定service方法的方法
findMethodsCallingService(ast, serviceName, methodName) {
const callingMethods = []
const self = this
traverse(ast, {
ObjectMethod(path) {
const methodName = path.node.key?.name
if (methodName) {
const hasServiceCall = self.checkMethodCallsService(path, serviceName, methodName)
if (hasServiceCall) {
callingMethods.push(methodName)
}
}
},
ObjectProperty(path) {
if (path.node.value && (path.node.value.type === 'ArrowFunctionExpression' || path.node.value.type === 'FunctionExpression')) {
const methodName = path.node.key?.name
if (methodName) {
const hasServiceCall = self.checkMethodCallsService(path.get('value'), serviceName, methodName)
if (hasServiceCall) {
callingMethods.push(methodName)
}
}
}
}
})
return callingMethods
},
// 检查方法是否调用了指定的service方法
checkMethodCallsService(methodPath, serviceName, methodName) {
let hasServiceCall = false
traverse(methodPath.node, {
CallExpression(path) {
const node = path.node
if (node.callee && node.callee.type === 'MemberExpression') {
const object = node.callee.object
const property = node.callee.property
if (object && property &&
object.name === serviceName &&
property.name === methodName) {
hasServiceCall = true
}
}
}
}, methodPath.scope, methodPath)
return hasServiceCall
},
// 在template中查找调用指定方法的button
findButtonsCallingMethods(templateContent, methodNames, componentName) {
const triggerSources = []
// 使用正则表达式查找button元素
const buttonRegex = /<button[^>]*name\s*=\s*["']([^"']+)["'][^>]*@click\s*=\s*["']([^"']+)["'][^>]*>/g
let match
while ((match = buttonRegex.exec(templateContent)) !== null) {
const buttonName = match[1]
const clickHandler = match[2]
// 检查clickHandler是否在methodNames中
if (methodNames.includes(clickHandler)) {
triggerSources.push(`${componentName}#${buttonName}`)
}
}
return triggerSources
},
// 分析Vue组件中的服务调用
analyzeVueComponentForServiceCalls(content, componentName, moduleName) {
const { descriptor } = parse(content)
@ -1060,6 +1349,7 @@ function routeMappingPlugin() { @@ -1060,6 +1349,7 @@ function routeMappingPlugin() {
try {
const configPath = resolve(__dirname, '../src/renderer/modules/config.js')
if (!existsSync(configPath)) {
console.warn('⚠ 模块配置文件不存在:', configPath)
return []
}
@ -1142,6 +1432,9 @@ function routeMappingPlugin() { @@ -1142,6 +1432,9 @@ function routeMappingPlugin() {
generateMappingFile(routes, apiMappings) {
const outputPath = resolve(__dirname, '../src/renderer/modules/route-sync/direct-route-mappings.js')
console.log('🔍 生成映射文件,API映射数量:', apiMappings.length)
console.log('🔍 API映射内容:', JSON.stringify(apiMappings, null, 2))
// 如果文件已存在,先删除文件内容(清空文件)
if (existsSync(outputPath)) {
console.log('🗑 检测到已存在的映射文件,正在清空文件内容...')

9
gofaster/app/scripts/generate-route-mappings.js

@ -4,6 +4,12 @@ const { resolve } = require('path') @@ -4,6 +4,12 @@ const { resolve } = require('path')
// 检查命令行参数
const isCheckOnly = process.argv.includes('--check-only')
const isSilent = process.argv.includes('--silent')
// 设置静默模式环境变量
if (isSilent) {
process.env.ROUTE_MAPPING_SILENT = 'true'
}
// 独立运行路由映射生成
if (isCheckOnly) {
@ -16,7 +22,8 @@ if (isCheckOnly) { @@ -16,7 +22,8 @@ if (isCheckOnly) {
process.exit(0)
} else {
console.log('⚠ 路由映射文件不存在,需要生成')
// 继续执行生成逻辑
console.log('💡 请运行不带 --check-only 参数的脚本来生成文件')
process.exit(1)
}
} else {
console.log('🔧 独立生成路由映射文件...')

3
gofaster/app/src/renderer/components/LoginModal.vue

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<template>
<template authType="public">
<div v-if="visible" class="login-modal-overlay">
<div class="login-modal" @click.stop>
<div class="login-modal-header">
@ -78,6 +78,7 @@ @@ -78,6 +78,7 @@
<!-- 登录按钮 -->
<div class="form-actions">
<button
name="submit-login"
type="submit"
class="login-btn"
:disabled="loading || !isFormValid"

22
gofaster/app/src/renderer/components/MainLayout.vue

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<template>
<template authType="public">
<div class="main-layout">
<!-- 顶部导航栏 -->
<header class="header">
@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
<div class="header-right">
<!-- 消息通知 -->
<div class="message-center">
<button class="message-btn" @click="toggleMessagePanel">
<button name="toggle-message-panel" class="message-btn" @click="toggleMessagePanel">
<i class="icon">📢</i>
<span v-if="unreadCount > 0" class="badge">{{ unreadCount }}</span>
</button>
@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
<div v-if="showMessagePanel" class="message-panel">
<div class="message-header">
<h3>消息</h3>
<button class="close-btn" @click="showMessagePanel = false">×</button>
<button name="close-message-panel" class="close-btn" @click="showMessagePanel = false">×</button>
</div>
<div class="message-list">
<div v-for="message in messages" :key="message.id" class="message-item">
@ -35,7 +35,7 @@ @@ -35,7 +35,7 @@
<div class="message-title">{{ message.title }}</div>
<div class="message-time">{{ formatTime(message.time) }}</div>
</div>
<button class="mark-read-btn" @click="markAsRead(message.id)">
<button name="mark-message-read" class="mark-read-btn" @click="markAsRead(message.id)">
</button>
</div>
@ -52,7 +52,7 @@ @@ -52,7 +52,7 @@
<!-- 未登录状态显示登录按钮 -->
<div v-else class="login-section">
<button class="login-btn" @click="showLoginModal">
<button name="show-login-modal" class="login-btn" @click="showLoginModal">
<i class="icon">🔐</i> 登录
</button>
</div>
@ -68,16 +68,16 @@ @@ -68,16 +68,16 @@
</div>
</div>
<div class="user-menu-items">
<button class="menu-item" @click="openProfile">
<button name="open-profile" class="menu-item" @click="openProfile">
<i class="icon">👤</i> 个人资料
</button>
<button class="menu-item" @click="openSettings">
<button name="open-settings" class="menu-item" @click="openSettings">
<i class="icon"></i> 用户设置
</button>
<button class="menu-item" @click="clearCache">
<button name="clear-cache" class="menu-item" @click="clearCache">
<i class="icon">🧹</i> 清理缓存
</button>
<button class="menu-item" @click="logout">
<button name="logout" class="menu-item" @click="logout">
<i class="icon">🚪</i> 退出登录
</button>
</div>
@ -103,6 +103,7 @@ @@ -103,6 +103,7 @@
<i class="nav-icon">{{ item.icon }}</i>
<span class="nav-text">{{ item.name }}</span>
<button
name="toggle-favorite"
v-if="item.favorite"
class="favorite-btn"
@click.stop="toggleFavorite(item.id)"
@ -125,6 +126,7 @@ @@ -125,6 +126,7 @@
<i class="nav-icon">{{ item.icon }}</i>
<span class="nav-text">{{ item.name }}</span>
<button
name="remove-favorite"
class="favorite-btn active"
@click.stop="toggleFavorite(item.id)"
>
@ -150,6 +152,7 @@ @@ -150,6 +152,7 @@
>
<span class="tab-title">{{ tab.title }}</span>
<button
name="close-tab"
v-if="openTabs.length > 1"
class="tab-close"
@click.stop="closeTab(tab.id)"
@ -161,6 +164,7 @@ @@ -161,6 +164,7 @@
</div>
<div class="tab-actions">
<button
name="close-all-tabs"
v-if="openTabs.length > 1"
class="close-all-btn"
@click="closeAllTabs"

1
gofaster/app/src/renderer/components/PasswordChangeModal.vue

@ -113,6 +113,7 @@ @@ -113,6 +113,7 @@
取消
</button>
<button
name="submit-password-change"
type="submit"
class="btn btn-primary"
:disabled="loading || !isFormValid"

20
gofaster/app/src/renderer/modules/core/components/MainLayout.vue

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
<div class="header-right">
<!-- 消息通知 -->
<div class="message-center">
<button class="message-btn" @click="toggleMessagePanel">
<button name="toggle-message-panel" class="message-btn" @click="toggleMessagePanel">
<i class="fas fa-bell"></i>
<span v-if="unreadCount > 0" class="badge">{{ unreadCount }}</span>
</button>
@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
<div v-if="showMessagePanel" class="message-panel">
<div class="message-header">
<h3><i class="fas fa-envelope"></i> 消息</h3>
<button class="close-btn" @click="showMessagePanel = false">
<button name="close-message-panel" class="close-btn" @click="showMessagePanel = false">
<i class="fas fa-times"></i>
</button>
</div>
@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
<div class="message-title">{{ message.title }}</div>
<div class="message-time">{{ formatTime(message.time) }}</div>
</div>
<button class="mark-read-btn" @click="markAsRead(message.id)">
<button name="mark-message-read" class="mark-read-btn" @click="markAsRead(message.id)">
<i class="fas fa-check"></i>
</button>
</div>
@ -54,7 +54,7 @@ @@ -54,7 +54,7 @@
<!-- 未登录状态显示登录按钮 -->
<div v-else class="login-section">
<button class="login-btn" @click="showLoginModal">
<button name="show-login-modal" class="login-btn" @click="showLoginModal">
<i class="fas fa-sign-in-alt"></i> 登录
</button>
</div>
@ -70,16 +70,16 @@ @@ -70,16 +70,16 @@
</div>
</div>
<div class="user-menu-items">
<button class="menu-item" @click="openProfile">
<button name="open-profile" class="menu-item" @click="openProfile">
<i class="fas fa-user"></i> 个人资料
</button>
<button class="menu-item" @click="openSettings">
<button name="open-settings" class="menu-item" @click="openSettings">
<i class="fas fa-cog"></i> 用户设置
</button>
<button class="menu-item" @click="clearCache">
<button name="clear-cache" class="menu-item" @click="clearCache">
<i class="fas fa-broom"></i> 清理缓存
</button>
<button class="menu-item" @click="logout">
<button name="logout" class="menu-item" @click="logout">
<i class="fas fa-sign-out-alt"></i> 退出登录
</button>
</div>
@ -105,6 +105,7 @@ @@ -105,6 +105,7 @@
<i class="nav-icon" :class="getNavIconClass(item.icon)"></i>
<span class="nav-text">{{ item.name }}</span>
<button
name="toggle-favorite"
v-if="item.favorite"
class="favorite-btn"
@click.stop="toggleFavorite(item.id)"
@ -127,6 +128,7 @@ @@ -127,6 +128,7 @@
<i class="nav-icon" :class="getNavIconClass(item.icon)"></i>
<span class="nav-text">{{ item.name }}</span>
<button
name="remove-favorite"
class="favorite-btn active"
@click.stop="toggleFavorite(item.id)"
>
@ -152,6 +154,7 @@ @@ -152,6 +154,7 @@
>
<span class="tab-title">{{ tab.title }}</span>
<button
name="close-tab"
v-if="openTabs.length > 1"
class="tab-close"
@click.stop="closeTab(tab.id)"
@ -163,6 +166,7 @@ @@ -163,6 +166,7 @@
</div>
<div class="tab-actions">
<button
name="close-all-tabs"
v-if="openTabs.length > 1"
class="close-all-btn"
@click="closeAllTabs"

4
gofaster/app/src/renderer/modules/core/components/StatusBar.vue

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
<!-- src/components/StatusBar.vue -->
<template>
<template authType="public">
<div class="status-bar">
<!-- 左侧用户和版本信息 -->
<div class="status-left">
@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
<div class="status-right">
<div class="status-item">服务地址: {{ serverUrl }}</div>
<div class="status-item">
<span class="error-link" @click="openLogFolder" title="点击打开日志文件夹">
<span name="open-log-folder" class="error-link" @click="openLogFolder" title="点击打开日志文件夹">
最近错误: {{ errorCount }}
</span>
</div>

4
gofaster/app/src/renderer/modules/core/components/Toast.vue

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<template>
<template authType="public">
<transition name="toast">
<div v-if="visible" class="toast" :class="type">
<div class="toast-icon">
@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
<div class="toast-title">{{ title }}</div>
<div v-if="content" class="toast-message">{{ content }}</div>
</div>
<button class="toast-close" @click="close">
<button name="close-toast" class="toast-close" @click="close">
<i class="fas fa-times"></i>
</button>
</div>

2
gofaster/app/src/renderer/modules/core/views/ConfigTest.vue

@ -45,7 +45,7 @@ @@ -45,7 +45,7 @@
<div class="config-section">
<h3>测试API连接</h3>
<button @click="testApiConnection" :disabled="testing">
<button name="test-api-connection" @click="testApiConnection" :disabled="testing">
{{ testing ? '测试中...' : '测试连接' }}
</button>
<div v-if="testResult" class="test-result">

6
gofaster/app/src/renderer/modules/core/views/Home.vue

@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
<div class="prompt-icon"><i class="fas fa-lock"></i></div>
<h3>需要登录</h3>
<p>登录后即可使用所有功能</p>
<button class="prompt-login-btn" @click="showLoginModalHandler">
<button name="show-login-modal" class="prompt-login-btn" @click="showLoginModalHandler">
<i class="fas fa-sign-in-alt"></i> 立即登录
</button>
</div>
@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
<section v-if="isLoggedIn" class="todo-section">
<div class="section-header">
<h3><i class="fas fa-tasks"></i> 待办事项</h3>
<button class="add-todo-btn" @click="addTodo">
<button name="add-todo" class="add-todo-btn" @click="addTodo">
<i class="fas fa-plus"></i> 添加
</button>
</div>
@ -65,7 +65,7 @@ @@ -65,7 +65,7 @@
/>
<span class="todo-text">{{ todo.text }}</span>
<span class="todo-date">{{ todo.date }}</span>
<button class="delete-btn" @click="deleteTodo(todo.id)">
<button name="delete-todo" class="delete-btn" @click="deleteTodo(todo.id)">
<i class="fas fa-trash"></i>
</button>
</div>

6
gofaster/app/src/renderer/modules/role-management/components/PermissionManager.vue

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
<div class="modal" @click.stop>
<div class="modal-header">
<h3>权限管理</h3>
<button class="close-btn" @click="handleClose">
<button name="close-permission-manager" class="close-btn" @click="handleClose">
<i class="fas fa-times"></i>
</button>
</div>
@ -54,8 +54,8 @@ @@ -54,8 +54,8 @@
</div>
<div class="modal-footer">
<button class="btn btn-secondary" @click="handleClose">取消</button>
<button class="btn btn-primary" @click="savePermissions" :disabled="saving">
<button name="cancel-permission-manager" class="btn btn-secondary" @click="handleClose">取消</button>
<button name="save-permissions" class="btn btn-primary" @click="savePermissions" :disabled="saving">
{{ saving ? '保存中...' : '保存权限' }}
</button>
</div>

4
gofaster/app/src/renderer/modules/role-management/components/RolePermissionAssignment.vue

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
<div class="modal large-modal" @click.stop>
<div class="modal-header">
<h3>角色权限分配 - {{ currentRole?.name }}</h3>
<button class="close-btn" @click="handleClose">
<button name="close-role-permission-assignment" class="close-btn" @click="handleClose">
<i class="fas fa-times"></i>
</button>
</div>
@ -38,6 +38,7 @@ @@ -38,6 +38,7 @@
<div class="permission-assignment">
<div class="assignment-tabs">
<button
name="tab-assigned-permissions"
class="tab-btn"
:class="{ active: activeTab === 'assigned' }"
@click="activeTab = 'assigned'"
@ -46,6 +47,7 @@ @@ -46,6 +47,7 @@
已分配权限 ({{ assignedPermissions.length }})
</button>
<button
name="tab-available-permissions"
class="tab-btn"
:class="{ active: activeTab === 'available' }"
@click="activeTab = 'available'"

Loading…
Cancel
Save