Merge pull request #2288 from xinnan-tech/sm2_test

登录、注册页面添加非对称加密
This commit is contained in:
hrz
2025-09-30 11:12:50 +08:00
committed by GitHub
53 changed files with 3079 additions and 803 deletions
+12 -4
View File
@@ -154,7 +154,7 @@ export default {
}).send()
},
// 获取公共配置
getPubConfig(callback) {
getPubConfig(callback, failCallback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/user/pub-config`)
.method('GET')
@@ -162,10 +162,16 @@ export default {
RequestService.clearRequestTime();
callback(res);
})
.fail((err) => {
RequestService.clearRequestTime();
if (failCallback) {
failCallback(err);
}
})
.networkFail((err) => {
console.error('获取公共配置失败:', err);
RequestService.reAjaxFun(() => {
this.getPubConfig(callback);
this.getPubConfig(callback, failCallback);
});
}).send();
},
@@ -177,7 +183,8 @@ export default {
.data({
phone: passwordData.phone,
code: passwordData.code,
password: passwordData.password
password: passwordData.password,
captchaId: passwordData.captchaId
})
.success((res) => {
RequestService.clearRequestTime();
@@ -192,5 +199,6 @@ export default {
this.retrievePassword(passwordData, callback, failCallback);
});
}).send()
}
},
}
+23 -12
View File
@@ -5,7 +5,7 @@ export default {
'login.requiredCaptcha': 'Captcha cannot be empty',
'login.requiredMobile': 'Please enter a valid mobile phone number',
'login.loginSuccess': 'Login successful!',
// HeaderBar组件文本
'header.smartManagement': 'Agents',
'header.modelConfig': 'Models',
@@ -38,7 +38,7 @@ export default {
'mcpToolCall.copyResult': 'Copy Result',
'mcpToolCall.noResultYet': 'No result yet',
'mcpToolCall.loadingToolList': 'Loading tool list...',
// Tool names
'mcpToolCall.toolName.getDeviceStatus': 'View Device Status',
'mcpToolCall.toolName.setVolume': 'Set Volume',
@@ -52,7 +52,7 @@ export default {
'mcpToolCall.toolName.snapshot': 'Screen Snapshot',
'mcpToolCall.toolName.previewImage': 'Preview Image',
'mcpToolCall.toolName.setDownloadUrl': 'Set Download URL',
// Tool categories
'mcpToolCall.category.audio': 'Audio',
'mcpToolCall.category.display': 'Display',
@@ -60,7 +60,7 @@ export default {
'mcpToolCall.category.system': 'System',
'mcpToolCall.category.assets': 'Assets',
'mcpToolCall.category.deviceInfo': 'Device Info',
// Table categories and properties
'mcpToolCall.table.audioSpeaker': 'Audio Speaker',
'mcpToolCall.table.screen': 'Screen',
@@ -80,7 +80,7 @@ export default {
'mcpToolCall.table.component': 'Component',
'mcpToolCall.table.property': 'Property',
'mcpToolCall.table.value': 'Value',
'mcpToolCall.prop.volume': 'Volume',
'mcpToolCall.prop.brightness': 'Brightness',
'mcpToolCall.prop.theme': 'Theme',
@@ -112,7 +112,7 @@ export default {
'mcpToolCall.prop.url': 'URL',
'mcpToolCall.prop.quality': 'Quality',
'mcpToolCall.prop.question': 'Question',
// Tool help texts
'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.',
@@ -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.previewImage': 'Preview images from the specified URL on the device screen.',
'mcpToolCall.help.setDownloadUrl': 'Set the download address for device resource files.',
// Other text
'mcpToolCall.text.strong': 'Strong',
'mcpToolCall.text.medium': 'Medium',
@@ -231,7 +231,7 @@ export default {
// AddModelDialog component related
'addModelDialog.requiredSupplier': 'Please select a supplier',
// Register page related
'register.title': 'Create Account',
'register.welcome': 'Welcome to XiaoZhi AI',
@@ -384,10 +384,12 @@ export default {
'register.registerSuccess': 'Registration successful!',
'register.registerFailed': 'Registration failed',
'register.passwordsNotMatch': 'The two passwords do not match',
'register.secondsLater': 'seconds later',
// Retrieve password page text
'retrievePassword.title': 'Reset Password',
'retrievePassword.welcome': 'PASSWORD RETRIEVE',
'retrievePassword.subtitle': 'Retrieve Password',
'retrievePassword.mobile': 'Mobile',
'retrievePassword.mobilePlaceholder': 'Please enter mobile number',
'retrievePassword.captcha': 'Verification Code',
@@ -399,6 +401,7 @@ export default {
'retrievePassword.confirmNewPassword': 'Confirm New Password',
'retrievePassword.confirmNewPasswordPlaceholder': 'Please confirm new password',
'retrievePassword.getMobileCaptcha': 'Get Verification Code',
'retrievePassword.resetButton': 'Reset Password',
'retrievePassword.updateButton': 'Update Now',
'retrievePassword.goToLogin': 'Back to Login',
'retrievePassword.inputCorrectMobile': 'Please enter correct mobile number',
@@ -411,6 +414,7 @@ export default {
'retrievePassword.newPasswordRequired': 'New password cannot be empty',
'retrievePassword.confirmNewPasswordRequired': 'Confirm new password cannot be empty',
'retrievePassword.passwordUpdateSuccess': 'Password updated successfully',
'retrievePassword.agreeTo': 'Reset means agree',
// Change password page text
'changePassword.title': 'Change Password',
@@ -631,7 +635,7 @@ export default {
'providerManagement.selectToDelete': 'Please select providers to delete first',
'providerManagement.confirmDelete': 'Are you sure to delete the selected {count} providers?',
'providerManagement.viewFields': 'View Fields',
// Common Text
'common.all': 'All',
'common.search': 'Search',
@@ -661,7 +665,7 @@ export default {
'common.confirm': 'Confirm',
'common.cancel': 'Cancel',
'common.sensitive': 'Sensitive',
// Language switch
'language.zhCN': '中文简体',
'language.zhTW': '中文繁體',
@@ -1029,7 +1033,7 @@ export default {
'agentTemplateManagement.batchDeleteFailed': 'Template batch deletion failed',
'agentTemplateManagement.deleteBackendError': 'Deletion failed, please check if the backend service is normal',
'agentTemplateManagement.deleteCancelled': 'Deletion cancelled',
// templateQuickConfig
'templateQuickConfig.title': 'Module Quick Configuration',
'templateQuickConfig.agentSettings.agentName': 'Nickname',
@@ -1047,5 +1051,12 @@ export default {
'templateQuickConfig.templateNotFound': 'Template not found',
'warning': 'Warning',
'info': 'Info',
'common.networkError': 'Network request failed'
'common.networkError': 'Network request failed',
'sm2.publicKeyNotConfigured': 'SM2 public key not configured, please contact administrator',
'sm2.encryptionFailed': 'Password encryption failed',
'sm2.keyGenerationFailed': 'Key pair generation failed',
'sm2.invalidPublicKey': 'Invalid public key format',
'sm2.encryptionError': 'Error occurred during encryption',
'sm2.publicKeyRetry': 'Retrying to get public key...',
'sm2.publicKeyRetryFailed': 'Public key retrieval retry failed'
}
+24 -12
View File
@@ -5,7 +5,7 @@ export default {
'login.requiredCaptcha': '验证码不能为空',
'login.requiredMobile': '请输入正确的手机号码',
'login.loginSuccess': '登录成功!',
// HeaderBar组件文本
'header.smartManagement': '智能体管理',
'header.modelConfig': '模型配置',
@@ -38,7 +38,7 @@ export default {
'mcpToolCall.copyResult': '复制结果',
'mcpToolCall.noResultYet': '暂无执行结果',
'mcpToolCall.loadingToolList': '正在获取工具列表...',
// 工具名称
'mcpToolCall.toolName.getDeviceStatus': '查看设备状态',
'mcpToolCall.toolName.setVolume': '设置音量',
@@ -52,7 +52,7 @@ export default {
'mcpToolCall.toolName.snapshot': '屏幕截图',
'mcpToolCall.toolName.previewImage': '预览图片',
'mcpToolCall.toolName.setDownloadUrl': '设置下载地址',
// 工具分类
'mcpToolCall.category.audio': '音频',
'mcpToolCall.category.display': '显示',
@@ -60,7 +60,7 @@ export default {
'mcpToolCall.category.system': '系统',
'mcpToolCall.category.assets': '资源',
'mcpToolCall.category.deviceInfo': '设备信息',
// 表格分类和属性
'mcpToolCall.table.audioSpeaker': '音频扬声器',
'mcpToolCall.table.screen': '屏幕',
@@ -80,7 +80,7 @@ export default {
'mcpToolCall.table.component': '组件',
'mcpToolCall.table.property': '属性',
'mcpToolCall.table.value': '值',
'mcpToolCall.prop.volume': '音量',
'mcpToolCall.prop.brightness': '亮度',
'mcpToolCall.prop.theme': '主题',
@@ -112,7 +112,7 @@ export default {
'mcpToolCall.prop.url': 'URL',
'mcpToolCall.prop.quality': '质量',
'mcpToolCall.prop.question': '问题',
// 工具帮助文本
'mcpToolCall.help.getDeviceStatus': '查看设备的当前运行状态,包括音量、屏幕、电池等信息。',
'mcpToolCall.help.setVolume': '调整设备的音量大小,请输入0-100之间的数值。',
@@ -126,7 +126,7 @@ export default {
'mcpToolCall.help.snapshot': '对当前屏幕进行截图并上传到指定URL。',
'mcpToolCall.help.previewImage': '在设备屏幕上预览指定URL的图片。',
'mcpToolCall.help.setDownloadUrl': '设置设备资源文件的下载地址。',
// 其他文本
'mcpToolCall.text.strong': '强',
'mcpToolCall.text.medium': '中',
@@ -231,7 +231,7 @@ export default {
// AddModelDialog组件相关
'addModelDialog.requiredSupplier': '请选择供应器',
// 注册页面相关
'register.title': '创建账号',
'register.welcome': '欢迎使用小智AI',
@@ -259,7 +259,7 @@ export default {
'register.requiredPassword': '密码不能为空',
'register.requiredCaptcha': '验证码不能为空',
'register.requiredMobileCaptcha': '请输入短信验证码',
'manualAddDeviceDialog.deviceType': '设备型号',
'manualAddDeviceDialog.deviceTypePlaceholder': '请选择设备型号',
'manualAddDeviceDialog.firmwareVersion': '固件版本',
@@ -384,10 +384,12 @@ export default {
'register.registerSuccess': '注册成功!',
'register.registerFailed': '注册失败',
'register.passwordsNotMatch': '两次输入的密码不一致',
'register.secondsLater': '秒后',
// 忘记密码页面文本
'retrievePassword.title': '重置密码',
'retrievePassword.welcome': '密码找回',
'retrievePassword.subtitle': '找回密码',
'retrievePassword.mobile': '手机号',
'retrievePassword.mobilePlaceholder': '请输入手机号码',
'retrievePassword.captcha': '验证码',
@@ -399,6 +401,7 @@ export default {
'retrievePassword.confirmNewPassword': '确认新密码',
'retrievePassword.confirmNewPasswordPlaceholder': '请确认新密码',
'retrievePassword.getMobileCaptcha': '获取验证码',
'retrievePassword.resetButton': '重置密码',
'retrievePassword.updateButton': '立即修改',
'retrievePassword.goToLogin': '返回登录',
'retrievePassword.inputCorrectMobile': '请输入正确的手机号码',
@@ -411,6 +414,7 @@ export default {
'retrievePassword.newPasswordRequired': '新密码不能为空',
'retrievePassword.confirmNewPasswordRequired': '确认新密码不能为空',
'retrievePassword.passwordUpdateSuccess': '密码修改成功',
'retrievePassword.agreeTo': '重置即同意',
// 修改密码页面文本
'changePassword.title': '修改密码',
@@ -631,7 +635,7 @@ export default {
'providerManagement.selectToDelete': '请先选择需要删除的供应器',
'providerManagement.confirmDelete': '确定要删除选中的{count}个供应器吗?',
'providerManagement.viewFields': '查看字段',
// 公共文本
'common.all': '全部',
'common.search': '搜索',
@@ -1029,7 +1033,7 @@ export default {
'agentTemplateManagement.deleteFailed': '模板删除失败',
'agentTemplateManagement.batchDeleteFailed': '模板批量删除失败',
'agentTemplateManagement.deleteBackendError': '删除失败,请检查后端服务是否正常',
// 模板快速配置页面文本
'templateQuickConfig.title': '模块快速配置',
'templateQuickConfig.agentSettings.agentName': '助手昵称',
@@ -1047,5 +1051,13 @@ export default {
'templateQuickConfig.resetSuccess': '重置成功',
'warning': '警告',
'info': '提示',
'common.networkError': '网络请求失败'
'common.networkError': '网络请求失败',
// SM2加密相关错误消息
'sm2.publicKeyNotConfigured': 'SM2公钥未配置,请联系管理员',
'sm2.encryptionFailed': '密码加密失败',
'sm2.keyGenerationFailed': '密钥对生成失败',
'sm2.invalidPublicKey': '无效的公钥格式',
'sm2.encryptionError': '加密过程中发生错误',
'sm2.publicKeyRetry': '正在重试获取公钥...',
'sm2.publicKeyRetryFailed': '公钥获取重试失败'
}
+81 -68
View File
@@ -5,7 +5,7 @@ export default {
'login.requiredCaptcha': '驗證碼不能為空',
'login.requiredMobile': '請輸入正確的手機號碼',
'login.loginSuccess': '登錄成功!',
// HeaderBar组件文本
'header.smartManagement': '智能體管理',
'header.modelConfig': '模型配置',
@@ -38,7 +38,7 @@ export default {
'mcpToolCall.copyResult': '複製結果',
'mcpToolCall.noResultYet': '暫無執行結果',
'mcpToolCall.loadingToolList': '正在獲取工具列表...',
// 工具名稱
'mcpToolCall.toolName.getDeviceStatus': '查看設備狀態',
'mcpToolCall.toolName.setVolume': '設置音量',
@@ -52,7 +52,7 @@ export default {
'mcpToolCall.toolName.snapshot': '螢幕截圖',
'mcpToolCall.toolName.previewImage': '預覽圖片',
'mcpToolCall.toolName.setDownloadUrl': '設置下載地址',
// 工具分類
'mcpToolCall.category.audio': '音頻',
'mcpToolCall.category.display': '顯示',
@@ -60,7 +60,7 @@ export default {
'mcpToolCall.category.system': '系統',
'mcpToolCall.category.assets': '資源',
'mcpToolCall.category.deviceInfo': '設備資訊',
// 表格分類和屬性
'mcpToolCall.table.audioSpeaker': '音頻揚聲器',
'mcpToolCall.table.screen': '螢幕',
@@ -80,7 +80,7 @@ export default {
'mcpToolCall.table.component': '組件',
'mcpToolCall.table.property': '屬性',
'mcpToolCall.table.value': '值',
'mcpToolCall.prop.volume': '音量',
'mcpToolCall.prop.brightness': '亮度',
'mcpToolCall.prop.theme': '主題',
@@ -112,7 +112,7 @@ export default {
'mcpToolCall.prop.url': 'URL',
'mcpToolCall.prop.quality': '品質',
'mcpToolCall.prop.question': '問題',
// 工具幫助文本
'mcpToolCall.help.getDeviceStatus': '查看設備的當前運行狀態,包括音量、螢幕、電池等資訊。',
'mcpToolCall.help.setVolume': '調整設備的音量大小,請輸入0-100之間的數值。',
@@ -126,7 +126,7 @@ export default {
'mcpToolCall.help.snapshot': '對當前螢幕進行截圖並上傳到指定URL。',
'mcpToolCall.help.previewImage': '在設備螢幕上預覽指定URL的圖片。',
'mcpToolCall.help.setDownloadUrl': '設置設備資源文件的下載地址。',
// 其他文本
'mcpToolCall.text.strong': '強',
'mcpToolCall.text.medium': '中',
@@ -231,7 +231,7 @@ export default {
// AddModelDialog組件相關
'addModelDialog.requiredSupplier': '請選擇供應器',
// 註冊頁面相關
'register.title': '建立帳號',
'register.welcome': '歡迎使用小智慧AI',
@@ -253,13 +253,14 @@ export default {
'register.captchaSendSuccess': '驗證碼發送成功',
'register.captchaSendFailed': '驗證碼發送失敗',
'register.passwordsNotMatch': '兩次輸入的密碼不一致',
'register.secondsLater': '秒後',
'register.registerSuccess': '註冊成功!',
'register.registerFailed': '註冊失敗',
'register.requiredUsername': '用戶名不能為空',
'register.requiredPassword': '密碼不能為空',
'register.requiredCaptcha': '驗證碼不能為空',
'register.requiredMobileCaptcha': '請輸入簡訊驗證碼',
'manualAddDeviceDialog.deviceType': '設備型號',
'manualAddDeviceDialog.deviceTypePlaceholder': '請選擇設備型號',
'manualAddDeviceDialog.firmwareVersion': '固件版本',
@@ -388,6 +389,7 @@ export default {
// 忘记密码页面文本
'retrievePassword.title': '重置密碼',
'retrievePassword.welcome': '密碼找回',
'retrievePassword.subtitle': '找回密碼',
'retrievePassword.mobile': '手機號',
'retrievePassword.mobilePlaceholder': '請輸入手機號碼',
'retrievePassword.captcha': '驗證碼',
@@ -399,6 +401,7 @@ export default {
'retrievePassword.confirmNewPassword': '確認新密碼',
'retrievePassword.confirmNewPasswordPlaceholder': '請確認新密碼',
'retrievePassword.getMobileCaptcha': '獲取驗證碼',
'retrievePassword.resetButton': '重置密碼',
'retrievePassword.updateButton': '立即修改',
'retrievePassword.goToLogin': '返回登錄',
'retrievePassword.inputCorrectMobile': '請輸入正確的手機號碼',
@@ -411,6 +414,7 @@ export default {
'retrievePassword.newPasswordRequired': '新密碼不能為空',
'retrievePassword.confirmNewPasswordRequired': '確認新密碼不能為空',
'retrievePassword.passwordUpdateSuccess': '密碼修改成功',
'retrievePassword.agreeTo': '重置即同意',
// 修改密码页面文本
'changePassword.title': '修改密碼',
@@ -548,41 +552,41 @@ export default {
'voiceprint.delete': '刪除聲紋',
// 字典管理頁面文本
'dictManagement.pageTitle': '字典管理',
'dictManagement.searchPlaceholder': '請輸入字典值標籤查詢',
'dictManagement.search': '搜索',
'dictManagement.dictTypeName': '字典類型名稱',
'dictManagement.operation': '操作',
'dictManagement.edit': '編輯',
'dictManagement.dictLabel': '字典標籤',
'dictManagement.dictValue': '字典值',
'dictManagement.sort': '排序',
'dictManagement.delete': '刪除',
'dictManagement.selectAll': '全選',
'dictManagement.deselectAll': '取消全選',
'dictManagement.addDictType': '新增字典類型',
'dictManagement.batchDeleteDictType': '批量刪除字典類型',
'dictManagement.addDictData': '新增字典數據',
'dictManagement.batchDeleteDictData': '批量刪除字典數據',
'dictManagement.itemsPerPage': '{items}條/頁',
'dictManagement.firstPage': '首頁',
'dictManagement.prevPage': '上一頁',
'dictManagement.nextPage': '下一頁',
'dictManagement.totalRecords': '共{total}條記錄',
'dictManagement.editDictType': '編輯字典類型',
'dictManagement.editDictData': '編輯字典數據',
'dictManagement.saveSuccess': '保存成功',
'dictManagement.deleteSuccess': '刪除成功',
'dictManagement.selectDictTypeToDelete': '請選擇要刪除的字典類型',
'dictManagement.confirmDeleteDictType': '確定要刪除選中的字典類型嗎?',
'dictManagement.confirm': '確定',
'dictManagement.cancel': '取消',
'dictManagement.selectDictTypeFirst': '請先選擇字典類型',
'dictManagement.confirmDeleteDictData': '確定要刪除該字典數據嗎?',
'dictManagement.selectDictDataToDelete': '請選擇要刪除的字典數據',
'dictManagement.confirmBatchDeleteDictData': '確定要刪除選中的{count}個字典數據嗎?',
'dictManagement.pageTitle': '字典管理',
'dictManagement.searchPlaceholder': '請輸入字典值標籤查詢',
'dictManagement.search': '搜索',
'dictManagement.dictTypeName': '字典類型名稱',
'dictManagement.operation': '操作',
'dictManagement.edit': '編輯',
'dictManagement.dictLabel': '字典標籤',
'dictManagement.dictValue': '字典值',
'dictManagement.sort': '排序',
'dictManagement.delete': '刪除',
'dictManagement.selectAll': '全選',
'dictManagement.deselectAll': '取消全選',
'dictManagement.addDictType': '新增字典類型',
'dictManagement.batchDeleteDictType': '批量刪除字典類型',
'dictManagement.addDictData': '新增字典數據',
'dictManagement.batchDeleteDictData': '批量刪除字典數據',
'dictManagement.itemsPerPage': '{items}條/頁',
'dictManagement.firstPage': '首頁',
'dictManagement.prevPage': '上一頁',
'dictManagement.nextPage': '下一頁',
'dictManagement.totalRecords': '共{total}條記錄',
'dictManagement.editDictType': '編輯字典類型',
'dictManagement.editDictData': '編輯字典數據',
'dictManagement.saveSuccess': '保存成功',
'dictManagement.deleteSuccess': '刪除成功',
'dictManagement.selectDictTypeToDelete': '請選擇要刪除的字典類型',
'dictManagement.confirmDeleteDictType': '確定要刪除選中的字典類型嗎?',
'dictManagement.confirm': '確定',
'dictManagement.cancel': '取消',
'dictManagement.selectDictTypeFirst': '請先選擇字典類型',
'dictManagement.confirmDeleteDictData': '確定要刪除該字典數據嗎?',
'dictManagement.selectDictDataToDelete': '請選擇要刪除的字典數據',
'dictManagement.confirmBatchDeleteDictData': '確定要刪除選中的{count}個字典數據嗎?',
'dictManagement.getDictDataFailed': '獲取字典數據失敗',
// 用户信息
'user.info': '用戶信息',
'user.username': '用戶名',
@@ -1005,31 +1009,31 @@ export default {
'providerDialog.batchDeleteFieldsSuccess': '成功刪除{count}個字段',
// 預設角色管理頁面文本
'agentTemplateManagement.title': '預設角色管理',
'agentTemplateManagement.templateName': '模板名稱',
'agentTemplateManagement.action': '操作',
'templateQuickConfig.saveSuccess': '配置保存成功',
'templateQuickConfig.saveFailed': '配置保存失敗',
'agentTemplateManagement.createTemplate': '建立模板',
'agentTemplateManagement.editTemplate': '編輯模板',
'agentTemplateManagement.deleteTemplate': '刪除模板',
'agentTemplateManagement.deleteSuccess': '模板刪除成功',
'agentTemplateManagement.batchDelete': '批次刪除',
'agentTemplateManagement.batchDeleteSuccess': '批次刪除成功',
'agentTemplateManagement.selectTemplate': '請選擇模板',
'agentTemplateManagement.select': '選擇',
'agentTemplateManagement.searchPlaceholder': '請輸入模板名稱搜尋',
'agentTemplateManagement.search': '搜尋',
'agentTemplateManagement.serialNumber': '序號',
'agentTemplateManagement.selectAll': '全選',
'agentTemplateManagement.deselectAll': '取消全選',
'agentTemplateManagement.loading': '拼命加載中',
'agentTemplateManagement.confirmSingleDelete': '確定要刪除這個模板嗎?',
'agentTemplateManagement.confirmBatchDelete': '確定要刪除選中的 {count} 個模板嗎?',
'agentTemplateManagement.deleteFailed': '模板刪除失敗',
'agentTemplateManagement.batchDeleteFailed': '模板批次刪除失敗',
'agentTemplateManagement.title': '預設角色管理',
'agentTemplateManagement.templateName': '模板名稱',
'agentTemplateManagement.action': '操作',
'templateQuickConfig.saveSuccess': '配置保存成功',
'templateQuickConfig.saveFailed': '配置保存失敗',
'agentTemplateManagement.createTemplate': '建立模板',
'agentTemplateManagement.editTemplate': '編輯模板',
'agentTemplateManagement.deleteTemplate': '刪除模板',
'agentTemplateManagement.deleteSuccess': '模板刪除成功',
'agentTemplateManagement.batchDelete': '批次刪除',
'agentTemplateManagement.batchDeleteSuccess': '批次刪除成功',
'agentTemplateManagement.selectTemplate': '請選擇模板',
'agentTemplateManagement.select': '選擇',
'agentTemplateManagement.searchPlaceholder': '請輸入模板名稱搜尋',
'agentTemplateManagement.search': '搜尋',
'agentTemplateManagement.serialNumber': '序號',
'agentTemplateManagement.selectAll': '全選',
'agentTemplateManagement.deselectAll': '取消全選',
'agentTemplateManagement.loading': '拼命加載中',
'agentTemplateManagement.confirmSingleDelete': '確定要刪除這個模板嗎?',
'agentTemplateManagement.confirmBatchDelete': '確定要刪除選中的 {count} 個模板嗎?',
'agentTemplateManagement.deleteFailed': '模板刪除失敗',
'agentTemplateManagement.batchDeleteFailed': '模板批次刪除失敗',
'agentTemplateManagement.deleteBackendError': '刪除失敗,請檢查後端服務是否正常',
// 模板快速配置
'templateQuickConfig.title': '模組快速設定',
'templateQuickConfig.agentSettings.agentName': '助手暱稱',
@@ -1047,5 +1051,14 @@ export default {
'templateQuickConfig.newTemplate': '新模板',
'warning': '警告',
'info': '提示',
'common.networkError': '網路請求失敗'
'common.networkError': '網路請求失敗',
// SM2加密相關錯誤消息
'sm2.publicKeyNotConfigured': 'SM2公鑰未配置,請聯繫管理員',
'sm2.encryptionFailed': '密碼加密失敗',
'sm2.keyGenerationFailed': '金鑰對生成失敗',
'sm2.invalidPublicKey': '無效的公鑰格式',
'sm2.encryptionError': '加密過程中發生錯誤',
'sm2.publicKeyRetry': '正在重試獲取公鑰...',
'sm2.publicKeyRetryFailed': '公鑰獲取重試失敗'
}
+2 -1
View File
@@ -15,7 +15,8 @@ export default new Vuex.Store({
version: '',
beianIcpNum: 'null',
beianGaNum: 'null',
allowUserRegister: false
allowUserRegister: false,
sm2PublicKey: ''
}
},
getters: {
+56
View File
@@ -200,3 +200,59 @@ export function validateMobile(mobile, areaCode) {
}
}
/**
* 生成SM2密钥对(十六进制格式)
* @returns {Object} 包含公钥和私钥的对象
*/
export function generateSm2KeyPairHex() {
// 使用sm-crypto库生成SM2密钥对
const sm2 = require('sm-crypto').sm2;
const keypair = sm2.generateKeyPairHex();
return {
publicKey: keypair.publicKey,
privateKey: keypair.privateKey,
clientPublicKey: keypair.publicKey, // 客户端公钥
clientPrivateKey: keypair.privateKey // 客户端私钥
};
}
/**
* SM2公钥加密
* @param {string} publicKey 公钥(十六进制格式)
* @param {string} plainText 明文
* @returns {string} 加密后的密文(十六进制格式)
*/
export function sm2Encrypt(publicKey, plainText) {
if (!publicKey) {
throw new Error('公钥不能为null或undefined');
}
if (!plainText) {
throw new Error('明文不能为空');
}
const sm2 = require('sm-crypto').sm2;
// SM2加密,添加04前缀表示未压缩公钥
const encrypted = sm2.doEncrypt(plainText, publicKey, 1);
// 转换为十六进制格式(与后端保持一致,添加04前缀)
const result = "04" + encrypted;
return result;
}
/**
* SM2私钥解密
* @param {string} privateKey 私钥(十六进制格式)
* @param {string} cipherText 密文(十六进制格式)
* @returns {string} 解密后的明文
*/
export function sm2Decrypt(privateKey, cipherText) {
const sm2 = require('sm-crypto').sm2;
// 移除04前缀(与后端保持一致)
const dataWithoutPrefix = cipherText.startsWith("04") ? cipherText.substring(2) : cipherText;
// SM2解密
return sm2.doDecrypt(dataWithoutPrefix, privateKey, 1);
}
+27 -6
View File
@@ -148,7 +148,7 @@
import Api from "@/apis/api";
import VersionFooter from "@/components/VersionFooter.vue";
import i18n, { changeLanguage } from "@/i18n";
import { getUUID, goToPage, showDanger, showSuccess, validateMobile } from "@/utils";
import { getUUID, goToPage, showDanger, showSuccess, sm2Encrypt, validateMobile } from "@/utils";
import { mapState } from "vuex";
export default {
@@ -161,6 +161,7 @@ export default {
allowUserRegister: (state) => state.pubConfig.allowUserRegister,
enableMobileRegister: (state) => state.pubConfig.enableMobileRegister,
mobileAreaList: (state) => state.pubConfig.mobileAreaList,
sm2PublicKey: (state) => state.pubConfig.sm2PublicKey,
}),
// 获取当前语言
currentLanguage() {
@@ -284,10 +285,31 @@ export default {
if (!this.validateInput(this.form.captcha, 'login.requiredCaptcha')) {
return;
}
// 加密密码
let encryptedPassword;
try {
// 拼接验证码和密码
const captchaAndPassword = this.form.captcha + this.form.password;
encryptedPassword = sm2Encrypt(this.sm2PublicKey, captchaAndPassword);
} catch (error) {
console.error("密码加密失败:", error);
showDanger(this.$t('sm2.encryptionFailed'));
return;
}
const plainUsername = this.form.username;
this.form.captchaId = this.captchaUuid;
// 加密
const loginData = {
username: plainUsername,
password: encryptedPassword,
captchaId: this.form.captchaId
};
Api.user.login(
this.form,
loginData,
({ data }) => {
showSuccess(this.$t('login.loginSuccess'));
this.$store.commit("setToken", JSON.stringify(data.data));
@@ -296,7 +318,7 @@ export default {
(err) => {
// 直接使用后端返回的国际化消息
let errorMessage = err.data.msg || "登录失败";
showDanger(errorMessage);
if (
err.data != null &&
@@ -319,7 +341,7 @@ export default {
},
goToForgetPassword() {
goToPage("/retrieve-password");
},
}
},
};
</script>
@@ -329,8 +351,7 @@ export default {
.login-type-container {
margin: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
justify-content: center;
}
.title-language-dropdown {
+40 -14
View File
@@ -45,9 +45,10 @@
<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;">
<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>
<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" />
</div>
@@ -56,7 +57,8 @@
<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;">
<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>
<el-button type="primary" class="send-captcha-btn" :disabled="!canSendMobileCaptcha"
@click="sendMobileCaptcha">
@@ -70,13 +72,15 @@
<!-- 密码输入框 -->
<div class="input-box">
<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 class="input-box">
<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>
<!-- 验证码部分保持相同 -->
@@ -121,11 +125,10 @@
<script>
import Api from '@/apis/api';
import VersionFooter from '@/components/VersionFooter.vue';
import { getUUID, goToPage, showDanger, showSuccess, validateMobile } from '@/utils';
import { getUUID, goToPage, showDanger, showSuccess, sm2Encrypt, validateMobile } from '@/utils';
import { mapState } from 'vuex';
// 导入语言切换功能
import { changeLanguage } from '@/i18n';
export default {
name: 'register',
@@ -136,7 +139,8 @@ export default {
...mapState({
allowUserRegister: state => state.pubConfig.allowUserRegister,
enableMobileRegister: state => state.pubConfig.enableMobileRegister,
mobileAreaList: state => state.pubConfig.mobileAreaList
mobileAreaList: state => state.pubConfig.mobileAreaList,
sm2PublicKey: state => state.pubConfig.sm2PublicKey,
}),
canSendMobileCaptcha() {
return this.countdown === 0 && validateMobile(this.form.mobile, this.form.areaCode);
@@ -156,7 +160,7 @@ export default {
},
captchaUrl: '',
countdown: 0,
timer: null
timer: null,
}
},
mounted() {
@@ -240,7 +244,7 @@ export default {
},
// 注册逻辑
register() {
async register() {
if (this.enableMobileRegister) {
// 手机号注册验证
if (!validateMobile(this.form.mobile, this.form.areaCode)) {
@@ -270,12 +274,34 @@ export default {
if (!this.validateInput(this.form.captcha, this.$t('register.requiredCaptcha'))) {
return;
}
if (this.enableMobileRegister) {
this.form.username = this.form.areaCode + this.form.mobile
// 加密
let encryptedPassword;
try {
// 拼接验证码和密码
const captchaAndPassword = this.form.captcha + this.form.password;
encryptedPassword = sm2Encrypt(this.sm2PublicKey, captchaAndPassword);
} catch (error) {
console.error("密码加密失败:", error);
showDanger(this.$t('sm2.encryptionFailed'));
return;
}
Api.user.register(this.form, ({ data }) => {
let plainUsername;
if (this.enableMobileRegister) {
plainUsername = this.form.areaCode + this.form.mobile;
} else {
plainUsername = this.form.username;
}
// 准备注册数据
const registerData = {
username: plainUsername,
password: encryptedPassword,
captchaId: this.form.captchaId,
mobileCaptcha: this.form.mobileCaptcha
};
Api.user.register(registerData, ({ data }) => {
showSuccess(this.$t('register.registerSuccess'))
goToPage('/login')
}, (err) => {
+32 -18
View File
@@ -53,7 +53,7 @@
<el-button type="primary" class="send-captcha-btn" :disabled="!canSendMobileCaptcha"
@click="sendMobileCaptcha">
<span>
{{ countdown > 0 ? `${countdown}秒后重试` : $t('retrievePassword.sendCaptcha') }}
{{ countdown > 0 ? `${countdown}${$t('register.secondsLater')}` : $t('retrievePassword.getMobileCaptcha') }}
</span>
</el-button>
</div>
@@ -67,7 +67,7 @@
<!-- 确认新密码 -->
<div class="input-box">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
<el-input v-model="form.confirmPassword" :placeholder="$t('retrievePassword.confirmPasswordPlaceholder')" type="password" show-password />
<el-input v-model="form.confirmPassword" :placeholder="$t('retrievePassword.confirmNewPasswordPlaceholder')" type="password" show-password />
</div>
<!-- 修改底部链接 -->
@@ -101,7 +101,7 @@
<script>
import Api from '@/apis/api';
import VersionFooter from '@/components/VersionFooter.vue';
import { getUUID, goToPage, showDanger, showSuccess, validateMobile } from '@/utils';
import { getUUID, goToPage, showDanger, showSuccess, validateMobile, sm2Encrypt } from '@/utils';
import { mapState } from 'vuex';
// 导入语言切换功能
@@ -115,7 +115,8 @@ export default {
computed: {
...mapState({
allowUserRegister: state => state.pubConfig.allowUserRegister,
mobileAreaList: state => state.pubConfig.mobileAreaList
mobileAreaList: state => state.pubConfig.mobileAreaList,
sm2PublicKey: state => state.pubConfig.sm2PublicKey
}),
canSendMobileCaptcha() {
return this.countdown === 0 && validateMobile(this.form.mobile, this.form.areaCode);
@@ -151,7 +152,7 @@ export default {
} else {
console.error('验证码加载异常:', error);
showDanger('验证码加载失败,点击刷新');
showDanger(this.$t('register.captchaLoadFailed'));
}
});
},
@@ -168,12 +169,12 @@ export default {
// 发送手机验证码
sendMobileCaptcha() {
if (!validateMobile(this.form.mobile, this.form.areaCode)) {
showDanger('请输入正确的手机号码');
showDanger(this.$t('retrievePassword.inputCorrectMobile'));
return;
}
// 验证图形验证码
if (!this.validateInput(this.form.captcha, '请输入图形验证码')) {
if (!this.validateInput(this.form.captcha, this.$t('retrievePassword.captchaRequired'))) {
this.fetchCaptcha();
return;
}
@@ -201,9 +202,9 @@ export default {
captcha: this.form.captcha,
captchaId: this.form.captchaId
}, (res) => {
showSuccess('验证码发送成功');
showSuccess(this.$t('retrievePassword.captchaSendSuccess'));
}, (err) => {
showDanger(err.data.msg || '验证码发送失败');
showDanger(err.data.msg || this.$t('register.captchaSendFailed'));
this.countdown = 0;
this.fetchCaptcha();
});
@@ -213,32 +214,45 @@ export default {
retrievePassword() {
// 验证逻辑
if (!validateMobile(this.form.mobile, this.form.areaCode)) {
showDanger('请输入正确的手机号码');
showDanger(this.$t('retrievePassword.inputCorrectMobile'));
return;
}
if (!this.form.captcha) {
showDanger('请输入图形验证码');
showDanger(this.$t('retrievePassword.captchaRequired'));
return;
}
if (!this.form.mobileCaptcha) {
showDanger('请输入短信验证码');
showDanger(this.$t('retrievePassword.mobileCaptchaRequired'));
return;
}
if (this.form.newPassword !== this.form.confirmPassword) {
showDanger('两次输入的密码不一致');
showDanger(this.$t('retrievePassword.passwordsNotMatch'));
return;
}
// 加密密码
let encryptedPassword;
try {
// 拼接图形验证码和新密码进行加密
const captchaAndPassword = this.form.captcha + this.form.newPassword;
encryptedPassword = sm2Encrypt(this.sm2PublicKey, captchaAndPassword);
} catch (error) {
console.error("密码加密失败:", error);
showDanger(this.$t('sm2.encryptionFailed'));
return;
}
Api.user.retrievePassword({
phone: this.form.areaCode + this.form.mobile,
password: this.form.newPassword,
code: this.form.mobileCaptcha
password: encryptedPassword,
code: this.form.mobileCaptcha,
captchaId: this.form.captchaId
}, (res) => {
showSuccess('密码重置成功');
showSuccess(this.$t('retrievePassword.passwordUpdateSuccess'));
goToPage('/login');
}, (err) => {
showDanger(err.data.msg || '重置失败');
if (err.data != null && err.data.msg != null && err.data.msg.indexOf('图形验证码') > -1) {
showDanger(err.data.msg || this.$t('message.error'));
if (err.data != null && err.data.msg != null && (err.data.msg.indexOf('图形验证码') > -1 || err.data.msg.indexOf('Captcha') > -1)) {
this.fetchCaptcha()
}
});