From abfde09eac076a73110d59d270c4edc7b7adef12 Mon Sep 17 00:00:00 2001
From: houzhongjian <houzhongyi@126.com>
Date: 星期一, 26 八月 2024 14:50:57 +0800
Subject: [PATCH] v1.0.1-sanpshot

---
 /dev/null    |  256 ---------------------------------------------------
 package.json |    6 
 2 files changed, 3 insertions(+), 259 deletions(-)

diff --git a/package.json b/package.json
index bf3c3e1..625d410 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "iailab-plat-ui-vue3",
-  "version": "1.0.0-snapshot",
+  "version": "1.0.1-sanpshot",
   "description": "基于vue3、vite4、element-plus、typesScript",
   "author": "iailab",
   "private": false,
@@ -119,7 +119,7 @@
     "stylelint-order": "^6.0.4",
     "terser": "^5.28.1",
     "typescript": "5.3.3",
-    "unocss": "^0.58.5",
+    "unocss": "^0.58.9",
     "unplugin-auto-import": "^0.16.7",
     "unplugin-element-plus": "^0.8.0",
     "unplugin-vue-components": "^0.25.2",
@@ -134,7 +134,7 @@
     "vue-eslint-parser": "^9.3.2",
     "vue-tsc": "^1.8.27"
   },
