This commit is contained in:
3030332422
2025-09-28 14:18:44 +08:00
9 changed files with 156 additions and 234 deletions
+1
View File
@@ -68,6 +68,7 @@ export interface PublicConfig {
beianIcpNum: string beianIcpNum: string
beianGaNum: string beianGaNum: string
name: string name: string
sm2PublicKey: string
} }
// 获取用户信息 // 获取用户信息
+2 -1
View File
@@ -1,7 +1,7 @@
import type { PublicConfig } from '@/api/auth' import type { PublicConfig } from '@/api/auth'
import { getPublicConfig } from '@/api/auth'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'
import { getPublicConfig } from '@/api/auth'
// 初始化状态 // 初始化状态
const initialConfigState: PublicConfig = { const initialConfigState: PublicConfig = {
@@ -12,6 +12,7 @@ const initialConfigState: PublicConfig = {
mobileAreaList: [], mobileAreaList: [],
beianIcpNum: '', beianIcpNum: '',
beianGaNum: '', beianGaNum: '',
sm2PublicKey: '',
name: import.meta.env.VITE_APP_TITLE, name: import.meta.env.VITE_APP_TITLE,
} }
+43 -5
View File
@@ -10,13 +10,16 @@
"dependencies": { "dependencies": {
"core-js": "^3.41.0", "core-js": "^3.41.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dotenv": "^16.5.0",
"element-ui": "^2.15.14", "element-ui": "^2.15.14",
"flyio": "^0.6.14", "flyio": "^0.6.14",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"opus-decoder": "^0.7.7", "opus-decoder": "^0.7.7",
"opus-recorder": "^8.0.5", "opus-recorder": "^8.0.5",
"sm-crypto": "^0.3.13",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-axios": "^3.5.2", "vue-axios": "^3.5.2",
"vue-i18n": "^8.28.2",
"vue-router": "^3.6.5", "vue-router": "^3.6.5",
"vuex": "^3.6.2", "vuex": "^3.6.2",
"xiaozhi": "file:" "xiaozhi": "file:"
@@ -2540,6 +2543,16 @@
} }
} }
}, },
"node_modules/@vue/cli-service/node_modules/dotenv": {
"version": "10.0.0",
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=10"
}
},
"node_modules/@vue/cli-shared-utils": { "node_modules/@vue/cli-shared-utils": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmmirror.com/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz", "resolved": "https://registry.npmmirror.com/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz",
@@ -4776,12 +4789,15 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "10.0.0", "version": "16.6.1",
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-10.0.0.tgz", "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"dev": true, "license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=10" "node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
} }
}, },
"node_modules/dotenv-expand": { "node_modules/dotenv-expand": {
@@ -10114,6 +10130,21 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/sm-crypto": {
"version": "0.3.13",
"resolved": "https://registry.npmmirror.com/sm-crypto/-/sm-crypto-0.3.13.tgz",
"integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==",
"license": "MIT",
"dependencies": {
"jsbn": "^1.1.0"
}
},
"node_modules/sm-crypto/node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
"license": "MIT"
},
"node_modules/smob": { "node_modules/smob": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmmirror.com/smob/-/smob-1.5.0.tgz", "resolved": "https://registry.npmmirror.com/smob/-/smob-1.5.0.tgz",
@@ -11131,6 +11162,13 @@
"integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
"dev": true "dev": true
}, },
"node_modules/vue-i18n": {
"version": "8.28.2",
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-8.28.2.tgz",
"integrity": "sha512-C5GZjs1tYlAqjwymaaCPDjCyGo10ajUphiwA922jKt9n7KPpqR7oM1PCwYzhB/E7+nT3wfdG3oRre5raIT1rKA==",
"deprecated": "Vue I18n v8.x has reached EOL and is no longer actively maintained. About maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html",
"license": "MIT"
},
"node_modules/vue-loader": { "node_modules/vue-loader": {
"version": "17.4.2", "version": "17.4.2",
"resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.4.2.tgz", "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.4.2.tgz",
+11 -12
View File
@@ -5,7 +5,7 @@ export default {
'login.requiredCaptcha': 'Captcha cannot be empty', 'login.requiredCaptcha': 'Captcha cannot be empty',
'login.requiredMobile': 'Please enter a valid mobile phone number', 'login.requiredMobile': 'Please enter a valid mobile phone number',
'login.loginSuccess': 'Login successful!', 'login.loginSuccess': 'Login successful!',
// HeaderBar组件文本 // HeaderBar组件文本
'header.smartManagement': 'Agents', 'header.smartManagement': 'Agents',
'header.modelConfig': 'Models', 'header.modelConfig': 'Models',
@@ -38,7 +38,7 @@ export default {
'mcpToolCall.copyResult': 'Copy Result', 'mcpToolCall.copyResult': 'Copy Result',
'mcpToolCall.noResultYet': 'No result yet', 'mcpToolCall.noResultYet': 'No result yet',
'mcpToolCall.loadingToolList': 'Loading tool list...', 'mcpToolCall.loadingToolList': 'Loading tool list...',
// Tool names // Tool names
'mcpToolCall.toolName.getDeviceStatus': 'View Device Status', 'mcpToolCall.toolName.getDeviceStatus': 'View Device Status',
'mcpToolCall.toolName.setVolume': 'Set Volume', 'mcpToolCall.toolName.setVolume': 'Set Volume',
@@ -52,7 +52,7 @@ export default {
'mcpToolCall.toolName.snapshot': 'Screen Snapshot', 'mcpToolCall.toolName.snapshot': 'Screen Snapshot',
'mcpToolCall.toolName.previewImage': 'Preview Image', 'mcpToolCall.toolName.previewImage': 'Preview Image',
'mcpToolCall.toolName.setDownloadUrl': 'Set Download URL', 'mcpToolCall.toolName.setDownloadUrl': 'Set Download URL',
// Tool categories // Tool categories
'mcpToolCall.category.audio': 'Audio', 'mcpToolCall.category.audio': 'Audio',
'mcpToolCall.category.display': 'Display', 'mcpToolCall.category.display': 'Display',
@@ -60,7 +60,7 @@ export default {
'mcpToolCall.category.system': 'System', 'mcpToolCall.category.system': 'System',
'mcpToolCall.category.assets': 'Assets', 'mcpToolCall.category.assets': 'Assets',
'mcpToolCall.category.deviceInfo': 'Device Info', 'mcpToolCall.category.deviceInfo': 'Device Info',
// Table categories and properties // Table categories and properties
'mcpToolCall.table.audioSpeaker': 'Audio Speaker', 'mcpToolCall.table.audioSpeaker': 'Audio Speaker',
'mcpToolCall.table.screen': 'Screen', 'mcpToolCall.table.screen': 'Screen',
@@ -80,7 +80,7 @@ export default {
'mcpToolCall.table.component': 'Component', 'mcpToolCall.table.component': 'Component',
'mcpToolCall.table.property': 'Property', 'mcpToolCall.table.property': 'Property',
'mcpToolCall.table.value': 'Value', 'mcpToolCall.table.value': 'Value',
'mcpToolCall.prop.volume': 'Volume', 'mcpToolCall.prop.volume': 'Volume',
'mcpToolCall.prop.brightness': 'Brightness', 'mcpToolCall.prop.brightness': 'Brightness',
'mcpToolCall.prop.theme': 'Theme', 'mcpToolCall.prop.theme': 'Theme',
@@ -112,7 +112,7 @@ export default {
'mcpToolCall.prop.url': 'URL', 'mcpToolCall.prop.url': 'URL',
'mcpToolCall.prop.quality': 'Quality', 'mcpToolCall.prop.quality': 'Quality',
'mcpToolCall.prop.question': 'Question', 'mcpToolCall.prop.question': 'Question',
// Tool help texts // Tool help texts
'mcpToolCall.help.getDeviceStatus': 'View the current running status of the device, including volume, screen, battery and other information.', 'mcpToolCall.help.getDeviceStatus': 'View the current running status of the device, including volume, screen, battery and other information.',
'mcpToolCall.help.setVolume': 'Adjust the volume of the device, please enter a value between 0-100.', 'mcpToolCall.help.setVolume': 'Adjust the volume of the device, please enter a value between 0-100.',
@@ -126,7 +126,7 @@ export default {
'mcpToolCall.help.snapshot': 'Take a screenshot of the current screen and upload it to the specified URL.', 'mcpToolCall.help.snapshot': 'Take a screenshot of the current screen and upload it to the specified URL.',
'mcpToolCall.help.previewImage': 'Preview images from the specified URL on the device screen.', 'mcpToolCall.help.previewImage': 'Preview images from the specified URL on the device screen.',
'mcpToolCall.help.setDownloadUrl': 'Set the download address for device resource files.', 'mcpToolCall.help.setDownloadUrl': 'Set the download address for device resource files.',
// Other text // Other text
'mcpToolCall.text.strong': 'Strong', 'mcpToolCall.text.strong': 'Strong',
'mcpToolCall.text.medium': 'Medium', 'mcpToolCall.text.medium': 'Medium',
@@ -231,7 +231,7 @@ export default {
// AddModelDialog component related // AddModelDialog component related
'addModelDialog.requiredSupplier': 'Please select a supplier', 'addModelDialog.requiredSupplier': 'Please select a supplier',
// Register page related // Register page related
'register.title': 'Create Account', 'register.title': 'Create Account',
'register.welcome': 'Welcome to XiaoZhi AI', 'register.welcome': 'Welcome to XiaoZhi AI',
@@ -632,7 +632,7 @@ export default {
'providerManagement.selectToDelete': 'Please select providers to delete first', 'providerManagement.selectToDelete': 'Please select providers to delete first',
'providerManagement.confirmDelete': 'Are you sure to delete the selected {count} providers?', 'providerManagement.confirmDelete': 'Are you sure to delete the selected {count} providers?',
'providerManagement.viewFields': 'View Fields', 'providerManagement.viewFields': 'View Fields',
// Common Text // Common Text
'common.all': 'All', 'common.all': 'All',
'common.search': 'Search', 'common.search': 'Search',
@@ -662,7 +662,7 @@ export default {
'common.confirm': 'Confirm', 'common.confirm': 'Confirm',
'common.cancel': 'Cancel', 'common.cancel': 'Cancel',
'common.sensitive': 'Sensitive', 'common.sensitive': 'Sensitive',
// Language switch // Language switch
'language.zhCN': '中文简体', 'language.zhCN': '中文简体',
'language.zhTW': '中文繁體', 'language.zhTW': '中文繁體',
@@ -1030,7 +1030,7 @@ export default {
'agentTemplateManagement.batchDeleteFailed': 'Template batch deletion failed', 'agentTemplateManagement.batchDeleteFailed': 'Template batch deletion failed',
'agentTemplateManagement.deleteBackendError': 'Deletion failed, please check if the backend service is normal', 'agentTemplateManagement.deleteBackendError': 'Deletion failed, please check if the backend service is normal',
'agentTemplateManagement.deleteCancelled': 'Deletion cancelled', 'agentTemplateManagement.deleteCancelled': 'Deletion cancelled',
// templateQuickConfig // templateQuickConfig
'templateQuickConfig.title': 'Module Quick Configuration', 'templateQuickConfig.title': 'Module Quick Configuration',
'templateQuickConfig.agentSettings.agentName': 'Nickname', 'templateQuickConfig.agentSettings.agentName': 'Nickname',
@@ -1050,7 +1050,6 @@ export default {
'info': 'Info', 'info': 'Info',
'common.networkError': 'Network request failed', 'common.networkError': 'Network request failed',
'sm2.publicKeyNotConfigured': 'SM2 public key not configured, please contact administrator', 'sm2.publicKeyNotConfigured': 'SM2 public key not configured, please contact administrator',
'sm2.failedToGetPublicKey': 'Failed to get SM2 public key',
'sm2.encryptionFailed': 'Password encryption failed', 'sm2.encryptionFailed': 'Password encryption failed',
'sm2.keyGenerationFailed': 'Key pair generation failed', 'sm2.keyGenerationFailed': 'Key pair generation failed',
'sm2.invalidPublicKey': 'Invalid public key format', 'sm2.invalidPublicKey': 'Invalid public key format',
+11 -12
View File
@@ -5,7 +5,7 @@ export default {
'login.requiredCaptcha': '验证码不能为空', 'login.requiredCaptcha': '验证码不能为空',
'login.requiredMobile': '请输入正确的手机号码', 'login.requiredMobile': '请输入正确的手机号码',
'login.loginSuccess': '登录成功!', 'login.loginSuccess': '登录成功!',
// HeaderBar组件文本 // HeaderBar组件文本
'header.smartManagement': '智能体管理', 'header.smartManagement': '智能体管理',
'header.modelConfig': '模型配置', 'header.modelConfig': '模型配置',
@@ -38,7 +38,7 @@ export default {
'mcpToolCall.copyResult': '复制结果', 'mcpToolCall.copyResult': '复制结果',
'mcpToolCall.noResultYet': '暂无执行结果', 'mcpToolCall.noResultYet': '暂无执行结果',
'mcpToolCall.loadingToolList': '正在获取工具列表...', 'mcpToolCall.loadingToolList': '正在获取工具列表...',
// 工具名称 // 工具名称
'mcpToolCall.toolName.getDeviceStatus': '查看设备状态', 'mcpToolCall.toolName.getDeviceStatus': '查看设备状态',
'mcpToolCall.toolName.setVolume': '设置音量', 'mcpToolCall.toolName.setVolume': '设置音量',
@@ -52,7 +52,7 @@ export default {
'mcpToolCall.toolName.snapshot': '屏幕截图', 'mcpToolCall.toolName.snapshot': '屏幕截图',
'mcpToolCall.toolName.previewImage': '预览图片', 'mcpToolCall.toolName.previewImage': '预览图片',
'mcpToolCall.toolName.setDownloadUrl': '设置下载地址', 'mcpToolCall.toolName.setDownloadUrl': '设置下载地址',
// 工具分类 // 工具分类
'mcpToolCall.category.audio': '音频', 'mcpToolCall.category.audio': '音频',
'mcpToolCall.category.display': '显示', 'mcpToolCall.category.display': '显示',
@@ -60,7 +60,7 @@ export default {
'mcpToolCall.category.system': '系统', 'mcpToolCall.category.system': '系统',
'mcpToolCall.category.assets': '资源', 'mcpToolCall.category.assets': '资源',
'mcpToolCall.category.deviceInfo': '设备信息', 'mcpToolCall.category.deviceInfo': '设备信息',
// 表格分类和属性 // 表格分类和属性
'mcpToolCall.table.audioSpeaker': '音频扬声器', 'mcpToolCall.table.audioSpeaker': '音频扬声器',
'mcpToolCall.table.screen': '屏幕', 'mcpToolCall.table.screen': '屏幕',
@@ -80,7 +80,7 @@ export default {
'mcpToolCall.table.component': '组件', 'mcpToolCall.table.component': '组件',
'mcpToolCall.table.property': '属性', 'mcpToolCall.table.property': '属性',
'mcpToolCall.table.value': '值', 'mcpToolCall.table.value': '值',
'mcpToolCall.prop.volume': '音量', 'mcpToolCall.prop.volume': '音量',
'mcpToolCall.prop.brightness': '亮度', 'mcpToolCall.prop.brightness': '亮度',
'mcpToolCall.prop.theme': '主题', 'mcpToolCall.prop.theme': '主题',
@@ -112,7 +112,7 @@ export default {
'mcpToolCall.prop.url': 'URL', 'mcpToolCall.prop.url': 'URL',
'mcpToolCall.prop.quality': '质量', 'mcpToolCall.prop.quality': '质量',
'mcpToolCall.prop.question': '问题', 'mcpToolCall.prop.question': '问题',
// 工具帮助文本 // 工具帮助文本
'mcpToolCall.help.getDeviceStatus': '查看设备的当前运行状态,包括音量、屏幕、电池等信息。', 'mcpToolCall.help.getDeviceStatus': '查看设备的当前运行状态,包括音量、屏幕、电池等信息。',
'mcpToolCall.help.setVolume': '调整设备的音量大小,请输入0-100之间的数值。', 'mcpToolCall.help.setVolume': '调整设备的音量大小,请输入0-100之间的数值。',
@@ -126,7 +126,7 @@ export default {
'mcpToolCall.help.snapshot': '对当前屏幕进行截图并上传到指定URL。', 'mcpToolCall.help.snapshot': '对当前屏幕进行截图并上传到指定URL。',
'mcpToolCall.help.previewImage': '在设备屏幕上预览指定URL的图片。', 'mcpToolCall.help.previewImage': '在设备屏幕上预览指定URL的图片。',
'mcpToolCall.help.setDownloadUrl': '设置设备资源文件的下载地址。', 'mcpToolCall.help.setDownloadUrl': '设置设备资源文件的下载地址。',
// 其他文本 // 其他文本
'mcpToolCall.text.strong': '强', 'mcpToolCall.text.strong': '强',
'mcpToolCall.text.medium': '中', 'mcpToolCall.text.medium': '中',
@@ -231,7 +231,7 @@ export default {
// AddModelDialog组件相关 // AddModelDialog组件相关
'addModelDialog.requiredSupplier': '请选择供应器', 'addModelDialog.requiredSupplier': '请选择供应器',
// 注册页面相关 // 注册页面相关
'register.title': '创建账号', 'register.title': '创建账号',
'register.welcome': '欢迎使用小智AI', 'register.welcome': '欢迎使用小智AI',
@@ -259,7 +259,7 @@ export default {
'register.requiredPassword': '密码不能为空', 'register.requiredPassword': '密码不能为空',
'register.requiredCaptcha': '验证码不能为空', 'register.requiredCaptcha': '验证码不能为空',
'register.requiredMobileCaptcha': '请输入短信验证码', 'register.requiredMobileCaptcha': '请输入短信验证码',
'manualAddDeviceDialog.deviceType': '设备型号', 'manualAddDeviceDialog.deviceType': '设备型号',
'manualAddDeviceDialog.deviceTypePlaceholder': '请选择设备型号', 'manualAddDeviceDialog.deviceTypePlaceholder': '请选择设备型号',
'manualAddDeviceDialog.firmwareVersion': '固件版本', 'manualAddDeviceDialog.firmwareVersion': '固件版本',
@@ -632,7 +632,7 @@ export default {
'providerManagement.selectToDelete': '请先选择需要删除的供应器', 'providerManagement.selectToDelete': '请先选择需要删除的供应器',
'providerManagement.confirmDelete': '确定要删除选中的{count}个供应器吗?', 'providerManagement.confirmDelete': '确定要删除选中的{count}个供应器吗?',
'providerManagement.viewFields': '查看字段', 'providerManagement.viewFields': '查看字段',
// 公共文本 // 公共文本
'common.all': '全部', 'common.all': '全部',
'common.search': '搜索', 'common.search': '搜索',
@@ -1030,7 +1030,7 @@ export default {
'agentTemplateManagement.deleteFailed': '模板删除失败', 'agentTemplateManagement.deleteFailed': '模板删除失败',
'agentTemplateManagement.batchDeleteFailed': '模板批量删除失败', 'agentTemplateManagement.batchDeleteFailed': '模板批量删除失败',
'agentTemplateManagement.deleteBackendError': '删除失败,请检查后端服务是否正常', 'agentTemplateManagement.deleteBackendError': '删除失败,请检查后端服务是否正常',
// 模板快速配置页面文本 // 模板快速配置页面文本
'templateQuickConfig.title': '模块快速配置', 'templateQuickConfig.title': '模块快速配置',
'templateQuickConfig.agentSettings.agentName': '助手昵称', 'templateQuickConfig.agentSettings.agentName': '助手昵称',
@@ -1051,7 +1051,6 @@ export default {
'common.networkError': '网络请求失败', 'common.networkError': '网络请求失败',
// SM2加密相关错误消息 // SM2加密相关错误消息
'sm2.publicKeyNotConfigured': 'SM2公钥未配置,请联系管理员', 'sm2.publicKeyNotConfigured': 'SM2公钥未配置,请联系管理员',
'sm2.failedToGetPublicKey': '获取SM2公钥失败',
'sm2.encryptionFailed': '密码加密失败', 'sm2.encryptionFailed': '密码加密失败',
'sm2.keyGenerationFailed': '密钥对生成失败', 'sm2.keyGenerationFailed': '密钥对生成失败',
'sm2.invalidPublicKey': '无效的公钥格式', 'sm2.invalidPublicKey': '无效的公钥格式',
+67 -68
View File
@@ -5,7 +5,7 @@ export default {
'login.requiredCaptcha': '驗證碼不能為空', 'login.requiredCaptcha': '驗證碼不能為空',
'login.requiredMobile': '請輸入正確的手機號碼', 'login.requiredMobile': '請輸入正確的手機號碼',
'login.loginSuccess': '登錄成功!', 'login.loginSuccess': '登錄成功!',
// HeaderBar组件文本 // HeaderBar组件文本
'header.smartManagement': '智能體管理', 'header.smartManagement': '智能體管理',
'header.modelConfig': '模型配置', 'header.modelConfig': '模型配置',
@@ -38,7 +38,7 @@ export default {
'mcpToolCall.copyResult': '複製結果', 'mcpToolCall.copyResult': '複製結果',
'mcpToolCall.noResultYet': '暫無執行結果', 'mcpToolCall.noResultYet': '暫無執行結果',
'mcpToolCall.loadingToolList': '正在獲取工具列表...', 'mcpToolCall.loadingToolList': '正在獲取工具列表...',
// 工具名稱 // 工具名稱
'mcpToolCall.toolName.getDeviceStatus': '查看設備狀態', 'mcpToolCall.toolName.getDeviceStatus': '查看設備狀態',
'mcpToolCall.toolName.setVolume': '設置音量', 'mcpToolCall.toolName.setVolume': '設置音量',
@@ -52,7 +52,7 @@ export default {
'mcpToolCall.toolName.snapshot': '螢幕截圖', 'mcpToolCall.toolName.snapshot': '螢幕截圖',
'mcpToolCall.toolName.previewImage': '預覽圖片', 'mcpToolCall.toolName.previewImage': '預覽圖片',
'mcpToolCall.toolName.setDownloadUrl': '設置下載地址', 'mcpToolCall.toolName.setDownloadUrl': '設置下載地址',
// 工具分類 // 工具分類
'mcpToolCall.category.audio': '音頻', 'mcpToolCall.category.audio': '音頻',
'mcpToolCall.category.display': '顯示', 'mcpToolCall.category.display': '顯示',
@@ -60,7 +60,7 @@ export default {
'mcpToolCall.category.system': '系統', 'mcpToolCall.category.system': '系統',
'mcpToolCall.category.assets': '資源', 'mcpToolCall.category.assets': '資源',
'mcpToolCall.category.deviceInfo': '設備資訊', 'mcpToolCall.category.deviceInfo': '設備資訊',
// 表格分類和屬性 // 表格分類和屬性
'mcpToolCall.table.audioSpeaker': '音頻揚聲器', 'mcpToolCall.table.audioSpeaker': '音頻揚聲器',
'mcpToolCall.table.screen': '螢幕', 'mcpToolCall.table.screen': '螢幕',
@@ -80,7 +80,7 @@ export default {
'mcpToolCall.table.component': '組件', 'mcpToolCall.table.component': '組件',
'mcpToolCall.table.property': '屬性', 'mcpToolCall.table.property': '屬性',
'mcpToolCall.table.value': '值', 'mcpToolCall.table.value': '值',
'mcpToolCall.prop.volume': '音量', 'mcpToolCall.prop.volume': '音量',
'mcpToolCall.prop.brightness': '亮度', 'mcpToolCall.prop.brightness': '亮度',
'mcpToolCall.prop.theme': '主題', 'mcpToolCall.prop.theme': '主題',
@@ -112,7 +112,7 @@ export default {
'mcpToolCall.prop.url': 'URL', 'mcpToolCall.prop.url': 'URL',
'mcpToolCall.prop.quality': '品質', 'mcpToolCall.prop.quality': '品質',
'mcpToolCall.prop.question': '問題', 'mcpToolCall.prop.question': '問題',
// 工具幫助文本 // 工具幫助文本
'mcpToolCall.help.getDeviceStatus': '查看設備的當前運行狀態,包括音量、螢幕、電池等資訊。', 'mcpToolCall.help.getDeviceStatus': '查看設備的當前運行狀態,包括音量、螢幕、電池等資訊。',
'mcpToolCall.help.setVolume': '調整設備的音量大小,請輸入0-100之間的數值。', 'mcpToolCall.help.setVolume': '調整設備的音量大小,請輸入0-100之間的數值。',
@@ -126,7 +126,7 @@ export default {
'mcpToolCall.help.snapshot': '對當前螢幕進行截圖並上傳到指定URL。', 'mcpToolCall.help.snapshot': '對當前螢幕進行截圖並上傳到指定URL。',
'mcpToolCall.help.previewImage': '在設備螢幕上預覽指定URL的圖片。', 'mcpToolCall.help.previewImage': '在設備螢幕上預覽指定URL的圖片。',
'mcpToolCall.help.setDownloadUrl': '設置設備資源文件的下載地址。', 'mcpToolCall.help.setDownloadUrl': '設置設備資源文件的下載地址。',
// 其他文本 // 其他文本
'mcpToolCall.text.strong': '強', 'mcpToolCall.text.strong': '強',
'mcpToolCall.text.medium': '中', 'mcpToolCall.text.medium': '中',
@@ -231,7 +231,7 @@ export default {
// AddModelDialog組件相關 // AddModelDialog組件相關
'addModelDialog.requiredSupplier': '請選擇供應器', 'addModelDialog.requiredSupplier': '請選擇供應器',
// 註冊頁面相關 // 註冊頁面相關
'register.title': '建立帳號', 'register.title': '建立帳號',
'register.welcome': '歡迎使用小智慧AI', 'register.welcome': '歡迎使用小智慧AI',
@@ -260,7 +260,7 @@ export default {
'register.requiredPassword': '密碼不能為空', 'register.requiredPassword': '密碼不能為空',
'register.requiredCaptcha': '驗證碼不能為空', 'register.requiredCaptcha': '驗證碼不能為空',
'register.requiredMobileCaptcha': '請輸入簡訊驗證碼', 'register.requiredMobileCaptcha': '請輸入簡訊驗證碼',
'manualAddDeviceDialog.deviceType': '設備型號', 'manualAddDeviceDialog.deviceType': '設備型號',
'manualAddDeviceDialog.deviceTypePlaceholder': '請選擇設備型號', 'manualAddDeviceDialog.deviceTypePlaceholder': '請選擇設備型號',
'manualAddDeviceDialog.firmwareVersion': '固件版本', 'manualAddDeviceDialog.firmwareVersion': '固件版本',
@@ -549,41 +549,41 @@ export default {
'voiceprint.delete': '刪除聲紋', 'voiceprint.delete': '刪除聲紋',
// 字典管理頁面文本 // 字典管理頁面文本
'dictManagement.pageTitle': '字典管理', 'dictManagement.pageTitle': '字典管理',
'dictManagement.searchPlaceholder': '請輸入字典值標籤查詢', 'dictManagement.searchPlaceholder': '請輸入字典值標籤查詢',
'dictManagement.search': '搜索', 'dictManagement.search': '搜索',
'dictManagement.dictTypeName': '字典類型名稱', 'dictManagement.dictTypeName': '字典類型名稱',
'dictManagement.operation': '操作', 'dictManagement.operation': '操作',
'dictManagement.edit': '編輯', 'dictManagement.edit': '編輯',
'dictManagement.dictLabel': '字典標籤', 'dictManagement.dictLabel': '字典標籤',
'dictManagement.dictValue': '字典值', 'dictManagement.dictValue': '字典值',
'dictManagement.sort': '排序', 'dictManagement.sort': '排序',
'dictManagement.delete': '刪除', 'dictManagement.delete': '刪除',
'dictManagement.selectAll': '全選', 'dictManagement.selectAll': '全選',
'dictManagement.deselectAll': '取消全選', 'dictManagement.deselectAll': '取消全選',
'dictManagement.addDictType': '新增字典類型', 'dictManagement.addDictType': '新增字典類型',
'dictManagement.batchDeleteDictType': '批量刪除字典類型', 'dictManagement.batchDeleteDictType': '批量刪除字典類型',
'dictManagement.addDictData': '新增字典數據', 'dictManagement.addDictData': '新增字典數據',
'dictManagement.batchDeleteDictData': '批量刪除字典數據', 'dictManagement.batchDeleteDictData': '批量刪除字典數據',
'dictManagement.itemsPerPage': '{items}條/頁', 'dictManagement.itemsPerPage': '{items}條/頁',
'dictManagement.firstPage': '首頁', 'dictManagement.firstPage': '首頁',
'dictManagement.prevPage': '上一頁', 'dictManagement.prevPage': '上一頁',
'dictManagement.nextPage': '下一頁', 'dictManagement.nextPage': '下一頁',
'dictManagement.totalRecords': '共{total}條記錄', 'dictManagement.totalRecords': '共{total}條記錄',
'dictManagement.editDictType': '編輯字典類型', 'dictManagement.editDictType': '編輯字典類型',
'dictManagement.editDictData': '編輯字典數據', 'dictManagement.editDictData': '編輯字典數據',
'dictManagement.saveSuccess': '保存成功', 'dictManagement.saveSuccess': '保存成功',
'dictManagement.deleteSuccess': '刪除成功', 'dictManagement.deleteSuccess': '刪除成功',
'dictManagement.selectDictTypeToDelete': '請選擇要刪除的字典類型', 'dictManagement.selectDictTypeToDelete': '請選擇要刪除的字典類型',
'dictManagement.confirmDeleteDictType': '確定要刪除選中的字典類型嗎?', 'dictManagement.confirmDeleteDictType': '確定要刪除選中的字典類型嗎?',
'dictManagement.confirm': '確定', 'dictManagement.confirm': '確定',
'dictManagement.cancel': '取消', 'dictManagement.cancel': '取消',
'dictManagement.selectDictTypeFirst': '請先選擇字典類型', 'dictManagement.selectDictTypeFirst': '請先選擇字典類型',
'dictManagement.confirmDeleteDictData': '確定要刪除該字典數據嗎?', 'dictManagement.confirmDeleteDictData': '確定要刪除該字典數據嗎?',
'dictManagement.selectDictDataToDelete': '請選擇要刪除的字典數據', 'dictManagement.selectDictDataToDelete': '請選擇要刪除的字典數據',
'dictManagement.confirmBatchDeleteDictData': '確定要刪除選中的{count}個字典數據嗎?', 'dictManagement.confirmBatchDeleteDictData': '確定要刪除選中的{count}個字典數據嗎?',
'dictManagement.getDictDataFailed': '獲取字典數據失敗', 'dictManagement.getDictDataFailed': '獲取字典數據失敗',
// 用户信息 // 用户信息
'user.info': '用戶信息', 'user.info': '用戶信息',
'user.username': '用戶名', 'user.username': '用戶名',
@@ -1006,31 +1006,31 @@ export default {
'providerDialog.batchDeleteFieldsSuccess': '成功刪除{count}個字段', 'providerDialog.batchDeleteFieldsSuccess': '成功刪除{count}個字段',
// 預設角色管理頁面文本 // 預設角色管理頁面文本
'agentTemplateManagement.title': '預設角色管理', 'agentTemplateManagement.title': '預設角色管理',
'agentTemplateManagement.templateName': '模板名稱', 'agentTemplateManagement.templateName': '模板名稱',
'agentTemplateManagement.action': '操作', 'agentTemplateManagement.action': '操作',
'templateQuickConfig.saveSuccess': '配置保存成功', 'templateQuickConfig.saveSuccess': '配置保存成功',
'templateQuickConfig.saveFailed': '配置保存失敗', 'templateQuickConfig.saveFailed': '配置保存失敗',
'agentTemplateManagement.createTemplate': '建立模板', 'agentTemplateManagement.createTemplate': '建立模板',
'agentTemplateManagement.editTemplate': '編輯模板', 'agentTemplateManagement.editTemplate': '編輯模板',
'agentTemplateManagement.deleteTemplate': '刪除模板', 'agentTemplateManagement.deleteTemplate': '刪除模板',
'agentTemplateManagement.deleteSuccess': '模板刪除成功', 'agentTemplateManagement.deleteSuccess': '模板刪除成功',
'agentTemplateManagement.batchDelete': '批次刪除', 'agentTemplateManagement.batchDelete': '批次刪除',
'agentTemplateManagement.batchDeleteSuccess': '批次刪除成功', 'agentTemplateManagement.batchDeleteSuccess': '批次刪除成功',
'agentTemplateManagement.selectTemplate': '請選擇模板', 'agentTemplateManagement.selectTemplate': '請選擇模板',
'agentTemplateManagement.select': '選擇', 'agentTemplateManagement.select': '選擇',
'agentTemplateManagement.searchPlaceholder': '請輸入模板名稱搜尋', 'agentTemplateManagement.searchPlaceholder': '請輸入模板名稱搜尋',
'agentTemplateManagement.search': '搜尋', 'agentTemplateManagement.search': '搜尋',
'agentTemplateManagement.serialNumber': '序號', 'agentTemplateManagement.serialNumber': '序號',
'agentTemplateManagement.selectAll': '全選', 'agentTemplateManagement.selectAll': '全選',
'agentTemplateManagement.deselectAll': '取消全選', 'agentTemplateManagement.deselectAll': '取消全選',
'agentTemplateManagement.loading': '拼命加載中', 'agentTemplateManagement.loading': '拼命加載中',
'agentTemplateManagement.confirmSingleDelete': '確定要刪除這個模板嗎?', 'agentTemplateManagement.confirmSingleDelete': '確定要刪除這個模板嗎?',
'agentTemplateManagement.confirmBatchDelete': '確定要刪除選中的 {count} 個模板嗎?', 'agentTemplateManagement.confirmBatchDelete': '確定要刪除選中的 {count} 個模板嗎?',
'agentTemplateManagement.deleteFailed': '模板刪除失敗', 'agentTemplateManagement.deleteFailed': '模板刪除失敗',
'agentTemplateManagement.batchDeleteFailed': '模板批次刪除失敗', 'agentTemplateManagement.batchDeleteFailed': '模板批次刪除失敗',
'agentTemplateManagement.deleteBackendError': '刪除失敗,請檢查後端服務是否正常', 'agentTemplateManagement.deleteBackendError': '刪除失敗,請檢查後端服務是否正常',
// 模板快速配置 // 模板快速配置
'templateQuickConfig.title': '模組快速設定', 'templateQuickConfig.title': '模組快速設定',
'templateQuickConfig.agentSettings.agentName': '助手暱稱', 'templateQuickConfig.agentSettings.agentName': '助手暱稱',
@@ -1052,7 +1052,6 @@ export default {
// SM2加密相關錯誤消息 // SM2加密相關錯誤消息
'sm2.publicKeyNotConfigured': 'SM2公鑰未配置,請聯繫管理員', 'sm2.publicKeyNotConfigured': 'SM2公鑰未配置,請聯繫管理員',
'sm2.failedToGetPublicKey': '獲取SM2公鑰失敗',
'sm2.encryptionFailed': '密碼加密失敗', 'sm2.encryptionFailed': '密碼加密失敗',
'sm2.keyGenerationFailed': '金鑰對生成失敗', 'sm2.keyGenerationFailed': '金鑰對生成失敗',
'sm2.invalidPublicKey': '無效的公鑰格式', 'sm2.invalidPublicKey': '無效的公鑰格式',
+2 -1
View File
@@ -15,7 +15,8 @@ export default new Vuex.Store({
version: '', version: '',
beianIcpNum: 'null', beianIcpNum: 'null',
beianGaNum: 'null', beianGaNum: 'null',
allowUserRegister: false allowUserRegister: false,
sm2PublicKey: ''
} }
}, },
getters: { getters: {
+6 -66
View File
@@ -148,9 +148,8 @@
import Api from "@/apis/api"; import Api from "@/apis/api";
import VersionFooter from "@/components/VersionFooter.vue"; import VersionFooter from "@/components/VersionFooter.vue";
import i18n, { changeLanguage } from "@/i18n"; import i18n, { changeLanguage } from "@/i18n";
import { getUUID, goToPage, showDanger, showSuccess, validateMobile, sm2Encrypt } from "@/utils"; import { getUUID, goToPage, showDanger, showSuccess, sm2Encrypt, validateMobile } from "@/utils";
import { mapState } from "vuex"; import { mapState } from "vuex";
import Constant from "@/utils/constant";
export default { export default {
name: "login", name: "login",
@@ -162,6 +161,7 @@ export default {
allowUserRegister: (state) => state.pubConfig.allowUserRegister, allowUserRegister: (state) => state.pubConfig.allowUserRegister,
enableMobileRegister: (state) => state.pubConfig.enableMobileRegister, enableMobileRegister: (state) => state.pubConfig.enableMobileRegister,
mobileAreaList: (state) => state.pubConfig.mobileAreaList, mobileAreaList: (state) => state.pubConfig.mobileAreaList,
sm2PublicKey: (state) => state.pubConfig.sm2PublicKey,
}), }),
// 获取当前语言 // 获取当前语言
currentLanguage() { currentLanguage() {
@@ -197,7 +197,6 @@ export default {
captchaUrl: "", captchaUrl: "",
isMobileLogin: false, isMobileLogin: false,
languageDropdownVisible: false, languageDropdownVisible: false,
serverPublicKey: "", // 服务器公钥
}; };
}, },
mounted() { mounted() {
@@ -206,38 +205,8 @@ export default {
// 根据配置决定默认登录方式 // 根据配置决定默认登录方式
this.isMobileLogin = this.enableMobileRegister; this.isMobileLogin = this.enableMobileRegister;
}); });
// 获取服务器公钥
this.getServerPublicKey();
}, },
methods: { methods: {
// 获取服务器公钥
getServerPublicKey() {
// 先从本地存储获取
const storedPublicKey = localStorage.getItem(Constant.STORAGE_KEY.PUBLIC_KEY);
if (storedPublicKey) {
this.serverPublicKey = storedPublicKey;
return;
}
// 从公共配置接口获取公钥
Api.user.getPubConfig(
(res) => {
if (res.data && res.data.data && res.data.data.sm2PublicKey) {
this.serverPublicKey = res.data.data.sm2PublicKey;
// 存储到本地
localStorage.setItem(Constant.STORAGE_KEY.PUBLIC_KEY, this.serverPublicKey);
} else {
showDanger(this.$t('sm2.failedToGetPublicKey'));
}
},
(err) => {
showDanger(this.$t('sm2.failedToGetPublicKey'));
}
);
},
fetchCaptcha() { fetchCaptcha() {
if (this.$store.getters.getToken) { if (this.$store.getters.getToken) {
if (this.$route.path !== "/home") { if (this.$route.path !== "/home") {
@@ -316,40 +285,12 @@ export default {
if (!this.validateInput(this.form.captcha, 'login.requiredCaptcha')) { if (!this.validateInput(this.form.captcha, 'login.requiredCaptcha')) {
return; return;
} }
// 检查服务器公钥是否已获取,如果未获取则重新获取
if (!this.serverPublicKey) {
try {
// 等待公钥获取完成
await new Promise((resolve, reject) => {
this.getServerPublicKey();
// 设置超时检查,最多等待3秒
const checkInterval = setInterval(() => {
if (this.serverPublicKey) {
clearInterval(checkInterval);
resolve();
}
}, 100);
setTimeout(() => {
clearInterval(checkInterval);
if (!this.serverPublicKey) {
reject(new Error('获取公钥超时'));
}
}, 3000);
});
} catch (error) {
showDanger(this.$t('sm2.failedToGetPublicKey'));
return;
}
}
// 加密密码 // 加密密码
let encryptedPassword; let encryptedPassword;
try { try {
// 拼接验证码和密码 // 拼接验证码和密码
const captchaAndPassword = this.form.captcha + this.form.password; const captchaAndPassword = this.form.captcha + this.form.password;
encryptedPassword = sm2Encrypt(this.serverPublicKey, captchaAndPassword); encryptedPassword = sm2Encrypt(this.sm2PublicKey, captchaAndPassword);
} catch (error) { } catch (error) {
console.error("密码加密失败:", error); console.error("密码加密失败:", error);
showDanger(this.$t('sm2.encryptionFailed')); showDanger(this.$t('sm2.encryptionFailed'));
@@ -359,7 +300,7 @@ export default {
const plainUsername = this.form.username; const plainUsername = this.form.username;
this.form.captchaId = this.captchaUuid; this.form.captchaId = this.captchaUuid;
// 加密 // 加密
const loginData = { const loginData = {
username: plainUsername, username: plainUsername,
@@ -377,7 +318,7 @@ export default {
(err) => { (err) => {
// 直接使用后端返回的国际化消息 // 直接使用后端返回的国际化消息
let errorMessage = err.data.msg || "登录失败"; let errorMessage = err.data.msg || "登录失败";
showDanger(errorMessage); showDanger(errorMessage);
if ( if (
err.data != null && err.data != null &&
@@ -410,8 +351,7 @@ export default {
.login-type-container { .login-type-container {
margin: 10px 20px; margin: 10px 20px;
display: flex; display: flex;
justify-content: space-between; justify-content: center;
align-items: center;
} }
.title-language-dropdown { .title-language-dropdown {
+13 -69
View File
@@ -45,9 +45,10 @@
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;"> <div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;"> <div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/shield.png" /> <img loading="lazy" alt="" class="input-icon" src="@/assets/login/shield.png" />
<el-input v-model="form.captcha" :placeholder="$t('register.captchaPlaceholder')" style="flex: 1;" /> <el-input v-model="form.captcha" :placeholder="$t('register.captchaPlaceholder')"
style="flex: 1;" />
</div> </div>
<img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码" <img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码"
style="width: 150px; height: 40px; cursor: pointer;" @click="fetchCaptcha" /> style="width: 150px; height: 40px; cursor: pointer;" @click="fetchCaptcha" />
</div> </div>
@@ -56,7 +57,8 @@
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;"> <div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;"> <div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/phone.png" /> <img loading="lazy" alt="" class="input-icon" src="@/assets/login/phone.png" />
<el-input v-model="form.mobileCaptcha" :placeholder="$t('register.mobileCaptchaPlaceholder')" style="flex: 1;" maxlength="6" /> <el-input v-model="form.mobileCaptcha" :placeholder="$t('register.mobileCaptchaPlaceholder')"
style="flex: 1;" maxlength="6" />
</div> </div>
<el-button type="primary" class="send-captcha-btn" :disabled="!canSendMobileCaptcha" <el-button type="primary" class="send-captcha-btn" :disabled="!canSendMobileCaptcha"
@click="sendMobileCaptcha"> @click="sendMobileCaptcha">
@@ -70,13 +72,15 @@
<!-- 密码输入框 --> <!-- 密码输入框 -->
<div class="input-box"> <div class="input-box">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" /> <img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
<el-input v-model="form.password" :placeholder="$t('register.passwordPlaceholder')" type="password" show-password /> <el-input v-model="form.password" :placeholder="$t('register.passwordPlaceholder')" type="password"
show-password />
</div> </div>
<!-- 新增确认密码 --> <!-- 新增确认密码 -->
<div class="input-box"> <div class="input-box">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" /> <img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
<el-input v-model="form.confirmPassword" :placeholder="$t('register.confirmPasswordPlaceholder')" type="password" show-password /> <el-input v-model="form.confirmPassword" :placeholder="$t('register.confirmPasswordPlaceholder')"
type="password" show-password />
</div> </div>
<!-- 验证码部分保持相同 --> <!-- 验证码部分保持相同 -->
@@ -121,12 +125,10 @@
<script> <script>
import Api from '@/apis/api'; import Api from '@/apis/api';
import VersionFooter from '@/components/VersionFooter.vue'; import VersionFooter from '@/components/VersionFooter.vue';
import { getUUID, goToPage, showDanger, showSuccess, validateMobile, sm2Encrypt } from '@/utils'; import { getUUID, goToPage, showDanger, showSuccess, sm2Encrypt, validateMobile } from '@/utils';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import Constant from '@/utils/constant';
// 导入语言切换功能 // 导入语言切换功能
import { changeLanguage } from '@/i18n';
export default { export default {
name: 'register', name: 'register',
@@ -137,7 +139,8 @@ export default {
...mapState({ ...mapState({
allowUserRegister: state => state.pubConfig.allowUserRegister, allowUserRegister: state => state.pubConfig.allowUserRegister,
enableMobileRegister: state => state.pubConfig.enableMobileRegister, enableMobileRegister: state => state.pubConfig.enableMobileRegister,
mobileAreaList: state => state.pubConfig.mobileAreaList mobileAreaList: state => state.pubConfig.mobileAreaList,
sm2PublicKey: state => state.pubConfig.sm2PublicKey,
}), }),
canSendMobileCaptcha() { canSendMobileCaptcha() {
return this.countdown === 0 && validateMobile(this.form.mobile, this.form.areaCode); return this.countdown === 0 && validateMobile(this.form.mobile, this.form.areaCode);
@@ -158,7 +161,6 @@ export default {
captchaUrl: '', captchaUrl: '',
countdown: 0, countdown: 0,
timer: null, timer: null,
serverPublicKey: "", // 服务器公钥
} }
}, },
mounted() { mounted() {
@@ -171,38 +173,8 @@ export default {
} }
}); });
this.fetchCaptcha(); this.fetchCaptcha();
// 获取服务器公钥
this.getServerPublicKey();
}, },
methods: { methods: {
// 获取服务器公钥
getServerPublicKey() {
// 先从本地存储获取
const storedPublicKey = localStorage.getItem(Constant.STORAGE_KEY.PUBLIC_KEY);
if (storedPublicKey) {
this.serverPublicKey = storedPublicKey;
return;
}
// 从公共配置接口获取公钥
Api.user.getPubConfig(
(res) => {
if (res.data && res.data.data && res.data.data.sm2PublicKey) {
this.serverPublicKey = res.data.data.sm2PublicKey;
// 存储到本地
localStorage.setItem(Constant.STORAGE_KEY.PUBLIC_KEY, this.serverPublicKey);
} else {
showDanger(this.$t('sm2.failedToGetPublicKey'));
}
},
(err) => {
showDanger(this.$t('sm2.failedToGetPublicKey'));
}
);
},
// 复用验证码获取方法 // 复用验证码获取方法
fetchCaptcha() { fetchCaptcha() {
this.form.captchaId = getUUID(); this.form.captchaId = getUUID();
@@ -302,40 +274,12 @@ export default {
if (!this.validateInput(this.form.captcha, this.$t('register.requiredCaptcha'))) { if (!this.validateInput(this.form.captcha, this.$t('register.requiredCaptcha'))) {
return; return;
} }
// 检查服务器公钥是否已获取,如果未获取则重新获取
if (!this.serverPublicKey) {
try {
// 等待公钥获取完成
await new Promise((resolve, reject) => {
this.getServerPublicKey();
// 设置超时检查,最多等待3秒
const checkInterval = setInterval(() => {
if (this.serverPublicKey) {
clearInterval(checkInterval);
resolve();
}
}, 100);
setTimeout(() => {
clearInterval(checkInterval);
if (!this.serverPublicKey) {
reject(new Error('获取公钥超时'));
}
}, 3000);
});
} catch (error) {
showDanger(this.$t('sm2.failedToGetPublicKey'));
return;
}
}
// 加密 // 加密
let encryptedPassword; let encryptedPassword;
try { try {
// 拼接验证码和密码 // 拼接验证码和密码
const captchaAndPassword = this.form.captcha + this.form.password; const captchaAndPassword = this.form.captcha + this.form.password;
encryptedPassword = sm2Encrypt(this.serverPublicKey, captchaAndPassword); encryptedPassword = sm2Encrypt(this.sm2PublicKey, captchaAndPassword);
} catch (error) { } catch (error) {
console.error("密码加密失败:", error); console.error("密码加密失败:", error);
showDanger(this.$t('sm2.encryptionFailed')); showDanger(this.$t('sm2.encryptionFailed'));