瀏覽代碼

【代码评审】AI:review 聊天对话的实现

YunaiV 1 年之前
父節點
當前提交
46eb89695d

+ 0 - 5
src/views/ai/chat/ChatEmpty.vue

@@ -1,4 +1,3 @@
-
 <template>
   <div class="chat-empty">
 
@@ -15,7 +14,6 @@
 </template>
 <script setup lang="ts">
 
-const router = useRouter()
 const promptList = ref<any[]>() // 角色列表
 promptList.value = [
   {
@@ -31,9 +29,6 @@ const emits = defineEmits(['onPrompt'])
 const handlerPromptClick = async ({ prompt }) => {
   emits('onPrompt', prompt)
 }
-
-onMounted(async () => {
-})
 </script>
 <style scoped lang="scss">
 .chat-empty {

+ 21 - 20
src/views/ai/chat/Conversation.vue

@@ -27,7 +27,6 @@
 
         <el-empty v-if="loading" description="." :v-loading="loading" />
 
-        <!-- TODO done @fain:置顶、聊天记录、一星期钱、30天前,前端对数据重新做一下分组,或者后端接口改一下 -->
         <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>
@@ -47,7 +46,6 @@
                 <img class="avatar" :src="conversation.roleAvatar"/>
                 <span class="title">{{ conversation.title }}</span>
               </div>
-              <!-- TODO done @fan:缺一个【置顶】按钮,效果改成 hover 上去展示 -->
               <div class="button-wrapper" v-show="hoverConversationId === conversation.id">
                 <el-button class="btn" link @click.stop="handlerTop(conversation)" >
                   <el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon>
@@ -74,6 +72,7 @@
     </div>
 
     <!-- 左底部:工具栏 -->
+    <!-- TODO @fan:下面两个 icon,可以使用类似 <Icon icon="ep:question-filled" /> 替代哈 -->
     <div class="tool-box">
       <div @click="handleRoleRepository">
         <Icon icon="ep:user"/>
@@ -87,7 +86,7 @@
 
     <!-- ============= 额外组件 ============= -->
 
-    <!--   角色仓库抽屉  -->
+    <!-- 角色仓库抽屉 -->
     <el-drawer v-model="drawer" title="角色仓库" size="754px">
       <Role/>
     </el-drawer>
@@ -109,7 +108,7 @@ const activeConversationId = ref<string | null>(null) // 选中的对话,默
 const hoverConversationId = ref<string | null>(null) // 悬浮上去的对话
 const conversationList = ref([] as ChatConversationVO[])  // 对话列表
 const conversationMap = ref<any>({})  // 对话分组 (置顶、今天、三天前、一星期前、一个月前)
-const drawer = ref<boolean>(false) // 角色仓库抽屉
+const drawer = ref<boolean>(false) // 角色仓库抽屉 TODO @fan:roleDrawer 会不会好点哈
 const loading = ref<boolean>(false) // 加载中
 const loadingTime = ref<any>() // 加载中定时器
 
@@ -154,6 +153,7 @@ const handleConversationClick = async (id: string) => {
     return item.id === id
   })
   // 回调 onConversationClick
+  // TODO @fan: 这里 idea 会报黄色警告,有办法解下么?
   const res = emits('onConversationClick', filterConversation[0])
   // 切换对话
   if (res) {
@@ -166,18 +166,18 @@ const handleConversationClick = async (id: string) => {
  */
 const getChatConversationList = async () => {
   try {
-    // 0加载中
+    // 0. 加载中
     loadingTime.value = setTimeout(() => {
       loading.value = true
     }, 50)
-    // 1获取 对话数据
+    // 1. 获取 对话数据
     const res = await ChatConversationApi.getChatConversationMyList()
-    // 2排序
+    // 2. 排序
     res.sort((a, b) => {
       return b.createTime - a.createTime
     })
     conversationList.value = res
-    // 3默认选中
+    // 3. 默认选中
     if (!activeId?.value) {
       // await handleConversationClick(res[0].id)
     } else {
@@ -189,13 +189,13 @@ const getChatConversationList = async () => {
       //   await handleConversationClick(res[0].id)
       // }
     }
-    // 4、没有 任何对话情况
+    // 4. 没有任何对话情况
     if (conversationList.value.length === 0) {
       activeConversationId.value = null
       conversationMap.value = {}
       return
     }
-    // 5对话根据时间分组(置顶、今天、一天前、三天前、七天前、30天前)
+    // 5. 对话根据时间分组(置顶、今天、一天前、三天前、七天前、30天前)
     conversationMap.value = await conversationTimeGroup(conversationList.value)
   } finally {
     // 清理定时器
@@ -253,15 +253,15 @@ const conversationTimeGroup = async (list: ChatConversationVO[]) => {
  * 对话 - 新建
  */
 const createConversation = async () => {
-  // 1新建对话
+  // 1. 新建对话
   const conversationId = await ChatConversationApi.createChatConversationMy(
     {} as unknown as ChatConversationVO
   )
-  // 2获取对话内容
+  // 2. 获取对话内容
   await getChatConversationList()
-  // 3选中对话
+  // 3. 选中对话
   await handleConversationClick(conversationId)
-  // 4回调
+  // 4. 回调
   emits('onConversationCreate')
 }
 
@@ -269,21 +269,21 @@ const createConversation = async () => {
  * 对话 - 更新标题
  */
 const updateConversationTitle = async (conversation: ChatConversationVO) => {
-  // 1二次确认
+  // 1. 二次确认
   const {value} = await ElMessageBox.prompt('修改标题', {
     inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
     inputErrorMessage: '标题不能为空',
     inputValue: conversation.title
   })
-  // 2发起修改
+  // 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
   })
@@ -316,6 +316,7 @@ const deleteChatConversation = async (conversation: ChatConversationVO) => {
 /**
  * 对话置顶
  */
+// TODO @fan:应该是 handleXXX,handler 是名词哈
 const handlerTop = async (conversation: ChatConversationVO) => {
   // 更新对话置顶
   conversation.pinned = !conversation.pinned
@@ -324,9 +325,9 @@ const handlerTop = async (conversation: ChatConversationVO) => {
   await getChatConversationList()
 }
 
+// TODO @fan:类似 ============ 分块的,最后后面也有 ============ 哈
 // ============ 角色仓库
 
-
 /**
  * 角色仓库抽屉
  */
@@ -336,11 +337,11 @@ const handleRoleRepository = async () => {
 
 // ============= 清空对话
 
-
 /**
  * 清空对话
  */
 const handleClearConversation = async () => {
+  // TODO @fan:可以使用 await message.confirm( 简化,然后使用 await 改成同步的逻辑,会更简洁
   ElMessageBox.confirm(
     '确认后对话会全部清空,置顶的对话除外。',
     '确认提示',

+ 3 - 1
src/views/ai/chat/Message.vue

@@ -57,7 +57,7 @@
       </div>
     </div>
   </div>
-  <!--  回到底部  -->
+  <!-- 回到底部 -->
   <div v-if="isScrolling" class="to-bottom" @click="handleGoBottom">
     <el-button :icon="ArrowDownBold" circle />
   </div>
@@ -106,6 +106,7 @@ const messageList = computed(() => {
 
 const scrollToBottom = async (isIgnore?: boolean) => {
   await nextTick(() => {
+    // TODO @fan:中文写作习惯,中英文之间要有空格;另外,nextick 哈,idea 如果有绿色波兰线,可以关注下
     //注意要使用nexttick以免获取不到dom
     if (isIgnore || !isScrolling.value) {
       messageContainer.value.scrollTop =
@@ -184,6 +185,7 @@ const handlerGoTop = async () => {
 }
 
 // 监听 list
+// TODO @fan:这个木有,是不是删除啦
 const { list, conversationId } = toRefs(props)
 watch(list, async (newValue, oldValue) => {
   console.log('watch list', list)

+ 0 - 2
src/views/ai/chat/components/Header.vue

@@ -45,6 +45,4 @@ defineProps({
     flex-direction: row;
   }
 }
-
-
 </style>

+ 29 - 12
src/views/ai/chat/index.vue

@@ -10,27 +10,27 @@
     />
     <!-- 右侧:对话详情 -->
     <el-container class="detail-container">
-      <!-- 右顶部 TODO 芋艿:右对齐 -->
       <el-header class="header">
         <div class="title">
           {{ activeConversation?.title ? activeConversation?.title : '对话' }}
           <span v-if="list.length">({{list.length}})</span>
         </div>
         <div class="btns" v-if="activeConversation">
-          <!-- TODO @fan:样式改下;这里我已经改成点击后,弹出了 -->
-          <el-button type="primary" bg text="plain" size="small" @click="openChatConversationUpdateForm">
+          <el-button type="primary" bg plain size="small" @click="openChatConversationUpdateForm">
             <span v-html="activeConversation?.modelName"></span>
             <Icon icon="ep:setting" style="margin-left: 10px"/>
           </el-button>
           <el-button size="small" class="btn" @click="handlerMessageClear">
+            <!-- TODO @fan:style 部分,可以考虑用 unocss 替代 -->
             <img src="@/assets/ai/clear.svg" style="height: 14px;" />
           </el-button>
+          <!-- TODO @fan:下面两个 icon,可以使用类似 <Icon icon="ep:question-filled" /> 替代哈 -->
           <el-button size="small" :icon="Download" class="btn"  />
           <el-button size="small" :icon="Top" class="btn"  @click="handlerGoTop" />
         </div>
       </el-header>
 
-      <!-- main -->
+      <!-- main:消息列表 -->
       <el-main class="main-container" >
         <div >
           <div class="message-container" >
@@ -87,7 +87,7 @@
     </el-container>
 
     <!--  ========= 额外组件 ==========  -->
-    <!-- 更新对话 form -->
+    <!-- 更新对话 Form -->
     <ChatConversationUpdateForm
       ref="chatConversationUpdateFormRef"
       @success="handlerTitleSuccess"
@@ -96,6 +96,7 @@
 </template>
 
 <script setup lang="ts">
+// TODO @fan:是不是把 index.vue 相关的,在这里新建一个 index 目录,然后挪进去哈。因为 /ai/chat 还会有其它功能。例如说,现在的 /ai/chat/manager 管理
 import Conversation from './Conversation.vue'
 import Message from './Message.vue'
 import ChatEmpty from './ChatEmpty.vue'
@@ -120,15 +121,17 @@ const prompt = ref<string>() // prompt
 const userInfo = ref<ProfileVO>() // 用户信息
 const enableContext = ref<boolean>(true) // 是否开启上下文
 
+// TODO @fan:这几个变量,可以注释在补下哈;另外,fullText 可以明确是生成中的消息 Text,这样更容易理解哈;
 const fullText = ref('');
 const displayedText = ref('');
 const textSpeed = ref<number>(50); // Typing speed in milliseconds
 const textRoleRunning = ref<boolean>(false); // Typing speed in milliseconds
 
 // chat message 列表
+// TODO @fan:list、listLoading、listLoadingTime 不能体现出来是消息列表,是不是可以变量再优化下
 const list = ref<ChatMessageVO[]>([]) // 列表的数据
 const listLoading = ref<boolean>(false) // 是否加载中
-const listLoadingTime = ref<any>() // time定时器,如果加载速度很快,就不进入加载中
+const listLoadingTime = ref<any>() // time 定时器,如果加载速度很快,就不进入加载中
 
 // 判断 消息列表 滚动的位置(用于判断是否需要滚动到消息最下方)
 const messageRef = ref()
@@ -140,6 +143,7 @@ const defaultRoleAvatar = 'http://test.yudao.iocoder.cn/eaef5f41acb911dd718429a0
 
 // =========== 自提滚动效果
 
+// TODO @fan:这个方法,要不加个方法注释
 const textRoll = async () => {
   let index = 0;
   try {
@@ -162,7 +166,7 @@ const textRoll = async () => {
       } else {
         textSpeed.value = 100
       }
-      // 对话结束,就按30的速度
+      // 对话结束,就按 30 的速度
       if (!conversationInProgress.value) {
         textSpeed.value = 10
       }
@@ -176,6 +180,7 @@ const textRoll = async () => {
         // 更新 message
         const lastMessage = list.value[list.value.length - 1]
         lastMessage.content = displayedText.value
+        // TODO @fan:ist.value?,还是 ist.value.length 哈?
         list.value[list.value - 1] = lastMessage
         // 滚动到住下面
         await scrollToBottom()
@@ -212,6 +217,7 @@ function scrollToBottom(isIgnore?: boolean) {
 
 // ============= 处理聊天输入回车发送 =============
 
+// TODO @fan:是不是可以通过 @keydown.enter、@keydown.shift.enter 来实现,回车发送、shift+回车换行;主要看看,是不是可以简化 isComposing 相关的逻辑
 const onCompositionstart = () => {
   isComposing.value = true
 }
@@ -276,12 +282,14 @@ const onSendBtn = async () => {
 
 const doSend = async (content: string) => {
   if (content.length < 2) {
+    // TODO @fan:这个 message.error(`上传文件大小不能超过${props.fileSize}MB!`) 可以替代,这种形式
     ElMessage({
       message: '请输入内容!',
       type: 'error'
     })
     return
   }
+  // TODO @fan:这个 message.error(`上传文件大小不能超过${props.fileSize}MB!`) 可以替代,这种形式
   if (activeConversationId.value == null) {
     ElMessage({
       message: '还没创建对话,不能发送!',
@@ -289,9 +297,9 @@ const doSend = async (content: string) => {
     })
     return
   }
-  // TODO 芋艿:这块交互要在优化;应该是先插入到 UI 界面,里面会有当前的消息,和正在思考中;之后发起请求;
   // 清空输入框
   prompt.value = ''
+  // TODO @fan:idea 这里会报类型错误,是不是可以解决下哈
   const userMessage = {
     conversationId: activeConversationId.value,
     content: content
@@ -309,6 +317,7 @@ const doSendStream = async (userMessage: ChatMessageVO) => {
   fullText.value = ''
   try {
     // 先添加两个假数据,等 stream 返回再替换
+    // TODO @fan:idea 这里会报类型错误,是不是可以解决下哈
     list.value.push({
       id: -1,
       conversationId: activeConversationId.value,
@@ -326,13 +335,14 @@ const doSendStream = async (userMessage: ChatMessageVO) => {
       createTime: new Date()
     } as ChatMessageVO)
     // 滚动到最下面
+    // TODO @fan:可以 await nextTick();然后同步调用 scrollToBottom()
     nextTick(async () => {
       await scrollToBottom()
     })
     // 开始滚动
     textRoll()
     // 发送 event stream
-    let isFirstMessage = true
+    let isFirstMessage = true // TODO @fan:isFirstChunk 会更精准
     ChatMessageApi.sendStream(
       userMessage.conversationId, // TODO 芋艿:这里可能要在优化;
       userMessage.content,
@@ -367,12 +377,14 @@ const doSendStream = async (userMessage: ChatMessageVO) => {
       },
       (error) => {
         message.alert(`对话异常! ${error}`)
+        // TODO @fan:是不是可以复用 stopStream 方法
         // 标记对话结束
         conversationInProgress.value = false
         // 结束 stream 对话
         conversationInAbortController.value.abort()
       },
       () => {
+        // TODO @fan:是不是可以复用 stopStream 方法
         // 标记对话结束
         conversationInProgress.value = false
         // 结束 stream 对话
@@ -412,6 +424,7 @@ const messageList = computed(() => {
   return []
 })
 
+// TODO @fan:一般情况下,项目方法注释用 /** */,啊哈,主要保持风格统一,= = 少占点行哈,
 /**
  * 获取 - message 列表
  */
@@ -500,6 +513,7 @@ const handleConversationClick = async (conversation: ChatConversationVO) => {
  * 对话 - 清理全部对话
  */
 const handlerConversationClear = async ()=> {
+  // TODO @fan:需要加一个 对话进行中,不允许切换
   activeConversationId.value = null
   activeConversation.value = null
   list.value = []
@@ -509,7 +523,7 @@ const handlerConversationClear = async ()=> {
  * 对话 - 删除
  */
 const handlerConversationDelete = async (delConversation: ChatConversationVO) => {
-  // 删除的对话如果是当前选中的,那么重置
+  // 删除的对话如果是当前选中的,那么重置
   if (activeConversationId.value === delConversation.id) {
     await handlerConversationClear()
   }
@@ -532,6 +546,7 @@ const getConversation = async (id: string | null) => {
 /**
  * 对话 - 新建
  */
+// TODO @fan:应该是 handleXXX,handler 是名词哈
 const handlerNewChat = async () => {
   // 创建对话
   await conversationRef.value.createConversation()
@@ -552,14 +567,14 @@ const handlerMessageDelete = async () => {
 }
 
 /**
- * 编辑 message
+ * 编辑 message:设置为 prompt,可以再次编辑
  */
 const handlerMessageEdit = async (message: ChatMessageVO) => {
   prompt.value = message.content
 }
 
 /**
- * 编辑 message
+ * 刷新 message:基于指定消息,再次发起对话
  */
 const handlerMessageRefresh = async (message: ChatMessageVO) => {
   await doSend(message.content)
@@ -579,10 +594,12 @@ const handlerMessageClear = async () => {
   if (!activeConversationId.value) {
     return
   }
+  // TODO @fan:需要 try catch 下,不然点击取消会报异常
   // 确认提示
   await message.delConfirm("确认清空对话消息?")
   // 清空对话
   await ChatMessageApi.deleteByConversationId(activeConversationId.value as string)
+  // TODO @fan:是不是直接置空就好啦;
   // 刷新 message 列表
   await getMessageList()
 }

+ 1 - 1
src/views/ai/chat/role/RoleList.vue

@@ -10,6 +10,7 @@
                 <el-icon><More /></el-icon>
               </el-button>
           </span>
+            <!-- TODO @fan:下面两个 icon,可以使用类似 <Icon icon="ep:question-filled" /> 替代哈 -->
             <template #dropdown>
               <el-dropdown-menu>
                 <el-dropdown-item :command="['edit', role]" >
@@ -31,7 +32,6 @@
           <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>

+ 51 - 50
src/views/ai/chat/role/index.vue

@@ -1,9 +1,9 @@
 <!-- chat 角色仓库 -->
 <template>
   <el-container class="role-container">
-    <ChatRoleForm ref="formRef" @success="handlerAddRoleSuccess"/>
+    <ChatRoleForm ref="formRef" @success="handlerAddRoleSuccess" />
     <!--  header  -->
-    <Header title="角色仓库" style="position: relative"/>
+    <Header title="角色仓库" style="position: relative" />
     <!--  main  -->
     <el-main class="role-main">
       <div class="search-container">
@@ -17,9 +17,15 @@
           :suffix-icon="Search"
           @change="getActiveTabsRole"
         />
-        <el-button v-if="activeRole == 'my-role'" type="primary" @click="handlerAddRole" style="margin-left: 20px;">
+        <el-button
+          v-if="activeRole == 'my-role'"
+          type="primary"
+          @click="handlerAddRole"
+          style="margin-left: 20px"
+        >
+          <!-- TODO @fan:下面两个 icon,可以使用类似 <Icon icon="ep:question-filled" /> 替代哈 -->
           <el-icon>
-            <User/>
+            <User />
           </el-icon>
           添加角色
         </el-button>
@@ -35,7 +41,8 @@
             @on-edit="handlerCardEdit"
             @on-use="handlerCardUse"
             @on-page="handlerCardPage('my')"
-            style="margin-top: 20px;"/>
+            style="margin-top: 20px"
+          />
         </el-tab-pane>
         <el-tab-pane label="公共角色" name="public-role">
           <RoleCategoryList
@@ -50,34 +57,33 @@
             @on-edit="handlerCardEdit"
             @on-use="handlerCardUse"
             @on-page="handlerCardPage('public')"
-            style="margin-top: 20px;"
-           loading/>
+            style="margin-top: 20px"
+            loading
+          />
         </el-tab-pane>
       </el-tabs>
     </el-main>
   </el-container>
-
 </template>
 
-<!--  setup  -->
 <script setup lang="ts">
-import {ref} from "vue";
+import { ref } from 'vue'
 import Header from '@/views/ai/chat/components/Header.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 {TabsPaneContext} from "element-plus";
-import {Search, User} from "@element-plus/icons-vue";
+import { ChatRoleApi, ChatRolePageReqVO, ChatRoleVO } from '@/api/ai/model/chatRole'
+import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
+import { TabsPaneContext } from 'element-plus'
+import { Search, User } from '@element-plus/icons-vue'
 
-// 获取路由
-const router = useRouter()
+const router = useRouter() // 路由对象
 
 // 属性定义
 const loading = ref<boolean>(false) // 加载中
-const activeRole = ref<string>('my-role') // 选中的角色
+const activeRole = ref<string>('my-role') // 选中的角色 TODO @fan:是不是叫 activeTab 会更明确一点哈。选中的角色,会以为是某个角色
 const search = ref<string>('') // 加载中
+// TODO @fan:要不 myPage、pubPage,搞成类似 const queryParams = reactive({ ,分别搞成两个大的参数哈?
 const myPageNo = ref<number>(1) // my 分页下标
 const myPageSize = ref<number>(50) // my 分页大小
 const myRoleList = ref<ChatRoleVO[]>([]) // my 分页大小
@@ -86,18 +92,16 @@ const publicPageSize = ref<number>(50) // public 分页大小
 const publicRoleList = ref<ChatRoleVO[]>([]) // public 分页大小
 const activeCategory = ref<string>('全部') // 选择中的分类
 const categoryList = ref<string[]>([]) // 角色分类类别
-/** 添加/修改操作 */
-const formRef = ref()
-// tabs 点击
+
+/** tabs 点击 */
 const handleTabsClick = async (tab: TabsPaneContext) => {
   // 设置切换状态
-  const activeTabs = tab.paneName + ''
-  activeRole.value = activeTabs;
+  activeRole.value = tab.paneName + ''
   // 切换的时候重新加载数据
   await getActiveTabsRole()
 }
 
-// 获取 my role
+/** 获取 my role 我的角色 */
 const getMyRole = async (append?: boolean) => {
   const params: ChatRolePageReqVO = {
     pageNo: myPageNo.value,
@@ -105,7 +109,7 @@ const getMyRole = async (append?: boolean) => {
     name: search.value,
     publicStatus: false
   }
-  const {total, list} = await ChatRoleApi.getMyPage(params)
+  const { total, list } = await ChatRoleApi.getMyPage(params)
   if (append) {
     myRoleList.value.push.apply(myRoleList.value, list)
   } else {
@@ -113,7 +117,7 @@ const getMyRole = async (append?: boolean) => {
   }
 }
 
-// 获取 public role
+/** 获取 public role 公共角色 */
 const getPublicRole = async (append?: boolean) => {
   const params: ChatRolePageReqVO = {
     pageNo: publicPageNo.value,
@@ -122,7 +126,7 @@ const getPublicRole = async (append?: boolean) => {
     name: search.value,
     publicStatus: true
   }
-  const {total, list} = await ChatRoleApi.getMyPage(params)
+  const { total, list } = await ChatRoleApi.getMyPage(params)
   if (append) {
     publicRoleList.value.push.apply(publicRoleList.value, list)
   } else {
@@ -130,7 +134,7 @@ const getPublicRole = async (append?: boolean) => {
   }
 }
 
-// 获取选中的 tabs 角色
+/** 获取选中的 tabs 角色 */
 const getActiveTabsRole = async () => {
   if (activeRole.value === 'my-role') {
     myPageNo.value = 1
@@ -141,14 +145,14 @@ const getActiveTabsRole = async () => {
   }
 }
 
-// 获取角色分类列表
+/** 获取角色分类列表 */
 const getRoleCategoryList = async () => {
   const res = await ChatRoleApi.getCategoryList()
   const defaultRole = ['全部']
   categoryList.value = [...defaultRole, ...res]
 }
 
-// 处理分类点击
+/** 处理分类点击 */
 const handlerCategoryClick = async (category: string) => {
   // 切换选择的分类
   activeCategory.value = category
@@ -156,11 +160,18 @@ const handlerCategoryClick = async (category: string) => {
   await getActiveTabsRole()
 }
 
-// 添加角色
+/** 添加/修改操作 */
+const formRef = ref()
 const handlerAddRole = async () => {
   formRef.value.open('my-create', null, '添加角色')
 }
 
+/** 添加角色成功 */
+const handlerAddRoleSuccess = async (e) => {
+  // 刷新数据
+  await getActiveTabsRole()
+}
+
 // card 删除
 const handlerCardDelete = async (role) => {
   await ChatRoleApi.deleteMy(role.id)
@@ -173,7 +184,7 @@ const handlerCardEdit = async (role) => {
   formRef.value.open('my-update', role.id, '编辑角色')
 }
 
-// card 分页
+/** card 分页:获取下一页 */
 const handlerCardPage = async (type) => {
   console.log('handlerCardPage', type)
   try {
@@ -190,39 +201,33 @@ const handlerCardPage = async (type) => {
   }
 }
 
-// card 使用
+/** 选择 card 角色:新建聊天对话 */
 const handlerCardUse = async (role) => {
+  // 1. 创建对话
   const data: ChatConversationVO = {
     roleId: role.id
   } as unknown as ChatConversationVO
-  // 创建对话
-  const conversation = await ChatConversationApi.createChatConversationMy(data)
-  // 调整页面
-  router.push({
+  const conversationId = await ChatConversationApi.createChatConversationMy(data)
+  // 2. 跳转页面
+  // TODO @fan:最好用 name,后续可能会改~~~
+  await router.push({
     path: `/ai/chat`,
     query: {
-      conversationId: conversation,
+      conversationId: conversationId
     }
   })
 }
 
-// 添加角色成功
-const handlerAddRoleSuccess = async (e) => {
-  console.log(e)
-  // 刷新数据
-  await getActiveTabsRole()
-}
-
-//
+/** 初始化 **/
 onMounted(async () => {
   // 获取分类
   await getRoleCategoryList()
   // 获取 role 数据
   await getActiveTabsRole()
 })
+// TODO @fan:css 是不是可以融合到 scss 里面呀?
 </script>
 <style lang="css">
-
 .el-tabs__content {
   position: relative;
   height: 100%;
@@ -232,11 +237,9 @@ onMounted(async () => {
 .el-tabs__nav-scroll {
   margin: 10px 20px;
 }
-
 </style>
 <!-- 样式 -->
 <style scoped lang="scss">
-
 // 跟容器
 .role-container {
   position: absolute;
@@ -290,6 +293,4 @@ onMounted(async () => {
     }
   }
 }
-
-
 </style>