diff --git a/package.json b/package.json index 1fa8864687416723d396e67ec259621b4a02c9a6..18be2c2efc17353e2ee0a7bdc77c45e33641eb5e 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "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", @@ -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", @@ -46,11 +47,12 @@ "driver.js": "^1.3.1", "echarts": "^5.5.0", "echarts-wordcloud": "^2.1.0", - "element-plus": "2.6.1", + "element-plus": "2.7.0", "fast-xml-parser": "^4.3.2", "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/pnpm-lock.yaml b/pnpm-lock.yaml index 0c2351e03184eb5aa218c4b0ab7616b88ff4f79b..55dceb8de11cd5d0f9332660b9da4a79390f8163 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ dependencies: '@iconify/iconify': specifier: ^3.1.1 version: 3.1.1 + '@microsoft/fetch-event-source': + specifier: ^2.0.1 + version: 2.0.1 '@videojs-player/vue': specifier: ^1.0.0 version: 1.0.0(@types/video.js@7.3.58)(video.js@7.21.5)(vue@3.4.21) @@ -69,8 +72,8 @@ dependencies: specifier: ^2.1.0 version: 2.1.0(echarts@5.5.0) element-plus: - specifier: 2.6.1 - version: 2.6.1(vue@3.4.21) + specifier: 2.7.0 + version: 2.7.0(vue@3.4.21) fast-xml-parser: specifier: ^4.3.2 version: 4.3.6 @@ -83,6 +86,9 @@ dependencies: lodash-es: specifier: ^4.17.21 version: 4.17.21 + marked: + specifier: ^12.0.2 + version: 12.0.2 min-dash: specifier: ^4.1.1 version: 4.2.1 @@ -2360,6 +2366,10 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@microsoft/fetch-event-source@2.0.1: + resolution: {integrity: sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==, tarball: https://registry.npmmirror.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz} + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, tarball: https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz} engines: {node: '>= 8'} @@ -5082,8 +5092,8 @@ packages: resolution: {integrity: sha512-9ItEpeu15hW5m8jKdriL+BQrgwDTXEL9pn4SkillWFu73ZNNNQ2BKKLS+ZHv2vC9UkNhosAeyfxOf/5OSeTCPA==, tarball: https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.750.tgz} dev: true - /element-plus@2.6.1(vue@3.4.21): - resolution: {integrity: sha512-6VRpLjwtIVdtUuITJPPKtpOH1NM6nuAkRE3q5O4Lrx0N1bYMhTkiqb2Jy7zfQuDPbOIkkF2OABTzegpNnzgsnQ==, tarball: https://registry.npmmirror.com/element-plus/-/element-plus-2.6.1.tgz} + /element-plus@2.7.0(vue@3.4.21): + resolution: {integrity: sha512-WAiaFLavuWFxof9qwkC27jvkh9nRcNnB506g1vvJSiVaVqjCBWUFCIyJKeN11M1qcv2cS5VV5PfSLjTIkrw87A==, tarball: https://registry.npmmirror.com/element-plus/-/element-plus-2.7.0.tgz} peerDependencies: vue: ^3.2.0 dependencies: @@ -6917,6 +6927,12 @@ packages: object-visit: 1.0.1 dev: true + /marked@12.0.2: + resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==, tarball: https://registry.npmmirror.com/marked/-/marked-12.0.2.tgz} + engines: {node: '>= 18'} + hasBin: true + dev: false + /matches-selector@1.2.0: resolution: {integrity: sha512-c4vLwYWyl+Ji+U43eU/G5FwxWd4ZH0ePUsFs5y0uwD9HUEFBXUQ1zUUan+78IpRD+y4pUfG0nAzNM292K7ItvA==, tarball: https://registry.npmmirror.com/matches-selector/-/matches-selector-1.2.0.tgz} dev: true diff --git a/src/App.vue b/src/App.vue index 7407d97adb22c317f2382b88db6b989a642edb7b..1f5f36d7a715e53aef7d6f4ff71b0bf976410487 100644 --- a/src/App.vue +++ b/src/App.vue @@ -54,4 +54,8 @@ body { .#{$prefix-cls}-grey-mode { filter: grayscale(100%); } + +.scrollbar__view { + height: 99%!important; +} diff --git a/src/api/ai/chat/conversation/index.ts b/src/api/ai/chat/conversation/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..08ad26a103c70af409367c192854a4c2625f7a16 --- /dev/null +++ b/src/api/ai/chat/conversation/index.ts @@ -0,0 +1,64 @@ +import request from '@/config/axios' + +// AI 聊天对话 VO +export interface ChatConversationVO { + id: string // ID 编号 + userId: number // 用户编号 + title: string // 对话标题 + pinned: boolean // 是否置顶 + roleId: number // 角色编号 + modelId: number // 模型编号 + model: string // 模型标志 + temperature: number // 温度参数 + maxTokens: number // 单条回复的最大 Token 数量 + maxContexts: number // 上下文的最大 Message 数量 + // 额外字段 + systemMessage?: string // 角色设定 + modelName?: string // 模型名字 + roleAvatar?: string // 角色头像 + modelMaxTokens?: string // 模型的单条回复的最大 Token 数量 + modelMaxContexts?: string // 模型的上下文的最大 Message 数量 +} + +// AI 聊天对话 API +export const ChatConversationApi = { + // 获得【我的】聊天对话 + getChatConversationMy: async (id: string) => { + 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: string) => { + 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` }) + }, + + // 获得对话分页 + getChatConversationPage: async (params: any) => { + return await request.get({ url: `/ai/chat/conversation/page`, params }) + }, + + // 管理员删除消息 + deleteChatConversationByAdmin: async (id: number) => { + return await request.delete({ url: `/ai/chat/conversation/delete-by-admin?id=${id}` }) + } +} diff --git a/src/api/ai/chat/message/index.ts b/src/api/ai/chat/message/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..f9e807a1e6cb1551e5ac47d6c7b66b4615eabd1b --- /dev/null +++ b/src/api/ai/chat/message/index.ts @@ -0,0 +1,88 @@ +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 // 创建时间 + roleAvatar: string // 角色头像 + userAvatar: string // 创建时间 +} + +export interface ChatMessageSendVO { + conversationId: string // 对话编号 + content: number // 聊天内容 +} + +// AI chat 聊天 +export const ChatMessageApi = { + // 消息列表 + messageList: async (conversationId: string | 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, + enableContext: boolean, + 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, + useContext: enableContext + }), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal + }) + }, + + // 删除消息 + delete: async (id: string) => { + return await request.delete({ url: `/ai/chat/message/delete?id=${id}` }) + }, + + // 删除消息 - 对话所有消息 + deleteByConversationId: async (conversationId: string) => { + return await request.delete({ + url: `/ai/chat/message/delete-by-conversation-id?conversationId=${conversationId}` + }) + }, + + // 获得消息分页 + getChatMessagePage: async (params: any) => { + return await request.get({ url: '/ai/chat/message/page', params }) + }, + + // 管理员删除消息 + deleteChatMessageByAdmin: async (id: number) => { + return await request.delete({ url: `/ai/chat/message/delete-by-admin?id=${id}` }) + } +} diff --git a/src/api/ai/image/index.ts b/src/api/ai/image/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b895986d38a0d7fd239c4142b9117d426f9fd299 --- /dev/null +++ b/src/api/ai/image/index.ts @@ -0,0 +1,109 @@ +import request from '@/config/axios' + +// AI 绘图 VO +export interface ImageVO { + id: number // 编号 + platform: string // 平台 + model: string // 模型 + prompt: string // 提示词 + width: number // 图片宽度 + height: number // 图片高度 + status: number // 状态 + publicStatus: boolean // 公开状态 + picUrl: string // 任务地址 + errorMessage: string // 错误信息 + options: object // 配置 Map + taskId: number // 任务编号 + buttons: ImageMjButtonsVO[] // mj 操作按钮 + createTime: string // 创建时间 +} + +export interface ImagePageReqVO { + pageNo: number // 分页编号 + pageSize: number // 分页大小 +} + +export interface ImageDrawReqVO { + platform: string // 平台 + prompt: string // 提示词 + model: string // 模型 + style: string // 图像生成的风格 + width: string // 图片宽度 + height: string // 图片高度 + options: object // 绘制参数,Map +} + +export interface ImageMidjourneyImagineReqVO { + prompt: string // 提示词 + model: string // 模型 mj nijj + base64Array: string[] // size不能为空 + width: string // 图片宽度 + height: string // 图片高度 + version: string // 版本 +} + +export interface ImageMjActionVO { + id: number // 图片编号 + customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识 +} + +export interface ImageMjButtonsVO { + customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识 + emoji: string // 图标 emoji + label: string // Make Variations 文本 + style: number // 样式: 2(Primary)、3(Green) +} + +// AI API 密钥 API +export const ImageApi = { + // 获取我的图片列表 + getImagePageMy: async (params: ImagePageReqVO) => { + return await request.get({ url: `/ai/image/my-page`, params }) + }, + // 获取我的图片 + getImageMy: async (id: number) => { + return await request.get({ url: `/ai/image/get-my?id=${id}` }) + }, + // 生成图片 + drawImage: async (data: ImageDrawReqVO) => { + return await request.post({ url: `/ai/image/draw`, data }) + }, + // 删除我的图片 + deleteImageMy: async (id: number) => { + return await request.delete({ url: `/ai/image/delete-my?id=${id}` }) + }, + + // ================ midjourney 专属 ================ + + // 【Midjourney】生成图片 + midjourneyImagine: async (data: ImageMidjourneyImagineReqVO) => { + return await request.post({ url: `/ai/image/midjourney/imagine`, data }) + }, + // 【Midjourney】Action 操作(二次生成图片) + midjourneyAction: async (data: ImageMjActionVO) => { + return await request.post({ url: `/ai/image/midjourney/action`, data }) + }, + + // ================ 绘图管理 ================ + + // 查询绘画分页 + getImagePage: async (params: any) => { + return await request.get({ url: `/ai/image/page`, params }) + }, + + // 更新绘画发布状态 + updateImagePublicStatus: async (id: number, publicStatus: boolean) => { + return await request.put({ + url: '/ai/image/update-public-status', + data: { + id, + publicStatus + } + }) + }, + + // 删除绘画 + deleteImage: async (id: number) => { + return await request.delete({ url: `/ai/image/delete?id=` + id }) + } +} diff --git a/src/api/ai/model/apiKey/index.ts b/src/api/ai/model/apiKey/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed94836e54669604a9bc53114c594872a568d090 --- /dev/null +++ b/src/api/ai/model/apiKey/index.ts @@ -0,0 +1,44 @@ +import request from '@/config/axios' + +// AI API 密钥 VO +export interface ApiKeyVO { + id: number // 编号 + name: string // 名称 + apiKey: string // 密钥 + platform: string // 平台 + url: string // 自定义 API 地址 + status: number // 状态 +} + +// AI API 密钥 API +export const ApiKeyApi = { + // 查询 API 密钥分页 + getApiKeyPage: async (params: any) => { + return await request.get({ url: `/ai/api-key/page`, params }) + }, + + // 获得 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 }) + }, + + // 新增 API 密钥 + createApiKey: async (data: ApiKeyVO) => { + return await request.post({ url: `/ai/api-key/create`, data }) + }, + + // 修改 API 密钥 + updateApiKey: async (data: ApiKeyVO) => { + return await request.put({ url: `/ai/api-key/update`, data }) + }, + + // 删除 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 0000000000000000000000000000000000000000..c2ef4c8d308ac26d5a735a744695ec0a2db74745 --- /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 0000000000000000000000000000000000000000..a9fce13c685675d2d8702b38323bc46e161c1663 --- /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/api/ai/music/index.ts b/src/api/ai/music/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9415d3a2f769a7e3ee46ebdd87c17e0bbb22a8c6 --- /dev/null +++ b/src/api/ai/music/index.ts @@ -0,0 +1,40 @@ +import request from '@/config/axios' + +// AI 音乐 VO +export interface MusicVO { + id: number // 编号 + userId: number // 用户编号 + title: string // 音乐名称 + lyric: string // 歌词 + imageUrl: string // 图片地址 + audioUrl: string // 音频地址 + videoUrl: string // 视频地址 + status: number // 音乐状态 + gptDescriptionPrompt: string // 描述词 + prompt: string // 提示词 + platform: string // 模型平台 + model: string // 模型 + generateMode: number // 生成模式 + tags: string // 音乐风格标签 + publicStatus: boolean // 是否发布 + taskId: string // 任务id + errorMessage: string // 错误信息 +} + +// AI 音乐 API +export const MusicApi = { + // 查询音乐分页 + getMusicPage: async (params: any) => { + return await request.get({ url: `/ai/music/page`, params }) + }, + + // 修改音乐 + updateMusic: async (data: MusicVO) => { + return await request.put({ url: `/ai/music/update`, data }) + }, + + // 删除音乐 + deleteMusic: async (id: number) => { + return await request.delete({ url: `/ai/music/delete?id=` + id }) + } +} diff --git a/src/assets/ai/clear.svg b/src/assets/ai/clear.svg new file mode 100644 index 0000000000000000000000000000000000000000..e75a4e8a608d6ac2d0aa1468c97948f8537606c2 --- /dev/null +++ b/src/assets/ai/clear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ai/copy-style2.svg b/src/assets/ai/copy-style2.svg new file mode 100644 index 0000000000000000000000000000000000000000..2d56a87f6b5b7a87a0981d099689145222c5fdf6 --- /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 0000000000000000000000000000000000000000..f51f8d81ceead09600895c212fce2c5a349a325e --- /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 0000000000000000000000000000000000000000..d2ee18edaded6d19f2ec781714a4f446d7331e75 --- /dev/null +++ b/src/assets/ai/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ai/gpt.svg b/src/assets/ai/gpt.svg new file mode 100644 index 0000000000000000000000000000000000000000..603e2e95a1fc9fde99f957b7180c0ae109e664f0 --- /dev/null +++ b/src/assets/ai/gpt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ContentWrap/src/ContentWrap.vue b/src/components/ContentWrap/src/ContentWrap.vue index 454e95c995665395fc5a4f377614e2837a2a141d..c75e4b71fc9cdbcba84b4ea56a750f59c0c665f8 100644 --- a/src/components/ContentWrap/src/ContentWrap.vue +++ b/src/components/ContentWrap/src/ContentWrap.vue @@ -10,12 +10,13 @@ const prefixCls = getPrefixCls('content-wrap') defineProps({ title: propTypes.string.def(''), - message: propTypes.string.def('') + message: propTypes.string.def(''), + bodyStyle: propTypes.object.def({ padding: '20px' }) }) diff --git a/src/components/FormCreate/src/components/useApiSelect.tsx b/src/components/FormCreate/src/components/useApiSelect.tsx index a67e6f68e6ec998a04c8f8cad52f869c9703b9f4..29cd3027a4eaba271dd08bddc82d7c644c2f45b3 100644 --- a/src/components/FormCreate/src/components/useApiSelect.tsx +++ b/src/components/FormCreate/src/components/useApiSelect.tsx @@ -27,6 +27,11 @@ export const useApiSelect = (option: ApiSelectProps) => { type: String, default: 'GET' }, + // 选项解析函数 + parseFunc: { + type: String, + default: '' + }, // 请求参数 data: { type: String, @@ -41,35 +46,121 @@ export const useApiSelect = (option: ApiSelectProps) => { multiple: { type: Boolean, default: false + }, + // 是否远程搜索 + remote: { + type: Boolean, + default: false + }, + // 远程搜索时携带的参数 + remoteField: { + type: String, + default: 'label' } }, setup(props) { const attrs = useAttrs() const options = ref([]) // 下拉数据 + const loading = ref(false) // 是否正在从远程获取数据 + const queryParam = ref() // 当前输入的值 const getOptions = async () => { options.value = [] // 接口选择器 if (isEmpty(props.url)) { return } - let data = [] switch (props.method) { case 'GET': - data = await request.get({ url: props.url }) + let url: string = props.url + if (props.remote) { + url = `${url}?${props.remoteField}=${queryParam.value}` + } + parseOptions(await request.get({ url: url })) break case 'POST': - data = await request.post({ url: props.url, data: jsonParse(props.data) }) + const data: any = jsonParse(props.data) + if (props.remote) { + data[props.remoteField] = queryParam.value + } + parseOptions(await request.post({ url: props.url, data: data })) break } + } + + function parseOptions(data: any) { + // 情况一:如果有自定义解析函数优先使用自定义解析 + if (!isEmpty(props.parseFunc)) { + options.value = parseFunc()?.(data) + return + } + // 情况二:返回的直接是一个列表 + if (Array.isArray(data)) { + parseOptions0(data) + return + } + // 情况二:返回的是分页数据,尝试读取 list + data = data.list + if (!!data && Array.isArray(data)) { + parseOptions0(data) + return + } + // 情况三:不是 yudao-vue-pro 标准返回 + console.warn( + `接口[${props.url}] 返回结果不是 yudao-vue-pro 标准返回建议采用自定义解析函数处理` + ) + } + function parseOptions0(data: any[]) { if (Array.isArray(data)) { options.value = data.map((item: any) => ({ - label: item[props.labelField], - value: item[props.valueField] + label: parseExpression(item, props.labelField), + value: parseExpression(item, props.valueField) })) return } - console.error(`接口[${props.url}] 返回结果不是一个数组`) + console.warn(`接口[${props.url}] 返回结果不是一个数组`) + } + + function parseFunc() { + let parse: any = null + if (!!props.parseFunc) { + // 解析字符串函数 + parse = new Function(`return ${props.parseFunc}`)() + } + return parse + } + + function parseExpression(data: any, template: string) { + // 检测是否使用了表达式 + if (template.indexOf('${') === -1) { + return data[template] + } + // 正则表达式匹配模板字符串中的 ${...} + const pattern = /\$\{([^}]*)}/g + // 使用replace函数配合正则表达式和回调函数来进行替换 + return template.replace(pattern, (_, expr) => { + // expr 是匹配到的 ${} 内的表达式(这里是属性名),从 data 中获取对应的值 + const result = data[expr.trim()] // 去除前后空白,以防用户输入带空格的属性名 + if (!result) { + console.warn( + `接口选择器选项模版[${template}][${expr.trim()}] 解析值失败结果为[${result}], 请检查属性名称是否存在于接口返回值中,存在则忽略此条!!!` + ) + } + return result + }) + } + + const remoteMethod = async (query: any) => { + if (!query) { + return + } + loading.value = true + try { + queryParam.value = query + await getOptions() + } finally { + loading.value = false + } } onMounted(async () => { @@ -80,15 +171,29 @@ export const useApiSelect = (option: ApiSelectProps) => { if (props.multiple) { // fix:多写此步是为了解决 multiple 属性问题 return ( - + {options.value.map((item, index) => ( ))} ) } + debugger return ( - + {options.value.map((item, index) => ( ))} diff --git a/src/components/FormCreate/src/config/selectRule.ts b/src/components/FormCreate/src/config/selectRule.ts index 281d37392d37f6a4d6fffe32fc537cdcd7855b08..a6f3841fa6ada9c55ec9b06327a2130f624be523 100644 --- a/src/components/FormCreate/src/config/selectRule.ts +++ b/src/components/FormCreate/src/config/selectRule.ts @@ -13,12 +13,30 @@ const selectRule = [ control: [ { value: 'select', - condition: '=', + condition: '==', method: 'hidden', - rule: ['multiple'] + rule: [ + 'multiple', + 'clearable', + 'collapseTags', + 'multipleLimit', + 'allowCreate', + 'filterable', + 'noMatchText', + 'remote', + 'remoteMethod', + 'reserveKeyword', + 'defaultFirstOption', + 'automaticDropdown' + ] } ] }, + { + type: 'switch', + field: 'filterable', + title: '是否可搜索' + }, { type: 'switch', field: 'multiple', title: '是否多选' }, { type: 'switch', @@ -43,27 +61,12 @@ const selectRule = [ title: 'autocomplete 属性' }, { type: 'input', field: 'placeholder', title: '占位符' }, - { - type: 'switch', - field: 'filterable', - title: '是否可搜索' - }, { type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' }, { type: 'input', field: 'noMatchText', title: '搜索条件无匹配时显示的文字' }, - { - type: 'switch', - field: 'remote', - title: '其中的选项是否从服务器远程加载' - }, - { - type: 'Struct', - field: 'remoteMethod', - title: '自定义远程搜索方法' - }, { type: 'input', field: 'noDataText', title: '选项为空时显示的文字' }, { type: 'switch', @@ -130,6 +133,7 @@ const apiSelectRule = [ type: 'input', field: 'labelField', title: 'label 属性', + info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', props: { placeholder: 'nickname' } @@ -138,9 +142,39 @@ const apiSelectRule = [ type: 'input', field: 'valueField', title: 'value 属性', + info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', props: { placeholder: 'id' } + }, + { + type: 'input', + field: 'parseFunc', + title: '选项解析函数', + info: `data 为接口返回值,需要写一个匿名函数解析返回值为选择器 options 列表 + (data: any)=>{ label: string; value: any }[]`, + props: { + autosize: true, + rows: { minRows: 2, maxRows: 6 }, + type: 'textarea', + placeholder: ` + function (data) { + console.log(data) + return data.list.map(item=> ({label: item.nickname,value: item.id})) + }` + } + }, + { + type: 'switch', + field: 'remote', + info: '是否可搜索', + title: '其中的选项是否从服务器远程加载' + }, + { + type: 'input', + field: 'remoteField', + title: '请求参数', + info: '远程请求时请求携带的参数名称,如:name' } ] diff --git a/src/components/FormCreate/src/config/useDictSelectRule.ts b/src/components/FormCreate/src/config/useDictSelectRule.ts index 3db630bcfb0ef17b55a3c237f328062c8284edca..5c5e8cadb5b9491cc427108c0c37f0e3dfa1df40 100644 --- a/src/components/FormCreate/src/config/useDictSelectRule.ts +++ b/src/components/FormCreate/src/config/useDictSelectRule.ts @@ -2,6 +2,7 @@ import { generateUUID } from '@/utils' import * as DictDataApi from '@/api/system/dict/dict.type' import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils' import { selectRule } from '@/components/FormCreate/src/config/selectRule' +import { cloneDeep } from 'lodash-es' /** * 字典选择器规则,如果规则使用到动态数据则需要单独配置不能使用 useSelectRule @@ -9,6 +10,7 @@ import { selectRule } from '@/components/FormCreate/src/config/selectRule' export const useDictSelectRule = () => { const label = '字典选择器' const name = 'DictSelect' + const rules = cloneDeep(selectRule) const dictOptions = ref<{ label: string; value: string }[]>([]) // 字典类型下拉数据 onMounted(async () => { const data = await DictDataApi.getSimpleDictTypeList() @@ -55,7 +57,7 @@ export const useDictSelectRule = () => { { label: '布尔值', value: 'bool' } ] }, - ...selectRule + ...rules ]) } } diff --git a/src/components/FormCreate/src/config/useSelectRule.ts b/src/components/FormCreate/src/config/useSelectRule.ts index bf20588db1e0fece33fd10f2fb7e16cae198c4d0..ff21a223479ac061b1b7f5799acdffec2dfcb80e 100644 --- a/src/components/FormCreate/src/config/useSelectRule.ts +++ b/src/components/FormCreate/src/config/useSelectRule.ts @@ -2,6 +2,7 @@ import { generateUUID } from '@/utils' import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils' import { selectRule } from '@/components/FormCreate/src/config/selectRule' import { SelectRuleOption } from '@/components/FormCreate/src/type' +import { cloneDeep } from 'lodash-es' /** * 通用选择器规则 hook @@ -11,6 +12,7 @@ import { SelectRuleOption } from '@/components/FormCreate/src/type' export const useSelectRule = (option: SelectRuleOption) => { const label = option.label const name = option.name + const rules = cloneDeep(selectRule) return { icon: option.icon, label, @@ -28,7 +30,7 @@ export const useSelectRule = (option: SelectRuleOption) => { if (!option.props) { option.props = [] } - return localeProps(t, name + '.props', [makeRequiredRule(), ...option.props, ...selectRule]) + return localeProps(t, name + '.props', [makeRequiredRule(), ...option.props, ...rules]) } } } diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts index 69c8f31443789a666d2b77a8d5f0ec557810f6c6..53fee78e569c9586f544fc101a00a9efc0f4ea6d 100644 --- a/src/components/FormCreate/src/useFormCreateDesigner.ts +++ b/src/components/FormCreate/src/useFormCreateDesigner.ts @@ -71,9 +71,9 @@ export const useFormCreateDesigner = async (designer: Ref) => { */ const buildSystemMenu = () => { // 移除自带的下拉选择器组件,使用 currencySelectRule 替代 - designer.value?.removeMenuItem('select') - designer.value?.removeMenuItem('radio') - designer.value?.removeMenuItem('checkbox') + // designer.value?.removeMenuItem('select') + // designer.value?.removeMenuItem('radio') + // designer.value?.removeMenuItem('checkbox') const components = [userSelectRule, deptSelectRule, dictSelectRule, apiSelectRule0] const menu: Menu = { name: 'system', diff --git a/src/components/FormCreate/src/utils/index.ts b/src/components/FormCreate/src/utils/index.ts index e5480981e044053e23247796bc1f6c45f4dbeb2f..2d4a6fd752f509ecf22028f5ae9e501482347856 100644 --- a/src/components/FormCreate/src/utils/index.ts +++ b/src/components/FormCreate/src/utils/index.ts @@ -1,4 +1,3 @@ -// TODO puhui999: 借鉴一下 form-create-designer utils 方法 🤣 (导入不了只能先 copy 过来用下) export function makeRequiredRule() { return { type: 'Required', @@ -17,63 +16,3 @@ export const localeProps = (t, prefix, rules) => { return rule }) } - -export function upper(str) { - return str.replace(str[0], str[0].toLocaleUpperCase()) -} - -export function makeOptionsRule(t, to, userOptions) { - console.log(userOptions[0]) - const options = [ - { label: t('props.optionsType.struct'), value: 0 }, - { label: t('props.optionsType.json'), value: 1 }, - { label: '用户数据', value: 2 } - ] - - const control = [ - { - value: 0, - rule: [ - { - type: 'TableOptions', - field: 'formCreate' + upper(to).replace('.', '>'), - props: { defaultValue: [] } - } - ] - }, - { - value: 1, - rule: [ - { - type: 'Struct', - field: 'formCreate' + upper(to).replace('.', '>'), - props: { defaultValue: [] } - } - ] - }, - { - value: 2, - rule: [ - { - type: 'TableOptions', - field: 'formCreate' + upper(to).replace('.', '>'), - props: { modelValue: [] } - } - ] - } - ] - options.splice(0, 0) - control.push() - - return { - type: 'radio', - title: t('props.options'), - field: '_optionType', - value: 0, - options, - props: { - type: 'button' - }, - control - } -} diff --git a/src/components/Icon/src/Icon.vue b/src/components/Icon/src/Icon.vue index 4246539fb7cf212746f4195fdb180865f86f22c1..a90bb37e8dd534e47c7bf625b122e03050074626 100644 --- a/src/components/Icon/src/Icon.vue +++ b/src/components/Icon/src/Icon.vue @@ -22,7 +22,7 @@ const props = defineProps({ const elRef = ref(null) -const isLocal = computed(() => props.icon.startsWith('svg-icon:')) +const isLocal = computed(() => props.icon?.startsWith('svg-icon:')) const symbolId = computed(() => { return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon diff --git a/src/components/MarkdownView/index.vue b/src/components/MarkdownView/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..bf9ddd607a4c633b682ad7fd70d575381d506378 --- /dev/null +++ b/src/components/MarkdownView/index.vue @@ -0,0 +1,235 @@ + + + + + + + diff --git a/src/layout/Layout.vue b/src/layout/Layout.vue index 43f9b69dd70c169e156a965c4a95138be2fdd531..af51970792073a4b4fb7de6529165cbddce9e1e6 100644 --- a/src/layout/Layout.vue +++ b/src/layout/Layout.vue @@ -72,7 +72,7 @@ $prefix-cls: #{$namespace}-layout; .#{$prefix-cls} { background-color: var(--app-content-bg-color); :deep(.#{$elNamespace}-scrollbar__view) { - height: 100% !important; + height: 99% !important; } } diff --git a/src/layout/components/AppView.vue b/src/layout/components/AppView.vue index 44341873252407a55514c34dd632c5d81bc4fc45..88d5f0cce10f9afdde1fb4057dba702175dccef5 100644 --- a/src/layout/components/AppView.vue +++ b/src/layout/components/AppView.vue @@ -38,24 +38,24 @@ provide('reload', reload) :class="[ 'p-[var(--app-content-padding)] w-[calc(100%-var(--app-content-padding)-var(--app-content-padding))] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]', { - '!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]': + '!h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]': (fixedHeader && (layout === 'classic' || layout === 'topLeft' || layout === 'top') && footer) || (!tagsView && layout === 'top' && footer), - '!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height))]': + '!h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height))]': tagsView && layout === 'top' && footer, - '!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--top-tool-height)-var(--app-footer-height))]': + '!h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--top-tool-height)-var(--app-footer-height))]': !fixedHeader && layout === 'classic' && footer, - '!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]': + '!h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]': !fixedHeader && layout === 'topLeft' && footer, - '!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding))]': + '!h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding))]': fixedHeader && layout === 'cutMenu' && footer, - '!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding)-var(--tags-view-height))]': + '!h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding)-var(--tags-view-height))]': !fixedHeader && layout === 'cutMenu' && footer } ]" diff --git a/src/permission.ts b/src/permission.ts index d538303be5736fa1443ec0b776e834ea20ec8b4b..b04bc3c13338e4cede4890219e948ac4c604cfb4 100644 --- a/src/permission.ts +++ b/src/permission.ts @@ -83,7 +83,7 @@ router.beforeEach(async (to, from, next) => { const redirectPath = from.query.redirect || to.path // 修复跳转时不带参数的问题 const redirect = decodeURIComponent(redirectPath as string) - const { basePath, paramsObject: query } = parseURL(redirect) + const { paramsObject: query } = parseURL(redirect) const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect, query } next(nextData) } else { diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index bc62a3c4975fdf42740f7322ca0de91cac76e359..6da3b0a462e2022df12ba5439a9bb4b7ff2520d5 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -70,6 +70,26 @@ const remainingRouter: AppRouteRecordRaw[] = [ } ] }, + { + path: '/ai/music', + component: Layout, + redirect: '/index', + name: 'AIMusic', + meta: {}, + children: [ + { + path: 'index', + component: () => import('@/views/ai/music/components/index.vue'), + name: 'AIMusicIndex', + meta: { + title: 'AI 音乐', + icon: 'ep:home-filled', + noCache: false, + affix: true + } + } + ] + }, { path: '/user', component: Layout, diff --git a/src/store/modules/dict.ts b/src/store/modules/dict.ts index e239fb0086dced6b6958930e5355b1819cb320cd..1a45335cc42b3a3dcc7907b79f7edfa259df8f2e 100644 --- a/src/store/modules/dict.ts +++ b/src/store/modules/dict.ts @@ -4,7 +4,7 @@ import { store } from '../index' import { DictDataVO } from '@/api/system/dict/types' import { CACHE_KEY, useCache } from '@/hooks/web/useCache' const { wsCache } = useCache('sessionStorage') -import { getSimpleDictDataList } from '@/api/system/dict/dict.data' +// import { getSimpleDictDataList } from '@/api/system/dict/dict.data' export interface DictValueType { value: any @@ -45,7 +45,8 @@ export const useDictStore = defineStore('dict', { this.dictMap = dictMap this.isSetDict = true } else { - const res = await getSimpleDictDataList() + const res = [] + // const res = await getSimpleDictDataList() // 设置数据 const dictDataMap = new Map() res.forEach((dictData: DictDataVO) => { @@ -75,7 +76,8 @@ export const useDictStore = defineStore('dict', { }, async resetDict() { wsCache.delete(CACHE_KEY.DICT_CACHE) - const res = await getSimpleDictDataList() + const res = [] + // const res = await getSimpleDictDataList() // 设置数据 const dictDataMap = new Map() res.forEach((dictData: DictDataVO) => { diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 631a40b06e46122ff54f23ec1b58174861460679..1b6b9b3fec5b67e1e472477c4722ded735a65602 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -1,8 +1,8 @@ /** * 数据字典工具类 */ -import {useDictStoreWithOut} from '@/store/modules/dict' -import {ElementPlusInfoType} from '@/types/elementPlus' +import { useDictStoreWithOut } from '@/store/modules/dict' +import { ElementPlusInfoType } from '@/types/elementPlus' const dictStore = useDictStoreWithOut() @@ -24,6 +24,10 @@ export interface NumberDictDataType extends DictDataType { value: number } +export interface StringDictDataType extends DictDataType { + value: string +} + export const getDictOptions = (dictType: string) => { return dictStore.getDictByType(dictType) || [] } @@ -44,8 +48,11 @@ export const getIntDictOptions = (dictType: string): NumberDictDataType[] => { } export const getStrDictOptions = (dictType: string) => { - const dictOption: DictDataType[] = [] + // 获得通用的 DictDataType 列表 const dictOptions: DictDataType[] = getDictOptions(dictType) + // 转换成 string 类型的 StringDictDataType 类型 + // why 需要特殊转换:避免 IDEA 在 v-for="dict in getStrDictOptions(...)" 时,el-option 的 key 会告警 + const dictOption: StringDictDataType[] = [] dictOptions.forEach((dict: DictDataType) => { dictOption.push({ ...dict, @@ -209,5 +216,9 @@ export enum DICT_TYPE { // ========== ERP - 企业资源计划模块 ========== ERP_AUDIT_STATUS = 'erp_audit_status', // ERP 审批状态 - ERP_STOCK_RECORD_BIZ_TYPE = 'erp_stock_record_biz_type' // 库存明细的业务类型 + ERP_STOCK_RECORD_BIZ_TYPE = 'erp_stock_record_biz_type', // 库存明细的业务类型 + + // ========== AI - 人工智能模块 ========== + AI_PLATFORM = 'ai_platform', // AI 平台 + AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态 } diff --git a/src/utils/download.ts b/src/utils/download.ts index ab2001494094265a1b361962fd0518d6d67229d3..fe24ee2711d957b73a62603e688885ef1e51552f 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/ai/chat/ChatEmpty.vue b/src/views/ai/chat/ChatEmpty.vue new file mode 100644 index 0000000000000000000000000000000000000000..4268b3d7e8a8d84e8153f7fc8710f87afb2466ea --- /dev/null +++ b/src/views/ai/chat/ChatEmpty.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/views/ai/chat/Conversation.vue b/src/views/ai/chat/Conversation.vue new file mode 100644 index 0000000000000000000000000000000000000000..2afeb9ac0e8bbca841ea0c5a761aeb53fcd2631e --- /dev/null +++ b/src/views/ai/chat/Conversation.vue @@ -0,0 +1,521 @@ + + + + + + diff --git a/src/views/ai/chat/Message.vue b/src/views/ai/chat/Message.vue new file mode 100644 index 0000000000000000000000000000000000000000..942961ed3ef38d54d51df61966fb348eb2def3a5 --- /dev/null +++ b/src/views/ai/chat/Message.vue @@ -0,0 +1,322 @@ + + + + diff --git a/src/views/ai/chat/MessageLoading.vue b/src/views/ai/chat/MessageLoading.vue new file mode 100644 index 0000000000000000000000000000000000000000..f3198cbda61ae3e2799f70760f267fb629a18c86 --- /dev/null +++ b/src/views/ai/chat/MessageLoading.vue @@ -0,0 +1,15 @@ + + + + + diff --git a/src/views/ai/chat/MessageNewChat.vue b/src/views/ai/chat/MessageNewChat.vue new file mode 100644 index 0000000000000000000000000000000000000000..aac5f905fe10a86525c8eacf87f4592837932e14 --- /dev/null +++ b/src/views/ai/chat/MessageNewChat.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/src/views/ai/chat/components/ChatConversationUpdateForm.vue b/src/views/ai/chat/components/ChatConversationUpdateForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..c9f5c84945581600a6b5034dca926231b125b51d --- /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 0000000000000000000000000000000000000000..17b1693bd2f8d20e4ec58f10d97eecc4df4a48a2 --- /dev/null +++ b/src/views/ai/chat/components/Header.vue @@ -0,0 +1,48 @@ + + + + + + diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index bc846a3dc93ff44de2f366cb5cb227eddb354c22..0910497a04f9d37bcf07a0a5ad47493dc4fb5b92 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -1,125 +1,676 @@ + + diff --git a/src/views/ai/chat/manager/ChatConversationList.vue b/src/views/ai/chat/manager/ChatConversationList.vue new file mode 100644 index 0000000000000000000000000000000000000000..23933f01cd6f4a2b30d1ff1306a60d396c4becee --- /dev/null +++ b/src/views/ai/chat/manager/ChatConversationList.vue @@ -0,0 +1,163 @@ + + + diff --git a/src/views/ai/chat/manager/ChatMessageList.vue b/src/views/ai/chat/manager/ChatMessageList.vue new file mode 100644 index 0000000000000000000000000000000000000000..0d84184081b2088d9a79f7f2842f531fda50338b --- /dev/null +++ b/src/views/ai/chat/manager/ChatMessageList.vue @@ -0,0 +1,175 @@ + + + diff --git a/src/views/ai/chat/manager/index.vue b/src/views/ai/chat/manager/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..ca2d092460eb9eed28145db74a35d7eddb837601 --- /dev/null +++ b/src/views/ai/chat/manager/index.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/views/ai/chat/role/RoleCategoryList.vue b/src/views/ai/chat/role/RoleCategoryList.vue new file mode 100644 index 0000000000000000000000000000000000000000..a2587998d912d2fc381753b08213e8fca889f5f1 --- /dev/null +++ b/src/views/ai/chat/role/RoleCategoryList.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/views/ai/chat/role/RoleList.vue b/src/views/ai/chat/role/RoleList.vue new file mode 100644 index 0000000000000000000000000000000000000000..25206cff18a1f745afaa9df5d1c8883cced79d6f --- /dev/null +++ b/src/views/ai/chat/role/RoleList.vue @@ -0,0 +1,188 @@ + + + + + + diff --git a/src/views/ai/chat/role/index.vue b/src/views/ai/chat/role/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..f6096fc6cc30d8bb4af41dd3dc758270125133f4 --- /dev/null +++ b/src/views/ai/chat/role/index.vue @@ -0,0 +1,296 @@ + + + + + + + diff --git a/src/views/ai/image/ImageDetailDrawer.vue b/src/views/ai/image/ImageDetailDrawer.vue new file mode 100644 index 0000000000000000000000000000000000000000..fca087da15982f849abf6207926b1a4a86440b08 --- /dev/null +++ b/src/views/ai/image/ImageDetailDrawer.vue @@ -0,0 +1,141 @@ + + + + diff --git a/src/views/ai/image/ImageTask.vue b/src/views/ai/image/ImageTask.vue new file mode 100644 index 0000000000000000000000000000000000000000..728ca7a607f6c03272f01b5c7e48e969e7126b3e --- /dev/null +++ b/src/views/ai/image/ImageTask.vue @@ -0,0 +1,214 @@ + + + + + + diff --git a/src/views/ai/image/ImageTaskCard.vue b/src/views/ai/image/ImageTaskCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..a8620b497c0cb4ae4e66d2873277ce10ef248da7 --- /dev/null +++ b/src/views/ai/image/ImageTaskCard.vue @@ -0,0 +1,145 @@ + + + + diff --git a/src/views/ai/image/dall3/index.vue b/src/views/ai/image/dall3/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..92071b7b2e7338cb86df2d2bccebdf65a951cd35 --- /dev/null +++ b/src/views/ai/image/dall3/index.vue @@ -0,0 +1,398 @@ + + + + diff --git a/src/views/ai/image/index.vue b/src/views/ai/image/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..50d4b4a956140f77b37dfda7c232394732d9b564 --- /dev/null +++ b/src/views/ai/image/index.vue @@ -0,0 +1,105 @@ + + + + + + diff --git a/src/views/ai/image/manager/index.vue b/src/views/ai/image/manager/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..d7ac6c81a6a9965d3f1122172d065c55c23051be --- /dev/null +++ b/src/views/ai/image/manager/index.vue @@ -0,0 +1,247 @@ + + + diff --git a/src/views/ai/image/midjourney/index.vue b/src/views/ai/image/midjourney/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..7f288747681b2ecc301daa08083e0b77ab821fa6 --- /dev/null +++ b/src/views/ai/image/midjourney/index.vue @@ -0,0 +1,384 @@ + + + + diff --git a/src/views/ai/image/stable-diffusion/index.vue b/src/views/ai/image/stable-diffusion/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..7b1db7ab646154c7b072d33fca553292d242a98a --- /dev/null +++ b/src/views/ai/image/stable-diffusion/index.vue @@ -0,0 +1,297 @@ + + + + diff --git a/src/views/ai/model/apiKey/ApiKeyForm.vue b/src/views/ai/model/apiKey/ApiKeyForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..a8fc0128c7eedb73b20f316bc8165ccd99c10139 --- /dev/null +++ b/src/views/ai/model/apiKey/ApiKeyForm.vue @@ -0,0 +1,132 @@ + + diff --git a/src/views/ai/model/apiKey/index.vue b/src/views/ai/model/apiKey/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..6daf6a7d207468beb49d0d750bda90da7bfe9a73 --- /dev/null +++ b/src/views/ai/model/apiKey/index.vue @@ -0,0 +1,180 @@ + + + diff --git a/src/views/ai/model/chatModel/ChatModelForm.vue b/src/views/ai/model/chatModel/ChatModelForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..e3f785c05bcc6b5211e937d0a309ff0b211eb9d0 --- /dev/null +++ b/src/views/ai/model/chatModel/ChatModelForm.vue @@ -0,0 +1,181 @@ + + diff --git a/src/views/ai/model/chatModel/index.vue b/src/views/ai/model/chatModel/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..c5506746713a5fec35efbea186ad1d1e10955e1c --- /dev/null +++ b/src/views/ai/model/chatModel/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/src/views/ai/model/chatRole/ChatRoleForm.vue b/src/views/ai/model/chatRole/ChatRoleForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..c33d289def88a2ac38a771bbbff7bbfec2b1533b --- /dev/null +++ b/src/views/ai/model/chatRole/ChatRoleForm.vue @@ -0,0 +1,190 @@ + + diff --git a/src/views/ai/model/chatRole/index.vue b/src/views/ai/model/chatRole/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..e870a5563a307d52dabdae748452e89eb53fa19f --- /dev/null +++ b/src/views/ai/model/chatRole/index.vue @@ -0,0 +1,187 @@ + + + diff --git a/src/views/ai/music/components/index.vue b/src/views/ai/music/components/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..21272522fdb7e65fdc1d5eadf819244cf3306a27 --- /dev/null +++ b/src/views/ai/music/components/index.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/views/ai/music/components/list/audioBar/index.vue b/src/views/ai/music/components/list/audioBar/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..2b25e40f97b49d38122295ef4e1ec2ddc69fcb20 --- /dev/null +++ b/src/views/ai/music/components/list/audioBar/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/views/ai/music/components/list/index.vue b/src/views/ai/music/components/list/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..6dce9b8c831aa724832294473c835a173ce3a1c8 --- /dev/null +++ b/src/views/ai/music/components/list/index.vue @@ -0,0 +1,94 @@ + + + + + + diff --git a/src/views/ai/music/components/list/songCard/index.vue b/src/views/ai/music/components/list/songCard/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..dc1ffa8b1e8641cc552e514f37631663a0e19504 --- /dev/null +++ b/src/views/ai/music/components/list/songCard/index.vue @@ -0,0 +1,29 @@ + + + diff --git a/src/views/ai/music/components/list/songInfo/index.vue b/src/views/ai/music/components/list/songInfo/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..4832bfcb01be3c038ef6a4471993adfd468bf331 --- /dev/null +++ b/src/views/ai/music/components/list/songInfo/index.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/views/ai/music/components/mode/desc.vue b/src/views/ai/music/components/mode/desc.vue new file mode 100644 index 0000000000000000000000000000000000000000..4488461e9fc3da3b96efad819bdc4de909b8f20e --- /dev/null +++ b/src/views/ai/music/components/mode/desc.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/views/ai/music/components/mode/index.vue b/src/views/ai/music/components/mode/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..bb6cf11609016a95da026e2759ec89247c32c367 --- /dev/null +++ b/src/views/ai/music/components/mode/index.vue @@ -0,0 +1,44 @@ + + + diff --git a/src/views/ai/music/components/mode/lyric.vue b/src/views/ai/music/components/mode/lyric.vue new file mode 100644 index 0000000000000000000000000000000000000000..f774003a8d591598eda8a868df47c70510230c07 --- /dev/null +++ b/src/views/ai/music/components/mode/lyric.vue @@ -0,0 +1,83 @@ + + + diff --git a/src/views/ai/music/components/title/index.vue b/src/views/ai/music/components/title/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..a0658027a4c9950fd5b99da3cf4991d15623ec55 --- /dev/null +++ b/src/views/ai/music/components/title/index.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/views/ai/music/manager/index.vue b/src/views/ai/music/manager/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..46c6b80d553bdf4a8902d2e809eb9766f263e543 --- /dev/null +++ b/src/views/ai/music/manager/index.vue @@ -0,0 +1,215 @@ + + + diff --git a/src/views/crm/statistics/performance/components/ContractCountPerformance.vue b/src/views/crm/statistics/performance/components/ContractCountPerformance.vue index f911bb2d6a85f58feec1c0747fe96ce58d278701..fa5a897ba44e2e4920e7b979e9f5f71fce9db5c5 100644 --- a/src/views/crm/statistics/performance/components/ContractCountPerformance.vue +++ b/src/views/crm/statistics/performance/components/ContractCountPerformance.vue @@ -1,5 +1,4 @@ -