-  "license": "IAILAB",
+  "license": "MIT",
   "repository": {
     "type": "git",
     "url": "git+https://"
diff --git a/src/api/ai/chat/conversation/index.ts b/src/api/ai/chat/conversation/index.ts
deleted file mode 100644
index 6ce4482..0000000
--- a/src/api/ai/chat/conversation/index.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-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 数量
-  createTime?: Date // 创建时间
-  // 额外字段
-  systemMessage?: string // 角色设定
-  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: string) => {
-    return await request.delete({ url: `/ai/chat/conversation/delete-my?id=${id}` })
-  },
-
-  // 删除【我的】所有对话,置顶除外
-  deleteChatConversationMyByUnpinned: async () => {
-    return await request.delete({ url: `/ai/chat/conversation/delete-by-unpinned` })
-  },
-
-  // 获得【我的】聊天对话列表
-  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
deleted file mode 100644
index ef1196a..0000000
--- a/src/api/ai/chat/message/index.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-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 // 创建时间
-}
-
-// AI chat 聊天
-export const ChatMessageApi = {
-  // 消息列表
-  getChatMessageListByConversationId: async (conversationId: number | null) => {
-    return await request.get({
-      url: `/ai/chat/message/list-by-conversation-id?conversationId=${conversationId}`
-    })
-  },
-
-  // 发送 Stream 消息
-  // 为什么不用 axios 呢?因为它不支持 SSE 调用
-  sendChatMessageStream: 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
-    })
-  },
-
-  // 删除消息
-  deleteChatMessage: async (id: string) => {
-    return await request.delete({ url: `/ai/chat/message/delete?id=${id}` })
-  },
-
-  // 删除指定对话的消息
-  deleteByConversationId: async (conversationId: number) => {
-    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
deleted file mode 100644
index 2f276c7..0000000
--- a/src/api/ai/image/index.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-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: any // 配置 Map<string, string>
-  taskId: number // 任务编号
-  buttons: ImageMidjourneyButtonsVO[] // mj 操作按钮
-  createTime: Date // 创建时间
-  finishTime: Date // 完成时间
-}
-
-export interface ImageDrawReqVO {
-  platform: string // 平台
-  prompt: string // 提示词
-  model: string // 模型
-  style: string // 图像生成的风格
-  width: string // 图片宽度
-  height: string // 图片高度
-  options: object // 绘制参数,Map<String, String>
-}
-
-export interface ImageMidjourneyImagineReqVO {
-  prompt: string // 提示词
-  model: string // 模型 mj nijj
-  base64Array: string[] // size不能为空
-  width: string // 图片宽度
-  height: string // 图片高度
-  version: string // 版本
-}
-
-export interface ImageMidjourneyActionVO {
-  id: number // 图片编号
-  customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识
-}
-
-export interface ImageMidjourneyButtonsVO {
-  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
-export const ImageApi = {
-  // 获取【我的】绘图分页
-  getImagePageMy: async (params: any) => {
-    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}` })
-  },
-  // 获取【我的】绘图记录列表
-  getImageListMyByIds: async (ids: number[]) => {
-    return await request.get({ url: `/ai/image/my-list-by-ids`, params: { ids: ids.join(',') } })
-  },
-  // 生成图片
-  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: ImageMidjourneyActionVO) => {
-    return await request.post({ url: `/ai/image/midjourney/action`, data })
-  },
-
-  // ================ 绘图管理 ================
-
-  // 查询绘画分页
-  getImagePage: async (params: any) => {
-    return await request.get({ url: `/ai/image/page`, params })
-  },
-
-  // 更新绘画发布状态
-  updateImage: async (data: any) => {
-    return await request.put({ url: '/ai/image/update', data })
-  },
-
-  // 删除绘画
-  deleteImage: async (id: number) => {
-    return await request.delete({ url: `/ai/image/delete?id=` + id })
-  }
-}
diff --git a/src/api/ai/mindmap/index.ts b/src/api/ai/mindmap/index.ts
deleted file mode 100644
index def113b..0000000
--- a/src/api/ai/mindmap/index.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { getAccessToken } from '@/utils/auth'
-import { fetchEventSource } from '@microsoft/fetch-event-source'
-import { config } from '@/config/axios/config'
-
-export interface AiMindMapGenerateReqVO {
-  prompt: string
-}
-
-export const AiMindMapApi = {
-  generateMindMap: ({
-    data,
-    onClose,
-    onMessage,
-    onError,
-    ctrl
-  }: {
-    data: AiMindMapGenerateReqVO
-    onMessage?: (res: any) => void
-    onError?: (...args: any[]) => void
-    onClose?: (...args: any[]) => void
-    ctrl: AbortController
-  }) => {
-    const token = getAccessToken()
-    return fetchEventSource(`${config.base_url}/ai/mind-map/generate-stream`, {
-      method: 'post',
-      headers: {
-        'Content-Type': 'application/json',
-        Authorization: `Bearer ${token}`
-      },
-      openWhenHidden: true,
-      body: JSON.stringify(data),
-      onmessage: onMessage,
-      onerror: onError,
-      onclose: onClose,
-      signal: ctrl.signal
-    })
-  }
-}
diff --git a/src/api/ai/model/apiKey/index.ts b/src/api/ai/model/apiKey/index.ts
deleted file mode 100644
index ed94836..0000000
--- a/src/api/ai/model/apiKey/index.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-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
deleted file mode 100644
index c2ef4c8..0000000
--- a/src/api/ai/model/chatModel/index.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-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
deleted file mode 100644
index a9fce13..0000000
--- a/src/api/ai/model/chatRole/index.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-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
deleted file mode 100644
index 74b8526..0000000
--- a/src/api/ai/music/index.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-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 // 音乐风格标签
-  duration: number // 音乐时长
-  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: any) => {
-    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/api/ai/write/index.ts b/src/api/ai/write/index.ts
deleted file mode 100644
index 013f998..0000000
--- a/src/api/ai/write/index.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { fetchEventSource } from '@microsoft/fetch-event-source'
-
-import { getAccessToken } from '@/utils/auth'
-import { config } from '@/config/axios/config'
-import { AiWriteTypeEnum } from '@/views/ai/utils/constants'
-import request from '@/config/axios'
-
-export interface WriteVO {
-  type: AiWriteTypeEnum.WRITING | AiWriteTypeEnum.REPLY // 1:撰写 2:回复
-  prompt: string // 写作内容提示 1。撰写 2回复
-  originalContent: string // 原文
-  length: number // 长度
-  format: number // 格式
-  tone: number // 语气
-  language: number // 语言
-  userId?: number // 用户编号
-  platform?: string // 平台
-  model?: string // 模型
-  generatedContent?: string // 生成的内容
-  errorMessage?: string // 错误信息
-  createTime?: Date // 创建时间
-}
-
-export interface AiWritePageReqVO extends PageParam {
-  userId?: number // 用户编号
-  type?: AiWriteTypeEnum //  写作类型
-  platform?: string // 平台
-  createTime?: [string, string] // 创建时间
-}
-
-export interface AiWriteRespVo {
-  id: number
-  userId: number
-  type: number
-  platform: string
-  model: string
-  prompt: string
-  generatedContent: string
-  originalContent: string
-  length: number
-  format: number
-  tone: number
-  language: number
-  errorMessage: string
-  createTime: string
-}
-
-export const WriteApi = {
-  writeStream: ({
-    data,
-    onClose,
-    onMessage,
-    onError,
-    ctrl
-  }: {
-    data: WriteVO
-    onMessage?: (res: any) => void
-    onError?: (...args: any[]) => void
-    onClose?: (...args: any[]) => void
-    ctrl: AbortController
-  }) => {
-    const token = getAccessToken()
-    return fetchEventSource(`${config.base_url}/ai/write/generate-stream`, {
-      method: 'post',
-      headers: {
-        'Content-Type': 'application/json',
-        Authorization: `Bearer ${token}`
-      },
-      openWhenHidden: true,
-      body: JSON.stringify(data),
-      onmessage: onMessage,
-      onerror: onError,
-      onclose: onClose,
-      signal: ctrl.signal
-    })
-  },
-  // 获取写作列表
-  getWritePage: (params: AiWritePageReqVO) => {
-    return request.get<PageResult<AiWriteRespVo[]>>({ url: `/ai/write/page`, params })
-  },
-  // 删除写作
-  deleteWrite(id: number) {
-    return request.delete({ url: `/ai/write/delete`, params: { id } })
-  }
-}
diff --git a/src/views/ai/chat/index/components/conversation/ConversationList.vue b/src/views/ai/chat/index/components/conversation/ConversationList.vue
deleted file mode 100644
index 54940f8..0000000
--- a/src/views/ai/chat/index/components/conversation/ConversationList.vue
+++ /dev/null
@@ -1,472 +0,0 @@
-<!--  AI 对话  -->
-<template>
-  <el-aside width="260px" class="conversation-container h-100%">
-    <!-- 左顶部:对话 -->
-    <div class="h-100%">
-      <el-button class="w-1/1 btn-new-conversation" type="primary" @click="createConversation">
-        <Icon icon="ep:plus" class="mr-5px" />
-        新建对话
-      </el-button>
-
-      <!-- 左顶部:搜索对话 -->
-      <el-input
-        v-model="searchName"
-        size="large"
-        class="mt-10px search-input"
-        placeholder="搜索历史记录"
-        @keyup="searchConversation"
-      >
-        <template #prefix>
-          <Icon icon="ep:search" />
-        </template>
-      </el-input>
-
-      <!-- 左中间:对话列表 -->
-      <div class="conversation-list">
-        <!-- 情况一:加载中 -->
-        <el-empty v-if="loading" description="." :v-loading="loading" />
-        <!-- 情况二:按照 group 分组,展示聊天会话 list 列表 -->
-        <div v-for="conversationKey in Object.keys(conversationMap)" :key="conversationKey">
-          <div
-            class="conversation-item classify-title"
-            v-if="conversationMap[conversationKey].length"
-          >
-            <el-text class="mx-1" size="small" tag="b">{{ conversationKey }}</el-text>
-          </div>
-          <div
-            class="conversation-item"
-            v-for="conversation in conversationMap[conversationKey]"
-            :key="conversation.id"
-            @click="handleConversationClick(conversation.id)"
-            @mouseover="hoverConversationId = conversation.id"
-            @mouseout="hoverConversationId = ''"
-          >
-            <div
-              :class="
-                conversation.id === activeConversationId ? 'conversation active' : 'conversation'
-              "
-            >
-              <div class="title-wrapper">
-                <img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg" />
-                <span class="title">{{ conversation.title }}</span>
-              </div>
-              <div class="button-wrapper" v-show="hoverConversationId === conversation.id">
-                <el-button class="btn" link @click.stop="handleTop(conversation)">
-                  <el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon>
-                  <el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon>
-                </el-button>
-                <el-button class="btn" link @click.stop="updateConversationTitle(conversation)">
-                  <el-icon title="编辑">
-                    <Icon icon="ep:edit" />
-                  </el-icon>
-                </el-button>
-                <el-button class="btn" link @click.stop="deleteChatConversation(conversation)">
-                  <el-icon title="删除对话">
-                    <Icon icon="ep:delete" />
-                  </el-icon>
-                </el-button>
-              </div>
-            </div>
-          </div>
-        </div>
-        <!-- 底部占位  -->
-        <div class="h-160px w-100%"></div>
-      </div>
-    </div>
-
-    <!-- 左底部:工具栏 -->
-    <div class="tool-box">
-      <div @click="handleRoleRepository">
-        <Icon icon="ep:user" />
-        <el-text size="small">角色仓库</el-text>
-      </div>
-      <div @click="handleClearConversation">
-        <Icon icon="ep:delete" />
-        <el-text size="small">清空未置顶对话</el-text>
-      </div>
-    </div>
-
-    <!-- 角色仓库抽屉 -->
-    <el-drawer v-model="roleRepositoryOpen" title="角色仓库" size="754px">
-      <RoleRepository />
-    </el-drawer>
-  </el-aside>
-</template>
-
-<script setup lang="ts">
-import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
-import RoleRepository from '../role/RoleRepository.vue'
-import { Bottom, Top } from '@element-plus/icons-vue'
-import roleAvatarDefaultImg from '@/assets/ai/gpt.svg'
-
-const message = useMessage() // 消息弹窗
-
-// 定义属性
-const searchName = ref<string>('') // 对话搜索
-const activeConversationId = ref<number | null>(null) // 选中的对话,默认为 null
-const hoverConversationId = ref<number | null>(null) // 悬浮上去的对话
-const conversationList = ref([] as ChatConversationVO[]) // 对话列表
-const conversationMap = ref<any>({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前)
-const loading = ref<boolean>(false) // 加载中
-const loadingTime = ref<any>() // 加载中定时器
-
-// 定义组件 props
-const props = defineProps({
-  activeId: {
-    type: String || null,
-    required: true
-  }
-})
-
-// 定义钩子
-const emits = defineEmits([
-  'onConversationCreate',
-  'onConversationClick',
-  'onConversationClear',
-  'onConversationDelete'
-])
-
-/** 搜索对话 */
-const searchConversation = async (e) => {
-  // 恢复数据
-  if (!searchName.value.trim().length) {
-    conversationMap.value = await getConversationGroupByCreateTime(conversationList.value)
-  } else {
-    // 过滤
-    const filterValues = conversationList.value.filter((item) => {
-      return item.title.includes(searchName.value.trim())
-    })
-    conversationMap.value = await getConversationGroupByCreateTime(filterValues)
-  }
-}
-
-/** 点击对话 */
-const handleConversationClick = async (id: number) => {
-  // 过滤出选中的对话
-  const filterConversation = conversationList.value.filter((item) => {
-    return item.id === id
-  })
-  // 回调 onConversationClick
-  // noinspection JSVoidFunctionReturnValueUsed
-  const success = emits('onConversationClick', filterConversation[0])
-  // 切换对话
-  if (success) {
-    activeConversationId.value = id
-  }
-}
-
-/** 获取对话列表 */
-const getChatConversationList = async () => {
-  try {
-    // 加载中
-    loadingTime.value = setTimeout(() => {
-      loading.value = true
-    }, 50)
-
-    // 1.1 获取 对话数据
-    conversationList.value = await ChatConversationApi.getChatConversationMyList()
-    // 1.2 排序
-    conversationList.value.sort((a, b) => {
-      return b.createTime - a.createTime
-    })
-    // 1.3 没有任何对话情况
-    if (conversationList.value.length === 0) {
-      activeConversationId.value = null
-      conversationMap.value = {}
-      return
-    }
-
-    // 2. 对话根据时间分组(置顶、今天、一天前、三天前、七天前、30 天前)
-    conversationMap.value = await getConversationGroupByCreateTime(conversationList.value)
-  } finally {
-    // 清理定时器
-    if (loadingTime.value) {
-      clearTimeout(loadingTime.value)
-    }
-    // 加载完成
-    loading.value = false
-  }
-}
-
-/** 按照 creteTime 创建时间,进行分组 */
-const getConversationGroupByCreateTime = async (list: ChatConversationVO[]) => {
-  // 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前)
-  // noinspection NonAsciiCharacters
-  const groupMap = {
-    置顶: [],
-    今天: [],
-    一天前: [],
-    三天前: [],
-    七天前: [],
-    三十天前: []
-  }
-  // 当前时间的时间戳
-  const now = Date.now()
-  // 定义时间间隔常量(单位:毫秒)
-  const oneDay = 24 * 60 * 60 * 1000
-  const threeDays = 3 * oneDay
-  const sevenDays = 7 * oneDay
-  const thirtyDays = 30 * oneDay
-  for (const conversation of list) {
-    // 置顶
-    if (conversation.pinned) {
-      groupMap['置顶'].push(conversation)
-      continue
-    }
-    // 计算时间差(单位:毫秒)
-    const diff = now - conversation.createTime
-    // 根据时间间隔判断
-    if (diff < oneDay) {
-      groupMap['今天'].push(conversation)
-    } else if (diff < threeDays) {
-      groupMap['一天前'].push(conversation)
-    } else if (diff < sevenDays) {
-      groupMap['三天前'].push(conversation)
-    } else if (diff < thirtyDays) {
-      groupMap['七天前'].push(conversation)
-    } else {
-      groupMap['三十天前'].push(conversation)
-    }
-  }
-  return groupMap
-}
-
-/** 新建对话 */
-const createConversation = async () => {
-  // 1. 新建对话
-  const conversationId = await ChatConversationApi.createChatConversationMy(
-    {} as unknown as ChatConversationVO
-  )
-  // 2. 获取对话内容
-  await getChatConversationList()
-  // 3. 选中对话
-  await handleConversationClick(conversationId)
-  // 4. 回调
-  emits('onConversationCreate')
-}
-
-/** 修改对话的标题 */
-const updateConversationTitle = async (conversation: ChatConversationVO) => {
-  // 1. 二次确认
-  const { value } = await ElMessageBox.prompt('修改标题', {
-    inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
-    inputErrorMessage: '标题不能为空',
-    inputValue: conversation.title
-  })
-  // 2. 发起修改
-  await ChatConversationApi.updateChatConversationMy({
-    id: conversation.id,
-    title: value
-  } as ChatConversationVO)
-  message.success('重命名成功')
-  // 3. 刷新列表
-  await getChatConversationList()
-  // 4. 过滤当前切换的
-  const filterConversationList = conversationList.value.filter((item) => {
-    return item.id === conversation.id
-  })
-  if (filterConversationList.length > 0) {
-    // tip:避免切换对话
-    if (activeConversationId.value === filterConversationList[0].id) {
-      emits('onConversationClick', filterConversationList[0])
-    }
-  }
-}
-
-/** 删除聊天对话 */
-const deleteChatConversation = async (conversation: ChatConversationVO) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm(`是否确认删除对话 - ${conversation.title}?`)
-    // 发起删除
-    await ChatConversationApi.deleteChatConversationMy(conversation.id)
-    message.success('对话已删除')
-    // 刷新列表
-    await getChatConversationList()
-    // 回调
-    emits('onConversationDelete', conversation)
-  } catch {}
-}
-
-/** 清空对话 */
-const handleClearConversation = async () => {
-  try {
-    await message.confirm('确认后对话会全部清空,置顶的对话除外。')
-    await ChatConversationApi.deleteChatConversationMyByUnpinned()
-    ElMessage({
-      message: '操作成功!',
-      type: 'success'
-    })
-    // 清空 对话 和 对话内容
-    activeConversationId.value = null
-    // 获取 对话列表
-    await getChatConversationList()
-    // 回调 方法
-    emits('onConversationClear')
-  } catch {}
-}
-
-/** 对话置顶 */
-const handleTop = async (conversation: ChatConversationVO) => {
-  // 更新对话置顶
-  conversation.pinned = !conversation.pinned
-  await ChatConversationApi.updateChatConversationMy(conversation)
-  // 刷新对话
-  await getChatConversationList()
-}
-
-// ============ 角色仓库 ============
-
-/** 角色仓库抽屉 */
-const roleRepositoryOpen = ref<boolean>(false) // 角色仓库是否打开
-const handleRoleRepository = async () => {
-  roleRepositoryOpen.value = !roleRepositoryOpen.value
-}
-
-/** 监听选中的对话 */
-const { activeId } = toRefs(props)
-watch(activeId, async (newValue, oldValue) => {
-  activeConversationId.value = newValue as string
-})
-
-// 定义 public 方法
-defineExpose({ createConversation })
-
-/** 初始化 */
-onMounted(async () => {
-  // 获取 对话列表
-  await getChatConversationList()
-  // 默认选中
-  if (props.activeId) {
-    activeConversationId.value = props.activeId
-  } else {
-    // 首次默认选中第一个
-    if (conversationList.value.length) {
-      activeConversationId.value = conversationList.value[0].id
-      // 回调 onConversationClick
-      await emits('onConversationClick', conversationList.value[0])
-    }
-  }
-})
-</script>
-
-<style scoped lang="scss">
-.conversation-container {
-  position: relative;
-  display: flex;
-  flex-direction: column;
-  justify-content: space-between;
-  padding: 10px 10px 0;
-  overflow: hidden;
-
-  .btn-new-conversation {
-    padding: 18px 0;
-  }
-
-  .search-input {
-    margin-top: 20px;
-  }
-
-  .conversation-list {
-    overflow: auto;
-    height: 100%;
-
-    .classify-title {
-      padding-top: 10px;
-    }
-
-    .conversation-item {
-      margin-top: 5px;
-    }
-
-    .conversation {
-      display: flex;
-      flex-direction: row;
-      justify-content: space-between;
-      flex: 1;
-      padding: 0 5px;
-      cursor: pointer;
-      border-radius: 5px;
-      align-items: center;
-      line-height: 30px;
-
-      &.active {
-        background-color: #e6e6e6;
-
-        .button {
-          display: inline-block;
-        }
-      }
-
-      .title-wrapper {
-        display: flex;
-        flex-direction: row;
-        align-items: center;
-      }
-
-      .title {
-        padding: 2px 10px;
-        max-width: 220px;
-        font-size: 14px;
-        font-weight: 400;
-        color: rgba(0, 0, 0, 0.77);
-        overflow: hidden;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-      }
-
-      .avatar {
-        width: 25px;
-        height: 25px;
-        border-radius: 5px;
-        display: flex;
-        flex-direction: row;
-        justify-items: center;
-      }
-
-      // 对话编辑、删除
-      .button-wrapper {
-        right: 2px;
-        display: flex;
-        flex-direction: row;
-        justify-items: center;
-        color: #606266;
-
-        .btn {
-          margin: 0;
-        }
-      }
-    }
-  }
-
-  // 角色仓库、清空未设置对话
-  .tool-box {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    //width: 100%;
-    padding: 0 20px;
-    background-color: #f4f4f4;
-    box-shadow: 0 0 1px 1px rgba(228, 228, 228, 0.8);
-    line-height: 35px;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    color: var(--el-text-color);
-
-    > div {
-      display: flex;
-      align-items: center;
-      color: #606266;
-      padding: 0;
-      margin: 0;
-      cursor: pointer;
-
-      > span {
-        margin-left: 5px;
-      }
-    }
-  }
-}
-</style>
diff --git a/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue b/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue
deleted file mode 100644
index bff094f..0000000
--- a/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue
+++ /dev/null
@@ -1,145 +0,0 @@
-<template>
-  <Dialog title="设定" v-model="dialogVisible">
-    <el-form
-      ref="formRef"
-      :model="formData"
-      :rules="formRules"
-      label-width="130px"
-      v-loading="formLoading"
-    >
-      <el-form-item label="角色设定" prop="systemMessage">
-        <el-input
-          type="textarea"
-          v-model="formData.systemMessage"
-          rows="4"
-          placeholder="请输入角色设定"
-        />
-      </el-form-item>
-      <el-form-item label="模型" prop="modelId">
-        <el-select v-model="formData.modelId" placeholder="请选择模型">
-          <el-option
-            v-for="chatModel in chatModelList"
-            :key="chatModel.id"
-            :label="chatModel.name"
-            :value="chatModel.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="温度参数" prop="temperature">
-        <el-input-number
-          v-model="formData.temperature"
-          placeholder="请输入温度参数"
-          :min="0"
-          :max="2"
-          :precision="2"
-        />
-      </el-form-item>
-      <el-form-item label="回复数 Token 数" prop="maxTokens">
-        <el-input-number
-          v-model="formData.maxTokens"
-          placeholder="请输入回复数 Token 数"
-          :min="0"
-          :max="4096"
-        />
-      </el-form-item>
-      <el-form-item label="上下文数量" prop="maxContexts">
-        <el-input-number
-          v-model="formData.maxContexts"
-          placeholder="请输入上下文数量"
-          :min="0"
-          :max="20"
-        />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-<script setup lang="ts">
-import { CommonStatusEnum } from '@/utils/constants'
-import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel'
-import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
-
-/** AI 聊天对话的更新表单 */
-defineOptions({ name: 'ChatConversationUpdateForm' })
-
-const message = useMessage() // 消息弹窗
-
-const dialogVisible = ref(false) // 弹窗的是否展示
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const formData = ref({
-  id: undefined,
-  systemMessage: undefined,
-  modelId: undefined,
-  temperature: undefined,
-  maxTokens: undefined,
-  maxContexts: undefined
-})
-const formRules = reactive({
-  modelId: [{ required: true, message: '模型不能为空', trigger: 'blur' }],
-  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
-  temperature: [{ required: true, message: '温度参数不能为空', trigger: 'blur' }],
-  maxTokens: [{ required: true, message: '回复数 Token 数不能为空', trigger: 'blur' }],
-  maxContexts: [{ required: true, message: '上下文数量不能为空', trigger: 'blur' }]
-})
-const formRef = ref() // 表单 Ref
-const chatModelList = ref([] as ChatModelVO[]) // 聊天模型列表
-
-/** 打开弹窗 */
-const open = async (id: number) => {
-  dialogVisible.value = true
-  resetForm()
-  // 修改时,设置数据
-  if (id) {
-    formLoading.value = true
-    try {
-      const data = await ChatConversationApi.getChatConversationMy(id)
-      formData.value = Object.keys(formData.value).reduce((obj, key) => {
-        if (data.hasOwnProperty(key)) {
-          obj[key] = data[key]
-        }
-        return obj
-      }, {})
-    } finally {
-      formLoading.value = false
-    }
-  }
-  // 获得下拉数据
-  chatModelList.value = await ChatModelApi.getChatModelSimpleList(CommonStatusEnum.ENABLE)
-}
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  await formRef.value.validate()
-  // 提交请求
-  formLoading.value = true
-  try {
-    const data = formData.value as unknown as ChatConversationVO
-    await ChatConversationApi.updateChatConversationMy(data)
-    message.success('对话配置已更新')
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-    id: undefined,
-    systemMessage: undefined,
-    modelId: undefined,
-    temperature: undefined,
-    maxTokens: undefined,
-    maxContexts: undefined
-  }
-  formRef.value?.resetFields()
-}
-</script>
diff --git a/src/views/ai/chat/index/components/message/MessageList.vue b/src/views/ai/chat/index/components/message/MessageList.vue
deleted file mode 100644
index 2cc8407..0000000
--- a/src/views/ai/chat/index/components/message/MessageList.vue
+++ /dev/null
@@ -1,282 +0,0 @@
-<template>
-  <div ref="messageContainer" class="h-100% overflow-y-auto relative">
-    <div class="chat-list" v-for="(item, index) in list" :key="index">
-      <!-- 靠左 message:system、assistant 类型 -->
-      <div class="left-message message-item" v-if="item.type !== 'user'">
-        <div class="avatar">
-          <el-avatar :src="roleAvatar" />
-        </div>
-        <div class="message">
-          <div>
-            <el-text class="time">{{ formatDate(item.createTime) }}</el-text>
-          </div>
-          <div class="left-text-container" ref="markdownViewRef">
-            <MarkdownView class="left-text" :content="item.content" />
-          </div>
-          <div class="left-btns">
-            <el-button class="btn-cus" link @click="copyContent(item.content)">
-              <img class="btn-image" src="@/assets/ai/copy.svg" />
-            </el-button>
-            <el-button v-if="item.id > 0" class="btn-cus" link @click="onDelete(item.id)">
-              <img class="btn-image h-17px" src="@/assets/ai/delete.svg" />
-            </el-button>
-          </div>
-        </div>
-      </div>
-      <!-- 靠右 message:user 类型 -->
-      <div class="right-message message-item" v-if="item.type === 'user'">
-        <div class="avatar">
-          <el-avatar :src="userAvatar" />
-        </div>
-        <div class="message">
-          <div>
-            <el-text class="time">{{ formatDate(item.createTime) }}</el-text>
-          </div>
-          <div class="right-text-container">
-            <div class="right-text">{{ item.content }}</div>
-          </div>
-          <div class="right-btns">
-            <el-button class="btn-cus" link @click="copyContent(item.content)">
-              <img class="btn-image" src="@/assets/ai/copy.svg" />
-            </el-button>
-            <el-button class="btn-cus" link @click="onDelete(item.id)">
-              <img class="btn-image h-17px mr-12px" src="@/assets/ai/delete.svg" />
-            </el-button>
-            <el-button class="btn-cus" link @click="onRefresh(item)">
-              <el-icon size="17"><RefreshRight /></el-icon>
-            </el-button>
-            <el-button class="btn-cus" link @click="onEdit(item)">
-              <el-icon size="17"><Edit /></el-icon>
-            </el-button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-  <!-- 回到底部 -->
-  <div v-if="isScrolling" class="to-bottom" @click="handleGoBottom">
-    <el-button :icon="ArrowDownBold" circle />
-  </div>
-</template>
-<script setup lang="ts">
-import { PropType } from 'vue'
-import { formatDate } from '@/utils/formatTime'
-import MarkdownView from '@/components/MarkdownView/index.vue'
-import { useClipboard } from '@vueuse/core'
-import { ArrowDownBold, Edit, RefreshRight } from '@element-plus/icons-vue'
-import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
-import { ChatConversationVO } from '@/api/ai/chat/conversation'
-import { useUserStore } from '@/store/modules/user'
-import userAvatarDefaultImg from '@/assets/imgs/avatar.gif'
-import roleAvatarDefaultImg from '@/assets/ai/gpt.svg'
-
-const message = useMessage() // 消息弹窗
-const { copy } = useClipboard() // 初始化 copy 到粘贴板
-const userStore = useUserStore()
-
-// 判断“消息列表”滚动的位置(用于判断是否需要滚动到消息最下方)
-const messageContainer: any = ref(null)
-const isScrolling = ref(false) //用于判断用户是否在滚动
-
-const userAvatar = computed(() => userStore.user.avatar ?? userAvatarDefaultImg)
-const roleAvatar = computed(() => props.conversation.roleAvatar ?? roleAvatarDefaultImg)
-
-// 定义 props
-const props = defineProps({
-  conversation: {
-    type: Object as PropType<ChatConversationVO>,
-    required: true
-  },
-  list: {
-    type: Array as PropType<ChatMessageVO[]>,
-    required: true
-  }
-})
-
-const { list } = toRefs(props) // 消息列表
-
-const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']) // 定义 emits
-
-// ============ 处理对话滚动 ==============
-
-/** 滚动到底部 */
-const scrollToBottom = async (isIgnore?: boolean) => {
-  // 注意要使用 nextTick 以免获取不到 dom
-  await nextTick()
-  if (isIgnore || !isScrolling.value) {
-    messageContainer.value.scrollTop =
-      messageContainer.value.scrollHeight - messageContainer.value.offsetHeight
-  }
-}
-
-function handleScroll() {
-  const scrollContainer = messageContainer.value
-  const scrollTop = scrollContainer.scrollTop
-  const scrollHeight = scrollContainer.scrollHeight
-  const offsetHeight = scrollContainer.offsetHeight
-  if (scrollTop + offsetHeight < scrollHeight - 100) {
-    // 用户开始滚动并在最底部之上,取消保持在最底部的效果
-    isScrolling.value = true
-  } else {
-    // 用户停止滚动并滚动到最底部,开启保持到最底部的效果
-    isScrolling.value = false
-  }
-}
-
-/** 回到底部 */
-const handleGoBottom = async () => {
-  const scrollContainer = messageContainer.value
-  scrollContainer.scrollTop = scrollContainer.scrollHeight
-}
-
-/** 回到顶部 */
-const handlerGoTop = async () => {
-  const scrollContainer = messageContainer.value
-  scrollContainer.scrollTop = 0
-}
-
-defineExpose({ scrollToBottom, handlerGoTop }) // 提供方法给 parent 调用
-
-// ============ 处理消息操作 ==============
-
-/** 复制 */
-const copyContent = async (content) => {
-  await copy(content)
-  message.success('复制成功!')
-}
-
-/** 删除 */
-const onDelete = async (id) => {
-  // 删除 message
-  await ChatMessageApi.deleteChatMessage(id)
-  message.success('删除成功!')
-  // 回调
-  emits('onDeleteSuccess')
-}
-
-/** 刷新 */
-const onRefresh = async (message: ChatMessageVO) => {
-  emits('onRefresh', message)
-}
-
-/** 编辑 */
-const onEdit = async (message: ChatMessageVO) => {
-  emits('onEdit', message)
-}
-
-/** 初始化 */
-onMounted(async () => {
-  messageContainer.value.addEventListener('scroll', handleScroll)
-})
-</script>
-
-<style scoped lang="scss">
-.message-container {
-  position: relative;
-  overflow-y: scroll;
-}
-
-// 中间
-.chat-list {
-  display: flex;
-  flex-direction: column;
-  overflow-y: hidden;
-  padding: 0 20px;
-  .message-item {
-    margin-top: 50px;
-  }
-
-  .left-message {
-    display: flex;
-    flex-direction: row;
-  }
-
-  .right-message {
-    display: flex;
-    flex-direction: row-reverse;
-    justify-content: flex-start;
-  }
-
-  .message {
-    display: flex;
-    flex-direction: column;
-    text-align: left;
-    margin: 0 15px;
-
-    .time {
-      text-align: left;
-      line-height: 30px;
-    }
-
-    .left-text-container {
-      position: relative;
-      display: flex;
-      flex-direction: column;
-      overflow-wrap: break-word;
-      background-color: rgba(228, 228, 228, 0.8);
-      box-shadow: 0 0 0 1px rgba(228, 228, 228, 0.8);
-      border-radius: 10px;
-      padding: 10px 10px 5px 10px;
-
-      .left-text {
-        color: #393939;
-        font-size: 0.95rem;
-      }
-    }
-
-    .right-text-container {
-      display: flex;
-      flex-direction: row-reverse;
-
-      .right-text {
-        font-size: 0.95rem;
-        color: #fff;
-        display: inline;
-        background-color: #267fff;
-        box-shadow: 0 0 0 1px #267fff;
-        border-radius: 10px;
-        padding: 10px;
-        width: auto;
-        overflow-wrap: break-word;
-        white-space: pre-wrap;
-      }
-    }
-
-    .left-btns {
-      display: flex;
-      flex-direction: row;
-      margin-top: 8px;
-    }
-
-    .right-btns {
-      display: flex;
-      flex-direction: row-reverse;
-      margin-top: 8px;
-    }
-  }
-
-  // 复制、删除按钮
-  .btn-cus {
-    display: flex;
-    background-color: transparent;
-    align-items: center;
-
-    .btn-image {
-      height: 20px;
-    }
-  }
-
-  .btn-cus:hover {
-    cursor: pointer;
-    background-color: #f6f6f6;
-  }
-}
-
-// 回到底部
-.to-bottom {
-  position: absolute;
-  z-index: 1000;
-  bottom: 0;
-  right: 50%;
-}
-</style>
diff --git a/src/views/ai/chat/index/components/message/MessageListEmpty.vue b/src/views/ai/chat/index/components/message/MessageListEmpty.vue
deleted file mode 100644
index e4d4539..0000000
--- a/src/views/ai/chat/index/components/message/MessageListEmpty.vue
+++ /dev/null
@@ -1,83 +0,0 @@
-<!-- 消息列表为空时,展示 prompt 列表 -->
-<template>
-  <div class="chat-empty">
-    <!-- title -->
-    <div class="center-container">
-      <div class="title">工业互联网平台 AI</div>
-      <div class="role-list">
-        <div
-          class="role-item"
-          v-for="prompt in promptList"
-          :key="prompt.prompt"
-          @click="handlerPromptClick(prompt)"
-        >
-          {{ prompt.prompt }}
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-<script setup lang="ts">
-const promptList = [
-  {
-    prompt: '今天气怎么样?'
-  },
-  {
-    prompt: '写一首好听的诗歌?'
-  }
-] // prompt 列表
-
-const emits = defineEmits(['onPrompt'])
-
-/** 选中 prompt 点击 */
-const handlerPromptClick = async ({ prompt }) => {
-  emits('onPrompt', prompt)
-}
-</script>
-<style scoped lang="scss">
-.chat-empty {
-  position: relative;
-  display: flex;
-  flex-direction: row;
-  justify-content: center;
-  width: 100%;
-  height: 100%;
-
-  .center-container {
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-
-    .title {
-      font-size: 28px;
-      font-weight: bold;
-      text-align: center;
-    }
-
-    .role-list {
-      display: flex;
-      flex-direction: row;
-      flex-wrap: wrap;
-      align-items: center;
-      justify-content: center;
-      width: 460px;
-      margin-top: 20px;
-
-      .role-item {
-        display: flex;
-        justify-content: center;
-        width: 180px;
-        line-height: 50px;
-        border: 1px solid #e4e4e4;
-        border-radius: 10px;
-        margin: 10px;
-        cursor: pointer;
-      }
-
-      .role-item:hover {
-        background-color: rgba(243, 243, 243, 0.73);
-      }
-    }
-  }
-}
-</style>
diff --git a/src/views/ai/chat/index/components/message/MessageLoading.vue b/src/views/ai/chat/index/components/message/MessageLoading.vue
deleted file mode 100644
index f3198cb..0000000
--- a/src/views/ai/chat/index/components/message/MessageLoading.vue
+++ /dev/null
@@ -1,15 +0,0 @@
-<!-- message 加载页面 -->
-<template>
-  <div class="message-loading" >
-    <el-skeleton animated />
-  </div>
-</template>
-
-<script setup lang="ts">
-
-</script>
-<style scoped lang="scss">
-.message-loading {
-  padding: 30px 30px;
-}
-</style>
diff --git a/src/views/ai/chat/index/components/message/MessageNewConversation.vue b/src/views/ai/chat/index/components/message/MessageNewConversation.vue
deleted file mode 100644
index 40c3107..0000000
--- a/src/views/ai/chat/index/components/message/MessageNewConversation.vue
+++ /dev/null
@@ -1,46 +0,0 @@
-<!-- 无聊天对话时,在 message 区域,可以新增对话 -->
-<template>
-  <div class="new-chat">
-    <div class="box-center">
-      <div class="tip">点击下方按钮,开始你的对话吧</div>
-      <div class="btns">
-        <el-button type="primary" round @click="handlerNewChat">新建对话</el-button>
-      </div>
-    </div>
-  </div>
-</template>
-<script setup lang="ts">
-const emits = defineEmits(['onNewConversation'])
-
-/** 新建 conversation 聊天对话 */
-const handlerNewChat = () => {
-  emits('onNewConversation')
-}
-</script>
-<style scoped lang="scss">
-.new-chat {
-  display: flex;
-  flex-direction: row;
-  justify-content: center;
-  width: 100%;
-  height: 100%;
-
-  .box-center {
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-
-    .tip {
-      font-size: 14px;
-      color: #858585;
-    }
-
-    .btns {
-      display: flex;
-      flex-direction: row;
-      justify-content: center;
-      margin-top: 20px;
-    }
-  }
-}
-</style>
diff --git a/src/views/ai/chat/index/components/role/RoleCategoryList.vue b/src/views/ai/chat/index/components/role/RoleCategoryList.vue
deleted file mode 100644
index c02126d..0000000
--- a/src/views/ai/chat/index/components/role/RoleCategoryList.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<template>
-  <div class="category-list">
-    <div class="category" v-for="category in categoryList" :key="category">
-      <el-button
-        plain
-        round
-        size="small"
-        :type="category === active ? 'primary' : ''"
-        @click="handleCategoryClick(category)"
-      >
-        {{ category }}
-      </el-button>
-    </div>
-  </div>
-</template>
-<script setup lang="ts">
-import { PropType } from 'vue'
-
-// 定义属性
-defineProps({
-  categoryList: {
-    type: Array as PropType<string[]>,
-    required: true
-  },
-  active: {
-    type: String,
-    required: false,
-    default: '全部'
-  }
-})
-
-// 定义回调
-const emits = defineEmits(['onCategoryClick'])
-
-/** 处理分类点击事件 */
-const handleCategoryClick = async (category: string) => {
-  emits('onCategoryClick', category)
-}
-</script>
-<style scoped lang="scss">
-.category-list {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  align-items: center;
-
-  .category {
-    display: flex;
-    flex-direction: row;
-    margin-right: 10px;
-  }
-}
-</style>
diff --git a/src/views/ai/chat/index/components/role/RoleHeader.vue b/src/views/ai/chat/index/components/role/RoleHeader.vue
deleted file mode 100644
index 17b1693..0000000
--- a/src/views/ai/chat/index/components/role/RoleHeader.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<!-- header -->
-<template>
-  <el-header class="chat-header">
-    <div class="title">
-      {{ title }}
-    </div>
-    <div class="title-right">
-      <slot></slot>
-    </div>
-  </el-header>
-</template>
-
-<script setup lang="ts">
-// 设置组件属性
-defineProps({
-  title: {
-    type: String,
-    required: true
-  }
-})
-</script>
-
-<style scoped lang="scss">
-.chat-header {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-  align-items: center;
-  padding: 0 10px;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  background-color: #ececec;
-  width: 100%;
-
-  .title {
-    font-size: 20px;
-    font-weight: bold;
-    overflow: hidden;
-    color: #3e3e3e;
-    max-width: 220px;
-  }
-
-  .title-right {
-    display: flex;
-    flex-direction: row;
-  }
-}
-</style>
diff --git a/src/views/ai/chat/index/components/role/RoleList.vue b/src/views/ai/chat/index/components/role/RoleList.vue
deleted file mode 100644
index b148b22..0000000
--- a/src/views/ai/chat/index/components/role/RoleList.vue
+++ /dev/null
@@ -1,174 +0,0 @@
-<template>
-  <div class="card-list" ref="tabsRef" @scroll="handleTabsScroll">
-    <div class="card-item" v-for="role in roleList" :key="role.id">
-      <el-card class="card" body-class="card-body">
-        <!-- 更多操作 -->
-        <div class="more-container" v-if="showMore">
-          <el-dropdown @command="handleMoreClick">
-            <span class="el-dropdown-link">
-              <el-button type="text">
-                <el-icon><More /></el-icon>
-              </el-button>
-            </span>
-            <template #dropdown>
-              <el-dropdown-menu>
-                <el-dropdown-item :command="['edit', role]">
-                  <Icon icon="ep:edit" color="#787878" />编辑
-                </el-dropdown-item>
-                <el-dropdown-item :command="['delete', role]" style="color: red">
-                  <Icon icon="ep:delete" color="red" />删除
-                </el-dropdown-item>
-              </el-dropdown-menu>
-            </template>
-          </el-dropdown>
-        </div>
-        <!-- 角色信息 -->
-        <div>
-          <img class="avatar" :src="role.avatar" />
-        </div>
-        <div class="right-container">
-          <div class="content-container">
-            <div class="title">{{ role.name }}</div>
-            <div class="description">{{ role.description }}</div>
-          </div>
-          <div class="btn-container">
-            <el-button type="primary" size="small" @click="handleUseClick(role)">使用</el-button>
-          </div>
-        </div>
-      </el-card>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import {ChatRoleVO} from '@/api/ai/model/chatRole'
-import {PropType, ref} from 'vue'
-import {More} from '@element-plus/icons-vue'
-
-const tabsRef = ref<any>() // tabs ref
-
-// 定义属性
-const props = defineProps({
-  loading: {
-    type: Boolean,
-    required: true
-  },
-  roleList: {
-    type: Array as PropType<ChatRoleVO[]>,
-    required: true
-  },
-  showMore: {
-    type: Boolean,
-    required: false,
-    default: false
-  }
-})
-
-// 定义钩子
-const emits = defineEmits(['onDelete', 'onEdit', 'onUse', 'onPage'])
-
-/** 操作:编辑、删除 */
-const handleMoreClick = async (data) => {
-  const type = data[0]
-  const role = data[1]
-  if (type === 'delete') {
-    emits('onDelete', role)
-  } else {
-    emits('onEdit', role)
-  }
-}
-
-/** 选中 */
-const handleUseClick = (role) => {
-  emits('onUse', role)
-}
-
-/** 滚动 */
-const handleTabsScroll = async () => {
-  if (tabsRef.value) {
-    const { scrollTop, scrollHeight, clientHeight } = tabsRef.value
-    if (scrollTop + clientHeight >= scrollHeight - 20 && !props.loading) {
-      await emits('onPage')
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-// 重写 card 组件 body 样式
-.card-body {
-  max-width: 240px;
-  width: 240px;
-  padding: 15px 15px 10px 15px;
-
-  display: flex;
-  flex-direction: row;
-  justify-content: flex-start;
-  position: relative;
-}
-</style>
-<style scoped lang="scss">
-// 卡片列表
-.card-list {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  position: relative;
-  height: 100%;
-  overflow: auto;
-  padding: 0px 25px;
-  padding-bottom: 140px;
-  align-items: start;
-  align-content: flex-start;
-  justify-content: start;
-
-  .card {
-    display: inline-block;
-    margin-right: 20px;
-    border-radius: 10px;
-    margin-bottom: 20px;
-    position: relative;
-
-    .more-container {
-      position: absolute;
-      top: 0;
-      right: 12px;
-    }
-
-    .avatar {
-      width: 40px;
-      height: 40px;
-      border-radius: 10px;
-      overflow: hidden;
-    }
-
-    .right-container {
-      margin-left: 10px;
-      width: 100%;
-      //height: 100px;
-
-      .content-container {
-        height: 85px;
-
-        .title {
-          font-size: 18px;
-          font-weight: bold;
-          color: #3e3e3e;
-        }
-
-        .description {
-          margin-top: 10px;
-          font-size: 14px;
-          color: #6a6a6a;
-        }
-      }
-
-      .btn-container {
-        display: flex;
-        flex-direction: row-reverse;
-        margin-top: 2px;
-      }
-    }
-  }
-}
-</style>
diff --git a/src/views/ai/chat/index/components/role/RoleRepository.vue b/src/views/ai/chat/index/components/role/RoleRepository.vue
deleted file mode 100644
index 246dcb4..0000000
--- a/src/views/ai/chat/index/components/role/RoleRepository.vue
+++ /dev/null
@@ -1,289 +0,0 @@
-<!-- chat 角色仓库 -->
-<template>
-  <el-container class="role-container">
-    <ChatRoleForm ref="formRef" @success="handlerAddRoleSuccess" />
-    <!-- header  -->
-    <RoleHeader title="角色仓库" class="relative" />
-    <!--  main  -->
-    <el-main class="role-main">
-      <div class="search-container">
-        <!-- 搜索按钮 -->
-        <el-input
-          :loading="loading"
-          v-model="search"
-          class="search-input"
-          size="default"
-          placeholder="请输入搜索的内容"
-          :suffix-icon="Search"
-          @change="getActiveTabsRole"
-        />
-        <el-button
-          v-if="activeTab == 'my-role'"
-          type="primary"
-          @click="handlerAddRole"
-          class="ml-20px"
-        >
-          <Icon icon="ep:user" style="margin-right: 5px;" />
-          添加角色
-        </el-button>
-      </div>
-      <!-- tabs -->
-      <el-tabs v-model="activeTab" class="tabs" @tab-click="handleTabsClick">
-        <el-tab-pane class="role-pane" label="我的角色" name="my-role">
-          <RoleList
-            :loading="loading"
-            :role-list="myRoleList"
-            :show-more="true"
-            @on-delete="handlerCardDelete"
-            @on-edit="handlerCardEdit"
-            @on-use="handlerCardUse"
-            @on-page="handlerCardPage('my')"
-            class="mt-20px"
-          />
-        </el-tab-pane>
-        <el-tab-pane label="公共角色" name="public-role">
-          <RoleCategoryList
-            class="role-category-list"
-            :category-list="categoryList"
-            :active="activeCategory"
-            @on-category-click="handlerCategoryClick"
-          />
-          <RoleList
-            :role-list="publicRoleList"
-            @on-delete="handlerCardDelete"
-            @on-edit="handlerCardEdit"
-            @on-use="handlerCardUse"
-            @on-page="handlerCardPage('public')"
-            class="mt-20px"
-            loading
-          />
-        </el-tab-pane>
-      </el-tabs>
-    </el-main>
-  </el-container>
-</template>
-
-<script setup lang="ts">
-import {ref} from 'vue'
-import RoleHeader from './RoleHeader.vue'
-import RoleList from './RoleList.vue'
-import ChatRoleForm from '@/views/ai/model/chatRole/ChatRoleForm.vue'
-import RoleCategoryList from './RoleCategoryList.vue'
-import {ChatRoleApi, ChatRolePageReqVO, ChatRoleVO} from '@/api/ai/model/chatRole'
-import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation'
-import {Search} from '@element-plus/icons-vue'
-import {TabsPaneContext} from 'element-plus'
-
-const router = useRouter() // 路由对象
-
-// 属性定义
-const loading = ref<boolean>(false) // 加载中
-const activeTab = ref<string>('my-role') // 选中的角色 Tab
-const search = ref<string>('') // 加载中
-const myRoleParams = reactive({
-  pageNo: 1,
-  pageSize: 50
-})
-const myRoleList = ref<ChatRoleVO[]>([]) // my 分页大小
-const publicRoleParams = reactive({
-  pageNo: 1,
-  pageSize: 50
-})
-const publicRoleList = ref<ChatRoleVO[]>([]) // public 分页大小
-const activeCategory = ref<string>('全部') // 选择中的分类
-const categoryList = ref<string[]>([]) // 角色分类类别
-
-/** tabs 点击 */
-const handleTabsClick = async (tab: TabsPaneContext) => {
-  // 设置切换状态
-  activeTab.value = tab.paneName + ''
-  // 切换的时候重新加载数据
-  await getActiveTabsRole()
-}
-
-/** 获取 my role 我的角色 */
-const getMyRole = async (append?: boolean) => {
-  const params: ChatRolePageReqVO = {
-    ...myRoleParams,
-    name: search.value,
-    publicStatus: false
-  }
-  const { list } = await ChatRoleApi.getMyPage(params)
-  if (append) {
-    myRoleList.value.push.apply(myRoleList.value, list)
-  } else {
-    myRoleList.value = list
-  }
-}
-
-/** 获取 public role 公共角色 */
-const getPublicRole = async (append?: boolean) => {
-  const params: ChatRolePageReqVO = {
-    ...publicRoleParams,
-    category: activeCategory.value === '全部' ? '' : activeCategory.value,
-    name: search.value,
-    publicStatus: true
-  }
-  const { total, list } = await ChatRoleApi.getMyPage(params)
-  if (append) {
-    publicRoleList.value.push.apply(publicRoleList.value, list)
-  } else {
-    publicRoleList.value = list
-  }
-}
-
-/** 获取选中的 tabs 角色 */
-const getActiveTabsRole = async () => {
-  if (activeTab.value === 'my-role') {
-    myRoleParams.pageNo = 1
-    await getMyRole()
-  } else {
-    publicRoleParams.pageNo = 1
-    await getPublicRole()
-  }
-}
-
-/** 获取角色分类列表 */
-const getRoleCategoryList = async () => {
-  categoryList.value = ['全部', ...(await ChatRoleApi.getCategoryList())]
-}
-
-/** 处理分类点击 */
-const handlerCategoryClick = async (category: string) => {
-  // 切换选择的分类
-  activeCategory.value = category
-  // 筛选
-  await getActiveTabsRole()
-}
-
-/** 添加/修改操作 */
-const formRef = ref()
-const handlerAddRole = async () => {
-  formRef.value.open('my-create', null, '添加角色')
-}
-/** 编辑角色 */
-const handlerCardEdit = async (role) => {
-  formRef.value.open('my-update', role.id, '编辑角色')
-}
-
-/** 添加角色成功 */
-const handlerAddRoleSuccess = async (e) => {
-  // 刷新数据
-  await getActiveTabsRole()
-}
-
-/** 删除角色 */
-const handlerCardDelete = async (role) => {
-  await ChatRoleApi.deleteMy(role.id)
-  // 刷新数据
-  await getActiveTabsRole()
-}
-
-/** 角色分页:获取下一页 */
-const handlerCardPage = async (type) => {
-  try {
-    loading.value = true
-    if (type === 'public') {
-      publicRoleParams.pageNo++
-      await getPublicRole(true)
-    } else {
-      myRoleParams.pageNo++
-      await getMyRole(true)
-    }
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 选择 card 角色:新建聊天对话 */
-const handlerCardUse = async (role) => {
-  // 1. 创建对话
-  const data: ChatConversationVO = {
-    roleId: role.id
-  } as unknown as ChatConversationVO
-  const conversationId = await ChatConversationApi.createChatConversationMy(data)
-
-  // 2. 跳转页面
-  await router.push({
-    name: 'AiChat',
-    query: {
-      conversationId: conversationId
-    }
-  })
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  // 获取分类
-  await getRoleCategoryList()
-  // 获取 role 数据
-  await getActiveTabsRole()
-})
-</script>
-<!-- 覆盖 element ui css -->
-<style lang="scss">
-.el-tabs__content {
-  position: relative;
-  height: 100%;
-  overflow: hidden;
-}
-.el-tabs__nav-scroll {
-  margin: 10px 20px;
-}
-</style>
-<!-- 样式 -->
-<style scoped lang="scss">
-// 跟容器
-.role-container {
-  position: absolute;
-  width: 100%;
-  height: 100%;
-  margin: 0;
-  padding: 0;
-  left: 0;
-  right: 0;
-  top: 0;
-  bottom: 0;
-  background-color: #ffffff;
-  overflow: hidden;
-  display: flex;
-  flex-direction: column;
-
-  .role-main {
-    flex: 1;
-    overflow: hidden;
-    margin: 0;
-    padding: 0;
-    position: relative;
-
-    .search-container {
-      margin: 20px 20px 0px 20px;
-      position: absolute;
-      right: 0;
-      top: -5px;
-      z-index: 100;
-    }
-
-    .search-input {
-      width: 240px;
-    }
-
-    .tabs {
-      position: relative;
-      height: 100%;
-
-      .role-category-list {
-        margin: 0 27px;
-      }
-    }
-
-    .role-pane {
-      display: flex;
-      flex-direction: column;
-      height: 100%;
-      overflow-y: auto;
-      position: relative;
-    }
-  }
-}
-</style>
diff --git a/src/views/ai/chat/index/index.vue b/src/views/ai/chat/index/index.vue
deleted file mode 100644
index 28f1d65..0000000
--- a/src/views/ai/chat/index/index.vue
+++ /dev/null
@@ -1,772 +0,0 @@
-<template>
-  <el-container class="ai-layout">
-    <!-- 左侧:对话列表 -->
-    <ConversationList
-      :active-id="activeConversationId"
-      ref="conversationListRef"
-      @on-conversation-create="handleConversationCreateSuccess"
-      @on-conversation-click="handleConversationClick"
-      @on-conversation-clear="handleConversationClear"
-      @on-conversation-delete="handlerConversationDelete"
-    />
-    <!-- 右侧:对话详情 -->
-    <el-container class="detail-container">
-      <el-header class="header">
-        <div class="title">
-          {{ activeConversation?.title ? activeConversation?.title : '对话' }}
-          <span v-if="activeMessageList.length">({{ activeMessageList.length }})</span>
-        </div>
-        <div class="btns" v-if="activeConversation">
-          <el-button type="primary" bg plain size="small" @click="openChatConversationUpdateForm">
-            <span v-html="activeConversation?.modelName"></span>
-            <Icon icon="ep:setting" class="ml-10px" />
-          </el-button>
-          <el-button size="small" class="btn" @click="handlerMessageClear">
-            <Icon icon="heroicons-outline:archive-box-x-mark" color="#787878" />
-          </el-button>
-          <el-button size="small" class="btn">
-            <Icon icon="ep:download" color="#787878" />
-          </el-button>
-          <el-button size="small" class="btn" @click="handleGoTopMessage">
-            <Icon icon="ep:top" color="#787878" />
-          </el-button>
-        </div>
-      </el-header>
-
-      <!-- main:消息列表 -->
-      <el-main class="main-container">
-        <div>
-          <div class="message-container">
-            <!-- 情况一:消息加载中 -->
-            <MessageLoading v-if="activeMessageListLoading" />
-            <!-- 情况二:无聊天对话时 -->
-            <MessageNewConversation
-              v-if="!activeConversation"
-              @on-new-conversation="handleConversationCreate"
-            />
-            <!-- 情况三:消息列表为空 -->
-            <MessageListEmpty
-              v-if="!activeMessageListLoading && messageList.length === 0 && activeConversation"
-              @on-prompt="doSendMessage"
-            />
-            <!-- 情况四:消息列表不为空 -->
-            <MessageList
-              v-if="!activeMessageListLoading && messageList.length > 0"
-              ref="messageRef"
-              :conversation="activeConversation"
-              :list="messageList"
-              @on-delete-success="handleMessageDelete"
-              @on-edit="handleMessageEdit"
-              @on-refresh="handleMessageRefresh"
-            />
-          </div>
-        </div>
-      </el-main>
-
-      <!-- 底部 -->
-      <el-footer class="footer-container">
-        <form class="prompt-from">
-          <textarea
-            class="prompt-input"
-            v-model="prompt"
-            @keydown="handleSendByKeydown"
-            @input="handlePromptInput"
-            @compositionstart="onCompositionstart"
-            @compositionend="onCompositionend"
-            placeholder="问我任何问题...(Shift+Enter 换行,按下 Enter 发送)"
-          ></textarea>
-          <div class="prompt-btns">
-            <div>
-              <el-switch v-model="enableContext" />
-              <span class="ml-5px text-14px text-#8f8f8f">上下文</span>
-            </div>
-            <el-button
-              type="primary"
-              size="default"
-              @click="handleSendByButton"
-              :loading="conversationInProgress"
-              v-if="conversationInProgress == false"
-            >
-              {{ conversationInProgress ? '进行中' : '发送' }}
-            </el-button>
-            <el-button
-              type="danger"
-              size="default"
-              @click="stopStream()"
-              v-if="conversationInProgress == true"
-            >
-              停止
-            </el-button>
-          </div>
-        </form>
-      </el-footer>
-    </el-container>
-
-    <!-- 更新对话 Form -->
-    <ConversationUpdateForm
-      ref="conversationUpdateFormRef"
-      @success="handleConversationUpdateSuccess"
-    />
-  </el-container>
-</template>
-
-<script setup lang="ts">
-import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
-import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
-import ConversationList from './components/conversation/ConversationList.vue'
-import ConversationUpdateForm from './components/conversation/ConversationUpdateForm.vue'
-import MessageList from './components/message/MessageList.vue'
-import MessageListEmpty from './components/message/MessageListEmpty.vue'
-import MessageLoading from './components/message/MessageLoading.vue'
-import MessageNewConversation from './components/message/MessageNewConversation.vue'
-
-/** AI 聊天对话 列表 */
-defineOptions({ name: 'AiChat' })
-
-const route = useRoute() // 路由
-const message = useMessage() // 消息弹窗
-
-// 聊天对话
-const conversationListRef = ref()
-const activeConversationId = ref<number | null>(null) // 选中的对话编号
-const activeConversation = ref<ChatConversationVO | null>(null) // 选中的 Conversation
-const conversationInProgress = ref(false) // 对话是否正在进行中。目前只有【发送】消息时,会更新为 true,避免切换对话、删除对话等操作
-
-// 消息列表
-const messageRef = ref()
-const activeMessageList = ref<ChatMessageVO[]>([]) // 选中对话的消息列表
-const activeMessageListLoading = ref<boolean>(false) // activeMessageList 是否正在加载中
-const activeMessageListLoadingTimer = ref<any>() // activeMessageListLoading Timer 定时器。如果加载速度很快,就不进入加载中
-// 消息滚动
-const textSpeed = ref<number>(50) // Typing speed in milliseconds
-const textRoleRunning = ref<boolean>(false) // Typing speed in milliseconds
-
-// 发送消息输入框
-const isComposing = ref(false) // 判断用户是否在输入
-const conversationInAbortController = ref<any>() // 对话进行中 abort 控制器(控制 stream 对话)
-const inputTimeout = ref<any>() // 处理输入中回车的定时器
-const prompt = ref<string>() // prompt
-const enableContext = ref<boolean>(true) // 是否开启上下文
-// 接收 Stream 消息
-const receiveMessageFullText = ref('')
-const receiveMessageDisplayedText = ref('')
-
-// =========== 【聊天对话】相关 ===========
-
-/** 获取对话信息 */
-const getConversation = async (id: number | null) => {
-  if (!id) {
-    return
-  }
-  const conversation: ChatConversationVO = await ChatConversationApi.getChatConversationMy(id)
-  if (!conversation) {
-    return
-  }
-  activeConversation.value = conversation
-  activeConversationId.value = conversation.id
-}
-
-/**
- * 点击某个对话
- *
- * @param conversation 选中的对话
- * @return 是否切换成功
- */
-const handleConversationClick = async (conversation: ChatConversationVO) => {
-  // 对话进行中,不允许切换
-  if (conversationInProgress.value) {
-    message.alert('对话中,不允许切换!')
-    return false
-  }
-
-  // 更新选中的对话 id
-  activeConversationId.value = conversation.id
-  activeConversation.value = conversation
-  // 刷新 message 列表
-  await getMessageList()
-  // 滚动底部
-  scrollToBottom(true)
-  // 清空输入框
-  prompt.value = ''
-  return true
-}
-
-/** 删除某个对话*/
-const handlerConversationDelete = async (delConversation: ChatConversationVO) => {
-  // 删除的对话如果是当前选中的,那么就重置
-  if (activeConversationId.value === delConversation.id) {
-    await handleConversationClear()
-  }
-}
-/** 清空选中的对话 */
-const handleConversationClear = async () => {
-  // 对话进行中,不允许切换
-  if (conversationInProgress.value) {
-    message.alert('对话中,不允许切换!')
-    return false
-  }
-  activeConversationId.value = null
-  activeConversation.value = null
-  activeMessageList.value = []
-}
-
-/** 修改聊天对话 */
-const conversationUpdateFormRef = ref()
-const openChatConversationUpdateForm = async () => {
-  conversationUpdateFormRef.value.open(activeConversationId.value)
-}
-const handleConversationUpdateSuccess = async () => {
-  // 对话更新成功,刷新最新信息
-  await getConversation(activeConversationId.value)
-}
-
-/** 处理聊天对话的创建成功 */
-const handleConversationCreate = async () => {
-  // 创建对话
-  await conversationListRef.value.createConversation()
-}
-/** 处理聊天对话的创建成功 */
-const handleConversationCreateSuccess = async () => {
-  // 创建新的对话,清空输入框
-  prompt.value = ''
-}
-
-// =========== 【消息列表】相关 ===========
-
-/** 获取消息 message 列表 */
-const getMessageList = async () => {
-  try {
-    if (activeConversationId.value === null) {
-      return
-    }
-    // Timer 定时器,如果加载速度很快,就不进入加载中
-    activeMessageListLoadingTimer.value = setTimeout(() => {
-      activeMessageListLoading.value = true
-    }, 60)
-
-    // 获取消息列表
-    activeMessageList.value = await ChatMessageApi.getChatMessageListByConversationId(
-      activeConversationId.value
-    )
-
-    // 滚动到最下面
-    await nextTick()
-    await scrollToBottom()
-  } finally {
-    // time 定时器,如果加载速度很快,就不进入加载中
-    if (activeMessageListLoadingTimer.value) {
-      clearTimeout(activeMessageListLoadingTimer.value)
-    }
-    // 加载结束
-    activeMessageListLoading.value = false
-  }
-}
-
-/**
- * 消息列表
- *
- * 和 {@link #getMessageList()} 的差异是,把 systemMessage 考虑进去
- */
-const messageList = computed(() => {
-  if (activeMessageList.value.length > 0) {
-    return activeMessageList.value
-  }
-  // 没有消息时,如果有 systemMessage 则展示它
-  if (activeConversation.value?.systemMessage) {
-    return [
-      {
-        id: 0,
-        type: 'system',
-        content: activeConversation.value.systemMessage
-      }
-    ]
-  }
-  return []
-})
-
-/** 处理删除 message 消息 */
-const handleMessageDelete = () => {
-  if (conversationInProgress.value) {
-    message.alert('回答中,不能删除!')
-    return
-  }
-  // 刷新 message 列表
-  getMessageList()
-}
-
-/** 处理 message 清空 */
-const handlerMessageClear = async () => {
-  if (!activeConversationId.value) {
-    return
-  }
-  try {
-    // 确认提示
-    await message.delConfirm('确认清空对话消息?')
-    // 清空对话
-    await ChatMessageApi.deleteByConversationId(activeConversationId.value)
-    // 刷新 message 列表
-    activeMessageList.value = []
-  } catch {}
-}
-
-/** 回到 message 列表的顶部 */
-const handleGoTopMessage = () => {
-  messageRef.value.handlerGoTop()
-}
-
-// =========== 【发送消息】相关 ===========
-
-/** 处理来自 keydown 的发送消息 */
-const handleSendByKeydown = async (event) => {
-  // 判断用户是否在输入
-  if (isComposing.value) {
-    return
-  }
-  // 进行中不允许发送
-  if (conversationInProgress.value) {
-    return
-  }
-  const content = prompt.value?.trim() as string
-  if (event.key === 'Enter') {
-    if (event.shiftKey) {
-      // 插入换行
-      prompt.value += '\r\n'
-      event.preventDefault() // 防止默认的换行行为
-    } else {
-      // 发送消息
-      await doSendMessage(content)
-      event.preventDefault() // 防止默认的提交行为
-    }
-  }
-}
-
-/** 处理来自【发送】按钮的发送消息 */
-const handleSendByButton = () => {
-  doSendMessage(prompt.value?.trim() as string)
-}
-
-/** 处理 prompt 输入变化 */
-const handlePromptInput = (event) => {
-  // 非输入法 输入设置为 true
-  if (!isComposing.value) {
-    // 回车 event data 是 null
-    if (event.data == null) {
-      return
-    }
-    isComposing.value = true
-  }
-  // 清理定时器
-  if (inputTimeout.value) {
-    clearTimeout(inputTimeout.value)
-  }
-  // 重置定时器
-  inputTimeout.value = setTimeout(() => {
-    isComposing.value = false
-  }, 400)
-}
-// TODO @芋艿:是不是可以通过 @keydown.enter、@keydown.shift.enter 来实现,回车发送、shift+回车换行;主要看看,是不是可以简化 isComposing 相关的逻辑
-const onCompositionstart = () => {
-  isComposing.value = true
-}
-const onCompositionend = () => {
-  // console.log('输入结束...')
-  setTimeout(() => {
-    isComposing.value = false
-  }, 200)
-}
-
-/** 真正执行【发送】消息操作 */
-const doSendMessage = async (content: string) => {
-  // 校验
-  if (content.length < 1) {
-    message.error('发送失败,原因:内容为空!')
-    return
-  }
-  if (activeConversationId.value == null) {
-    message.error('还没创建对话,不能发送!')
-    return
-  }
-  // 清空输入框
-  prompt.value = ''
-  // 执行发送
-  await doSendMessageStream({
-    conversationId: activeConversationId.value,
-    content: content
-  } as ChatMessageVO)
-}
-
-/** 真正执行【发送】消息操作 */
-const doSendMessageStream = async (userMessage: ChatMessageVO) => {
-  // 创建 AbortController 实例,以便中止请求
-  conversationInAbortController.value = new AbortController()
-  // 标记对话进行中
-  conversationInProgress.value = true
-  // 设置为空
-  receiveMessageFullText.value = ''
-
-  try {
-    // 1.1 先添加两个假数据,等 stream 返回再替换
-    activeMessageList.value.push({
-      id: -1,
-      conversationId: activeConversationId.value,
-      type: 'user',
-      content: userMessage.content,
-      createTime: new Date()
-    } as ChatMessageVO)
-    activeMessageList.value.push({
-      id: -2,
-      conversationId: activeConversationId.value,
-      type: 'assistant',
-      content: '思考中...',
-      createTime: new Date()
-    } as ChatMessageVO)
-    // 1.2 滚动到最下面
-    await nextTick()
-    await scrollToBottom() // 底部
-    // 1.3 开始滚动
-    textRoll()
-
-    // 2. 发送 event stream
-    let isFirstChunk = true // 是否是第一个 chunk 消息段
-    await ChatMessageApi.sendChatMessageStream(
-      userMessage.conversationId,
-      userMessage.content,
-      conversationInAbortController.value,
-      enableContext.value,
-      async (res) => {
-        const { code, data, msg } = JSON.parse(res.data)
-        if (code !== 0) {
-          message.alert(`对话异常! ${msg}`)
-          return
-        }
-
-        // 如果内容为空,就不处理。
-        if (data.receive.content === '') {
-          return
-        }
-        // 首次返回需要添加一个 message 到页面,后面的都是更新
-        if (isFirstChunk) {
-          isFirstChunk = false
-          // 弹出两个假数据
-          activeMessageList.value.pop()
-          activeMessageList.value.pop()
-          // 更新返回的数据
-          activeMessageList.value.push(data.send)
-          activeMessageList.value.push(data.receive)
-        }
-        // debugger
-        receiveMessageFullText.value = receiveMessageFullText.value + data.receive.content
-        // 滚动到最下面
-        await scrollToBottom()
-      },
-      (error) => {
-        message.alert(`对话异常! ${error}`)
-        stopStream()
-      },
-      () => {
-        stopStream()
-      }
-    )
-  } catch {}
-}
-
-/** 停止 stream 流式调用 */
-const stopStream = async () => {
-  // tip:如果 stream 进行中的 message,就需要调用 controller 结束
-  if (conversationInAbortController.value) {
-    conversationInAbortController.value.abort()
-  }
-  // 设置为 false
-  conversationInProgress.value = false
-}
-
-/** 编辑 message:设置为 prompt,可以再次编辑 */
-const handleMessageEdit = (message: ChatMessageVO) => {
-  prompt.value = message.content
-}
-
-/** 刷新 message:基于指定消息,再次发起对话 */
-const handleMessageRefresh = (message: ChatMessageVO) => {
-  doSendMessage(message.content)
-}
-
-// ============== 【消息滚动】相关 =============
-
-/** 滚动到 message 底部 */
-const scrollToBottom = async (isIgnore?: boolean) => {
-  await nextTick()
-  if (messageRef.value) {
-    messageRef.value.scrollToBottom(isIgnore)
-  }
-}
-
-/** 自提滚动效果 */
-const textRoll = async () => {
-  let index = 0
-  try {
-    // 只能执行一次
-    if (textRoleRunning.value) {
-      return
-    }
-    // 设置状态
-    textRoleRunning.value = true
-    receiveMessageDisplayedText.value = ''
-    const task = async () => {
-      // 调整速度
-      const diff =
-        (receiveMessageFullText.value.length - receiveMessageDisplayedText.value.length) / 10
-      if (diff > 5) {
-        textSpeed.value = 10
-      } else if (diff > 2) {
-        textSpeed.value = 30
-      } else if (diff > 1.5) {
-        textSpeed.value = 50
-      } else {
-        textSpeed.value = 100
-      }
-      // 对话结束,就按 30 的速度
-      if (!conversationInProgress.value) {
-        textSpeed.value = 10
-      }
-
-      if (index < receiveMessageFullText.value.length) {
-        receiveMessageDisplayedText.value += receiveMessageFullText.value[index]
-        index++
-
-        // 更新 message
-        const lastMessage = activeMessageList.value[activeMessageList.value.length - 1]
-        lastMessage.content = receiveMessageDisplayedText.value
-        // 滚动到住下面
-        await scrollToBottom()
-        // 重新设置任务
-        timer = setTimeout(task, textSpeed.value)
-      } else {
-        // 不是对话中可以结束
-        if (!conversationInProgress.value) {
-          textRoleRunning.value = false
-          clearTimeout(timer)
-        } else {
-          // 重新设置任务
-          timer = setTimeout(task, textSpeed.value)
-        }
-      }
-    }
-    let timer = setTimeout(task, textSpeed.value)
-  } catch {}
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  // 如果有 conversationId 参数,则默认选中
-  if (route.query.conversationId) {
-    const id = route.query.conversationId as unknown as number
-    activeConversationId.value = id
-    await getConversation(id)
-  }
-
-  // 获取列表数据
-  activeMessageListLoading.value = true
-  await getMessageList()
-})
-</script>
-
-<style lang="scss" scoped>
-.ai-layout {
-  position: absolute;
-  flex: 1;
-  top: 0;
-  left: 0;
-  height: 100%;
-  width: 100%;
-}
-
-.conversation-container {
-  position: relative;
-  display: flex;
-  flex-direction: column;
-  justify-content: space-between;
-  padding: 10px 10px 0;
-
-  .btn-new-conversation {
-    padding: 18px 0;
-  }
-
-  .search-input {
-    margin-top: 20px;
-  }
-
-  .conversation-list {
-    margin-top: 20px;
-
-    .conversation {
-      display: flex;
-      flex-direction: row;
-      justify-content: space-between;
-      flex: 1;
-      padding: 0 5px;
-      margin-top: 10px;
-      cursor: pointer;
-      border-radius: 5px;
-      align-items: center;
-      line-height: 30px;
-
-      &.active {
-        background-color: #e6e6e6;
-
-        .button {
-          display: inline-block;
-        }
-      }
-
-      .title-wrapper {
-        display: flex;
-        flex-direction: row;
-        align-items: center;
-      }
-
-      .title {
-        padding: 5px 10px;
-        max-width: 220px;
-        font-size: 14px;
-        overflow: hidden;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-      }
-
-      .avatar {
-        width: 28px;
-        height: 28px;
-        display: flex;
-        flex-direction: row;
-        justify-items: center;
-      }
-
-      // 对话编辑、删除
-      .button-wrapper {
-        right: 2px;
-        display: flex;
-        flex-direction: row;
-        justify-items: center;
-        color: #606266;
-
-        .el-icon {
-          margin-right: 5px;
-        }
-      }
-    }
-  }
-
-  // 角色仓库、清空未设置对话
-  .tool-box {
-    line-height: 35px;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    color: var(--el-text-color);
-
-    > div {
-      display: flex;
-      align-items: center;
-      color: #606266;
-      padding: 0;
-      margin: 0;
-      cursor: pointer;
-
-      > span {
-        margin-left: 5px;
-      }
-    }
-  }
-}
-
-// 头部
-.detail-container {
-  background: #ffffff;
-
-  .header {
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    justify-content: space-between;
-    background: #fbfbfb;
-    box-shadow: 0 0 0 0 #dcdfe6;
-
-    .title {
-      font-size: 18px;
-      font-weight: bold;
-    }
-
-    .btns {
-      display: flex;
-      width: 300px;
-      flex-direction: row;
-      justify-content: flex-end;
-      //justify-content: space-between;
-
-      .btn {
-        padding: 10px;
-      }
-    }
-  }
-}
-
-// main 容器
-.main-container {
-  margin: 0;
-  padding: 0;
-  position: relative;
-  height: 100%;
-  width: 100%;
-
-  .message-container {
-    position: absolute;
-    top: 0;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    overflow-y: hidden;
-    padding: 0;
-    margin: 0;
-  }
-}
-
-// 底部
-.footer-container {
-  display: flex;
-  flex-direction: column;
-  height: auto;
-  margin: 0;
-  padding: 0;
-
-  .prompt-from {
-    display: flex;
-    flex-direction: column;
-    height: auto;
-    border: 1px solid #e3e3e3;
-    border-radius: 10px;
-    margin: 10px 20px 20px 20px;
-    padding: 9px 10px;
-  }
-
-  .prompt-input {
-    height: 80px;
-    //box-shadow: none;
-    border: none;
-    box-sizing: border-box;
-    resize: none;
-    padding: 0 2px;
-    overflow: auto;
-  }
-
-  .prompt-input:focus {
-    outline: none;
-  }
-
-  .prompt-btns {
-    display: flex;
-    justify-content: space-between;
-    padding-bottom: 0;
-    padding-top: 5px;
-  }
-}
-</style>
diff --git a/src/views/ai/chat/manager/ChatConversationList.vue b/src/views/ai/chat/manager/ChatConversationList.vue
deleted file mode 100644
index 23933f0..0000000
--- a/src/views/ai/chat/manager/ChatConversationList.vue
+++ /dev/null
@@ -1,163 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="用户编号" prop="userId">
-        <el-select
-          v-model="queryParams.userId"
-          clearable
-          placeholder="请输入用户编号"
-          class="!w-240px"
-        >
-          <el-option
-            v-for="item in userList"
-            :key="item.id"
-            :label="item.nickname"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="聊天编号" prop="title">
-        <el-input
-          v-model="queryParams.title"
-          placeholder="请输入聊天编号"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker
-          v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="对话编号" align="center" prop="id" width="180" fixed="left" />
-      <el-table-column label="对话标题" align="center" prop="title" width="180" fixed="left" />
-      <el-table-column label="用户" align="center" prop="userId" width="180">
-        <template #default="scope">
-          <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="角色" align="center" prop="roleName" width="180" />
-      <el-table-column label="模型标识" align="center" prop="model" width="180" />
-      <el-table-column label="消息数" align="center" prop="messageCount" />
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <el-table-column label="温度参数" align="center" prop="temperature" />
-      <el-table-column label="回复 Token 数" align="center" prop="maxTokens" width="120" />
-      <el-table-column label="上下文数量" align="center" prop="maxContexts" width="120" />
-      <el-table-column label="操作" align="center" width="180" fixed="right">
-        <template #default="scope">
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['ai:chat-conversation:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-</template>
-
-<script setup lang="ts">
-import { dateFormatter } from '@/utils/formatTime'
-import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
-import * as UserApi from '@/api/system/user'
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(true) // 列表的加载中
-const list = ref<ChatConversationVO[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  userId: undefined,
-  title: undefined,
-  createTime: []
-})
-const queryFormRef = ref() // 搜索的表单
-const userList = ref<UserApi.UserVO[]>([]) // 用户列表
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await ChatConversationApi.getChatConversationPage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ChatConversationApi.deleteChatConversationByAdmin(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  getList()
-  // 获得用户列表
-  userList.value = await UserApi.getSimpleUserList()
-})
-</script>
diff --git a/src/views/ai/chat/manager/ChatMessageList.vue b/src/views/ai/chat/manager/ChatMessageList.vue
deleted file mode 100644
index 0d84184..0000000
--- a/src/views/ai/chat/manager/ChatMessageList.vue
+++ /dev/null
@@ -1,175 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="对话编号" prop="conversationId">
-        <el-input
-          v-model="queryParams.conversationId"
-          placeholder="请输入对话编号"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="用户编号" prop="userId">
-        <el-select
-          v-model="queryParams.userId"
-          clearable
-          placeholder="请输入用户编号"
-          class="!w-240px"
-        >
-          <el-option
-            v-for="item in userList"
-            :key="item.id"
-            :label="item.nickname"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker
-          v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="消息编号" align="center" prop="id" width="180" fixed="left" />
-      <el-table-column
-        label="对话编号"
-        align="center"
-        prop="conversationId"
-        width="180"
-        fixed="left"
-      />
-      <el-table-column label="用户" align="center" prop="userId" width="180">
-        <template #default="scope">
-          <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="角色" align="center" prop="roleName" width="180" />
-      <el-table-column label="消息类型" align="center" prop="type" width="100" />
-      <el-table-column label="模型标识" align="center" prop="model" width="180" />
-      <el-table-column label="消息内容" align="center" prop="content" width="300" />
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <el-table-column label="回复消息编号" align="center" prop="replyId" width="180" />
-      <el-table-column label="携带上下文" align="center" prop="useContext" width="100">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.useContext" />
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" fixed="right">
-        <template #default="scope">
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['ai:chat-message:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-</template>
-
-<script setup lang="ts">
-import { dateFormatter } from '@/utils/formatTime'
-import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
-import * as UserApi from '@/api/system/user'
-import { DICT_TYPE } from '@/utils/dict'
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(true) // 列表的加载中
-const list = ref<ChatMessageVO[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  conversationId: undefined,
-  userId: undefined,
-  content: undefined,
-  createTime: []
-})
-const queryFormRef = ref() // 搜索的表单
-const userList = ref<UserApi.UserVO[]>([]) // 用户列表
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await ChatMessageApi.getChatMessagePage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ChatMessageApi.deleteChatMessageByAdmin(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  getList()
-  // 获得用户列表
-  userList.value = await UserApi.getSimpleUserList()
-})
-</script>
diff --git a/src/views/ai/chat/manager/index.vue b/src/views/ai/chat/manager/index.vue
deleted file mode 100644
index ca2d092..0000000
--- a/src/views/ai/chat/manager/index.vue
+++ /dev/null
@@ -1,20 +0,0 @@
-<template>
-  <ContentWrap>
-    <el-tabs>
-      <el-tab-pane label="对话列表">
-        <ChatConversationList />
-      </el-tab-pane>
-      <el-tab-pane label="消息列表">
-        <ChatMessageList />
-      </el-tab-pane>
-    </el-tabs>
-  </ContentWrap>
-</template>
-
-<script setup lang="ts">
-import ChatConversationList from './ChatConversationList.vue'
-import ChatMessageList from './ChatMessageList.vue'
-
-/** AI 聊天对话 列表 */
-defineOptions({ name: 'AiChatManager' })
-</script>
diff --git a/src/views/ai/image/index/components/ImageCard.vue b/src/views/ai/image/index/components/ImageCard.vue
deleted file mode 100644
index 4ba78ca..0000000
--- a/src/views/ai/image/index/components/ImageCard.vue
+++ /dev/null
@@ -1,162 +0,0 @@
-<template>
-  <el-card body-class="" class="image-card">
-    <div class="image-operation">
-      <div>
-        <el-button type="primary" text bg v-if="detail?.status === AiImageStatusEnum.IN_PROGRESS">
-          生成中
-        </el-button>
-        <el-button text bg v-else-if="detail?.status === AiImageStatusEnum.SUCCESS">
-          已完成
-        </el-button>
-        <el-button type="danger" text bg v-else-if="detail?.status === AiImageStatusEnum.FAIL">
-          异常
-        </el-button>
-      </div>
-      <!-- 操作区 -->
-      <div>
-        <el-button
-          class="btn"
-          text
-          :icon="Download"
-          @click="handleButtonClick('download', detail)"
-        />
-        <el-button
-          class="btn"
-          text
-          :icon="RefreshRight"
-          @click="handleButtonClick('regeneration', detail)"
-        />
-        <el-button class="btn" text :icon="Delete" @click="handleButtonClick('delete', detail)" />
-        <el-button class="btn" text :icon="More" @click="handleButtonClick('more', detail)" />
-      </div>
-    </div>
-    <div class="image-wrapper" ref="cardImageRef">
-      <el-image
-        class="image"
-        :src="detail?.picUrl"
-        :preview-src-list="[detail.picUrl]"
-        preview-teleported
-      />
-      <div v-if="detail?.status === AiImageStatusEnum.FAIL">
-        {{ detail?.errorMessage }}
-      </div>
-    </div>
-    <!-- Midjourney 专属操作 -->
-    <div class="image-mj-btns">
-      <el-button
-        size="small"
-        v-for="button in detail?.buttons"
-        :key="button"
-        class="min-w-40px ml-0 mr-10px mt-5px"
-        @click="handleMidjourneyBtnClick(button)"
-      >
-        {{ button.label }}{{ button.emoji }}
-      </el-button>
-    </div>
-  </el-card>
-</template>
-<script setup lang="ts">
-import { Delete, Download, More, RefreshRight } from '@element-plus/icons-vue'
-import { ImageVO, ImageMidjourneyButtonsVO } from '@/api/ai/image'
-import { PropType } from 'vue'
-import { ElLoading, LoadingOptionsResolved } from 'element-plus'
-import { AiImageStatusEnum } from '@/views/ai/utils/constants'
-
-const message = useMessage() // 消息
-
-const props = defineProps({
-  detail: {
-    type: Object as PropType<ImageVO>,
-    require: true
-  }
-})
-
-const cardImageRef = ref<any>() // 卡片 image ref
-const cardImageLoadingInstance = ref<any>() // 卡片 image ref
-
-/** 处理点击事件  */
-const handleButtonClick = async (type, detail: ImageVO) => {
-  emits('onBtnClick', type, detail)
-}
-
-/** 处理 Midjourney 按钮点击事件  */
-const handleMidjourneyBtnClick = async (button: ImageMidjourneyButtonsVO) => {
-  // 确认窗体
-  await message.confirm(`确认操作 "${button.label} ${button.emoji}" ?`)
-  emits('onMjBtnClick', button, props.detail)
-}
-
-const emits = defineEmits(['onBtnClick', 'onMjBtnClick']) // emits
-
-/** 监听详情 */
-const { detail } = toRefs(props)
-watch(detail, async (newVal, oldVal) => {
-  await handleLoading(newVal.status as string)
-})
-
-/** 处理加载状态 */
-const handleLoading = async (status: number) => {
-  // 情况一:如果是生成中,则设置加载中的 loading
-  if (status === AiImageStatusEnum.IN_PROGRESS) {
-    cardImageLoadingInstance.value = ElLoading.service({
-      target: cardImageRef.value,
-      text: '生成中...'
-    } as LoadingOptionsResolved)
-    // 情况二:如果已经生成结束,则移除 loading
-  } else {
-    if (cardImageLoadingInstance.value) {
-      cardImageLoadingInstance.value.close()
-      cardImageLoadingInstance.value = null
-    }
-  }
-}
-
-/** 初始化 */
-onMounted(async () => {
-  await handleLoading(props.detail.status as string)
-})
-</script>
-
-<style scoped lang="scss">
-.image-card {
-  width: 320px;
-  height: auto;
-  border-radius: 10px;
-  position: relative;
-  display: flex;
-  flex-direction: column;
-
-  .image-operation {
-    display: flex;
-    flex-direction: row;
-    justify-content: space-between;
-
-    .btn {
-      //border: 1px solid red;
-      padding: 10px;
-      margin: 0;
-    }
-  }
-
-  .image-wrapper {
-    overflow: hidden;
-    margin-top: 20px;
-    height: 280px;
-    flex: 1;
-
-    .image {
-      width: 100%;
-      border-radius: 10px;
-    }
-  }
-
-  .image-mj-btns {
-    margin-top: 5px;
-    width: 100%;
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    justify-content: flex-start;
-  }
-}
-</style>
diff --git a/src/views/ai/image/index/components/ImageDetail.vue b/src/views/ai/image/index/components/ImageDetail.vue
deleted file mode 100644
index ad15aa8..0000000
--- a/src/views/ai/image/index/components/ImageDetail.vue
+++ /dev/null
@@ -1,224 +0,0 @@
-<template>
-  <el-drawer
-    v-model="showDrawer"
-    title="图片详细"
-    @close="handleDrawerClose"
-    custom-class="drawer-class"
-  >
-    <!-- 图片 -->
-    <div class="item">
-      <div class="body">
-        <el-image
-          class="image"
-          :src="detail?.picUrl"
-          :preview-src-list="[detail.picUrl]"
-          preview-teleported
-        />
-      </div>
-    </div>
-    <!-- 时间 -->
-    <div class="item">
-      <div class="tip">时间</div>
-      <div class="body">
-        <div>提交时间:{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}</div>
-        <div>生成时间:{{ formatTime(detail.finishTime, 'yyyy-MM-dd HH:mm:ss') }}</div>
-      </div>
-    </div>
-    <!-- 模型 -->
-    <div class="item">
-      <div class="tip">模型</div>
-      <div class="body"> {{ detail.model }}({{ detail.height }}x{{ detail.width }}) </div>
-    </div>
-    <!-- 提示词 -->
-    <div class="item">
-      <div class="tip">提示词</div>
-      <div class="body">
-        {{ detail.prompt }}
-      </div>
-    </div>
-    <!-- 地址 -->
-    <div class="item">
-      <div class="tip">图片地址</div>
-      <div class="body">
-        {{ detail.picUrl }}
-      </div>
-    </div>
-    <!-- StableDiffusion 专属区域 -->
-    <div
-      class="item"
-      v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.sampler"
-    >
-      <div class="tip">采样方法</div>
-      <div class="body">
-        {{
-          StableDiffusionSamplers.find(
-            (item: ImageModelVO) => item.key === detail?.options?.sampler
-          )?.name
-        }}
-      </div>
-    </div>
-    <div
-      class="item"
-      v-if="
-        detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.clipGuidancePreset
-      "
-    >
-      <div class="tip">CLIP</div>
-      <div class="body">
-        {{
-          StableDiffusionClipGuidancePresets.find(
-            (item: ImageModelVO) => item.key === detail?.options?.clipGuidancePreset
-          )?.name
-        }}
-      </div>
-    </div>
-    <div
-      class="item"
-      v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.stylePreset"
-    >
-      <div class="tip">风格</div>
-      <div class="body">
-        {{
-          StableDiffusionStylePresets.find(
-            (item: ImageModelVO) => item.key === detail?.options?.stylePreset
-          )?.name
-        }}
-      </div>
-    </div>
-    <div
-      class="item"
-      v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.steps"
-    >
-      <div class="tip">迭代步数</div>
-      <div class="body">
-        {{ detail?.options?.steps }}
-      </div>
-    </div>
-    <div
-      class="item"
-      v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.scale"
-    >
-      <div class="tip">引导系数</div>
-      <div class="body">
-        {{ detail?.options?.scale }}
-      </div>
-    </div>
-    <div
-      class="item"
-      v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.seed"
-    >
-      <div class="tip">随机因子</div>
-      <div class="body">
-        {{ detail?.options?.seed }}
-      </div>
-    </div>
-    <!-- Dall3 专属区域 -->
-    <div class="item" v-if="detail.platform === AiPlatformEnum.OPENAI && detail?.options?.style">
-      <div class="tip">风格选择</div>
-      <div class="body">
-        {{ Dall3StyleList.find((item: ImageModelVO) => item.key === detail?.options?.style)?.name }}
-      </div>
-    </div>
-    <!-- Midjourney 专属区域 -->
-    <div
-      class="item"
-      v-if="detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.version"
-    >
-      <div class="tip">模型版本</div>
-      <div class="body">
-        {{ detail?.options?.version }}
-      </div>
-    </div>
-    <div
-      class="item"
-      v-if="detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.referImageUrl"
-    >
-      <div class="tip">参考图</div>
-      <div class="body">
-        <el-image :src="detail.options.referImageUrl" />
-      </div>
-    </div>
-  </el-drawer>
-</template>
-
-<script setup lang="ts">
-import { ImageApi, ImageVO } from '@/api/ai/image'
-import {
-  AiPlatformEnum,
-  Dall3StyleList,
-  ImageModelVO,
-  StableDiffusionClipGuidancePresets,
-  StableDiffusionSamplers,
-  StableDiffusionStylePresets
-} from '@/views/ai/utils/constants'
-import { formatTime } from '@/utils'
-
-const showDrawer = ref<boolean>(false) // 是否显示
-const detail = ref<ImageVO>({} as ImageVO) // 图片详细信息
-
-const props = defineProps({
-  show: {
-    type: Boolean,
-    require: true,
-    default: false
-  },
-  id: {
-    type: Number,
-    required: true
-  }
-})
-
-/** 关闭抽屉  */
-const handleDrawerClose = async () => {
-  emits('handleDrawerClose')
-}
-
-/** 监听 drawer 是否打开 */
-const { show } = toRefs(props)
-watch(show, async (newValue, oldValue) => {
-  showDrawer.value = newValue as boolean
-})
-
-/**  获取图片详情  */
-const getImageDetail = async (id: number) => {
-  detail.value = await ImageApi.getImageMy(id)
-}
-
-/** 监听 id 变化,加载最新图片详情 */
-const { id } = toRefs(props)
-watch(id, async (newVal, oldVal) => {
-  if (newVal) {
-    await getImageDetail(newVal)
-  }
-})
-
-const emits = defineEmits(['handleDrawerClose'])
-</script>
-<style scoped lang="scss">
-.item {
-  margin-bottom: 20px;
-  width: 100%;
-  overflow: hidden;
-  word-wrap: break-word;
-
-  .header {
-    display: flex;
-    flex-direction: row;
-    justify-content: space-between;
-  }
-
-  .tip {
-    font-weight: bold;
-    font-size: 16px;
-  }
-
-  .body {
-    margin-top: 10px;
-    color: #616161;
-
-    .taskImage {
-      border-radius: 10px;
-    }
-  }
-}
-</style>
diff --git a/src/views/ai/image/index/components/ImageList.vue b/src/views/ai/image/index/components/ImageList.vue
deleted file mode 100644
index 9ffde77..0000000
--- a/src/views/ai/image/index/components/ImageList.vue
+++ /dev/null
@@ -1,245 +0,0 @@
-<template>
-  <el-card class="dr-task" body-class="task-card" shadow="never">
-    <template #header>
-      绘画任务
-      <!-- TODO @fan:看看,怎么优化下这个样子哈。 -->
-      <el-button @click="handleViewPublic">绘画作品</el-button>
-    </template>
-    <!-- 图片列表 -->
-    <div class="task-image-list" ref="imageListRef">
-      <ImageCard
-        v-for="image in imageList"
-        :key="image.id"
-        :detail="image"
-        @on-btn-click="handleImageButtonClick"
-        @on-mj-btn-click="handleImageMidjourneyButtonClick"
-      />
-    </div>
-    <div class="task-image-pagination">
-      <Pagination
-        :total="pageTotal"
-        v-model:page="queryParams.pageNo"
-        v-model:limit="queryParams.pageSize"
-        @pagination="getImageList"
-      />
-    </div>
-  </el-card>
-
-  <!-- 图片详情 -->
-  <ImageDetail
-    :show="isShowImageDetail"
-    :id="showImageDetailId"
-    @handle-drawer-close="handleDetailClose"
-  />
-</template>
-<script setup lang="ts">
-import {
-  ImageApi,
-  ImageVO,
-  ImageMidjourneyActionVO,
-  ImageMidjourneyButtonsVO
-} from '@/api/ai/image'
-import ImageDetail from './ImageDetail.vue'
-import ImageCard from './ImageCard.vue'
-import { ElLoading, LoadingOptionsResolved } from 'element-plus'
-import { AiImageStatusEnum } from '@/views/ai/utils/constants'
-import download from '@/utils/download'
-
-const message = useMessage() // 消息弹窗
-const router = useRouter() // 路由
-
-// 图片分页相关的参数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10
-})
-const pageTotal = ref<number>(0) // page size
-const imageList = ref<ImageVO[]>([]) // image 列表
-const imageListLoadingInstance = ref<any>() // image 列表是否正在加载中
-const imageListRef = ref<any>() // ref
-// 图片轮询相关的参数(正在生成中的)
-const inProgressImageMap = ref<{}>({}) // 监听的 image 映射,一般是生成中(需要轮询),key 为 image 编号,value 为 image
-const inProgressTimer = ref<any>() // 生成中的 image 定时器,轮询生成进展
-// 图片详情相关的参数
-const isShowImageDetail = ref<boolean>(false) // 图片详情是否展示
-const showImageDetailId = ref<number>(0) // 图片详情的图片编号
-
-/** 处理查看绘图作品 */
-const handleViewPublic = () => {
-  router.push({
-    name: 'AiImageSquare'
-  })
-}
-
-/** 查看图片的详情  */
-const handleDetailOpen = async () => {
-  isShowImageDetail.value = true
-}
-
-/** 关闭图片的详情  */
-const handleDetailClose = async () => {
-  isShowImageDetail.value = false
-}
-
-/** 获得 image 图片列表 */
-const getImageList = async () => {
-  try {
-    // 1. 加载图片列表
-    imageListLoadingInstance.value = ElLoading.service({
-      target: imageListRef.value,
-      text: '加载中...'
-    } as LoadingOptionsResolved)
-    const { list, total } = await ImageApi.getImagePageMy(queryParams)
-    imageList.value = list
-    pageTotal.value = total
-
-    // 2. 计算需要轮询的图片
-    const newWatImages = {}
-    imageList.value.forEach((item) => {
-      if (item.status === AiImageStatusEnum.IN_PROGRESS) {
-        newWatImages[item.id] = item
-      }
-    })
-    inProgressImageMap.value = newWatImages
-  } finally {
-    // 关闭正在“加载中”的 Loading
-    if (imageListLoadingInstance.value) {
-      imageListLoadingInstance.value.close()
-      imageListLoadingInstance.value = null
-    }
-  }
-}
-
-/** 轮询生成中的 image 列表 */
-const refreshWatchImages = async () => {
-  const imageIds = Object.keys(inProgressImageMap.value).map(Number)
-  if (imageIds.length == 0) {
-    return
-  }
-  const list = (await ImageApi.getImageListMyByIds(imageIds)) as ImageVO[]
-  const newWatchImages = {}
-  list.forEach((image) => {
-    if (image.status === AiImageStatusEnum.IN_PROGRESS) {
-      newWatchImages[image.id] = image
-    } else {
-      const index = imageList.value.findIndex((oldImage) => image.id === oldImage.id)
-      if (index >= 0) {
-        // 更新 imageList
-        imageList.value[index] = image
-      }
-    }
-  })
-  inProgressImageMap.value = newWatchImages
-}
-
-/** 图片的点击事件 */
-const handleImageButtonClick = async (type: string, imageDetail: ImageVO) => {
-  // 详情
-  if (type === 'more') {
-    showImageDetailId.value = imageDetail.id
-    await handleDetailOpen()
-    return
-  }
-  // 删除
-  if (type === 'delete') {
-    await message.confirm(`是否删除照片?`)
-    await ImageApi.deleteImageMy(imageDetail.id)
-    await getImageList()
-    message.success('删除成功!')
-    return
-  }
-  // 下载
-  if (type === 'download') {
-    await download.image(imageDetail.picUrl)
-    return
-  }
-  // 重新生成
-  if (type === 'regeneration') {
-    await emits('onRegeneration', imageDetail)
-    return
-  }
-}
-
-/** 处理 Midjourney 按钮点击事件  */
-const handleImageMidjourneyButtonClick = async (
-  button: ImageMidjourneyButtonsVO,
-  imageDetail: ImageVO
-) => {
-  // 1. 构建 params 参数
-  const data = {
-    id: imageDetail.id,
-    customId: button.customId
-  } as ImageMidjourneyActionVO
-  // 2. 发送 action
-  await ImageApi.midjourneyAction(data)
-  // 3. 刷新列表
-  await getImageList()
-}
-
-defineExpose({ getImageList }) // 暴露组件方法
-
-const emits = defineEmits(['onRegeneration'])
-
-/** 组件挂在的时候 */
-onMounted(async () => {
-  // 获取 image 列表
-  await getImageList()
-  // 自动刷新 image 列表
-  inProgressTimer.value = setInterval(async () => {
-    await refreshWatchImages()
-  }, 1000 * 3)
-})
-
-/** 组件取消挂在的时候 */
-onUnmounted(async () => {
-  if (inProgressTimer.value) {
-    clearInterval(inProgressTimer.value)
-  }
-})
-</script>
-<style lang="scss">
-.dr-task {
-  width: 100%;
-  height: 100%;
-}
-.task-card {
-  margin: 0;
-  padding: 0;
-  height: 100%;
-  position: relative;
-}
-
-.task-image-list {
-  position: relative;
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  align-content: flex-start;
-  height: 100%;
-  overflow: auto;
-  padding: 20px 20px 140px;
-  box-sizing: border-box; /* 确保内边距不会增加高度 */
-
-  > div {
-    margin-right: 20px;
-    margin-bottom: 20px;
-  }
-  > div:last-of-type {
-    //margin-bottom: 100px;
-  }
-}
-
-.task-image-pagination {
-  position: absolute;
-  bottom: 60px;
-  height: 50px;
-  line-height: 90px;
-  width: 100%;
-  z-index: 999;
-  background-color: #ffffff;
-  display: flex;
-  flex-direction: row;
-  justify-content: center;
-  align-items: center;
-}
-</style>
diff --git a/src/views/ai/image/index/components/dall3/index.vue b/src/views/ai/image/index/components/dall3/index.vue
deleted file mode 100644
index 5c891ab..0000000
--- a/src/views/ai/image/index/components/dall3/index.vue
+++ /dev/null
@@ -1,320 +0,0 @@
-<!-- dall3 -->
-<template>
-  <div class="prompt">
-    <el-text tag="b">画面描述</el-text>
-    <el-text tag="p">建议使用“形容词+动词+风格”的格式,使用“,”隔开</el-text>
-    <el-input
-      v-model="prompt"
-      maxlength="1024"
-      rows="5"
-      class="w-100% mt-15px"
-      input-style="border-radius: 7px;"
-      placeholder="例如:童话里的小屋应该是什么样子?"
-      show-word-limit
-      type="textarea"
-    />
-  </div>
-  <div class="hot-words">
-    <div>
-      <el-text tag="b">随机热词</el-text>
-    </div>
-    <el-space wrap class="word-list">
-      <el-button
-        round
-        class="btn"
-        :type="selectHotWord === hotWord ? 'primary' : 'default'"
-        v-for="hotWord in ImageHotWords"
-        :key="hotWord"
-        @click="handleHotWordClick(hotWord)"
-      >
-        {{ hotWord }}
-      </el-button>
-    </el-space>
-  </div>
-  <div class="model">
-    <div>
-      <el-text tag="b">模型选择</el-text>
-    </div>
-    <el-space wrap class="model-list">
-      <div
-        :class="selectModel === model.key ? 'modal-item selectModel' : 'modal-item'"
-        v-for="model in Dall3Models"
-        :key="model.key"
-      >
-        <el-image :src="model.image" fit="contain" @click="handleModelClick(model)" />
-        <div class="model-font">{{ model.name }}</div>
-      </div>
-    </el-space>
-  </div>
-  <div class="image-style">
-    <div>
-      <el-text tag="b">风格选择</el-text>
-    </div>
-    <el-space wrap class="image-style-list">
-      <div
-        :class="style === imageStyle.key ? 'image-style-item selectImageStyle' : 'image-style-item'"
-        v-for="imageStyle in Dall3StyleList"
-        :key="imageStyle.key"
-      >
-        <el-image :src="imageStyle.image" fit="contain" @click="handleStyleClick(imageStyle)" />
-        <div class="style-font">{{ imageStyle.name }}</div>
-      </div>
-    </el-space>
-  </div>
-  <div class="image-size">
-    <div>
-      <el-text tag="b">画面比例</el-text>
-    </div>
-    <el-space wrap class="size-list">
-      <div
-        class="size-item"
-        v-for="imageSize in Dall3SizeList"
-        :key="imageSize.key"
-        @click="handleSizeClick(imageSize)"
-      >
-        <div
-          :class="selectSize === imageSize.key ? 'size-wrapper selectImageSize' : 'size-wrapper'"
-        >
-          <div :style="imageSize.style"></div>
-        </div>
-        <div class="size-font">{{ imageSize.name }}</div>
-      </div>
-    </el-space>
-  </div>
-  <div class="btns">
-    <el-button type="primary" size="large" round :loading="drawIn" @click="handleGenerateImage">
-      {{ drawIn ? '生成中' : '生成内容' }}
-    </el-button>
-  </div>
-</template>
-<script setup lang="ts">
-import { ImageApi, ImageDrawReqVO, ImageVO } from '@/api/ai/image'
-import {
-  Dall3Models,
-  Dall3StyleList,
-  ImageHotWords,
-  Dall3SizeList,
-  ImageModelVO,
-  AiPlatformEnum
-} from '@/views/ai/utils/constants'
-
-const message = useMessage() // 消息弹窗
-
-// 定义属性
-const prompt = ref<string>('') // 提示词
-const drawIn = ref<boolean>(false) // 生成中
-const selectHotWord = ref<string>('') // 选中的热词
-const selectModel = ref<string>('dall-e-3') // 模型
-const selectSize = ref<string>('1024x1024') // 选中 size
-const style = ref<string>('vivid') // style 样式
-
-const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits
-
-/** 选择热词 */
-const handleHotWordClick = async (hotWord: string) => {
-  // 情况一:取消选中
-  if (selectHotWord.value == hotWord) {
-    selectHotWord.value = ''
-    return
-  }
-
-  // 情况二:选中
-  selectHotWord.value = hotWord
-  prompt.value = hotWord
-}
-
-/** 选择 model 模型 */
-const handleModelClick = async (model: ImageModelVO) => {
-  selectModel.value = model.key
-}
-
-/** 选择 style 样式  */
-const handleStyleClick = async (imageStyle: ImageModelVO) => {
-  style.value = imageStyle.key
-}
-
-/** 选择 size 大小  */
-const handleSizeClick = async (imageSize: ImageSizeVO) => {
-  selectSize.value = imageSize.key
-}
-
-/**  图片生产  */
-const handleGenerateImage = async () => {
-  // 二次确认
-  await message.confirm(`确认生成内容?`)
-  try {
-    // 加载中
-    drawIn.value = true
-    // 回调
-    emits('onDrawStart', AiPlatformEnum.OPENAI)
-    const imageSize = Dall3SizeList.find((item) => item.key === selectSize.value) as ImageSizeVO
-    const form = {
-      platform: AiPlatformEnum.OPENAI,
-      prompt: prompt.value, // 提示词
-      model: selectModel.value, // 模型
-      width: imageSize.width, // size 不能为空
-      height: imageSize.height, // size 不能为空
-      options: {
-        style: style.value // 图像生成的风格
-      }
-    } as ImageDrawReqVO
-    // 发送请求
-    await ImageApi.drawImage(form)
-  } finally {
-    // 回调
-    emits('onDrawComplete', AiPlatformEnum.OPENAI)
-    // 加载结束
-    drawIn.value = false
-  }
-}
-
-/** 填充值 */
-const settingValues = async (detail: ImageVO) => {
-  prompt.value = detail.prompt
-  selectModel.value = detail.model
-  style.value = detail.options?.style
-  const imageSize = Dall3SizeList.find(
-    (item) => item.key === `${detail.width}x${detail.height}`
-  ) as ImageSizeVO
-  await handleSizeClick(imageSize)
-}
-
-/** 暴露组件方法 */
-defineExpose({ settingValues })
-</script>
-<style scoped lang="scss">
-// 提示词
-.prompt {
-}
-
-// 热词
-.hot-words {
-  display: flex;
-  flex-direction: column;
-  margin-top: 30px;
-
-  .word-list {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    justify-content: start;
-    margin-top: 15px;
-
-    .btn {
-      margin: 0;
-    }
-  }
-}
-
-// 模型
-.model {
-  margin-top: 30px;
-
-  .model-list {
-    margin-top: 15px;
-
-    .modal-item {
-      width: 110px;
-      //outline: 1px solid blue;
-      overflow: hidden;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      border: 3px solid transparent;
-      cursor: pointer;
-
-      .model-font {
-        font-size: 14px;
-        color: #3e3e3e;
-        font-weight: bold;
-      }
-    }
-
-    .selectModel {
-      border: 3px solid #1293ff;
-      border-radius: 5px;
-    }
-  }
-}
-
-// 样式 style
-.image-style {
-  margin-top: 30px;
-
-  .image-style-list {
-    margin-top: 15px;
-
-    .image-style-item {
-      width: 110px;
-      //outline: 1px solid blue;
-      overflow: hidden;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      border: 3px solid transparent;
-      cursor: pointer;
-
-      .style-font {
-        font-size: 14px;
-        color: #3e3e3e;
-        font-weight: bold;
-      }
-    }
-
-    .selectImageStyle {
-      border: 3px solid #1293ff;
-      border-radius: 5px;
-    }
-  }
-}
-
-// 尺寸
-.image-size {
-  width: 100%;
-  margin-top: 30px;
-
-  .size-list {
-    display: flex;
-    flex-direction: row;
-    justify-content: space-between;
-    width: 100%;
-    margin-top: 20px;
-
-    .size-item {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      cursor: pointer;
-
-      .size-wrapper {
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        border-radius: 7px;
-        padding: 4px;
-        width: 50px;
-        height: 50px;
-        background-color: #fff;
-        border: 1px solid #fff;
-      }
-
-      .size-font {
-        font-size: 14px;
-        color: #3e3e3e;
-        font-weight: bold;
-      }
-    }
-  }
-
-  .selectImageSize {
-    border: 1px solid #1293ff !important;
-  }
-}
-
-.btns {
-  display: flex;
-  justify-content: center;
-  margin-top: 50px;
-}
-</style>
diff --git a/src/views/ai/image/index/components/midjourney/index.vue b/src/views/ai/image/index/components/midjourney/index.vue
deleted file mode 100644
index 1d7fda1..0000000
--- a/src/views/ai/image/index/components/midjourney/index.vue
+++ /dev/null
@@ -1,326 +0,0 @@
-<!-- dall3 -->
-<template>
-  <div class="prompt">
-    <el-text tag="b">画面描述</el-text>
-    <el-text tag="p">建议使用“形容词+动词+风格”的格式,使用“,”隔开.</el-text>
-    <el-input
-      v-model="prompt"
-      maxlength="1024"
-      rows="5"
-      class="w-100% mt-15px"
-      input-style="border-radius: 7px;"
-      placeholder="例如:童话里的小屋应该是什么样子?"
-      show-word-limit
-      type="textarea"
-    />
-  </div>
-  <div class="hot-words">
-    <div>
-      <el-text tag="b">随机热词</el-text>
-    </div>
-    <el-space wrap class="word-list">
-      <el-button
-        round
-        class="btn"
-        :type="selectHotWord === hotWord ? 'primary' : 'default'"
-        v-for="hotWord in ImageHotWords"
-        :key="hotWord"
-        @click="handleHotWordClick(hotWord)"
-      >
-        {{ hotWord }}
-      </el-button>
-    </el-space>
-  </div>
-  <div class="image-size">
-    <div>
-      <el-text tag="b">尺寸</el-text>
-    </div>
-    <el-space wrap class="size-list">
-      <div
-        class="size-item"
-        v-for="imageSize in MidjourneySizeList"
-        :key="imageSize.key"
-        @click="handleSizeClick(imageSize)"
-      >
-        <div
-          :class="selectSize === imageSize.key ? 'size-wrapper selectImageSize' : 'size-wrapper'"
-        >
-          <div :style="imageSize.style"></div>
-        </div>
-        <div class="size-font">{{ imageSize.key }}</div>
-      </div>
-    </el-space>
-  </div>
-  <div class="model">
-    <div>
-      <el-text tag="b">模型</el-text>
-    </div>
-    <el-space wrap class="model-list">
-      <div
-        :class="selectModel === model.key ? 'modal-item selectModel' : 'modal-item'"
-        v-for="model in MidjourneyModels"
-        :key="model.key"
-      >
-        <el-image :src="model.image" fit="contain" @click="handleModelClick(model)" />
-        <div class="model-font">{{ model.name }}</div>
-      </div>
-    </el-space>
-  </div>
-  <div class="version">
-    <div>
-      <el-text tag="b">版本</el-text>
-    </div>
-    <el-space wrap class="version-list">
-      <el-select
-        v-model="selectVersion"
-        class="version-select !w-350px"
-        clearable
-        placeholder="请选择版本"
-      >
-        <el-option
-          v-for="item in versionList"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
-        />
-      </el-select>
-    </el-space>
-  </div>
-  <div class="model">
-    <div>
-      <el-text tag="b">参考图</el-text>
-    </div>
-    <el-space wrap class="model-list">
-      <UploadImg v-model="referImageUrl" height="120px" width="120px" />
-    </el-space>
-  </div>
-  <div class="btns">
-    <el-button type="primary" size="large" round @click="handleGenerateImage">
-      {{ drawIn ? '生成中' : '生成内容' }}
-    </el-button>
-  </div>
-</template>
-<script setup lang="ts">
-import { ImageApi, ImageMidjourneyImagineReqVO, ImageVO } from '@/api/ai/image'
-import {
-  AiPlatformEnum,
-  ImageHotWords,
-  ImageSizeVO,
-  ImageModelVO,
-  MidjourneyModels,
-  MidjourneySizeList,
-  MidjourneyVersions,
-  NijiVersionList
-} from '@/views/ai/utils/constants'
-
-const message = useMessage() // 消息弹窗
-
-// 定义属性
-const drawIn = ref<boolean>(false) // 生成中
-const selectHotWord = ref<string>('') // 选中的热词
-// 表单
-const prompt = ref<string>('') // 提示词
-const referImageUrl = ref<any>() // 参考图
-const selectModel = ref<string>('midjourney') // 选中的模型
-const selectSize = ref<string>('1:1') // 选中 size
-const selectVersion = ref<any>('6.0') // 选中的 version
-const versionList = ref<any>(MidjourneyVersions) // version 列表
-const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits
-
-/** 选择热词 */
-const handleHotWordClick = async (hotWord: string) => {
-  // 情况一:取消选中
-  if (selectHotWord.value == hotWord) {
-    selectHotWord.value = ''
-    return
-  }
-
-  // 情况二:选中
-  selectHotWord.value = hotWord // 选中
-  prompt.value = hotWord // 设置提示次
-}
-
-/** 点击 size 尺寸 */
-const handleSizeClick = async (imageSize: ImageSizeVO) => {
-  selectSize.value = imageSize.key
-}
-
-/** 点击 model 模型 */
-const handleModelClick = async (model: ImageModelVO) => {
-  selectModel.value = model.key
-  if (model.key === 'niji') {
-    versionList.value = NijiVersionList // 默认选择 niji
-  } else {
-    versionList.value = MidjourneyVersions // 默认选择 midjourney
-  }
-  selectVersion.value = versionList.value[0].value
-}
-
-/** 图片生成 */
-const handleGenerateImage = async () => {
-  // 二次确认
-  await message.confirm(`确认生成内容?`)
-  try {
-    // 加载中
-    drawIn.value = true
-    // 回调
-    emits('onDrawStart', AiPlatformEnum.MIDJOURNEY)
-    // 发送请求
-    const imageSize = MidjourneySizeList.find(
-      (item) => selectSize.value === item.key
-    ) as ImageSizeVO
-    const req = {
-      prompt: prompt.value,
-      model: selectModel.value,
-      width: imageSize.width,
-      height: imageSize.height,
-      version: selectVersion.value,
-      referImageUrl: referImageUrl.value
-    } as ImageMidjourneyImagineReqVO
-    await ImageApi.midjourneyImagine(req)
-  } finally {
-    // 回调
-    emits('onDrawComplete', AiPlatformEnum.MIDJOURNEY)
-    // 加载结束
-    drawIn.value = false
-  }
-}
-
-/** 填充值 */
-const settingValues = async (detail: ImageVO) => {
-  // 提示词
-  prompt.value = detail.prompt
-  // image size
-  const imageSize = MidjourneySizeList.find(
-    (item) => item.key === `${detail.width}:${detail.height}`
-  ) as ImageSizeVO
-  selectSize.value = imageSize.key
-  // 选中模型
-  const model = MidjourneyModels.find((item) => item.key === detail.options?.model) as ImageModelVO
-  await handleModelClick(model)
-  // 版本
-  selectVersion.value = versionList.value.find(
-    (item) => item.value === detail.options?.version
-  ).value
-  // image
-  referImageUrl.value = detail.options.referImageUrl
-}
-
-/** 暴露组件方法 */
-defineExpose({ settingValues })
-</script>
-<style scoped lang="scss">
-// 提示词
-.prompt {
-}
-
-// 热词
-.hot-words {
-  display: flex;
-  flex-direction: column;
-  margin-top: 30px;
-
-  .word-list {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    justify-content: start;
-    margin-top: 15px;
-
-    .btn {
-      margin: 0;
-    }
-  }
-}
-
-// version
-.version {
-  margin-top: 20px;
-
-  .version-list {
-    margin-top: 20px;
-    width: 100%;
-  }
-}
-
-// 模型
-.model {
-  margin-top: 30px;
-
-  .model-list {
-    margin-top: 15px;
-
-    .modal-item {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      width: 150px;
-      //outline: 1px solid blue;
-      overflow: hidden;
-      border: 3px solid transparent;
-      cursor: pointer;
-
-      .model-font {
-        font-size: 14px;
-        color: #3e3e3e;
-        font-weight: bold;
-      }
-    }
-
-    .selectModel {
-      border: 3px solid #1293ff;
-      border-radius: 5px;
-    }
-  }
-}
-
-// 尺寸
-.image-size {
-  width: 100%;
-  margin-top: 30px;
-
-  .size-list {
-    display: flex;
-    flex-direction: row;
-    justify-content: space-between;
-    width: 100%;
-    margin-top: 20px;
-
-    .size-item {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      cursor: pointer;
-
-      .size-wrapper {
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        border-radius: 7px;
-        padding: 4px;
-        width: 50px;
-        height: 50px;
-        background-color: #fff;
-        border: 1px solid #fff;
-      }
-
-      .size-font {
-        font-size: 14px;
-        color: #3e3e3e;
-        font-weight: bold;
-      }
-    }
-  }
-
-  .selectImageSize {
-    border: 1px solid #1293ff !important;
-  }
-}
-
-.btns {
-  display: flex;
-  justify-content: center;
-  margin-top: 50px;
-}
-</style>
diff --git a/src/views/ai/image/index/components/other/index.vue b/src/views/ai/image/index/components/other/index.vue
deleted file mode 100644
index a688be1..0000000
--- a/src/views/ai/image/index/components/other/index.vue
+++ /dev/null
@@ -1,216 +0,0 @@
-<!-- dall3 -->
-<template>
-  <div class="prompt">
-    <el-text tag="b">画面描述</el-text>
-    <el-text tag="p">建议使用“形容词+动词+风格”的格式,使用“,”隔开</el-text>
-    <el-input
-      v-model="prompt"
-      maxlength="1024"
-      rows="5"
-      class="w-100% mt-15px"
-      input-style="border-radius: 7px;"
-      placeholder="例如:童话里的小屋应该是什么样子?"
-      show-word-limit
-      type="textarea"
-    />
-  </div>
-  <div class="hot-words">
-    <div>
-      <el-text tag="b">随机热词</el-text>
-    </div>
-    <el-space wrap class="word-list">
-      <el-button
-        round
-        class="btn"
-        :type="selectHotWord === hotWord ? 'primary' : 'default'"
-        v-for="hotWord in ImageHotWords"
-        :key="hotWord"
-        @click="handleHotWordClick(hotWord)"
-      >
-        {{ hotWord }}
-      </el-button>
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">平台</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-select
-        v-model="otherPlatform"
-        placeholder="Select"
-        size="large"
-        class="!w-350px"
-        @change="handlerPlatformChange"
-      >
-        <el-option
-          v-for="item in OtherPlatformEnum"
-          :key="item.key"
-          :label="item.name"
-          :value="item.key"
-        />
-      </el-select>
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">模型</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-select v-model="model" placeholder="Select" size="large" class="!w-350px">
-        <el-option v-for="item in models" :key="item.key" :label="item.name" :value="item.key" />
-      </el-select>
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">图片尺寸</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-input v-model="width" type="number" class="w-170px" placeholder="图片宽度" />
-      <el-input v-model="height" type="number" class="w-170px" placeholder="图片高度" />
-    </el-space>
-  </div>
-  <div class="btns">
-    <el-button type="primary" size="large" round :loading="drawIn" @click="handleGenerateImage">
-      {{ drawIn ? '生成中' : '生成内容' }}
-    </el-button>
-  </div>
-</template>
-<script setup lang="ts">
-import { ImageApi, ImageDrawReqVO, ImageVO } from '@/api/ai/image'
-import {
-  AiPlatformEnum,
-  ChatGlmModels,
-  ImageHotWords,
-  ImageModelVO,
-  OtherPlatformEnum,
-  QianFanModels,
-  TongYiWanXiangModels
-} from '@/views/ai/utils/constants'
-
-const message = useMessage() // 消息弹窗
-
-// 定义属性
-const drawIn = ref<boolean>(false) // 生成中
-const selectHotWord = ref<string>('') // 选中的热词
-// 表单
-const prompt = ref<string>('') // 提示词
-const width = ref<number>(512) // 图片宽度
-const height = ref<number>(512) // 图片高度
-const otherPlatform = ref<string>(AiPlatformEnum.TONG_YI) // 平台
-const models = ref<ImageModelVO[]>(TongYiWanXiangModels) // 模型  TongYiWanXiangModels、QianFanModels
-const model = ref<string>(models.value[0].key) // 模型
-
-const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits
-
-/** 选择热词 */
-const handleHotWordClick = async (hotWord: string) => {
-  // 情况一:取消选中
-  if (selectHotWord.value == hotWord) {
-    selectHotWord.value = ''
-    return
-  }
-
-  // 情况二:选中
-  selectHotWord.value = hotWord // 选中
-  prompt.value = hotWord // 替换提示词
-}
-
-/** 图片生成 */
-const handleGenerateImage = async () => {
-  // 二次确认
-  await message.confirm(`确认生成内容?`)
-  try {
-    // 加载中
-    drawIn.value = true
-    // 回调
-    emits('onDrawStart', AiPlatformEnum.STABLE_DIFFUSION)
-    // 发送请求
-    const form = {
-      platform: otherPlatform.value,
-      model: model.value, // 模型
-      prompt: prompt.value, // 提示词
-      width: width.value, // 图片宽度
-      height: height.value, // 图片高度
-      options: {}
-    } as unknown as ImageDrawReqVO
-    await ImageApi.drawImage(form)
-  } finally {
-    // 回调
-    emits('onDrawComplete', AiPlatformEnum.STABLE_DIFFUSION)
-    // 加载结束
-    drawIn.value = false
-  }
-}
-
-/** 填充值 */
-const settingValues = async (detail: ImageVO) => {
-  prompt.value = detail.prompt
-  width.value = detail.width
-  height.value = detail.height
-}
-
-/** 平台切换 */
-const handlerPlatformChange = async (platform: string) => {
-  // 切换平台,切换模型、风格
-  if (AiPlatformEnum.TONG_YI === platform) {
-    models.value = TongYiWanXiangModels
-  } else if (AiPlatformEnum.YI_YAN === platform) {
-    models.value = QianFanModels
-  } else if (AiPlatformEnum.ZHI_PU === platform) {
-    models.value = ChatGlmModels
-  } else {
-    models.value = []
-  }
-  // 切换平台,默认选择一个风格
-  if (models.value.length > 0) {
-    model.value = models.value[0].key
-  } else {
-    model.value = ''
-  }
-}
-
-/** 暴露组件方法 */
-defineExpose({ settingValues })
-</script>
-<style scoped lang="scss">
-// 提示词
-.prompt {
-}
-
-// 热词
-.hot-words {
-  display: flex;
-  flex-direction: column;
-  margin-top: 30px;
-
-  .word-list {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    justify-content: start;
-    margin-top: 15px;
-
-    .btn {
-      margin: 0;
-    }
-  }
-}
-
-// 模型
-.group-item {
-  margin-top: 30px;
-
-  .group-item-body {
-    margin-top: 15px;
-    width: 100%;
-  }
-}
-
-.btns {
-  display: flex;
-  justify-content: center;
-  margin-top: 50px;
-}
-</style>
diff --git a/src/views/ai/image/index/components/stableDiffusion/index.vue b/src/views/ai/image/index/components/stableDiffusion/index.vue
deleted file mode 100644
index 169938f..0000000
--- a/src/views/ai/image/index/components/stableDiffusion/index.vue
+++ /dev/null
@@ -1,272 +0,0 @@
-<!-- dall3 -->
-<template>
-  <div class="prompt">
-    <el-text tag="b">画面描述</el-text>
-    <el-text tag="p">建议使用“形容词+动词+风格”的格式,使用“,”隔开</el-text>
-    <el-input
-      v-model="prompt"
-      maxlength="1024"
-      rows="5"
-      class="w-100% mt-15px"
-      input-style="border-radius: 7px;"
-      placeholder="例如:童话里的小屋应该是什么样子?"
-      show-word-limit
-      type="textarea"
-    />
-  </div>
-  <div class="hot-words">
-    <div>
-      <el-text tag="b">随机热词</el-text>
-    </div>
-    <el-space wrap class="word-list">
-      <el-button
-        round
-        class="btn"
-        :type="selectHotWord === hotWord ? 'primary' : 'default'"
-        v-for="hotWord in ImageHotEnglishWords"
-        :key="hotWord"
-        @click="handleHotWordClick(hotWord)"
-      >
-        {{ hotWord }}
-      </el-button>
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">采样方法</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-select v-model="sampler" placeholder="Select" size="large" class="!w-350px">
-        <el-option
-          v-for="item in StableDiffusionSamplers"
-          :key="item.key"
-          :label="item.name"
-          :value="item.key"
-        />
-      </el-select>
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">CLIP</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-select v-model="clipGuidancePreset" placeholder="Select" size="large" class="!w-350px">
-        <el-option
-          v-for="item in StableDiffusionClipGuidancePresets"
-          :key="item.key"
-          :label="item.name"
-          :value="item.key"
-        />
-      </el-select>
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">风格</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-select v-model="stylePreset" placeholder="Select" size="large" class="!w-350px">
-        <el-option
-          v-for="item in StableDiffusionStylePresets"
-          :key="item.key"
-          :label="item.name"
-          :value="item.key"
-        />
-      </el-select>
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">图片尺寸</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-input v-model="width" class="w-170px" placeholder="图片宽度" />
-      <el-input v-model="height" class="w-170px" placeholder="图片高度" />
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">迭代步数</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-input
-        v-model="steps"
-        type="number"
-        size="large"
-        class="!w-350px"
-        placeholder="Please input"
-      />
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">引导系数</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-input
-        v-model="scale"
-        type="number"
-        size="large"
-        class="!w-350px"
-        placeholder="Please input"
-      />
-    </el-space>
-  </div>
-  <div class="group-item">
-    <div>
-      <el-text tag="b">随机因子</el-text>
-    </div>
-    <el-space wrap class="group-item-body">
-      <el-input
-        v-model="seed"
-        type="number"
-        size="large"
-        class="!w-350px"
-        placeholder="Please input"
-      />
-    </el-space>
-  </div>
-  <div class="btns">
-    <el-button type="primary" size="large" round :loading="drawIn" @click="handleGenerateImage">
-      {{ drawIn ? '生成中' : '生成内容' }}
-    </el-button>
-  </div>
-</template>
-<script setup lang="ts">
-import { ImageApi, ImageDrawReqVO, ImageVO } from '@/api/ai/image'
-import { hasChinese } from '@/views/ai/utils/utils'
-import {
-  AiPlatformEnum,
-  ImageHotEnglishWords,
-  StableDiffusionClipGuidancePresets,
-  StableDiffusionSamplers,
-  StableDiffusionStylePresets
-} from '@/views/ai/utils/constants'
-
-const message = useMessage() // 消息弹窗
-
-// 定义属性
-const drawIn = ref<boolean>(false) // 生成中
-const selectHotWord = ref<string>('') // 选中的热词
-// 表单
-const prompt = ref<string>('') // 提示词
-const width = ref<number>(512) // 图片宽度
-const height = ref<number>(512) // 图片高度
-const sampler = ref<string>('DDIM') // 采样方法
-const steps = ref<number>(20) // 迭代步数
-const seed = ref<number>(42) // 控制生成图像的随机性
-const scale = ref<number>(7.5) // 引导系数
-const clipGuidancePreset = ref<string>('NONE') // 文本提示相匹配的图像(clip_guidance_preset) 简称 CLIP
-const stylePreset = ref<string>('3d-model') // 风格
-
-const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits
-
-/** 选择热词 */
-const handleHotWordClick = async (hotWord: string) => {
-  // 情况一:取消选中
-  if (selectHotWord.value == hotWord) {
-    selectHotWord.value = ''
-    return
-  }
-
-  // 情况二:选中
-  selectHotWord.value = hotWord // 选中
-  prompt.value = hotWord // 替换提示词
-}
-
-/** 图片生成 */
-const handleGenerateImage = async () => {
-  // 二次确认
-  if (hasChinese(prompt.value)) {
-    message.alert('暂不支持中文!')
-    return
-  }
-  await message.confirm(`确认生成内容?`)
-
-  try {
-    // 加载中
-    drawIn.value = true
-    // 回调
-    emits('onDrawStart', AiPlatformEnum.STABLE_DIFFUSION)
-    // 发送请求
-    const form = {
-      platform: AiPlatformEnum.STABLE_DIFFUSION,
-      model: 'stable-diffusion-v1-6',
-      prompt: prompt.value, // 提示词
-      width: width.value, // 图片宽度
-      height: height.value, // 图片高度
-      options: {
-        seed: seed.value, // 随机种子
-        steps: steps.value, // 图片生成步数
-        scale: scale.value, // 引导系数
-        sampler: sampler.value, // 采样算法
-        clipGuidancePreset: clipGuidancePreset.value, // 文本提示相匹配的图像 CLIP
-        stylePreset: stylePreset.value // 风格
-      }
-    } as ImageDrawReqVO
-    await ImageApi.drawImage(form)
-  } finally {
-    // 回调
-    emits('onDrawComplete', AiPlatformEnum.STABLE_DIFFUSION)
-    // 加载结束
-    drawIn.value = false
-  }
-}
-
-/** 填充值 */
-const settingValues = async (detail: ImageVO) => {
-  prompt.value = detail.prompt
-  width.value = detail.width
-  height.value = detail.height
-  seed.value = detail.options?.seed
-  steps.value = detail.options?.steps
-  scale.value = detail.options?.scale
-  sampler.value = detail.options?.sampler
-  clipGuidancePreset.value = detail.options?.clipGuidancePreset
-  stylePreset.value = detail.options?.stylePreset
-}
-
-/** 暴露组件方法 */
-defineExpose({ settingValues })
-</script>
-<style scoped lang="scss">
-// 提示词
-.prompt {
-}
-
-// 热词
-.hot-words {
-  display: flex;
-  flex-direction: column;
-  margin-top: 30px;
-
-  .word-list {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    justify-content: start;
-    margin-top: 15px;
-
-    .btn {
-      margin: 0;
-    }
-  }
-}
-
-// 模型
-.group-item {
-  margin-top: 30px;
-
-  .group-item-body {
-    margin-top: 15px;
-    width: 100%;
-  }
-}
-
-.btns {
-  display: flex;
-  justify-content: center;
-  margin-top: 50px;
-}
-</style>
diff --git a/src/views/ai/image/index/index.vue b/src/views/ai/image/index/index.vue
deleted file mode 100644
index 1217e79..0000000
--- a/src/views/ai/image/index/index.vue
+++ /dev/null
@@ -1,141 +0,0 @@
-<!-- image -->
-<template>
-  <div class="ai-image">
-    <div class="left">
-      <div class="segmented">
-        <el-segmented v-model="selectPlatform" :options="platformOptions" />
-      </div>
-      <div class="modal-switch-container">
-        <Dall3
-          v-if="selectPlatform === AiPlatformEnum.OPENAI"
-          ref="dall3Ref"
-          @on-draw-start="handleDrawStart"
-          @on-draw-complete="handleDrawComplete"
-        />
-        <Midjourney v-if="selectPlatform === AiPlatformEnum.MIDJOURNEY" ref="midjourneyRef" />
-        <StableDiffusion
-          v-if="selectPlatform === AiPlatformEnum.STABLE_DIFFUSION"
-          ref="stableDiffusionRef"
-          @on-draw-complete="handleDrawComplete"
-        />
-        <Other
-          v-if="selectPlatform === 'other'"
-          ref="otherRef"
-          @on-draw-complete="handleDrawComplete"
-        />
-      </div>
-    </div>
-    <div class="main">
-      <ImageList ref="imageListRef" @on-regeneration="handleRegeneration" />
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import ImageList from './components/ImageList.vue'
-import { AiPlatformEnum } from '@/views/ai/utils/constants'
-import { ImageVO } from '@/api/ai/image'
-import Dall3 from './components/dall3/index.vue'
-import Midjourney from './components/midjourney/index.vue'
-import StableDiffusion from './components/stableDiffusion/index.vue'
-import Other from './components/other/index.vue'
-
-const imageListRef = ref<any>() // image 列表 ref
-const dall3Ref = ref<any>() // dall3(openai) ref
-const midjourneyRef = ref<any>() // midjourney ref
-const stableDiffusionRef = ref<any>() // stable diffusion ref
-const otherRef = ref<any>() // stable diffusion ref
-
-// 定义属性
-const selectPlatform = ref(AiPlatformEnum.MIDJOURNEY)
-const platformOptions = [
-  {
-    label: 'DALL3 绘画',
-    value: AiPlatformEnum.OPENAI
-  },
-  {
-    label: 'MJ 绘画',
-    value: AiPlatformEnum.MIDJOURNEY
-  },
-  {
-    label: 'Stable Diffusion',
-    value: AiPlatformEnum.STABLE_DIFFUSION
-  },
-  {
-    label: '其它',
-    value: 'other'
-  }
-]
-
-/** 绘画 start  */
-const handleDrawStart = async (platform: string) => {}
-
-/** 绘画 complete */
-const handleDrawComplete = async (platform: string) => {
-  await imageListRef.value.getImageList()
-}
-
-/**  重新生成:将画图详情填充到对应平台  */
-const handleRegeneration = async (image: ImageVO) => {
-  // 切换平台
-  selectPlatform.value = image.platform
-  // 根据不同平台填充 image
-  await nextTick()
-  if (image.platform === AiPlatformEnum.MIDJOURNEY) {
-    midjourneyRef.value.settingValues(image)
-  } else if (image.platform === AiPlatformEnum.OPENAI) {
-    dall3Ref.value.settingValues(image)
-  } else if (image.platform === AiPlatformEnum.STABLE_DIFFUSION) {
-    stableDiffusionRef.value.settingValues(image)
-  }
-  // TODO @fan:貌似 other 重新设置不行?
-}
-</script>
-
-<style scoped lang="scss">
-.ai-image {
-  position: absolute;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  top: 0;
-
-  display: flex;
-  flex-direction: row;
-  height: 100%;
-  width: 100%;
-
-  .left {
-    display: flex;
-    flex-direction: column;
-    padding: 20px;
-    width: 350px;
-
-    .segmented {
-    }
-
-    .segmented .el-segmented {
-      --el-border-radius-base: 16px;
-      --el-segmented-item-selected-color: #fff;
-      background-color: #ececec;
-      width: 350px;
-    }
-
-    .modal-switch-container {
-      height: 100%;
-      overflow-y: auto;
-      margin-top: 30px;
-    }
-  }
-
-  .main {
-    flex: 1;
-    background-color: #fff;
-  }
-
-  .right {
-    width: 350px;
-    background-color: #f7f8fa;
-  }
-}
-</style>
diff --git a/src/views/ai/image/manager/index.vue b/src/views/ai/image/manager/index.vue
deleted file mode 100644
index 84403f3..0000000
--- a/src/views/ai/image/manager/index.vue
+++ /dev/null
@@ -1,251 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="用户编号" prop="userId">
-        <el-select
-          v-model="queryParams.userId"
-          clearable
-          placeholder="请输入用户编号"
-          class="!w-240px"
-        >
-          <el-option
-            v-for="item in userList"
-            :key="item.id"
-            :label="item.nickname"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="平台" prop="platform">
-        <el-select v-model="queryParams.status" placeholder="请选择平台" clearable class="!w-240px">
-          <el-option
-            v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="绘画状态" prop="status">
-        <el-select
-          v-model="queryParams.status"
-          placeholder="请选择绘画状态"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.AI_IMAGE_STATUS)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="是否发布" prop="publicStatus">
-        <el-select
-          v-model="queryParams.publicStatus"
-          placeholder="请选择是否发布"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker
-          v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-220px"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="编号" align="center" prop="id" width="180" fixed="left" />
-      <el-table-column label="图片" align="center" prop="picUrl" width="110px" fixed="left">
-        <template #default="{ row }">
-          <el-image
-            class="h-80px w-80px"
-            lazy
-            :src="row.picUrl"
-            :preview-src-list="[row.picUrl]"
-            preview-teleported
-            fit="cover"
-            v-if="row.picUrl?.length > 0"
-          />
-        </template>
-      </el-table-column>
-      <el-table-column label="用户" align="center" prop="userId" width="180">
-        <template #default="scope">
-          <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="平台" align="center" prop="platform" width="120">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" />
-        </template>
-      </el-table-column>
-      <el-table-column label="模型" align="center" prop="model" width="180" />
-      <el-table-column label="绘画状态" align="center" prop="status" width="100">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_IMAGE_STATUS" :value="scope.row.status" />
-        </template>
-      </el-table-column>
-      <el-table-column label="是否发布" align="center" prop="publicStatus">
-        <template #default="scope">
-          <el-switch
-            v-model="scope.row.publicStatus"
-            :active-value="true"
-            :inactive-value="false"
-            @change="handleUpdatePublicStatusChange(scope.row)"
-            :disabled="scope.row.status !== AiImageStatusEnum.SUCCESS"
-          />
-        </template>
-      </el-table-column>
-      <el-table-column label="提示词" align="center" prop="prompt" width="180" />
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <el-table-column label="宽度" align="center" prop="width" />
-      <el-table-column label="高度" align="center" prop="height" />
-      <el-table-column label="错误信息" align="center" prop="errorMessage" />
-      <el-table-column label="任务编号" align="center" prop="taskId" />
-      <el-table-column label="操作" align="center" width="100" fixed="right">
-        <template #default="scope">
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['ai:image:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-</template>
-
-<script setup lang="ts">
-import { getIntDictOptions, DICT_TYPE, getStrDictOptions, getBoolDictOptions } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
-import { ImageApi, ImageVO } from '@/api/ai/image'
-import * as UserApi from '@/api/system/user'
-import { AiImageStatusEnum } from '@/views/ai/utils/constants'
-
-/** AI 绘画 列表 */
-defineOptions({ name: 'AiImageManager' })
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(true) // 列表的加载中
-const list = ref<ImageVO[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  userId: undefined,
-  platform: undefined,
-  status: undefined,
-  publicStatus: undefined,
-  createTime: []
-})
-const queryFormRef = ref() // 搜索的表单
-const userList = ref<UserApi.UserVO[]>([]) // 用户列表
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await ImageApi.getImagePage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ImageApi.deleteImage(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 修改是否发布 */
-const handleUpdatePublicStatusChange = async (row: ImageVO) => {
-  try {
-    // 修改状态的二次确认
-    const text = row.publicStatus ? '公开' : '私有'
-    await message.confirm('确认要"' + text + '"该图片吗?')
-    // 发起修改状态
-    await ImageApi.updateImage({
-      id: row.id,
-      publicStatus: row.publicStatus
-    })
-    await getList()
-  } catch {
-    row.publicStatus = !row.publicStatus
-  }
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  getList()
-  // 获得用户列表
-  userList.value = await UserApi.getSimpleUserList()
-})
-</script>
diff --git a/src/views/ai/image/square/index.vue b/src/views/ai/image/square/index.vue
deleted file mode 100644
index 3da6cde..0000000
--- a/src/views/ai/image/square/index.vue
+++ /dev/null
@@ -1,104 +0,0 @@
-<template>
-  <div class="square-container">
-    <!-- TODO @fan:style 建议换成 unocss -->
-    <!-- TODO @fan:Search 可以换成 Icon 组件么? -->
-    <el-input
-      v-model="queryParams.prompt"
-      style="width: 100%; margin-bottom: 20px"
-      size="large"
-      placeholder="请输入要搜索的内容"
-      :suffix-icon="Search"
-      @keyup.enter="handleQuery"
-    />
-    <div class="gallery">
-      <!-- TODO @fan:这个图片的风格,要不和 ImageCard.vue 界面一致?(只有卡片,没有操作);因为看着更有相框的感觉~~~ -->
-      <div v-for="item in list" :key="item.id" class="gallery-item">
-        <img :src="item.picUrl" class="img" />
-      </div>
-    </div>
-    <!-- TODO @fan:缺少翻页 -->
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </div>
-</template>
-<script setup lang="ts">
-import { ImageApi, ImageVO } from '@/api/ai/image'
-import { Search } from '@element-plus/icons-vue'
-
-// TODO @fan:加个 loading 加载中的状态
-const loading = ref(true) // 列表的加载中
-const list = ref<ImageVO[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  publicStatus: true,
-  prompt: undefined
-})
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await ImageApi.getImagePageMy(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 初始化 */
-onMounted(async () => {
-  await getList()
-})
-</script>
-<style scoped lang="scss">
-.square-container {
-  background-color: #fff;
-  padding: 20px;
-
-  .gallery {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
-    gap: 10px;
-    //max-width: 1000px;
-    background-color: #fff;
-    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
-  }
-
-  .gallery-item {
-    position: relative;
-    overflow: hidden;
-    background: #f0f0f0;
-    cursor: pointer;
-    transition: transform 0.3s;
-  }
-
-  .gallery-item img {
-    width: 100%;
-    height: auto;
-    display: block;
-    transition: transform 0.3s;
-  }
-
-  .gallery-item:hover img {
-    transform: scale(1.1);
-  }
-
-  .gallery-item:hover {
-    transform: scale(1.05);
-  }
-}
-</style>
diff --git a/src/views/ai/mindmap/index/components/Left.vue b/src/views/ai/mindmap/index/components/Left.vue
deleted file mode 100644
index e684b88..0000000
--- a/src/views/ai/mindmap/index/components/Left.vue
+++ /dev/null
@@ -1,78 +0,0 @@
-<template>
-  <div class="w-[350px] p-5 flex flex-col bg-[#f5f7f9]">
-    <h3 class="w-full h-full h-7 text-5 text-center leading-[28px] title">思维导图创作中心</h3>
-    <!--下面表单部分-->
-    <div class="flex-grow overflow-y-auto">
-      <div class="mt-[30ppx]">
-        <el-text tag="b">您的需求?</el-text>
-        <el-input
-          v-model="formData.prompt"
-          maxlength="1024"
-          rows="5"
-          class="w-100% mt-15px"
-          input-style="border-radius: 7px;"
-          placeholder="请输入提示词,让AI帮你完善"
-          show-word-limit
-          type="textarea"
-        />
-        <el-button
-          class="!w-full mt-[15px]"
-          type="primary"
-          :loading="isGenerating"
-          @click="emits('submit', formData)"
-        >
-          智能生成思维导图
-        </el-button>
-      </div>
-      <div class="mt-[30px]">
-        <el-text tag="b">使用已有内容生成?</el-text>
-        <el-input
-          v-model="generatedContent"
-          maxlength="1024"
-          rows="5"
-          class="w-100% mt-15px"
-          input-style="border-radius: 7px;"
-          placeholder="例如:童话里的小屋应该是什么样子?"
-          show-word-limit
-          type="textarea"
-        />
-        <el-button
-          class="!w-full mt-[15px]"
-          type="primary"
-          @click="emits('directGenerate', generatedContent)"
-          :disabled="isGenerating"
-        >
-          直接生成
-        </el-button>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { MindMapContentExample } from '@/views/ai/utils/constants'
-
-const emits = defineEmits(['submit', 'directGenerate'])
-defineProps<{
-  isGenerating: boolean
-}>()
-// 提交的提示词字段
-const formData = reactive({
-  prompt: ''
-})
-
-const generatedContent = ref(MindMapContentExample) // 已有的内容
-
-defineExpose({
-  setGeneratedContent(newContent: string) {
-    // 设置已有的内容,在生成结束的时候将结果赋值给该值
-    generatedContent.value = newContent
-  }
-})
-</script>
-
-<style lang="scss" scoped>
-.title {
-  color: var(--el-color-primary);
-}
-</style>
diff --git a/src/views/ai/mindmap/index/components/Right.vue b/src/views/ai/mindmap/index/components/Right.vue
deleted file mode 100644
index 0550650..0000000
--- a/src/views/ai/mindmap/index/components/Right.vue
+++ /dev/null
@@ -1,175 +0,0 @@
-<template>
-  <el-card class="my-card h-full flex-grow">
-    <template #header>
-      <h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
-        <span>思维导图预览</span>
-        <!-- 展示在右上角 -->
-        <el-button type="primary" v-show="isEnd" @click="downloadImage" size="small">
-          <template #icon>
-            <Icon icon="ph:copy-bold" />
-          </template>
-          下载图片
-        </el-button>
-      </h3>
-    </template>
-
-    <div ref="contentRef" class="hide-scroll-bar h-full box-border">
-      <!--展示 markdown 的容器,最终生成的是 html 字符串,直接用 v-html 嵌入-->
-      <div v-if="isGenerating" ref="mdContainerRef" class="wh-full overflow-y-auto">
-        <div class="flex flex-col items-center justify-center" v-html="html"></div>
-      </div>
-
-      <div ref="mindmapRef" class="wh-full">
-        <svg ref="svgRef" class="w-full" :style="{ height: `${contentAreaHeight}px` }" />
-        <div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div>
-      </div>
-    </div>
-  </el-card>
-</template>
-
-<script setup lang="ts">
-import { Markmap } from 'markmap-view'
-import { Transformer } from 'markmap-lib'
-import { Toolbar } from 'markmap-toolbar'
-import markdownit from 'markdown-it'
-
-const md = markdownit()
-const message = useMessage() // 消息弹窗
-
-// TODO @hhero:mindmap 改成 mindMap 更精准哈
-const props = defineProps<{
-  mindmapResult: string // 生成结果 TODO @hhero 改成 generatedContent 会不会好点
-  isEnd: boolean // 是否结束
-  isGenerating: boolean // 是否正在生成
-  isStart: boolean // 开始状态,开始时需要清除 html
-}>()
-const contentRef = ref<HTMLDivElement>() // 右侧出来header以下的区域
-const mdContainerRef = ref<HTMLDivElement>() // markdown 的容器,用来滚动到底下的
-const mindmapRef = ref<HTMLDivElement>() // 思维导图的容器
-const svgRef = ref<SVGElement>() // 思维导图的渲染 svg
-const toolBarRef = ref<HTMLDivElement>() // 思维导图右下角的工具栏,缩放等
-const html = ref('') // 生成过程中的文本
-const contentAreaHeight = ref(0) // 生成区域的高度,出去 header 部分
-let markMap: Markmap | null = null
-const transformer = new Transformer()
-
-onMounted(() => {
-  contentAreaHeight.value = contentRef.value?.clientHeight || 0 // 获取区域高度
-  /** 初始化思维导图 **/
-  try {
-    markMap = Markmap.create(svgRef.value!)
-    const { el } = Toolbar.create(markMap)
-    toolBarRef.value?.append(el)
-    nextTick(update)
-  } catch (e) {
-    message.error('思维导图初始化失败')
-  }
-})
-
-watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => {
-  // 开始生成的时候清空一下 markdown 的内容
-  if (isStart) {
-    html.value = ''
-  }
-  // 生成内容的时候使用 markdown 来渲染
-  if (isGenerating) {
-    html.value = md.render(mindmapResult)
-  }
-  if (isEnd) {
-    update()
-  }
-})
-
-/** 更新思维导图的展示 */
-const update = () => {
-  try {
-    const { root } = transformer.transform(processContent(props.mindmapResult))
-    markMap?.setData(root)
-    markMap?.fit()
-  } catch (e) {
-    console.error(e)
-  }
-}
-
-/** 处理内容 */
-const processContent = (text: string) => {
-  const arr: string[] = []
-  const lines = text.split('\n')
-  for (let line of lines) {
-    if (line.indexOf('```') !== -1) {
-      continue
-    }
-    line = line.replace(/([*_~`>])|(\d+\.)\s/g, '')
-    arr.push(line)
-  }
-  return arr.join('\n')
-}
-
-/** 下载图片 */
-// TODO @hhhero:可以抽到 download 这个里面,src/utils/download.ts 么?复用 image 方法?
-// download SVG to png file
-const downloadImage = () => {
-  const svgElement = mindmapRef.value
-  // 将 SVG 渲染到图片对象
-  const serializer = new XMLSerializer()
-  const source =
-    '<?xml version="1.0" standalone="no"?>\r\n' + serializer.serializeToString(svgRef.value!)
-  const image = new Image()
-  image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(source)
-
-  // 将图片对象渲染
-  const canvas = document.createElement('canvas')
-  canvas.width = svgElement?.offsetWidth || 0
-  canvas.height = svgElement?.offsetHeight || 0
-  let context = canvas.getContext('2d')
-  context?.clearRect(0, 0, canvas.width, canvas.height)
-
-  image.onload = function () {
-    context?.drawImage(image, 0, 0)
-    const a = document.createElement('a')
-    a.download = 'mindmap.png'
-    a.href = canvas.toDataURL(`image/png`)
-    a.click()
-  }
-}
-
-defineExpose({
-  scrollBottom() {
-    mdContainerRef.value?.scrollTo(0, mdContainerRef.value?.scrollHeight)
-  }
-})
-</script>
-<style lang="scss" scoped>
-.hide-scroll-bar {
-  -ms-overflow-style: none;
-  scrollbar-width: none;
-
-  &::-webkit-scrollbar {
-    width: 0;
-    height: 0;
-  }
-}
-.my-card {
-  display: flex;
-  flex-direction: column;
-
-  :deep(.el-card__body) {
-    box-sizing: border-box;
-    flex-grow: 1;
-    overflow-y: auto;
-    padding: 0;
-    @extend .hide-scroll-bar;
-  }
-}
-// markmap的tool样式覆盖
-:deep(.markmap) {
-  width: 100%;
-}
-:deep(.mm-toolbar-brand) {
-  display: none;
-}
-:deep(.mm-toolbar) {
-  display: flex;
-  flex-direction: row;
-}
-</style>
diff --git a/src/views/ai/mindmap/index/index.vue b/src/views/ai/mindmap/index/index.vue
deleted file mode 100644
index bae7408..0000000
--- a/src/views/ai/mindmap/index/index.vue
+++ /dev/null
@@ -1,92 +0,0 @@
-<template>
-  <div class="absolute top-0 left-0 right-0 bottom-0 flex">
-    <!--表单区域-->
-    <Left
-      ref="leftRef"
-      @submit="submit"
-      @direct-generate="directGenerate"
-      :is-generating="isGenerating"
-    />
-    <!--右边生成思维导图区域-->
-    <Right
-      ref="rightRef"
-      :mindmapResult="mindmapResult"
-      :isEnd="isEnd"
-      :isGenerating="isGenerating"
-      :isStart="isStart"
-    />
-  </div>
-</template>
-
-<script setup lang="ts">
-import Left from './components/Left.vue'
-import Right from './components/Right.vue'
-import { AiMindMapApi, AiMindMapGenerateReqVO } from '@/api/ai/mindmap'
-import { MindMapContentExample } from '@/views/ai/utils/constants'
-
-defineOptions({
-  name: 'AiMindMap'
-})
-const ctrl = ref<AbortController>() // 请求控制
-const isGenerating = ref(false) // 是否正在生成思维导图
-const isStart = ref(false) // 开始生成,用来清空思维导图
-const isEnd = ref(true) // 用来判断结束的时候渲染思维导图
-const message = useMessage() // 消息提示
-
-const mindmapResult = ref('') // 生成思维导图结果
-
-const leftRef = ref<InstanceType<typeof Left>>() // 左边组件
-const rightRef = ref<InstanceType<typeof Right>>() // 右边组件
-
-/** 使用已有内容直接生成 **/
-const directGenerate = (existPrompt: string) => {
-  isEnd.value = false // 先设置为false再设置为true,让子组建的watch能够监听到
-  mindmapResult.value = existPrompt
-  isEnd.value = true
-}
-
-/** 停止 stream 生成 */
-const stopStream = () => {
-  isGenerating.value = false
-  isStart.value = false
-  ctrl.value?.abort()
-}
-
-/** 提交生成 */
-const submit = (data: AiMindMapGenerateReqVO) => {
-  isGenerating.value = true
-  isStart.value = true
-  isEnd.value = false
-  ctrl.value = new AbortController() // 请求控制赋值
-  mindmapResult.value = '' // 清空生成数据
-  AiMindMapApi.generateMindMap({
-    data,
-    onMessage: async (res) => {
-      const { code, data, msg } = JSON.parse(res.data)
-      if (code !== 0) {
-        message.alert(`生成思维导图异常! ${msg}`)
-        stopStream()
-        return
-      }
-      mindmapResult.value = mindmapResult.value + data
-      await nextTick()
-      rightRef.value?.scrollBottom()
-    },
-    onClose() {
-      isEnd.value = true
-      leftRef.value?.setGeneratedContent(mindmapResult.value)
-      stopStream()
-    },
-    onError(err) {
-      console.error('生成思维导图失败', err)
-      stopStream()
-    },
-    ctrl: ctrl.value
-  })
-}
-
-/** 初始化 */
-onMounted(() => {
-  mindmapResult.value = MindMapContentExample
-})
-</script>
diff --git a/src/views/ai/model/apiKey/ApiKeyForm.vue b/src/views/ai/model/apiKey/ApiKeyForm.vue
deleted file mode 100644
index a8fc012..0000000
--- a/src/views/ai/model/apiKey/ApiKeyForm.vue
+++ /dev/null
@@ -1,132 +0,0 @@
-<template>
-  <Dialog :title="dialogTitle" v-model="dialogVisible">
-    <el-form
-      ref="formRef"
-      :model="formData"
-      :rules="formRules"
-      label-width="120px"
-      v-loading="formLoading"
-    >
-      <el-form-item label="所属平台" prop="platform">
-        <el-select v-model="formData.platform" placeholder="请输入平台" clearable>
-          <el-option
-            v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="名称" prop="name">
-        <el-input v-model="formData.name" placeholder="请输入名称" />
-      </el-form-item>
-      <el-form-item label="密钥" prop="apiKey">
-        <el-input v-model="formData.apiKey" placeholder="请输入密钥" />
-      </el-form-item>
-      <el-form-item label="自定义 API URL" prop="url">
-        <el-input v-model="formData.url" placeholder="请输入自定义 API URL" />
-      </el-form-item>
-      <el-form-item label="状态" prop="status">
-        <el-radio-group v-model="formData.status">
-          <el-radio
-            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-            :key="dict.value"
-            :label="dict.value"
-          >
-            {{ dict.label }}
-          </el-radio>
-        </el-radio-group>
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-<script setup lang="ts">
-import { getIntDictOptions, DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey'
-import { CommonStatusEnum } from '@/utils/constants'
-
-/** AI API 密钥 表单 */
-defineOptions({ name: 'ApiKeyForm' })
-
-const { t } = useI18n() // 国际化
-const message = useMessage() // 消息弹窗
-
-const dialogVisible = ref(false) // 弹窗的是否展示
-const dialogTitle = ref('') // 弹窗的标题
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const formType = ref('') // 表单的类型:create - 新增;update - 修改
-const formData = ref({
-  id: undefined,
-  name: undefined,
-  apiKey: undefined,
-  platform: undefined,
-  url: undefined,
-  status: CommonStatusEnum.ENABLE
-})
-const formRules = reactive({
-  name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
-  apiKey: [{ required: true, message: '密钥不能为空', trigger: 'blur' }],
-  platform: [{ required: true, message: '平台不能为空', trigger: 'blur' }],
-  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
-})
-const formRef = ref() // 表单 Ref
-
-/** 打开弹窗 */
-const open = async (type: string, id?: number) => {
-  dialogVisible.value = true
-  dialogTitle.value = t('action.' + type)
-  formType.value = type
-  resetForm()
-  // 修改时,设置数据
-  if (id) {
-    formLoading.value = true
-    try {
-      formData.value = await ApiKeyApi.getApiKey(id)
-    } finally {
-      formLoading.value = false
-    }
-  }
-}
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  await formRef.value.validate()
-  // 提交请求
-  formLoading.value = true
-  try {
-    const data = formData.value as unknown as ApiKeyVO
-    if (formType.value === 'create') {
-      await ApiKeyApi.createApiKey(data)
-      message.success(t('common.createSuccess'))
-    } else {
-      await ApiKeyApi.updateApiKey(data)
-      message.success(t('common.updateSuccess'))
-    }
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-    id: undefined,
-    name: undefined,
-    apiKey: undefined,
-    platform: undefined,
-    url: undefined,
-    status: CommonStatusEnum.ENABLE
-  }
-  formRef.value?.resetFields()
-}
-</script>
diff --git a/src/views/ai/model/apiKey/index.vue b/src/views/ai/model/apiKey/index.vue
deleted file mode 100644
index 6daf6a7..0000000
--- a/src/views/ai/model/apiKey/index.vue
+++ /dev/null
@@ -1,180 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="名称" prop="name">
-        <el-input
-          v-model="queryParams.name"
-          placeholder="请输入名称"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="平台" prop="platform">
-        <el-select
-          v-model="queryParams.platform"
-          placeholder="请输入平台"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="状态" prop="status">
-        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px">
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button
-          type="primary"
-          plain
-          @click="openForm('create')"
-          v-hasPermi="['ai:api-key:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="所属平台" align="center" prop="platform">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" />
-        </template>
-      </el-table-column>
-      <el-table-column label="名称" align="center" prop="name" />
-      <el-table-column label="密钥" align="center" prop="apiKey" />
-      <el-table-column label="自定义 API URL" align="center" prop="url" />
-      <el-table-column label="状态" align="center" prop="status">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['ai:api-key:update']"
-          >
-            编辑
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['ai:api-key:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-
-  <!-- 表单弹窗:添加/修改 -->
-  <ApiKeyForm ref="formRef" @success="getList" />
-</template>
-
-<script setup lang="ts">
-import { getIntDictOptions, DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey'
-import ApiKeyForm from './ApiKeyForm.vue'
-
-/** AI API 密钥 列表 */
-defineOptions({ name: 'AiApiKey' })
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(true) // 列表的加载中
-const list = ref<ApiKeyVO[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  name: undefined,
-  platform: undefined,
-  status: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await ApiKeyApi.getApiKeyPage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 添加/修改操作 */
-const formRef = ref()
-const openForm = (type: string, id?: number) => {
-  formRef.value.open(type, id)
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ApiKeyApi.deleteApiKey(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
-</script>
diff --git a/src/views/ai/model/chatModel/ChatModelForm.vue b/src/views/ai/model/chatModel/ChatModelForm.vue
deleted file mode 100644
index e3f785c..0000000
--- a/src/views/ai/model/chatModel/ChatModelForm.vue
+++ /dev/null
@@ -1,181 +0,0 @@
-<template>
-  <Dialog :title="dialogTitle" v-model="dialogVisible">
-    <el-form
-      ref="formRef"
-      :model="formData"
-      :rules="formRules"
-      label-width="120px"
-      v-loading="formLoading"
-    >
-      <el-form-item label="所属平台" prop="platform">
-        <el-select v-model="formData.platform" placeholder="请输入平台" clearable>
-          <el-option
-            v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="API 秘钥" prop="keyId">
-        <el-select v-model="formData.keyId" placeholder="请选择 API 秘钥" clearable>
-          <el-option
-            v-for="apiKey in apiKeyList"
-            :key="apiKey.id"
-            :label="apiKey.name"
-            :value="apiKey.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="模型名字" prop="name">
-        <el-input v-model="formData.name" placeholder="请输入模型名字" />
-      </el-form-item>
-      <el-form-item label="模型标识" prop="model">
-        <el-input v-model="formData.model" placeholder="请输入模型标识" />
-      </el-form-item>
-      <el-form-item label="模型排序" prop="sort">
-        <el-input-number v-model="formData.sort" placeholder="请输入模型排序" class="!w-1/1" />
-      </el-form-item>
-      <el-form-item label="开启状态" prop="status">
-        <el-radio-group v-model="formData.status">
-          <el-radio
-            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-            :key="dict.value"
-            :label="dict.value"
-          >
-            {{ dict.label }}
-          </el-radio>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="温度参数" prop="temperature">
-        <el-input-number
-          v-model="formData.temperature"
-          placeholder="请输入温度参数"
-          :min="0"
-          :max="2"
-          :precision="2"
-        />
-      </el-form-item>
-      <el-form-item label="回复数 Token 数" prop="maxTokens">
-        <el-input-number
-          v-model="formData.maxTokens"
-          placeholder="请输入回复数 Token 数"
-          :min="0"
-          :max="4096"
-        />
-      </el-form-item>
-      <el-form-item label="上下文数量" prop="maxContexts">
-        <el-input-number
-          v-model="formData.maxContexts"
-          placeholder="请输入上下文数量"
-          :min="0"
-          :max="20"
-        />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-<script setup lang="ts">
-import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel'
-import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey'
-import { CommonStatusEnum } from '@/utils/constants'
-import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
-
-/** API 聊天模型 表单 */
-defineOptions({ name: 'ChatModelForm' })
-
-const { t } = useI18n() // 国际化
-const message = useMessage() // 消息弹窗
-
-const dialogVisible = ref(false) // 弹窗的是否展示
-const dialogTitle = ref('') // 弹窗的标题
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const formType = ref('') // 表单的类型:create - 新增;update - 修改
-const formData = ref({
-  id: undefined,
-  keyId: undefined,
-  name: undefined,
-  model: undefined,
-  platform: undefined,
-  sort: undefined,
-  status: CommonStatusEnum.ENABLE,
-  temperature: undefined,
-  maxTokens: undefined,
-  maxContexts: undefined
-})
-const formRules = reactive({
-  keyId: [{ required: true, message: 'API 秘钥不能为空', trigger: 'blur' }],
-  name: [{ required: true, message: '模型名字不能为空', trigger: 'blur' }],
-  model: [{ required: true, message: '模型标识不能为空', trigger: 'blur' }],
-  platform: [{ required: true, message: '所属平台不能为空', trigger: 'blur' }],
-  sort: [{ required: true, message: '排序不能为空', trigger: 'blur' }],
-  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
-})
-const formRef = ref() // 表单 Ref
-const apiKeyList = ref([] as ApiKeyVO[]) // API 密钥列表
-
-/** 打开弹窗 */
-const open = async (type: string, id?: number) => {
-  dialogVisible.value = true
-  dialogTitle.value = t('action.' + type)
-  formType.value = type
-  resetForm()
-  // 修改时,设置数据
-  if (id) {
-    formLoading.value = true
-    try {
-      formData.value = await ChatModelApi.getChatModel(id)
-    } finally {
-      formLoading.value = false
-    }
-  }
-  // 获得下拉数据
-  apiKeyList.value = await ApiKeyApi.getApiKeySimpleList(CommonStatusEnum.ENABLE)
-}
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  await formRef.value.validate()
-  // 提交请求
-  formLoading.value = true
-  try {
-    const data = formData.value as unknown as ChatModelVO
-    if (formType.value === 'create') {
-      await ChatModelApi.createChatModel(data)
-      message.success(t('common.createSuccess'))
-    } else {
-      await ChatModelApi.updateChatModel(data)
-      message.success(t('common.updateSuccess'))
-    }
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-    id: undefined,
-    keyId: undefined,
-    name: undefined,
-    model: undefined,
-    platform: undefined,
-    sort: undefined,
-    status: CommonStatusEnum.ENABLE,
-    temperature: undefined,
-    maxTokens: undefined,
-    maxContexts: undefined
-  }
-  formRef.value?.resetFields()
-}
-</script>
diff --git a/src/views/ai/model/chatModel/index.vue b/src/views/ai/model/chatModel/index.vue
deleted file mode 100644
index c550674..0000000
--- a/src/views/ai/model/chatModel/index.vue
+++ /dev/null
@@ -1,185 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="模型名字" prop="name">
-        <el-input
-          v-model="queryParams.name"
-          placeholder="请输入模型名字"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="模型标识" prop="model">
-        <el-input
-          v-model="queryParams.model"
-          placeholder="请输入模型标识"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="模型平台" prop="platform">
-        <el-input
-          v-model="queryParams.platform"
-          placeholder="请输入模型平台"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button
-          type="primary"
-          plain
-          @click="openForm('create')"
-          v-hasPermi="['ai:chat-model:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="所属平台" align="center" prop="platform">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" />
-        </template>
-      </el-table-column>
-      <el-table-column label="模型名字" align="center" prop="name" />
-      <el-table-column label="模型标识" align="center" prop="model" />
-      <el-table-column label="API 秘钥" align="center" prop="keyId" min-width="140">
-        <template #default="scope">
-          <span>{{ apiKeyList.find((item) => item.id === scope.row.keyId)?.name }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="排序" align="center" prop="sort" />
-      <el-table-column label="状态" align="center" prop="status">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
-        </template>
-      </el-table-column>
-      <el-table-column label="温度参数" align="center" prop="temperature" />
-      <el-table-column label="回复数 Token 数" align="center" prop="maxTokens" min-width="140" />
-      <el-table-column label="上下文数量" align="center" prop="maxContexts" />
-      <el-table-column label="操作" align="center">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['ai:chat-model:update']"
-          >
-            编辑
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['ai:chat-model:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-
-  <!-- 表单弹窗:添加/修改 -->
-  <ChatModelForm ref="formRef" @success="getList" />
-</template>
-
-<script setup lang="ts">
-import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel'
-import ChatModelForm from './ChatModelForm.vue'
-import { DICT_TYPE } from '@/utils/dict'
-import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey'
-
-/** API 聊天模型 列表 */
-defineOptions({ name: 'AiChatModel' })
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(true) // 列表的加载中
-const list = ref<ChatModelVO[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  name: undefined,
-  model: undefined,
-  platform: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-const apiKeyList = ref([] as ApiKeyVO[]) // API 密钥列表
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await ChatModelApi.getChatModelPage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 添加/修改操作 */
-const formRef = ref()
-const openForm = (type: string, id?: number) => {
-  formRef.value.open(type, id)
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ChatModelApi.deleteChatModel(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  getList()
-  // 获得下拉数据
-  apiKeyList.value = await ApiKeyApi.getApiKeySimpleList()
-})
-</script>
diff --git a/src/views/ai/model/chatRole/ChatRoleForm.vue b/src/views/ai/model/chatRole/ChatRoleForm.vue
deleted file mode 100644
index 3c49e8e..0000000
--- a/src/views/ai/model/chatRole/ChatRoleForm.vue
+++ /dev/null
@@ -1,183 +0,0 @@
-<template>
-  <Dialog :title="dialogTitle" v-model="dialogVisible">
-    <el-form
-      ref="formRef"
-      :model="formData"
-      :rules="formRules"
-      label-width="100px"
-      v-loading="formLoading"
-    >
-      <el-form-item label="角色名称" prop="name">
-        <el-input v-model="formData.name" placeholder="请输入角色名称" />
-      </el-form-item>
-      <el-form-item label="角色头像" prop="avatar">
-        <UploadImg v-model="formData.avatar" height="60px" width="60px" />
-      </el-form-item>
-      <el-form-item label="绑定模型" prop="modelId" v-if="!isUser">
-        <el-select v-model="formData.modelId" placeholder="请选择模型" clearable>
-          <el-option
-            v-for="chatModel in chatModelList"
-            :key="chatModel.id"
-            :label="chatModel.name"
-            :value="chatModel.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="角色类别" prop="category" v-if="!isUser">
-        <el-input v-model="formData.category" placeholder="请输入角色类别" />
-      </el-form-item>
-      <el-form-item label="角色描述" prop="description">
-        <el-input type="textarea" v-model="formData.description" placeholder="请输入角色描述" />
-      </el-form-item>
-      <el-form-item label="角色设定" prop="systemMessage">
-        <el-input type="textarea" v-model="formData.systemMessage" placeholder="请输入角色设定" />
-      </el-form-item>
-      <el-form-item label="是否公开" prop="publicStatus" v-if="!isUser">
-        <el-radio-group v-model="formData.publicStatus">
-          <el-radio
-            v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-            :key="dict.value"
-            :label="dict.value"
-          >
-            {{ dict.label }}
-          </el-radio>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="角色排序" prop="sort" v-if="!isUser">
-        <el-input-number v-model="formData.sort" placeholder="请输入角色排序" class="!w-1/1" />
-      </el-form-item>
-      <el-form-item label="开启状态" prop="status" v-if="!isUser">
-        <el-radio-group v-model="formData.status">
-          <el-radio
-            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-            :key="dict.value"
-            :label="dict.value"
-          >
-            {{ dict.label }}
-          </el-radio>
-        </el-radio-group>
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-<script setup lang="ts">
-import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
-import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole'
-import { CommonStatusEnum } from '@/utils/constants'
-import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel'
-import {FormRules} from "element-plus";
-
-/** AI 聊天角色 表单 */
-defineOptions({ name: 'ChatRoleForm' })
-
-const { t } = useI18n() // 国际化
-const message = useMessage() // 消息弹窗
-
-const dialogVisible = ref(false) // 弹窗的是否展示
-const dialogTitle = ref('') // 弹窗的标题
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const formType = ref('') // 表单的类型:create - 新增;update - 修改
-const formData = ref({
-  id: undefined,
-  modelId: undefined,
-  name: undefined,
-  avatar: undefined,
-  category: undefined,
-  sort: undefined,
-  description: undefined,
-  systemMessage: undefined,
-  publicStatus: true,
-  status: CommonStatusEnum.ENABLE
-})
-const formRef = ref() // 表单 Ref
-const chatModelList = ref([] as ChatModelVO[]) // 聊天模型列表
-
-/** 是否【我】自己创建,私有角色 */
-const isUser = computed(() => {
-  return formType.value === 'my-create' || formType.value === 'my-update'
-})
-
-const formRules = reactive<FormRules>({
-  name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
-  avatar: [{ required: true, message: '角色头像不能为空', trigger: 'blur' }],
-  category: [{ required: true, message: '角色类别不能为空', trigger: 'blur' }],
-  sort: [{ required: true, message: '角色排序不能为空', trigger: 'blur' }],
-  description: [{ required: true, message: '角色描述不能为空', trigger: 'blur' }],
-  systemMessage: [{ required: true, message: '角色设定不能为空', trigger: 'blur' }],
-  publicStatus: [{ required: true, message: '是否公开不能为空', trigger: 'blur' }]
-})
-
-/** 打开弹窗 */
-// TODO @fan:title 是不是收敛到 type 判断生成 title,会更合理
-const open = async (type: string, id?: number, title?: string) => {
-  dialogVisible.value = true
-  dialogTitle.value = title || t('action.' + type)
-  formType.value = type
-  resetForm()
-  // 修改时,设置数据
-  if (id) {
-    formLoading.value = true
-    try {
-      formData.value = await ChatRoleApi.getChatRole(id)
-    } finally {
-      formLoading.value = false
-    }
-  }
-  // 获得下拉数据
-  chatModelList.value = await ChatModelApi.getChatModelSimpleList(CommonStatusEnum.ENABLE)
-}
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  await formRef.value.validate()
-  // 提交请求
-  formLoading.value = true
-  try {
-    const data = formData.value as unknown as ChatRoleVO
-    // tip: my-create、my-update 是 chat 角色仓库调用
-    // tip: create、else 是后台管理调用
-    if (formType.value === 'my-create') {
-      await ChatRoleApi.createMy(data)
-      message.success(t('common.createSuccess'))
-    } else if (formType.value === 'my-update') {
-      await ChatRoleApi.updateMy(data)
-      message.success(t('common.updateSuccess'))
-    } else if (formType.value === 'create') {
-      await ChatRoleApi.createChatRole(data)
-      message.success(t('common.createSuccess'))
-    } else {
-      await ChatRoleApi.updateChatRole(data)
-      message.success(t('common.updateSuccess'))
-    }
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-    id: undefined,
-    modelId: undefined,
-    name: undefined,
-    avatar: undefined,
-    category: undefined,
-    sort: undefined,
-    description: undefined,
-    systemMessage: undefined,
-    publicStatus: true,
-    status: CommonStatusEnum.ENABLE
-  }
-  formRef.value?.resetFields()
-}
-</script>
diff --git a/src/views/ai/model/chatRole/index.vue b/src/views/ai/model/chatRole/index.vue
deleted file mode 100644
index e870a55..0000000
--- a/src/views/ai/model/chatRole/index.vue
+++ /dev/null
@@ -1,187 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="角色名称" prop="name">
-        <el-input
-          v-model="queryParams.name"
-          placeholder="请输入角色名称"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="角色类别" prop="category">
-        <el-input
-          v-model="queryParams.category"
-          placeholder="请输入角色类别"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="是否公开" prop="publicStatus">
-        <el-select
-          v-model="queryParams.publicStatus"
-          placeholder="请选择是否公开"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button
-          type="primary"
-          plain
-          @click="openForm('create')"
-          v-hasPermi="['ai:chat-role:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="角色名称" align="center" prop="name" />
-      <el-table-column label="绑定模型" align="center" prop="modelName" />
-      <el-table-column label="角色头像" align="center" prop="avatar">
-        <template #default="scope">
-          <el-image :src="scope?.row.avatar" class="w-32px h-32px" />
-        </template>
-      </el-table-column>
-      <el-table-column label="角色类别" align="center" prop="category" />
-      <el-table-column label="角色描述" align="center" prop="description" />
-      <el-table-column label="角色设定" align="center" prop="systemMessage" />
-      <el-table-column label="是否公开" align="center" prop="publicStatus">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.publicStatus" />
-        </template>
-      </el-table-column>
-      <el-table-column label="状态" align="center" prop="status">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
-        </template>
-      </el-table-column>
-      <el-table-column label="角色排序" align="center" prop="sort" />
-      <el-table-column label="操作" align="center">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['ai:chat-role:update']"
-          >
-            编辑
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['ai:chat-role:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-
-  <!-- 表单弹窗:添加/修改 -->
-  <ChatRoleForm ref="formRef" @success="getList" />
-</template>
-
-<script setup lang="ts">
-import { getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
-import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole'
-import ChatRoleForm from './ChatRoleForm.vue'
-
-/** AI 聊天角色 列表 */
-defineOptions({ name: 'AiChatRole' })
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(true) // 列表的加载中
-const list = ref<ChatRoleVO[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  name: undefined,
-  category: undefined,
-  publicStatus: true
-})
-const queryFormRef = ref() // 搜索的表单
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await ChatRoleApi.getChatRolePage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 添加/修改操作 */
-const formRef = ref()
-const openForm = (type: string, id?: number) => {
-  formRef.value.open(type, id)
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ChatRoleApi.deleteChatRole(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
-</script>
diff --git a/src/views/ai/music/index/index.vue b/src/views/ai/music/index/index.vue
deleted file mode 100644
index 413792a..0000000
--- a/src/views/ai/music/index/index.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<template>
-<div class="flex h-full items-stretch">
-    <!-- 模式 -->
-    <Mode class="flex-none" @generate-music="generateMusic"/>
-    <!-- 音频列表 -->
-    <List ref="listRef" class="flex-auto"/>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import Mode from './mode/index.vue'
-import List from './list/index.vue'
-
-defineOptions({ name: 'Index' })
-
-const listRef = ref<Nullable<{generateMusic: (...args) => void}>>(null)
-
-/*
- *@Description: 拿到左侧配置信息调用右侧音乐生成的方法
- *@MethodAuthor: xiaohong
- *@Date: 2024-07-19 11:13:38
-*/
-function generateMusic (args: {formData: Recordable}) {
- unref(listRef)?.generateMusic(args.formData)
-}
-</script>
diff --git a/src/views/ai/music/index/list/audioBar/index.vue b/src/views/ai/music/index/list/audioBar/index.vue
deleted file mode 100644
index db7f767..0000000
--- a/src/views/ai/music/index/list/audioBar/index.vue
+++ /dev/null
@@ -1,70 +0,0 @@
-<template>
-  <div class="flex items-center justify-between px-2 h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none">
-    <!-- 歌曲信息 -->
-    <div class="flex gap-[10px]">
-      <el-image src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" class="w-[45px]"/>
-      <div>
-        <div>{{currentSong.name}}</div>
-        <div class="text-[12px] text-gray-400">{{currentSong.singer}}</div>
-      </div>
-    </div>
-      
-    <!-- 音频controls -->
-    <div class="flex gap-[12px] items-center">
-      <Icon icon="majesticons:back-circle" :size="20" class="text-gray-300 cursor-pointer"/>
-      <Icon :icon="audioProps.paused ? 'mdi:arrow-right-drop-circle' : 'solar:pause-circle-bold'" :size="30" class=" cursor-pointer" @click="toggleStatus('paused')"/>
-      <Icon icon="majesticons:next-circle" :size="20" class="text-gray-300 cursor-pointer"/>
-      <div class="flex gap-[16px] items-center">
-        <span>{{audioProps.currentTime}}</span>
-        <el-slider v-model="audioProps.duration" color="#409eff" class="w-[160px!important] "/>
-        <span>{{ audioProps.duration }}</span>
-      </div>
-      <!-- 音频 -->
-      <audio v-bind="audioProps" ref="audioRef" controls v-show="!audioProps" @timeupdate="audioTimeUpdate">
-        <source :src="audioUrl"/>
-      </audio>
-    </div>
-
-    <!-- 音量控制器 -->
-    <div class="flex gap-[16px] items-center">
-      <Icon :icon="audioProps.muted ? 'tabler:volume-off' : 'tabler:volume'" :size="20" class="cursor-pointer" @click="toggleStatus('muted')"/>
-      <el-slider v-model="audioProps.volume" color="#409eff" class="w-[160px!important] "/>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import { formatPast } from '@/utils/formatTime'
-import audioUrl from '@/assets/audio/response.mp3'
-
-defineOptions({ name: 'Index' })
-
-const currentSong = inject('currentSong', {})
-
-const audioRef = ref<Nullable<HTMLElement>>(null)
-  // 音频相关属性https://www.runoob.com/tags/ref-av-dom.html
-const audioProps = reactive({
-  autoplay: true,
-  paused: false,
-  currentTime: '00:00',
-  duration: '00:00',
-  muted:  false,
-  volume: 50,
-})
-
-function toggleStatus (type: string) {
-  audioProps[type] = !audioProps[type]
-  if (type === 'paused' && audioRef.value) {
-    if (audioProps[type]) {
-      audioRef.value.pause()
-    } else {
-      audioRef.value.play()
-    }
-  }
-}
-
-// 更新播放位置
-function audioTimeUpdate (args) {
-  audioProps.currentTime = formatPast(new Date(args.timeStamp), 'mm:ss')
-}
-</script>
diff --git a/src/views/ai/music/index/list/index.vue b/src/views/ai/music/index/list/index.vue
deleted file mode 100644
index 6c33f56..0000000
--- a/src/views/ai/music/index/list/index.vue
+++ /dev/null
@@ -1,108 +0,0 @@
-<template>
-  <div class="flex flex-col">
-    <div class="flex-auto flex overflow-hidden">
-      <el-tabs v-model="currentType" class="flex-auto px-[var(--app-content-padding)]">
-        <!-- 我的创作 -->
-        <el-tab-pane v-loading="loading" label="我的创作" name="mine">
-          <el-row v-if="mySongList.length" :gutter="12">
-            <el-col v-for="song in mySongList" :key="song.id" :span="24">
-              <songCard :songInfo="song" @play="setCurrentSong(song)"/>
-            </el-col>
-          </el-row>
-          <el-empty v-else description="暂无音乐"/>
-        </el-tab-pane>
-
-        <!-- 试听广场 -->
-        <el-tab-pane v-loading="loading" label="试听广场" name="square">
-          <el-row v-if="squareSongList.length" v-loading="loading" :gutter="12">
-            <el-col v-for="song in squareSongList" :key="song.id" :span="24">
-              <songCard :songInfo="song" @play="setCurrentSong(song)"/>
-            </el-col>
-          </el-row>
-          <el-empty v-else description="暂无音乐"/>
-        </el-tab-pane>
-      </el-tabs>
-      <!-- songInfo -->
-      <songInfo class="flex-none"/>
-    </div>
-    <audioBar class="flex-none"/>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import songCard from './songCard/index.vue'
-import songInfo from './songInfo/index.vue'
-import audioBar from './audioBar/index.vue'
-
-defineOptions({ name: 'Index' })
-
-
-const currentType = ref('mine')
-// loading 状态
-const loading = ref(false)
-// 当前音乐
-const currentSong = ref({})
-
-const mySongList = ref<Recordable[]>([])
-const squareSongList = ref<Recordable[]>([])
-
-provide('currentSong', currentSong)
-
-/*
- *@Description: 调接口生成音乐列表
- *@MethodAuthor: xiaohong
- *@Date: 2024-06-27 17:06:44
-*/
-function generateMusic (formData: Recordable) {
-  console.log(formData);
-  loading.value = true
-  setTimeout(() => {
-    mySongList.value = Array.from({ length: 20 }, (_, index) => {
-      return {
-        id: index,
-        audioUrl: '',
-        videoUrl: '',
-        title: '我走后' + index,
-        imageUrl: 'https://www.carsmp3.com/data/attachment/forum/201909/19/091020q5kgre20fidreqyt.jpg',
-        desc: 'Metal, symphony, film soundtrack, grand, majesticMetal, dtrack, grand, majestic',
-        date: '2024年04月30日 14:02:57',
-        lyric: `<div class="_words_17xen_66"><div>大江东去,浪淘尽,千古风流人物。
-          </div><div>故垒西边,人道是,三国周郎赤壁。
-          </div><div>乱石穿空,惊涛拍岸,卷起千堆雪。
-          </div><div>江山如画,一时多少豪杰。
-          </div><div>
-          </div><div>遥想公瑾当年,小乔初嫁了,雄姿英发。
-          </div><div>羽扇纶巾,谈笑间,樯橹灰飞烟灭。
-          </div><div>故国神游,多情应笑我,早生华发。
-          </div><div>人生如梦,一尊还酹江月。</div></div>`
-      }
-    })
-    loading.value = false
-  }, 3000)
-}
-
-/*
- *@Description: 设置当前播放的音乐
- *@MethodAuthor: xiaohong
- *@Date: 2024-07-19 11:22:33
-*/
-function setCurrentSong (music: Recordable) {
-  currentSong.value = music
-}
-
-defineExpose({
-  generateMusic
-})
-</script>
-
-
-<style lang="scss" scoped>
-:deep(.el-tabs) {
-  display: flex;
-  flex-direction: column;
-  .el-tabs__content {
-    padding: 0 7px;
-    overflow: auto;
-  }
-}
-</style>
diff --git a/src/views/ai/music/index/list/songCard/index.vue b/src/views/ai/music/index/list/songCard/index.vue
deleted file mode 100644
index 0534251..0000000
--- a/src/views/ai/music/index/list/songCard/index.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<template>
-  <div class="flex bg-[var(--el-bg-color-overlay)] p-12px mb-12px rounded-1">
-    <div class="relative" @click="playSong">
-      <el-image :src="songInfo.imageUrl" class="flex-none w-80px"/>
-      <div class="bg-black bg-op-40 absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer">
-        <Icon :icon="currentSong.id === songInfo.id ?  'solar:pause-circle-bold':'mdi:arrow-right-drop-circle'" :size="30" />
-      </div>
-    </div>
-    <div class="ml-8px">
-      <div>{{ songInfo.title }}</div>
-      <div class="mt-8px text-12px text-[var(--el-text-color-secondary)] line-clamp-2">
-        {{ songInfo.desc }}
-      </div>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-
-defineOptions({ name: 'Index' })
-
-defineProps({
-  songInfo: {
-    type: Object,
-    default: () => ({})
-  }
-})
-
-const emits = defineEmits(['play'])
-
-const currentSong = inject('currentSong', {})
-
-function playSong () {
-  emits('play')
-}
-</script>
diff --git a/src/views/ai/music/index/list/songInfo/index.vue b/src/views/ai/music/index/list/songInfo/index.vue
deleted file mode 100644
index 8d67c4d..0000000
--- a/src/views/ai/music/index/list/songInfo/index.vue
+++ /dev/null
@@ -1,22 +0,0 @@
-<template>
-  <ContentWrap class="w-300px mb-[0!important] line-height-24px">
-    <el-image :src="currentSong.imageUrl"/>
-    <div class="">{{ currentSong.title }}</div>
-    <div class="text-[var(--el-text-color-secondary)] text-12px line-clamp-1">
-      {{ currentSong.desc }}
-    </div>
-    <div class="text-[var(--el-text-color-secondary)] text-12px">
-      {{ currentSong.date }}
-    </div>
-    <el-button size="small" round class="my-6px">信息复用</el-button>
-    <div class="text-[var(--el-text-color-secondary)] text-12px" v-html="currentSong.lyric"></div>
-  </ContentWrap>
-</template>
-
-<script lang="ts" setup>
-
-defineOptions({ name: 'Index' })
-
-const currentSong = inject('currentSong', {})
-
-</script>
diff --git a/src/views/ai/music/index/mode/desc.vue b/src/views/ai/music/index/mode/desc.vue
deleted file mode 100644
index 4488461..0000000
--- a/src/views/ai/music/index/mode/desc.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-<template>
-  <div>
-    <Title title="音乐/歌词说明" desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲">
-      <el-input
-        v-model="formData.desc"
-        :autosize="{ minRows: 6, maxRows: 6}"
-        resize="none"
-        type="textarea"
-        maxlength="1200"
-        show-word-limit
-        placeholder="一首关于糟糕分手的欢快歌曲"
-      />
-    </Title>
-
-    <Title title="纯音乐" desc="创建一首没有歌词的歌曲">
-      <template #extra>
-        <el-switch v-model="formData.pure" size="small"/>
-      </template>
-    </Title>
-
-    <Title title="版本" desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲">
-      <el-select v-model="formData.version" placeholder="请选择">
-        <el-option
-          v-for="item in [{
-            value: '3',
-            label: 'V3'
-          }, {
-            value: '2',
-            label: 'V2'
-          }]"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
-        />
-      </el-select>
-    </Title>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import Title from '../title/index.vue'
-
-defineOptions({ name: 'Desc' })
-
-const formData = reactive({
-  desc: '',
-  pure: false,
-  version: '3'
-})
-
-defineExpose({
-  formData
-})
-
-</script>
diff --git a/src/views/ai/music/index/mode/index.vue b/src/views/ai/music/index/mode/index.vue
deleted file mode 100644
index 85ef893..0000000
--- a/src/views/ai/music/index/mode/index.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-<template>
-  <ContentWrap class="w-300px h-full mb-[0!important]">
-    <el-radio-group v-model="generateMode" class="mb-15px">
-      <el-radio-button label="desc">
-        描述模式
-      </el-radio-button>
-      <el-radio-button label="lyric">
-        歌词模式
-      </el-radio-button>
-    </el-radio-group>
-
-    <!-- 描述模式/歌词模式 切换 -->
-    <component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef"/>
-
-    <el-button type="primary" round class="w-full" @click="generateMusic">
-      创作音乐
-    </el-button>
-  </ContentWrap>
-</template>
-
-<script lang="ts" setup>
-import desc from './desc.vue'
-import lyric from './lyric.vue'
-
-defineOptions({ name: 'Index' })
-
-const emits = defineEmits(['generate-music'])
-
-const generateMode = ref('lyric')
-
-const modeRef = ref<Nullable<{ formData: Recordable }>>(null)
-
-/*
- *@Description: 根据信息生成音乐
- *@MethodAuthor: xiaohong
- *@Date: 2024-06-27 16:40:16
-*/
-function generateMusic () {
-  emits('generate-music', {formData: unref(modeRef)?.formData})
-}
-</script>
diff --git a/src/views/ai/music/index/mode/lyric.vue b/src/views/ai/music/index/mode/lyric.vue
deleted file mode 100644
index f774003..0000000
--- a/src/views/ai/music/index/mode/lyric.vue
+++ /dev/null
@@ -1,83 +0,0 @@
-<template>
-  <div class="">
-    <Title title="歌词" desc="自己编写歌词或使用Ai生成歌词,两节/8行效果最佳">
-      <el-input
-        v-model="formData.lyric"
-        :autosize="{ minRows: 6, maxRows: 6}"
-        resize="none"
-        type="textarea"
-        maxlength="1200"
-        show-word-limit
-        placeholder="请输入您自己的歌词"
-      />
-    </Title>
-
-    <Title title="音乐风格">
-      <el-space class="flex-wrap">
-        <el-tag v-for="tag in tags" :key="tag" round class="mb-8px">{{tag}}</el-tag>
-      </el-space>
-
-      <el-button
-        :type="showCustom ? 'primary': 'default'" 
-        round 
-        size="small" 
-        class="mb-6px"
-        @click="showCustom = !showCustom"
-      >自定义风格
-      </el-button>
-    </Title>
-
-    <Title v-show="showCustom" desc="描述您想要的音乐风格,Suno无法识别艺术家的名字,但可以理解流派和氛围" class="-mt-12px">
-      <el-input
-        v-model="formData.style"
-        :autosize="{ minRows: 4, maxRows: 4}"
-        resize="none"
-        type="textarea"
-        maxlength="256"
-        show-word-limit
-        placeholder="输入音乐风格(英文)"
-      />
-    </Title>
-
-    <Title title="音乐/歌曲名称">
-      <el-input v-model="formData.name" placeholder="请输入音乐/歌曲名称"/>
-    </Title>
-
-    <Title title="版本">
-      <el-select v-model="formData.version" placeholder="请选择">
-        <el-option
-          v-for="item in [{
-            value: '3',
-            label: 'V3'
-          }, {
-            value: '2',
-            label: 'V2'
-          }]"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
-        />
-      </el-select>
-    </Title>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import Title from '../title/index.vue'
-defineOptions({ name: 'Lyric' })
-
-const tags = ['rock', 'punk', 'jazz', 'soul', 'country', 'kidsmusic', 'pop']
-
-const showCustom = ref(false)
-
-const formData = reactive({
-  lyric: '',
-  style: '',
-  name: '',
-  version: ''
-})
-
-defineExpose({
-  formData
-})
-</script>
diff --git a/src/views/ai/music/index/title/index.vue b/src/views/ai/music/index/title/index.vue
deleted file mode 100644
index a065802..0000000
--- a/src/views/ai/music/index/title/index.vue
+++ /dev/null
@@ -1,25 +0,0 @@
-<template>
-  <div class="mb-12px">
-    <div class="flex text-[var(--el-text-color-primary)] justify-between items-center">
-      <span>{{title}}</span>
-      <slot name="extra"></slot>
-    </div>
-    <div class="text-[var(--el-text-color-secondary)] text-12px my-8px">
-      {{desc}}
-    </div>
-    <slot></slot>
-  </div>
-</template>
-
-<script lang="ts" setup>
-defineOptions({ name: 'Index' })
-
-defineProps({
-  title: {
-    type: String
-  },
-  desc: {
-    type: String
-  }
-})
-</script>
diff --git a/src/views/ai/music/manager/index.vue b/src/views/ai/music/manager/index.vue
deleted file mode 100644
index 462a88d..0000000
--- a/src/views/ai/music/manager/index.vue
+++ /dev/null
@@ -1,292 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="用户编号" prop="userId">
-        <el-select
-          v-model="queryParams.userId"
-          clearable
-          placeholder="请输入用户编号"
-          class="!w-240px"
-        >
-          <el-option
-            v-for="item in userList"
-            :key="item.id"
-            :label="item.nickname"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="音乐名称" prop="title">
-        <el-input
-          v-model="queryParams.title"
-          placeholder="请输入音乐名称"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="音乐状态" prop="status">
-        <el-select
-          v-model="queryParams.status"
-          placeholder="请选择音乐状态"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.AI_MUSIC_STATUS)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="生成模式" prop="generateMode">
-        <el-select
-          v-model="queryParams.generateMode"
-          placeholder="请选择生成模式"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.AI_GENERATE_MODE)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker
-          v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-220px"
-        />
-      </el-form-item>
-      <el-form-item label="是否发布" prop="publicStatus">
-        <el-select
-          v-model="queryParams.publicStatus"
-          placeholder="请选择是否发布"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="编号" align="center" prop="id" width="180" fixed="left" />
-      <el-table-column label="音乐名称" align="center" prop="title" width="180px" fixed="left" />
-      <el-table-column label="用户" align="center" prop="userId" width="180">
-        <template #default="scope">
-          <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="音乐状态" align="center" prop="status" width="100">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_MUSIC_STATUS" :value="scope.row.status" />
-        </template>
-      </el-table-column>
-      <el-table-column label="模型" align="center" prop="model" width="180" />
-      <el-table-column label="内容" align="center" width="180">
-        <template #default="{ row }">
-          <el-link
-            v-if="row.audioUrl?.length > 0"
-            type="primary"
-            :href="row.audioUrl"
-            target="_blank"
-          >
-            音乐
-          </el-link>
-          <el-link
-            v-if="row.videoUrl?.length > 0"
-            type="primary"
-            :href="row.videoUrl"
-            target="_blank"
-            class="!pl-5px"
-          >
-            视频
-          </el-link>
-          <el-link
-            v-if="row.imageUrl?.length > 0"
-            type="primary"
-            :href="row.imageUrl"
-            target="_blank"
-            class="!pl-5px"
-          >
-            封面
-          </el-link>
-        </template>
-      </el-table-column>
-      <el-table-column label="时长(秒)" align="center" prop="duration" width="100" />
-      <el-table-column label="提示词" align="center" prop="prompt" width="180" />
-      <el-table-column label="歌词" align="center" prop="lyric" width="180" />
-      <el-table-column label="描述" align="center" prop="gptDescriptionPrompt" width="180" />
-      <el-table-column label="生成模式" align="center" prop="generateMode" width="100">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_GENERATE_MODE" :value="scope.row.generateMode" />
-        </template>
-      </el-table-column>
-      <el-table-column label="风格标签" align="center" prop="tags" width="180">
-        <template #default="scope">
-          <el-tag v-for="tag in scope.row.tags" :key="tag" round class="ml-2px">
-            {{ tag }}
-          </el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="是否发布" align="center" prop="publicStatus">
-        <template #default="scope">
-          <el-switch
-            v-model="scope.row.publicStatus"
-            :active-value="true"
-            :inactive-value="false"
-            @change="handleUpdatePublicStatusChange(scope.row)"
-            :disabled="scope.row.status !== AiMusicStatusEnum.SUCCESS"
-          />
-        </template>
-      </el-table-column>
-      <el-table-column label="任务编号" align="center" prop="taskId" width="180" />
-      <el-table-column label="错误信息" align="center" prop="errorMessage" />
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <el-table-column label="操作" align="center" width="100" fixed="right">
-        <template #default="scope">
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['ai:music:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-</template>
-
-<script setup lang="ts">
-import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
-import { MusicApi, MusicVO } from '@/api/ai/music'
-import * as UserApi from '@/api/system/user'
-import { AiMusicStatusEnum } from '@/views/ai/utils/constants'
-
-/** AI 音乐 列表 */
-defineOptions({ name: 'AiMusicManager' })
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(true) // 列表的加载中
-const list = ref<MusicVO[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  userId: undefined,
-  title: undefined,
-  status: undefined,
-  generateMode: undefined,
-  createTime: [],
-  publicStatus: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-const userList = ref<UserApi.UserVO[]>([]) // 用户列表
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await MusicApi.getMusicPage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await MusicApi.deleteMusic(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 修改是否发布 */
-const handleUpdatePublicStatusChange = async (row: MusicVO) => {
-  try {
-    // 修改状态的二次确认
-    const text = row.publicStatus ? '公开' : '私有'
-    await message.confirm('确认要"' + text + '"该音乐吗?')
-    // 发起修改状态
-    await MusicApi.updateMusic({
-      id: row.id,
-      publicStatus: row.publicStatus
-    })
-    await getList()
-  } catch {
-    row.publicStatus = !row.publicStatus
-  }
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  getList()
-  // 获得用户列表
-  userList.value = await UserApi.getSimpleUserList()
-})
-</script>
diff --git a/src/views/ai/utils/constants.ts b/src/views/ai/utils/constants.ts
deleted file mode 100644
index 8888662..0000000
--- a/src/views/ai/utils/constants.ts
+++ /dev/null
@@ -1,481 +0,0 @@
-/**
- * Created by iailab
- *
- * AI 枚举类
- *
- * 问题:为什么不放在 src/utils/constants.ts 呢?
- * 回答:主要 AI 是可选模块,考虑到独立、解耦,所以放在了 /views/ai/utils/constants.ts
- */
-
-/**
- * AI 平台的枚举
- */
-export const AiPlatformEnum = {
-  TONG_YI: 'TongYi', // 阿里
-  YI_YAN: 'YiYan', // 百度
-  DEEP_SEEK: 'DeepSeek', // DeepSeek
-  ZHI_PU: 'ZhiPu', // 智谱 AI
-  XING_HUO: 'XingHuo', // 讯飞
-  OPENAI: 'OpenAI',
-  Ollama: 'Ollama',
-  STABLE_DIFFUSION: 'StableDiffusion', // Stability AI
-  MIDJOURNEY: 'Midjourney', // Midjourney
-  SUNO: 'Suno' // Suno AI
-}
-
-export const OtherPlatformEnum: ImageModelVO[] = [
-  {
-    key: AiPlatformEnum.TONG_YI,
-    name: '通义万相'
-  },
-  {
-    key: AiPlatformEnum.YI_YAN,
-    name: '百度千帆'
-  },
-  {
-    key: AiPlatformEnum.ZHI_PU,
-    name: '智谱 AI'
-  }
-]
-
-/**
- * AI 图像生成状态的枚举
- */
-export const AiImageStatusEnum = {
-  IN_PROGRESS: 10, // 进行中
-  SUCCESS: 20, // 已完成
-  FAIL: 30 // 已失败
-}
-
-/**
- * AI 音乐生成状态的枚举
- */
-export const AiMusicStatusEnum = {
-  IN_PROGRESS: 10, // 进行中
-  SUCCESS: 20, // 已完成
-  FAIL: 30 // 已失败
-}
-
-/**
- * AI 写作类型的枚举
- */
-export enum AiWriteTypeEnum {
-  WRITING = 1, // 撰写
-  REPLY // 回复
-}
-
-// 表格展示对照map
-export const AiWriteTypeTableRender = {
-  [AiWriteTypeEnum.WRITING]: '撰写',
-  [AiWriteTypeEnum.REPLY]: '回复'
-}
-
-// ========== 【图片 UI】相关的枚举 ==========
-
-export const ImageHotWords = [
-  '中国旗袍',
-  '古装美女',
-  '卡通头像',
-  '机甲战士',
-  '童话小屋',
-  '中国长城'
-] // 图片热词
-
-export const ImageHotEnglishWords = [
-  'Chinese Cheongsam',
-  'Ancient Beauty',
-  'Cartoon Avatar',
-  'Mech Warrior',
-  'Fairy Tale Cottage',
-  'The Great Wall of China'
-] // 图片热词(英文)
-
-export interface ImageModelVO {
-  key: string
-  name: string
-  image?: string
-}
-
-export const StableDiffusionSamplers: ImageModelVO[] = [
-  {
-    key: 'DDIM',
-    name: 'DDIM'
-  },
-  {
-    key: 'DDPM',
-    name: 'DDPM'
-  },
-  {
-    key: 'K_DPMPP_2M',
-    name: 'K_DPMPP_2M'
-  },
-  {
-    key: 'K_DPMPP_2S_ANCESTRAL',
-    name: 'K_DPMPP_2S_ANCESTRAL'
-  },
-  {
-    key: 'K_DPM_2',
-    name: 'K_DPM_2'
-  },
-  {
-    key: 'K_DPM_2_ANCESTRAL',
-    name: 'K_DPM_2_ANCESTRAL'
-  },
-  {
-    key: 'K_EULER',
-    name: 'K_EULER'
-  },
-  {
-    key: 'K_EULER_ANCESTRAL',
-    name: 'K_EULER_ANCESTRAL'
-  },
-  {
-    key: 'K_HEUN',
-    name: 'K_HEUN'
-  },
-  {
-    key: 'K_LMS',
-    name: 'K_LMS'
-  }
-]
-
-export const StableDiffusionStylePresets: ImageModelVO[] = [
-  {
-    key: '3d-model',
-    name: '3d-model'
-  },
-  {
-    key: 'analog-film',
-    name: 'analog-film'
-  },
-  {
-    key: 'anime',
-    name: 'anime'
-  },
-  {
-    key: 'cinematic',
-    name: 'cinematic'
-  },
-  {
-    key: 'comic-book',
-    name: 'comic-book'
-  },
-  {
-    key: 'digital-art',
-    name: 'digital-art'
-  },
-  {
-    key: 'enhance',
-    name: 'enhance'
-  },
-  {
-    key: 'fantasy-art',
-    name: 'fantasy-art'
-  },
-  {
-    key: 'isometric',
-    name: 'isometric'
-  },
-  {
-    key: 'line-art',
-    name: 'line-art'
-  },
-  {
-    key: 'low-poly',
-    name: 'low-poly'
-  },
-  {
-    key: 'modeling-compound',
-    name: 'modeling-compound'
-  },
-  // neon-punk origami photographic pixel-art tile-texture
-  {
-    key: 'neon-punk',
-    name: 'neon-punk'
-  },
-  {
-    key: 'origami',
-    name: 'origami'
-  },
-  {
-    key: 'photographic',
-    name: 'photographic'
-  },
-  {
-    key: 'pixel-art',
-    name: 'pixel-art'
-  },
-  {
-    key: 'tile-texture',
-    name: 'tile-texture'
-  }
-]
-
-export const TongYiWanXiangModels: ImageModelVO[] = [
-  {
-    key: 'wanx-v1',
-    name: 'wanx-v1'
-  },
-  {
-    key: 'wanx-sketch-to-image-v1',
-    name: 'wanx-sketch-to-image-v1'
-  }
-]
-
-export const QianFanModels: ImageModelVO[] = [
-  {
-    key: 'sd_xl',
-    name: 'sd_xl'
-  }
-]
-
-export const ChatGlmModels: ImageModelVO[] = [
-  {
-    key: 'cogview-3',
-    name: 'cogview-3'
-  }
-]
-
-export const StableDiffusionClipGuidancePresets: ImageModelVO[] = [
-  {
-    key: 'NONE',
-    name: 'NONE'
-  },
-  {
-    key: 'FAST_BLUE',
-    name: 'FAST_BLUE'
-  },
-  {
-    key: 'FAST_GREEN',
-    name: 'FAST_GREEN'
-  },
-  {
-    key: 'SIMPLE',
-    name: 'SIMPLE'
-  },
-  {
-    key: 'SLOW',
-    name: 'SLOW'
-  },
-  {
-    key: 'SLOWER',
-    name: 'SLOWER'
-  },
-  {
-    key: 'SLOWEST',
-    name: 'SLOWEST'
-  }
-]
-
-export const Dall3Models: ImageModelVO[] = [
-  {
-    key: 'dall-e-3',
-    name: 'DALL·E 3',
-    image: `/src/assets/ai/dall2.jpg`
-  },
-  {
-    key: 'dall-e-2',
-    name: 'DALL·E 2',
-    image: `/src/assets/ai/dall3.jpg`
-  }
-]
-
-export const Dall3StyleList: ImageModelVO[] = [
-  {
-    key: 'vivid',
-    name: '清晰',
-    image: `/src/assets/ai/qingxi.jpg`
-  },
-  {
-    key: 'natural',
-    name: '自然',
-    image: `/src/assets/ai/ziran.jpg`
-  }
-]
-
-export interface ImageSizeVO {
-  key: string
-  name?: string
-  style: string
-  width: string
-  height: string
-}
-
-export const Dall3SizeList: ImageSizeVO[] = [
-  {
-    key: '1024x1024',
-    name: '1:1',
-    width: '1024',
-    height: '1024',
-    style: 'width: 30px; height: 30px;background-color: #dcdcdc;'
-  },
-  {
-    key: '1024x1792',
-    name: '3:5',
-    width: '1024',
-    height: '1792',
-    style: 'width: 30px; height: 50px;background-color: #dcdcdc;'
-  },
-  {
-    key: '1792x1024',
-    name: '5:3',
-    width: '1792',
-    height: '1024',
-    style: 'width: 50px; height: 30px;background-color: #dcdcdc;'
-  }
-]
-
-export const MidjourneyModels: ImageModelVO[] = [
-  {
-    key: 'midjourney',
-    name: 'MJ',
-    image: 'https://bigpt8.com/pc/_nuxt/mj.34a61377.png'
-  },
-  {
-    key: 'niji',
-    name: 'NIJI',
-    image: 'https://bigpt8.com/pc/_nuxt/nj.ca79b143.png'
-  }
-]
-
-export const MidjourneySizeList: ImageSizeVO[] = [
-  {
-    key: '1:1',
-    width: '1',
-    height: '1',
-    style: 'width: 30px; height: 30px;background-color: #dcdcdc;'
-  },
-  {
-    key: '3:4',
-    width: '3',
-    height: '4',
-    style: 'width: 30px; height: 40px;background-color: #dcdcdc;'
-  },
-  {
-    key: '4:3',
-    width: '4',
-    height: '3',
-    style: 'width: 40px; height: 30px;background-color: #dcdcdc;'
-  },
-  {
-    key: '9:16',
-    width: '9',
-    height: '16',
-    style: 'width: 30px; height: 50px;background-color: #dcdcdc;'
-  },
-  {
-    key: '16:9',
-    width: '16',
-    height: '9',
-    style: 'width: 50px; height: 30px;background-color: #dcdcdc;'
-  }
-]
-
-export const MidjourneyVersions = [
-  {
-    value: '6.0',
-    label: 'v6.0'
-  },
-  {
-    value: '5.2',
-    label: 'v5.2'
-  },
-  {
-    value: '5.1',
-    label: 'v5.1'
-  },
-  {
-    value: '5.0',
-    label: 'v5.0'
-  },
-  {
-    value: '4.0',
-    label: 'v4.0'
-  }
-]
-
-export const NijiVersionList = [
-  {
-    value: '5',
-    label: 'v5'
-  }
-]
-
-// ========== 【写作 UI】相关的枚举 ==========
-
-/** 写作点击示例时的数据 **/
-export const WriteExample = {
-  write: {
-    prompt: 'vue',
-    data: 'Vue.js 是一种用于构建用户界面的渐进式 JavaScript 框架。它的核心库只关注视图层,易于上手,同时也便于与其他库或已有项目整合。\n\nVue.js 的特点包括:\n- 响应式的数据绑定:Vue.js 会自动将数据与 DOM 同步,使得状态管理变得更加简单。\n- 组件化:Vue.js 允许开发者通过小型、独立和通常可复用的组件构建大型应用。\n- 虚拟 DOM:Vue.js 使用虚拟 DOM 实现快速渲染,提高了性能。\n\n在 Vue.js 中,一个典型的应用结构可能包括:\n1. 根实例:每个 Vue 应用都需要一个根实例作为入口点。\n2. 组件系统:可以创建自定义的可复用组件。\n3. 指令:特殊的带有前缀 v- 的属性,为 DOM 元素提供特殊的行为。\n4. 插值:用于文本内容,将数据动态地插入到 HTML。\n5. 计算属性和侦听器:用于处理数据的复杂逻辑和响应数据变化。\n6. 条件渲染:根据条件决定元素的渲染。\n7. 列表渲染:用于显示列表数据。\n8. 事件处理:响应用户交互。\n9. 表单输入绑定:处理表单输入和验证。\n10. 组件生命周期钩子:在组件的不同阶段执行特定的函数。\n\nVue.js 还提供了官方的路由器 Vue Router 和状态管理库 Vuex,以支持构建复杂的单页应用(SPA)。\n\n在开发过程中,开发者通常会使用 Vue CLI,这是一个强大的命令行工具,用于快速生成 Vue 项目脚手架,集成了诸如 Babel、Webpack 等现代前端工具,以及热重载、代码检测等开发体验优化功能。\n\nVue.js 的生态系统还包括大量的第三方库和插件,如 Vuetify(UI 组件库)、Vue Test Utils(测试工具)等,这些都极大地丰富了 Vue.js 的开发生态。\n\n总的来说,Vue.js 是一个灵活、高效的前端框架,适合从小型项目到大型企业级应用的开发。它的易用性、灵活性和强大的社区支持使其成为许多开发者的首选框架之一。'
-  },
-  reply: {
-    originalContent: '领导,我想请假',
-    prompt: '不批',
-    data: '您的请假申请已收悉,经核实和考虑,暂时无法批准您的请假申请。\n\n如有特殊情况或紧急事务,请及时与我联系。\n\n祝工作顺利。\n\n谢谢。'
-  }
-}
-
-// ========== 【思维导图 UI】相关的枚举 ==========
-
-/** 思维导图已有内容生成示例 **/
-export const MindMapContentExample = `# Java 技术栈
-
-## 核心技术
-### Java SE
-### Java EE
-
-## 框架
-### Spring
-#### Spring Boot
-#### Spring MVC
-#### Spring Data
-### Hibernate
-### MyBatis
-
-## 构建工具
-### Maven
-### Gradle
-
-## 版本控制
-### Git
-### SVN
-
-## 测试工具
-### JUnit
-### Mockito
-### Selenium
-
-## 应用服务器
-### Tomcat
-### Jetty
-### WildFly
-
-## 数据库
-### MySQL
-### PostgreSQL
-### Oracle
-### MongoDB
-
-## 消息队列
-### Kafka
-### RabbitMQ
-### ActiveMQ
-
-## 微服务
-### Spring Cloud
-### Dubbo
-
-## 容器化
-### Docker
-### Kubernetes
-
-## 云服务
-### AWS
-### Azure
-### Google Cloud
-
-## 开发工具
-### IntelliJ IDEA
-### Eclipse
-### Visual Studio Code`
diff --git a/src/views/ai/utils/utils.ts b/src/views/ai/utils/utils.ts
deleted file mode 100644
index 4a70484..0000000
--- a/src/views/ai/utils/utils.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * Created by iailab
- *
- * AI 枚举类
- *
- * 问题:为什么不放在 src/utils/common-utils.ts 呢?
- * 回答:主要 AI 是可选模块,考虑到独立、解耦,所以放在了 /views/ai/utils/common-utils.ts
- */
-
-/**  判断字符串是否包含中文  */
-export const hasChinese = (str: string) => {
-  return /[\u4e00-\u9fa5]/.test(str)
-}
diff --git a/src/views/ai/write/index/components/Left.vue b/src/views/ai/write/index/components/Left.vue
deleted file mode 100644
index 05cc04a..0000000
--- a/src/views/ai/write/index/components/Left.vue
+++ /dev/null
@@ -1,213 +0,0 @@
-<template>
-  <!-- 定义 tab 组件:撰写/回复等 -->
-  <DefineTab v-slot="{ active, text, itemClick }">
-    <span
-      class="inline-block w-1/2 rounded-full cursor-pointer text-center leading-[30px] relative z-1 text-[5C6370] hover:text-black"
-      :class="active ? 'text-black shadow-md' : 'hover:bg-[#DDDFE3]'"
-      @click="itemClick"
-    >
-      {{ text }}
-    </span>
-  </DefineTab>
-  <!-- 定义 label 组件:长度/格式/语气/语言等 -->
-  <DefineLabel v-slot="{ label, hint, hintClick }">
-    <h3 class="mt-5 mb-3 flex items-center justify-between text-[14px]">
-      <span>{{ label }}</span>
-      <span
-        @click="hintClick"
-        v-if="hint"
-        class="flex items-center text-[12px] text-[#846af7] cursor-pointer select-none"
-      >
-        <Icon icon="ep:question-filled" />
-        {{ hint }}
-      </span>
-    </h3>
-  </DefineLabel>
-
-  <div class="flex flex-col" v-bind="$attrs">
-    <!-- tab -->
-    <div class="w-full pt-2 bg-[#f5f7f9] flex justify-center">
-      <div class="w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10">
-        <div
-          class="flex items-center relative after:content-[''] after:block after:bg-white after:h-[30px] after:w-1/2 after:absolute after:top-0 after:left-0 after:transition-transform after:rounded-full"
-          :class="
-            selectedTab === AiWriteTypeEnum.REPLY && 'after:transform after:translate-x-[100%]'
-          "
-        >
-          <ReuseTab
-            v-for="tab in tabs"
-            :key="tab.value"
-            :text="tab.text"
-            :active="tab.value === selectedTab"
-            :itemClick="() => switchTab(tab.value)"
-          />
-        </div>
-      </div>
-    </div>
-    <div
-      class="px-7 pb-2 flex-grow overflow-y-auto lg:block w-[380px] box-border bg-[#f5f7f9] h-full"
-    >
-      <div>
-        <template v-if="selectedTab === 1">
-          <ReuseLabel label="写作内容" hint="示例" :hint-click="() => example('write')" />
-          <el-input
-            type="textarea"
-            :rows="5"
-            :maxlength="500"
-            v-model="formData.prompt"
-            placeholder="请输入写作内容"
-            showWordLimit
-          />
-        </template>
-
-        <template v-else>
-          <ReuseLabel label="原文" hint="示例" :hint-click="() => example('reply')" />
-          <el-input
-            type="textarea"
-            :rows="5"
-            :maxlength="500"
-            v-model="formData.originalContent"
-            placeholder="请输入原文"
-            showWordLimit
-          />
-
-          <ReuseLabel label="回复内容" />
-          <el-input
-            type="textarea"
-            :rows="5"
-            :maxlength="500"
-            v-model="formData.prompt"
-            placeholder="请输入回复内容"
-            showWordLimit
-          />
-        </template>
-
-        <ReuseLabel label="长度" />
-        <Tag v-model="formData.length" :tags="getIntDictOptions(DICT_TYPE.AI_WRITE_LENGTH)" />
-        <ReuseLabel label="格式" />
-        <Tag v-model="formData.format" :tags="getIntDictOptions(DICT_TYPE.AI_WRITE_FORMAT)" />
-        <ReuseLabel label="语气" />
-        <Tag v-model="formData.tone" :tags="getIntDictOptions(DICT_TYPE.AI_WRITE_TONE)" />
-        <ReuseLabel label="语言" />
-        <Tag v-model="formData.language" :tags="getIntDictOptions(DICT_TYPE.AI_WRITE_LANGUAGE)" />
-
-        <div class="flex items-center justify-center mt-3">
-          <el-button :disabled="isWriting" @click="reset">重置</el-button>
-          <el-button :loading="isWriting" @click="submit" color="#846af7">生成</el-button>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { createReusableTemplate } from '@vueuse/core'
-import { ref } from 'vue'
-import Tag from './Tag.vue'
-import { WriteVO } from 'src/api/ai/write'
-import { omit } from 'lodash-es'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { AiWriteTypeEnum, WriteExample } from '@/views/ai/utils/constants'
-
-type TabType = WriteVO['type']
-
-const message = useMessage() // 消息弹窗
-
-defineProps<{
-  isWriting: boolean
-}>()
-
-const emits = defineEmits<{
-  (e: 'submit', params: Partial<WriteVO>)
-  (e: 'example', param: 'write' | 'reply')
-  (e: 'reset')
-}>()
-
-/** 点击示例的时候,将定义好的文章作为示例展示出来 **/
-const example = (type: 'write' | 'reply') => {
-  formData.value = {
-    ...initData,
-    ...omit(WriteExample[type], ['data'])
-  }
-  emits('example', type)
-}
-
-/** 重置,将表单值作为初选值 **/
-const reset = () => {
-  formData.value = { ...initData }
-  emits('reset')
-}
-
-const selectedTab = ref<TabType>(AiWriteTypeEnum.WRITING)
-const tabs: {
-  text: string
-  value: TabType
-}[] = [
-  { text: '撰写', value: AiWriteTypeEnum.WRITING },
-  { text: '回复', value: AiWriteTypeEnum.REPLY }
-]
-const [DefineTab, ReuseTab] = createReusableTemplate<{
-  active?: boolean
-  text: string
-  itemClick: () => void
-}>()
-
-/**
- * 可以在 template 里边定义可复用的组件,DefineLabel,ReuseLabel 是采用的解构赋值,都是 Vue 组件
- *
- * 直接通过组件的形式使用,<DefineLabel v-slot="{ label, hint, hintClick }"> 中间是需要复用的组件代码 <DefineLabel />,通过 <ReuseLabel /> 来使用定义的组件
- * DefineLabel 里边的 v-slot="{ label, hint, hintClick }"相当于是解构了组件的 prop,需要注意的是 boolean 类型,需要显式的赋值比如 <ReuseLabel :flag="true" />
- * 事件也得以 prop 形式传入,不能是 @event的形式,比如下面的 hintClick 需要<ReuseLabel :hintClick="() => { doSomething }"/>
- *
- * @see https://vueuse.org/createReusableTemplate
- */
-const [DefineLabel, ReuseLabel] = createReusableTemplate<{
-  label: string
-  class?: string
-  hint?: string
-  hintClick?: () => void
-}>()
-
-const initData: WriteVO = {
-  type: 1,
-  prompt: '',
-  originalContent: '',
-  tone: 1,
-  language: 1,
-  length: 1,
-  format: 1
-}
-const formData = ref<WriteVO>({ ...initData })
-
-/** 用来记录切换之前所填写的数据,切换的时候给赋值回来 **/
-const recordFormData = {} as Record<AiWriteTypeEnum, WriteVO>
-
-/** 切换tab **/
-const switchTab = (value: TabType) => {
-  if (value !== selectedTab.value) {
-    // 保存之前的久数据
-    recordFormData[selectedTab.value] = formData.value
-    selectedTab.value = value
-    // 将之前的旧数据赋值回来
-    formData.value = { ...initData, ...recordFormData[value] }
-  }
-}
-
-/** 提交写作 */
-const submit = () => {
-  if (selectedTab.value === 2 && !formData.value.originalContent) {
-    message.warning('请输入原文')
-    return
-  }
-  if (!formData.value.prompt) {
-    message.warning(`请输入${selectedTab.value === 1 ? '写作' : '回复'}内容`)
-    return
-  }
-  emits('submit', {
-    /** 撰写的时候没有 originalContent 字段**/
-    ...(selectedTab.value === 1 ? omit(formData.value, ['originalContent']) : formData.value),
-    /** 使用选中 tab 值覆盖当前的 type 类型 **/
-    type: selectedTab.value
-  })
-}
-</script>
diff --git a/src/views/ai/write/index/components/Right.vue b/src/views/ai/write/index/components/Right.vue
deleted file mode 100644
index d0aada5..0000000
--- a/src/views/ai/write/index/components/Right.vue
+++ /dev/null
@@ -1,120 +0,0 @@
-<template>
-  <el-card class="my-card h-full">
-    <template #header>
-      <h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
-        <span>预览</span>
-        <!-- 展示在右上角 -->
-        <el-button color="#846af7" v-show="showCopy" @click="copyContent" size="small">
-          <template #icon>
-            <Icon icon="ph:copy-bold" />
-          </template>
-          复制
-        </el-button>
-      </h3>
-    </template>
-
-    <div ref="contentRef" class="hide-scroll-bar h-full box-border overflow-y-auto">
-      <div class="w-full min-h-full relative flex-grow bg-white box-border p-3 sm:p-7">
-        <!-- 终止生成内容的按钮 -->
-        <el-button
-          v-show="isWriting"
-          class="absolute bottom-2 sm:bottom-5 left-1/2 -translate-x-1/2 z-36"
-          @click="emits('stopStream')"
-          size="small"
-        >
-          <template #icon>
-            <Icon icon="material-symbols:stop" />
-          </template>
-          终止生成
-        </el-button>
-        <el-input
-          id="inputId"
-          type="textarea"
-          v-model="compContent"
-          autosize
-          :input-style="{ boxShadow: 'none' }"
-          resize="none"
-          placeholder="生成的内容……"
-        />
-      </div>
-    </div>
-  </el-card>
-</template>
-
-<script setup lang="ts">
-import { useClipboard } from '@vueuse/core'
-
-const message = useMessage() // 消息弹窗
-const { copied, copy } = useClipboard() // 粘贴板
-
-const props = defineProps({
-  content: {
-    // 生成的结果
-    type: String,
-    default: ''
-  },
-  isWriting: {
-    // 是否正在生成文章
-    type: Boolean,
-    default: false
-  }
-})
-
-const emits = defineEmits(['update:content', 'stopStream'])
-
-/** 通过计算属性,双向绑定,更改生成的内容,考虑到用户想要更改生成文章的情况 */
-const compContent = computed({
-  get() {
-    return props.content
-  },
-  set(val) {
-    emits('update:content', val)
-  }
-})
-
-/** 滚动 */
-const contentRef = ref<HTMLDivElement>()
-defineExpose({
-  scrollToBottom() {
-    contentRef.value?.scrollTo(0, contentRef.value?.scrollHeight)
-  }
-})
-
-/** 点击复制的时候复制内容 */
-const showCopy = computed(() => props.content && !props.isWriting) // 是否展示复制按钮,在生成内容完成的时候展示
-const copyContent = () => {
-  copy(props.content)
-}
-
-/** 复制成功的时候 copied.value 为 true */
-watch(copied, (val) => {
-  if (val) {
-    message.success('复制成功')
-  }
-})
-</script>
-
-<style lang="scss" scoped>
-.hide-scroll-bar {
-  -ms-overflow-style: none;
-  scrollbar-width: none;
-
-  &::-webkit-scrollbar {
-    width: 0;
-    height: 0;
-  }
-}
-
-.my-card {
-  display: flex;
-  flex-direction: column;
-
-  :deep(.el-card__body) {
-    box-sizing: border-box;
-    flex-grow: 1;
-    overflow-y: auto;
-    padding: 0;
-    @extend .hide-scroll-bar;
-  }
-}
-</style>
diff --git a/src/views/ai/write/index/components/Tag.vue b/src/views/ai/write/index/components/Tag.vue
deleted file mode 100644
index 3d616be..0000000
--- a/src/views/ai/write/index/components/Tag.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-<!-- 标签选项 -->
-<template>
-  <div class="flex flex-wrap gap-[8px]">
-    <span
-      v-for="tag in props.tags"
-      :key="tag.value"
-      class="tag mb-2 border-[2px] border-solid border-[#DDDFE3] px-2 leading-6 text-[12px] bg-[#DDDFE3] rounded-[4px] cursor-pointer"
-      :class="modelValue === tag.value && '!border-[#846af7] text-[#846af7]'"
-      @click="emits('update:modelValue', tag.value)"
-    >
-      {{ tag.label }}
-    </span>
-  </div>
-</template>
-
-<script setup lang="ts">
-const props = withDefaults(
-  defineProps<{
-    tags: { label: string; value: string }[]
-    modelValue: string
-    [k: string]: any
-  }>(),
-  {
-    tags: () => []
-  }
-)
-
-const emits = defineEmits<{
-  (e: 'update:modelValue', value: string): void
-}>()
-</script>
-<style scoped></style>
diff --git a/src/views/ai/write/index/index.vue b/src/views/ai/write/index/index.vue
deleted file mode 100644
index 0dfda74..0000000
--- a/src/views/ai/write/index/index.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<template>
-  <div class="absolute top-0 left-0 right-0 bottom-0 flex">
-    <Left
-      :is-writing="isWriting"
-      class="h-full"
-      @submit="submit"
-      @reset="reset"
-      @example="handleExampleClick"
-    />
-    <Right
-      :is-writing="isWriting"
-      @stop-stream="stopStream"
-      ref="rightRef"
-      class="flex-grow"
-      v-model:content="writeResult"
-    />
-  </div>
-</template>
-
-<script setup lang="ts">
-import Left from './components/Left.vue'
-import Right from './components/Right.vue'
-import { WriteApi, WriteVO } from '@/api/ai/write'
-import { WriteExample } from '@/views/ai/utils/constants'
-
-const message = useMessage()
-
-const writeResult = ref('') // 写作结果
-const isWriting = ref(false) // 是否正在写作中
-const abortController = ref<AbortController>() // // 写作进行中 abort 控制器(控制 stream 写作)
-
-/** 停止 stream 生成 */
-const stopStream = () => {
-  abortController.value?.abort()
-  isWriting.value = false
-}
-
-/** 执行写作 */
-const rightRef = ref<InstanceType<typeof Right>>()
-const submit = (data: WriteVO) => {
-  abortController.value = new AbortController()
-  writeResult.value = ''
-  isWriting.value = true
-  WriteApi.writeStream({
-    data,
-    onMessage: async (res) => {
-      const { code, data, msg } = JSON.parse(res.data)
-      if (code !== 0) {
-        message.alert(`写作异常! ${msg}`)
-        stopStream()
-        return
-      }
-      writeResult.value = writeResult.value + data
-      // 滚动到底部
-      await nextTick()
-      rightRef.value?.scrollToBottom()
-    },
-    ctrl: abortController.value,
-    onClose: stopStream,
-    onError: (...err) => {
-      console.error('写作异常', ...err)
-      stopStream()
-    }
-  })
-}
-
-/** 点击示例触发 */
-const handleExampleClick = (type: keyof typeof WriteExample) => {
-  writeResult.value = WriteExample[type].data
-}
-
-/** 点击重置的时候清空写作的结果**/
-const reset = () => {
-  writeResult.value = ''
-}
-</script>
diff --git a/src/views/ai/write/manager/index.vue b/src/views/ai/write/manager/index.vue
deleted file mode 100644
index 169f6b1..0000000
--- a/src/views/ai/write/manager/index.vue
+++ /dev/null
@@ -1,256 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="用户编号" prop="userId">
-        <el-select
-          v-model="queryParams.userId"
-          clearable
-          placeholder="请输入用户编号"
-          class="!w-240px"
-        >
-          <el-option
-            v-for="item in userList"
-            :key="item.id"
-            :label="item.nickname"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="写作类型" prop="type">
-        <el-select
-          v-model="queryParams.type"
-          placeholder="请选择写作类型"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.AI_WRITE_TYPE)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="平台" prop="platform">
-        <el-select v-model="queryParams.platform" placeholder="请选择平台" clearable class="!w-240px">
-          <el-option
-            v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker
-          v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button
-          type="primary"
-          plain
-          @click="openForm('create')"
-          v-hasPermi="['ai:write:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
-        </el-button>
-        <!-- TODO @iailab  目前没有导出接口,需要导出吗 -->
-        <el-button
-          type="success"
-          plain
-          @click="handleExport"
-          :loading="exportLoading"
-          v-hasPermi="['ai:write:export']"
-        >
-          <Icon icon="ep:download" class="mr-5px" /> 导出
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="编号" align="center" prop="id" width="120" fixed="left" />
-      <el-table-column label="用户" align="center" prop="userId" width="180">
-        <template #default="scope">
-          <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="写作类型" align="center" prop="type">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_WRITE_TYPE" :value="scope.row.type" />
-        </template>
-      </el-table-column>
-      <el-table-column label="平台" align="center" prop="platform" width="120">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" />
-        </template>
-      </el-table-column>
-      <el-table-column label="模型" align="center" prop="model" width="180" />
-      <el-table-column
-        label="生成内容提示"
-        align="center"
-        prop="prompt"
-        width="180"
-        show-overflow-tooltip
-      />
-      <el-table-column label="生成的内容" align="center" prop="generatedContent" width="180" />
-      <el-table-column label="原文" align="center" prop="originalContent" width="180" />
-      <el-table-column label="长度" align="center" prop="length">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_WRITE_LENGTH" :value="scope.row.length" />
-        </template>
-      </el-table-column>
-      <el-table-column label="格式" align="center" prop="format">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_WRITE_FORMAT" :value="scope.row.format" />
-        </template>
-      </el-table-column>
-      <el-table-column label="语气" align="center" prop="tone">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_WRITE_TONE" :value="scope.row.tone" />
-        </template>
-      </el-table-column>
-      <el-table-column label="语言" align="center" prop="language">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.AI_WRITE_LANGUAGE" :value="scope.row.language" />
-        </template>
-      </el-table-column>
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <el-table-column label="错误信息" align="center" prop="errorMessage" />
-      <el-table-column label="操作" align="center">
-        <template #default="scope">
-<!--          TODO @iailab 目前没有修改接口,写作要可以更改吗-->
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['ai:write:update']"
-          >
-            编辑
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['ai:write:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-</template>
-
-<script setup lang="ts">
-import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
-import { useRouter } from 'vue-router'
-import { WriteApi, AiWritePageReqVO, AiWriteRespVo } from '@/api/ai/write'
-import * as UserApi from '@/api/system/user'
-
-/** AI 写作列表 */
-defineOptions({ name: 'AiWriteManager' })
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-const router = useRouter() // 路由
-
-const loading = ref(true) // 列表的加载中
-const list = ref<AiWriteRespVo[]>([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive<AiWritePageReqVO>({
-  pageNo: 1,
-  pageSize: 10,
-  userId: undefined,
-  type: undefined,
-  platform: undefined,
-  createTime: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-const userList = ref<UserApi.UserVO[]>([]) // 用户列表
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await WriteApi.getWritePage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 新增方法,跳转到写作页面 **/
-const openForm = (type: string, id?: number) => {
-  switch (type) {
-    case 'create':
-      router.push('/ai/write')
-      break
-  }
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id: number) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await WriteApi.deleteWrite(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  getList()
-  // 获得用户列表
-  userList.value = await UserApi.getSimpleUserList()
-})
-</script>

--
Gitblit v1.9.3