diff --git a/package.json b/package.json index b450bf69..c8dab2b8 100644 --- a/package.json +++ b/package.json @@ -6,17 +6,17 @@ "private": false, "scripts": { "i": "pnpm install", - "dev": "vite", + "dev": "vite --mode env.local", "dev-server": "vite --mode dev", "ts:check": "vue-tsc --noEmit", "build:local": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build", - "build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode local-dev", + "build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev", "build:test": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode test", "build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage", "build:prod": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode prod", "serve:dev": "vite preview --mode dev", "serve:prod": "vite preview --mode prod", - "preview": "pnpm build:local-dev && vite preview", + "preview": "pnpm build:local && vite preview", "clean": "npx rimraf node_modules", "clean:cache": "npx rimraf node_modules/.cache", "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src", @@ -29,6 +29,7 @@ "@form-create/designer": "^3.1.3", "@form-create/element-ui": "^3.1.24", "@iconify/iconify": "^3.1.1", + "@microsoft/fetch-event-source": "^2.0.1", "@videojs-player/vue": "^1.0.0", "@vueuse/core": "^10.9.0", "@wangeditor/editor": "^5.1.23", @@ -51,6 +52,7 @@ "highlight.js": "^11.9.0", "jsencrypt": "^3.3.2", "lodash-es": "^4.17.21", + "marked": "^12.0.2", "min-dash": "^4.1.1", "mitt": "^3.0.1", "nprogress": "^0.2.0", diff --git a/src/api/ai/chat/conversation/index.ts b/src/api/ai/chat/conversation/index.ts new file mode 100644 index 00000000..683646ea --- /dev/null +++ b/src/api/ai/chat/conversation/index.ts @@ -0,0 +1,54 @@ +import request from '@/config/axios' + +// AI 聊天会话 VO +export interface ChatConversationVO { + id: number // ID 编号 + userId: number // 用户编号 + title: string // 会话标题 + pinned: boolean // 是否置顶 + roleId: number // 角色编号 + modelId: number // 模型编号 + model: string // 模型标志 + temperature: number // 温度参数 + maxTokens: number // 单条回复的最大 Token 数量 + maxContexts: number // 上下文的最大 Message 数量 + updateTime: number // 更新时间 + // 额外字段 + modelName?: string // 模型名字 + roleAvatar?: string // 角色头像 + modelMaxTokens?: string // 模型的单条回复的最大 Token 数量 + modelMaxContexts?: string // 模型的上下文的最大 Message 数量 +} + +// AI 聊天会话 API +export const ChatConversationApi = { + // 获得【我的】聊天会话 + getChatConversationMy: async (id: number) => { + return await request.get({ url: `/ai/chat/conversation/get-my?id=${id}` }) + }, + + // 新增【我的】聊天会话 + createChatConversationMy: async (data?: ChatConversationVO) => { + return await request.post({ url: `/ai/chat/conversation/create-my`, data }) + }, + + // 更新【我的】聊天会话 + updateChatConversationMy: async (data: ChatConversationVO) => { + return await request.put({ url: `/ai/chat/conversation/update-my`, data }) + }, + + // 删除【我的】聊天会话 + deleteChatConversationMy: async (id: number) => { + return await request.delete({ url: `/ai/chat/conversation/delete-my?id=${id}` }) + }, + + // 删除【我的】所有对话,置顶除外 + deleteMyAllExceptPinned: async () => { + return await request.delete({ url: `/ai/chat/conversation/delete-my-all-except-pinned` }) + }, + + // 获得【我的】聊天会话列表 + getChatConversationMyList: async () => { + return await request.get({ url: `/ai/chat/conversation/my-list` }) + } +} diff --git a/src/api/ai/chat/message/index.ts b/src/api/ai/chat/message/index.ts new file mode 100644 index 00000000..05b4d804 --- /dev/null +++ b/src/api/ai/chat/message/index.ts @@ -0,0 +1,67 @@ +import request from '@/config/axios' +import { fetchEventSource } from '@microsoft/fetch-event-source' +import { getAccessToken } from '@/utils/auth' +import { config } from '@/config/axios/config' + +// 聊天VO +export interface ChatMessageVO { + id: number // 编号 + conversationId: number // 会话编号 + type: string // 消息类型 + userId: string // 用户编号 + roleId: string // 角色编号 + model: number // 模型标志 + modelId: number // 模型编号 + content: string // 聊天内容 + tokens: number // 消耗 Token 数量 + createTime: Date // 创建时间 +} + +export interface ChatMessageSendVO { + conversationId: string // 会话编号 + content: number // 聊天内容 +} + +// AI chat 聊天 +export const ChatMessageApi = { + // 消息列表 + messageList: async (conversationId: number | null) => { + return await request.get({ + url: `/ai/chat/message/list-by-conversation-id?conversationId=${conversationId}` + }) + }, + + // 发送 send stream 消息 + // TODO axios 可以么? https://apifox.com/apiskills/how-to-create-axios-stream/ + sendStream: async ( + conversationId: number, + content: string, + ctrl, + onMessage, + onError, + onClose + ) => { + const token = getAccessToken() + return fetchEventSource(`${config.base_url}/ai/chat/message/send-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + openWhenHidden: true, + body: JSON.stringify({ + conversationId, + content + }), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal + }) + }, + + // 发送 send 消息 + delete: async (id: string) => { + return await request.delete({ url: `/ai/chat/message/delete?id=${id}` }) + } +} diff --git a/src/api/ai/model/apiKey/index.ts b/src/api/ai/model/apiKey/index.ts index 47e415e4..ed94836e 100644 --- a/src/api/ai/model/apiKey/index.ts +++ b/src/api/ai/model/apiKey/index.ts @@ -12,27 +12,32 @@ export interface ApiKeyVO { // AI API 密钥 API export const ApiKeyApi = { - // 查询AI API 密钥分页 + // 查询 API 密钥分页 getApiKeyPage: async (params: any) => { return await request.get({ url: `/ai/api-key/page`, params }) }, - // 查询AI API 密钥详情 + // 获得 API 密钥列表 + getApiKeySimpleList: async () => { + return await request.get({ url: `/ai/api-key/simple-list` }) + }, + + // 查询 API 密钥详情 getApiKey: async (id: number) => { return await request.get({ url: `/ai/api-key/get?id=` + id }) }, - // 新增AI API 密钥 + // 新增 API 密钥 createApiKey: async (data: ApiKeyVO) => { return await request.post({ url: `/ai/api-key/create`, data }) }, - // 修改AI API 密钥 + // 修改 API 密钥 updateApiKey: async (data: ApiKeyVO) => { return await request.put({ url: `/ai/api-key/update`, data }) }, - // 删除AI API 密钥 + // 删除 API 密钥 deleteApiKey: async (id: number) => { return await request.delete({ url: `/ai/api-key/delete?id=` + id }) } diff --git a/src/api/ai/model/chatModel/index.ts b/src/api/ai/model/chatModel/index.ts new file mode 100644 index 00000000..c2ef4c8d --- /dev/null +++ b/src/api/ai/model/chatModel/index.ts @@ -0,0 +1,53 @@ +import request from '@/config/axios' + +// AI 聊天模型 VO +export interface ChatModelVO { + id: number // 编号 + keyId: number // API 秘钥编号 + name: string // 模型名字 + model: string // 模型标识 + platform: string // 模型平台 + sort: number // 排序 + status: number // 状态 + temperature: number // 温度参数 + maxTokens: number // 单条回复的最大 Token 数量 + maxContexts: number // 上下文的最大 Message 数量 +} + +// AI 聊天模型 API +export const ChatModelApi = { + // 查询聊天模型分页 + getChatModelPage: async (params: any) => { + return await request.get({ url: `/ai/chat-model/page`, params }) + }, + + // 获得聊天模型列表 + getChatModelSimpleList: async (status?: number) => { + return await request.get({ + url: `/ai/chat-model/simple-list`, + params: { + status + } + }) + }, + + // 查询聊天模型详情 + getChatModel: async (id: number) => { + return await request.get({ url: `/ai/chat-model/get?id=` + id }) + }, + + // 新增聊天模型 + createChatModel: async (data: ChatModelVO) => { + return await request.post({ url: `/ai/chat-model/create`, data }) + }, + + // 修改聊天模型 + updateChatModel: async (data: ChatModelVO) => { + return await request.put({ url: `/ai/chat-model/update`, data }) + }, + + // 删除聊天模型 + deleteChatModel: async (id: number) => { + return await request.delete({ url: `/ai/chat-model/delete?id=` + id }) + } +} diff --git a/src/api/ai/model/chatRole/index.ts b/src/api/ai/model/chatRole/index.ts new file mode 100644 index 00000000..a9fce13c --- /dev/null +++ b/src/api/ai/model/chatRole/index.ts @@ -0,0 +1,80 @@ +import request from '@/config/axios' + +// AI 聊天角色 VO +export interface ChatRoleVO { + id: number // 角色编号 + modelId: number // 模型编号 + name: string // 角色名称 + avatar: string // 角色头像 + category: string // 角色类别 + sort: number // 角色排序 + description: string // 角色描述 + systemMessage: string // 角色设定 + welcomeMessage: string // 角色设定 + publicStatus: boolean // 是否公开 + status: number // 状态 +} + +// AI 聊天角色 分页请求 vo +export interface ChatRolePageReqVO { + name?: string // 角色名称 + category?: string // 角色类别 + publicStatus: boolean // 是否公开 + pageNo: number // 是否公开 + pageSize: number // 是否公开 +} + +// AI 聊天角色 API +export const ChatRoleApi = { + // 查询聊天角色分页 + getChatRolePage: async (params: any) => { + return await request.get({ url: `/ai/chat-role/page`, params }) + }, + + // 查询聊天角色详情 + getChatRole: async (id: number) => { + return await request.get({ url: `/ai/chat-role/get?id=` + id }) + }, + + // 新增聊天角色 + createChatRole: async (data: ChatRoleVO) => { + return await request.post({ url: `/ai/chat-role/create`, data }) + }, + + // 修改聊天角色 + updateChatRole: async (data: ChatRoleVO) => { + return await request.put({ url: `/ai/chat-role/update`, data }) + }, + + // 删除聊天角色 + deleteChatRole: async (id: number) => { + return await request.delete({ url: `/ai/chat-role/delete?id=` + id }) + }, + + // ======= chat 聊天 + + // 获取 my role + getMyPage: async (params: ChatRolePageReqVO) => { + return await request.get({ url: `/ai/chat-role/my-page`, params}) + }, + + // 获取角色分类 + getCategoryList: async () => { + return await request.get({ url: `/ai/chat-role/category-list`}) + }, + + // 创建角色 + createMy: async (data: ChatRoleVO) => { + return await request.post({ url: `/ai/chat-role/create-my`, data}) + }, + + // 更新角色 + updateMy: async (data: ChatRoleVO) => { + return await request.put({ url: `/ai/chat-role/update-my`, data}) + }, + + // 删除角色 my + deleteMy: async (id: number) => { + return await request.delete({ url: `/ai/chat-role/delete-my?id=` + id }) + }, +} diff --git a/src/assets/ai/copy-style2.svg b/src/assets/ai/copy-style2.svg new file mode 100644 index 00000000..2d56a87f --- /dev/null +++ b/src/assets/ai/copy-style2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ai/copy.svg b/src/assets/ai/copy.svg new file mode 100644 index 00000000..f51f8d81 --- /dev/null +++ b/src/assets/ai/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ai/delete.svg b/src/assets/ai/delete.svg new file mode 100644 index 00000000..d2ee18ed --- /dev/null +++ b/src/assets/ai/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/MarkdownView/index.vue b/src/components/MarkdownView/index.vue new file mode 100644 index 00000000..6ecb2ea5 --- /dev/null +++ b/src/components/MarkdownView/index.vue @@ -0,0 +1,213 @@ + + + + + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue b/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue index 345670ae..1715d73b 100644 --- a/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue +++ b/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue @@ -79,13 +79,13 @@ const resetFlowCondition = () => { bpmnElement.value = bpmnInstances().bpmnElement bpmnElementSource.value = bpmnElement.value.source bpmnElementSourceRef.value = bpmnElement.value.businessObject.sourceRef + // 初始化默认type为default + flowConditionForm.value = { type: 'default' } if ( bpmnElementSourceRef.value && bpmnElementSourceRef.value.default && - bpmnElementSourceRef.value.default.id === bpmnElement.value.id && - flowConditionForm.value.type == 'default' + bpmnElementSourceRef.value.default.id === bpmnElement.value.id ) { - // 默认 flowConditionForm.value = { type: 'default' } } else if (!bpmnElement.value.businessObject.conditionExpression) { // 普通 diff --git a/src/utils/download.ts b/src/utils/download.ts index ab200149..fe24ee27 100644 --- a/src/utils/download.ts +++ b/src/utils/download.ts @@ -29,7 +29,7 @@ const download = { html: (data: Blob, fileName: string) => { download0(data, fileName, 'text/html') }, - // 下载 Markdown 方法 + // 下载 MarkdownView 方法 markdown: (data: Blob, fileName: string) => { download0(data, fileName, 'text/markdown') } diff --git a/src/views/Login/SocialLogin.vue b/src/views/Login/SocialLogin.vue index 1eb9293c..eff59ba6 100644 --- a/src/views/Login/SocialLogin.vue +++ b/src/views/Login/SocialLogin.vue @@ -64,7 +64,7 @@ - + { // 情况一,未开启:则直接登录 - if (loginData.captchaEnable) { + if (!loginData.captchaEnable) { await handleLogin({}) } else { // 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录 diff --git a/src/views/ai/chat/components/ChatConversationUpdateForm.vue b/src/views/ai/chat/components/ChatConversationUpdateForm.vue new file mode 100644 index 00000000..fe08ffb2 --- /dev/null +++ b/src/views/ai/chat/components/ChatConversationUpdateForm.vue @@ -0,0 +1,141 @@ + + diff --git a/src/views/ai/chat/components/Header.vue b/src/views/ai/chat/components/Header.vue new file mode 100644 index 00000000..7d859c2b --- /dev/null +++ b/src/views/ai/chat/components/Header.vue @@ -0,0 +1,50 @@ + + + + + + diff --git a/src/views/ai/chat/components/MessageList.vue b/src/views/ai/chat/components/MessageList.vue new file mode 100644 index 00000000..0f77f8ec --- /dev/null +++ b/src/views/ai/chat/components/MessageList.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index ec49b774..0da53f1e 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -4,8 +4,8 @@
- - + + 新建对话 @@ -17,45 +17,51 @@ @keyup="searchConversation" > -
- -
- 置顶 -
- -
-
- - {{ conversation.title }} -
-
- - - - - - -
+
+ +
+
+ {{conversationKey}}
- + +
+
+ + {{ conversation.title }} +
+ +
+ + + + + + +
+
+
+
-
- +
+ 角色仓库
-
- - 清空未置顶对话 +
+ + 清空未置顶对话
@@ -64,73 +70,579 @@
- 标题...... + {{ useConversation?.title }}
- 3.5-turbo-0125 - - + + + + - + - + + + +
- 对话列表 - - + + + +
+
+ + +
+
+ +
+
+
+ {{ formatDate(item.createTime) }} +
+
+ + + +
+
+
+ + 复制 +
+
+ + 删除 +
+
+
+
+ +
+
+ +
+
+
+ {{ formatDate(item.createTime) }} +
+
+
{{ item.content }}
+ +
+
+
+ + 复制 +
+
+ + 删除 +
+
+
+
+
+
+ + + + +
+ +
+ +
+ + + {{ conversationInProgress ? '进行中' : '发送' }} + + + 停止 + +
+
+ + + diff --git a/src/views/ai/chat/role/RoleCategoryList.vue b/src/views/ai/chat/role/RoleCategoryList.vue new file mode 100644 index 00000000..549a4416 --- /dev/null +++ b/src/views/ai/chat/role/RoleCategoryList.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/views/ai/chat/role/RoleList.vue b/src/views/ai/chat/role/RoleList.vue new file mode 100644 index 00000000..834eb3cb --- /dev/null +++ b/src/views/ai/chat/role/RoleList.vue @@ -0,0 +1,155 @@ + + + + + + diff --git a/src/views/ai/chat/role/index.vue b/src/views/ai/chat/role/index.vue new file mode 100644 index 00000000..924a6784 --- /dev/null +++ b/src/views/ai/chat/role/index.vue @@ -0,0 +1,219 @@ + + + + + + + diff --git a/src/views/ai/model/apiKey/ApiKeyForm.vue b/src/views/ai/model/apiKey/ApiKeyForm.vue index 2f9ba580..a8fc0128 100644 --- a/src/views/ai/model/apiKey/ApiKeyForm.vue +++ b/src/views/ai/model/apiKey/ApiKeyForm.vue @@ -7,13 +7,7 @@ label-width="120px" v-loading="formLoading" > - - - - - - - + - - + + + + + + + + diff --git a/src/views/ai/model/apiKey/index.vue b/src/views/ai/model/apiKey/index.vue index fcafc463..6daf6a7d 100644 --- a/src/views/ai/model/apiKey/index.vue +++ b/src/views/ai/model/apiKey/index.vue @@ -60,15 +60,14 @@ - - - - + - + + +