From 5b6a484d96269ef67cdb0e684776af6bf6b54a12 Mon Sep 17 00:00:00 2001 From: wangdan-fit2cloud Date: Thu, 29 Oct 2026 16:04:10 +0800 Subject: [PATCH] fix: chat user --- ui/src/api/chat/README.md | 5 + ui/src/api/chat/chat-user.ts | 17 ++- ui/src/api/types/chat-user.ts | 9 ++ ui/src/conversation-panel/core/types.ts | 1 + .../account-menu/ApiKeyDialog.vue | 74 +++++++++++ .../account-menu/UserPwdDialog.vue | 123 ++++++++++++++++++ .../left-sidebar/account-menu/index.vue | 107 +++++++++++++++ .../conversation-list/AccountMenu.vue | 84 ------------ .../left-sidebar/conversation-list/index.vue | 2 +- .../portal-conversation-list/index.vue | 2 +- ui/src/conversation-panel/view/chat/index.ts | 4 +- ui/src/styles/element-plus.scss | 11 +- ui/src/views/VIEW_README.md | 2 + 13 files changed, 347 insertions(+), 94 deletions(-) create mode 100644 ui/src/conversation-panel/left-sidebar/account-menu/ApiKeyDialog.vue create mode 100644 ui/src/conversation-panel/left-sidebar/account-menu/UserPwdDialog.vue create mode 100644 ui/src/conversation-panel/left-sidebar/account-menu/index.vue delete mode 100644 ui/src/conversation-panel/left-sidebar/conversation-list/AccountMenu.vue diff --git a/ui/src/api/chat/README.md b/ui/src/api/chat/README.md index 47748919cbe..dfea401bea9 100644 --- a/ui/src/api/chat/README.md +++ b/ui/src/api/chat/README.md @@ -24,3 +24,8 @@ Chat 业务接口统一调用后端 v3 路由(`/chat/api/v3/...`);文件 `postAnonymousAuth(accessToken?)` 与 `postLogin(loginRequest, accessToken?)` 传入 accessToken 时为应用认证, 否则为门户认证。`getCaptcha(username, accessToken)` 获取应用登录验证码,后端以 `accessToken` 查询参数接收应用令牌,暂不支持门户。`chat-user.ts` 的 `getChatUserProfile()` 获取当前对话用户。 + +`chat-user.ts` 的 `postResetPassword({ encryptedData })` 提交 RSA 加密的 `{ password, re_password }`, +公钥取当前认证配置的 `rsa_key`;成功后当前 token 失效,调用方清理认证状态并重新导航。 +`getApiKey()` 与 `postApiKey()` 分别读取、重新生成 `/v3/api_key`,返回 `ChatUserApiKey`。 +首次读取由后端创建密钥;每个用户仅保留一个,重新生成后旧密钥立即失效。 diff --git a/ui/src/api/chat/chat-user.ts b/ui/src/api/chat/chat-user.ts index 16ff5f427e7..03c85afc9fe 100644 --- a/ui/src/api/chat/chat-user.ts +++ b/ui/src/api/chat/chat-user.ts @@ -1,11 +1,20 @@ -/** 提供当前对话用户档案接口。 */ +/** 提供当前对话用户档案、密码与 API Key 接口。 */ -import { get } from './core/request' -import type { ChatUserProfile } from '@/api/types' +import { get, post } from './core/request' +import type { ChatUserProfile, ChatUserApiKey } from '@/api/types' /** 获取当前登录的对话用户档案。 */ const getChatUserProfile = () => { return get('/v3/chat_user/profile') } -export default { getChatUserProfile } +/** 修改当前用户密码,提交 RSA 加密后的表单。 */ +const postResetPassword = (data: { encryptedData: string }) => post<{ encryptedData: string }, boolean>('/v3/chat_user/current/reset_password', data) + +/** 获取当前用户 API Key;首次读取由服务端创建。 */ +const getApiKey = () => get('/v3/api_key') + +/** 重新生成当前用户 API Key,旧 Key 立即失效。 */ +const postApiKey = () => post('/v3/api_key') + +export default { getChatUserProfile, postResetPassword, getApiKey, postApiKey } diff --git a/ui/src/api/types/chat-user.ts b/ui/src/api/types/chat-user.ts index 4944e6eb3cd..96429f6c459 100644 --- a/ui/src/api/types/chat-user.ts +++ b/ui/src/api/types/chat-user.ts @@ -130,3 +130,12 @@ export interface ChatUserAuthorizationPayload { chat_user_id: string is_auth: boolean } + +/** 当前对话用户的唯一 API Key。 */ +export interface ChatUserApiKey { + id: string + secret_key: string + is_active: boolean + create_time: string + user_id: string +} diff --git a/ui/src/conversation-panel/core/types.ts b/ui/src/conversation-panel/core/types.ts index dc45fc3e0df..a0f15bc38dd 100644 --- a/ui/src/conversation-panel/core/types.ts +++ b/ui/src/conversation-panel/core/types.ts @@ -11,6 +11,7 @@ export type ConversationAppInfo = ChatApplicationProfile export interface ConversationAccount { nickName?: string username?: string + token_quota?: any } export interface Conversation { diff --git a/ui/src/conversation-panel/left-sidebar/account-menu/ApiKeyDialog.vue b/ui/src/conversation-panel/left-sidebar/account-menu/ApiKeyDialog.vue new file mode 100644 index 00000000000..b497c17e11b --- /dev/null +++ b/ui/src/conversation-panel/left-sidebar/account-menu/ApiKeyDialog.vue @@ -0,0 +1,74 @@ + + + diff --git a/ui/src/conversation-panel/left-sidebar/account-menu/UserPwdDialog.vue b/ui/src/conversation-panel/left-sidebar/account-menu/UserPwdDialog.vue new file mode 100644 index 00000000000..83b9628eadd --- /dev/null +++ b/ui/src/conversation-panel/left-sidebar/account-menu/UserPwdDialog.vue @@ -0,0 +1,123 @@ + + + diff --git a/ui/src/conversation-panel/left-sidebar/account-menu/index.vue b/ui/src/conversation-panel/left-sidebar/account-menu/index.vue new file mode 100644 index 00000000000..23d1e6479c8 --- /dev/null +++ b/ui/src/conversation-panel/left-sidebar/account-menu/index.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/ui/src/conversation-panel/left-sidebar/conversation-list/AccountMenu.vue b/ui/src/conversation-panel/left-sidebar/conversation-list/AccountMenu.vue deleted file mode 100644 index 45b1c4c19c8..00000000000 --- a/ui/src/conversation-panel/left-sidebar/conversation-list/AccountMenu.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - diff --git a/ui/src/conversation-panel/left-sidebar/conversation-list/index.vue b/ui/src/conversation-panel/left-sidebar/conversation-list/index.vue index 48bc0f3e3ef..59f8aebc668 100644 --- a/ui/src/conversation-panel/left-sidebar/conversation-list/index.vue +++ b/ui/src/conversation-panel/left-sidebar/conversation-list/index.vue @@ -2,7 +2,7 @@ import { relativeTimeFormat } from '@/utils/time' import type { Conversation } from '../../core/types' import { useConversationListStore } from './index' -import AccountMenu from './AccountMenu.vue' +import AccountMenu from '../account-menu/index.vue' defineOptions({ name: 'ConversationList' }) diff --git a/ui/src/conversation-panel/left-sidebar/portal-conversation-list/index.vue b/ui/src/conversation-panel/left-sidebar/portal-conversation-list/index.vue index 41c475f6247..f2965282f8a 100644 --- a/ui/src/conversation-panel/left-sidebar/portal-conversation-list/index.vue +++ b/ui/src/conversation-panel/left-sidebar/portal-conversation-list/index.vue @@ -2,7 +2,7 @@ import { relativeTimeFormat } from '@/utils/time' import type { Conversation } from '../../core/types' import { useConversationListStore } from '../conversation-list/index' -import AccountMenu from '../conversation-list/AccountMenu.vue' +import AccountMenu from '../account-menu/index.vue' import { usePortalConversationListStore, type PortalApplicationGroup } from './index' defineOptions({ name: 'PortalConversationList' }) diff --git a/ui/src/conversation-panel/view/chat/index.ts b/ui/src/conversation-panel/view/chat/index.ts index eec9ea958c2..75c8dec1bef 100644 --- a/ui/src/conversation-panel/view/chat/index.ts +++ b/ui/src/conversation-panel/view/chat/index.ts @@ -77,7 +77,7 @@ export function createChatConversation(routing: ChatConversationRouting = useApp rename: api.rename, account: computed(() => { const profile = user.chatUserProfile - return profile?.username ? { nickName: profile.nick_name, username: profile.username } : null + return profile?.username ? { nickName: profile.nick_name, username: profile.username, token_quota: profile.token_quota } : null }), appInfo, }) @@ -120,7 +120,7 @@ export function createChatConversation(routing: ChatConversationRouting = useApp const applicationId = getApplicationId() if (!applicationId) return ApplicationApi.getApplicationProfile(applicationId).then((profile) => { - if (applicationId === getApplicationId()) appInfo.value = {...APPLICATION_DISPLAY_SETTING_DEFAULT,...profile} + if (applicationId === getApplicationId()) appInfo.value = { ...APPLICATION_DISPLAY_SETTING_DEFAULT, ...profile } }) } loadApplicationProfile() diff --git a/ui/src/styles/element-plus.scss b/ui/src/styles/element-plus.scss index 644bba6e8c0..cb15a772941 100644 --- a/ui/src/styles/element-plus.scss +++ b/ui/src/styles/element-plus.scss @@ -805,8 +805,15 @@ } /* progress */ -.el-progress-bar__inner { - background: linear-gradient(90deg, rgb(var(--mk-primary-rgb) / 20%) 0%, var(--el-color-primary) 100%); +.el-progress { + .el-progress-bar__inner { + background: linear-gradient(90deg, rgb(var(--mk-primary-rgb) / 20%) 0%, var(--el-color-primary) 100%); + } + &.is-warning { + .el-progress-bar__inner { + background: linear-gradient(90deg, rgb(var(--mk-warning-rgb) / 20%) 0%, var(--el-color-warning) 100%); + } + } } /* radio */ diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 8b231c5b8a6..aa5f1b7adf1 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -435,5 +435,7 @@ View 负责路由、资源与模式、图加载、保存/发布/调试编排 组件不直接引用某个入口的 Store 或 API;接口、回调地址与登录后跳转由页面传入的 `LoginService` 提供。 - Chat/Portal 页面以 auth token 为面板 key,token 变化重建并加载用户档案;路由认证见路由文档。 会话装配在 conversation-panel,列表 Store 不感知 Router;门户复用 chat 装配,保留各智能体最近会话分组。 + 账户菜单位于 `conversation-panel/left-sidebar/account-menu/index.vue`,单应用与门户共用;直接调用 Chat API 的 `postLogout()`,成功后清理当前认证场景并重新导航,由守卫处理后续认证。 + 同目录的 `UserPwdDialog.vue` 修改当前对话用户密码,`ApiKeyDialog.vue` 展示门户地址及用户唯一 API Key,支持复制完整密钥和重新生成;密钥在界面中仅显示末四位。 没有当前智能体时不查询其信息/会话、禁用输入,分组加载后选首个;无可用智能体显示空态。 全部智能体与选择抽屉复用卡片,抽屉每次打开重新查询,搜索状态独立。