Browse Source

6-8调整

chenrong 1 year ago
parent
commit
c93df29a6b
3 changed files with 329 additions and 67 deletions
  1. 43 0
      src/api/chat.js
  2. 1 1
      src/utils/request.js
  3. 285 66
      src/views/chat/index.vue

+ 43 - 0
src/api/chat.js

@@ -20,6 +20,49 @@ export function getModelListApi(data) {
     params: data
   })
 }
+// 查询用户对话记录
+export function getChatCharacterRecordsApi(params) {
+  return request({
+    url: `/appUser/chatCharacterRecords`,
+    method: 'get',
+    params
+  })
+}
+
+// 查询角色对话记录
+export function getChatRecordApi(params) {
+  return request({
+    url: `/appUser/chatRecord`,
+    method: 'get',
+    params
+  })
+}
+// 修改对话记录标题
+export function updateTitleApi(data) {
+  return request({
+    url: `/appUser/updateTitle`,
+    method: 'post',
+    data
+  })
+}
+
+export function clearRecordApi(params) {
+  return request({
+    url: `/appUser/clearRecord`,
+    method: 'get',
+    params
+  })
+}
+
+// /appUser/getVoiceFile
+// 新增用户对话
+export function addChatApi(data) {
+  return request({
+    url: `/appUser/addChat`,
+    method: 'post',
+    data
+  })
+}
 
 // ai对话AXIOS
 // export function streamChatWithWebApi(data) {

+ 1 - 1
src/utils/request.js

@@ -35,7 +35,7 @@ service.interceptors.request.use(
     // 是否需要防止数据重复提交
     const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
     if (getToken() && !isToken) {
-      console.log(getToken(), '请求拦截')
+      // console.log(getToken(), '请求拦截')
       config.headers["UserToken"] = getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
     }
     // get请求映射params参数

+ 285 - 66
src/views/chat/index.vue

@@ -3,14 +3,47 @@
     <div class="flex items-center justify-center h-screen">
       <div class="leftInfo relative">
         <div class="history">
-          <div class="historyBox flex historyActive">
-            <div class="photo">
-              <img class="object-cover" src="@/assets/images/1.png" alt="">
-            </div>
-            <div class="info flex-1 ml-2 flex flex-col justify-around">
-              <div class="text-sm font-semibold">角色名称</div>
-              <div class="text-sm text-gray-400">角色的聊天信息xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</div>
+          <!-- historyActive -->
+          <div class="historyBox" v-for="(item, index) in allRecords" :key="index">
+            <div class="top flex w-full cursor-pointer" @click="item.open = !item.open">
+              <div class="photo">
+                <img class="object-cover" :src="baseApi + item.picture" alt="">
+              </div>
+              <div class="info flex-1 ml-2 flex flex-col justify-around">
+                <div class="text-sm font-semibold">{{ item.characterName }}</div>
+                <div class="text-sm text-gray-400">{{ item.prologue }}</div>
+              </div>
+              <div class="flex items-center text-lg" style="height: 48px;">
+                <i v-if="!item.open" class="el-icon-arrow-down" style="font-size: 16px;"></i>
+                <i v-else class="el-icon-arrow-up" style="font-size: 16px;"></i>
+              </div>
             </div>
+            <!-- <transition name="el-zoom-in-top"> -->
+              <div class="dialogueList mt-4" v-show="item.open">
+                <div class="dialogue flex justify-between items-center w-full h-16 rounded-md px-2 mb-2 cursor-pointer relative"
+                  :class="info.id == item.id && index2 == recordsIndex && 'historyActive'"
+                  v-for="(item2, index2) in item.chatCharacterList"
+                  :key="index2"
+                  @click.self="clickDialogue(item, item2, index2)"
+                >
+                  <span @click.self="clickDialogue(item, item2, index2)">{{ item2.chatTitle }}</span>
+                  <span @click.self="clickDialogue(item, item2, index2)" class=" text-sm text-gray-500">{{ item2.createTime }}</span>
+                  <el-dropdown 
+                    class="dialogueIconBg absolute h-16 w-20 right-0 flex justify-end items-center pr-4"
+                    trigger="click" 
+                    @command="handleCommand($event, item, item2 )" 
+                  >
+                    <div >
+                      <i slot="reference" class="el-icon-more"></i>
+                    </div>
+                    <el-dropdown-menu slot="dropdown" >
+                      <el-dropdown-item command="1">修改标题</el-dropdown-item>
+                      <el-dropdown-item command="2">删除</el-dropdown-item>
+                    </el-dropdown-menu>
+                  </el-dropdown>
+                </div>
+              </div>
+            <!-- </transition> -->
           </div>
         </div>
         <div class="chatInfo absolute top-0 left-0 w-full h-full z-30 bg-white" v-show="showInfo">
@@ -149,7 +182,7 @@
               </el-tooltip>
             </div>
           </div>
-          <audio ref="audio" v-show="false" src="@/assets/file/我将在黎明出发.mp3" @ended="audioEnd" controls></audio>
+          <audio ref="audio" muted v-show="false" :src="audioUrl" autoplay="" @ended="audioEnd" controls></audio>
           <div class="messages" ref="messages">
             <template v-for="(item, index) in returnMessage">
               <div v-if="item.role == 'assistant'" class="mb-4 flex" :key="index">
@@ -240,7 +273,6 @@
         <div class="flex items-start py-3 border-b border-gray-200">
           <el-radio-group class="radio" v-model="configForm.radio2">
             <el-radio-button v-for="(item, index) in modelList" :key="index" :label="item.id">{{ item.model }}</el-radio-button>
-
           </el-radio-group>
         </div>
         <p class="text-base mt-3">角色语言</p>
@@ -265,7 +297,16 @@
 
 <script>
 import Header from "@/views/homeComponents/Header.vue"
-import { streamChatWithWebApi, getGuidanceApi, getModelListApi } from "@/api/chat.js"
+import { 
+  streamChatWithWebApi, 
+  getGuidanceApi, 
+  getModelListApi, 
+  addChatApi,
+  getChatCharacterRecordsApi,
+  getChatRecordApi,
+  updateTitleApi,
+  clearRecordApi
+} from "@/api/chat.js"
 import { detailApi } from "@/api/detail.js"
 import { Message, MessageBox, Notification, Loading } from 'element-ui'
 import 'vue-awesome/icons/paper-plane'
@@ -277,6 +318,13 @@ export default {
   },
   data() {
     return {
+      // 角色所有聊天列表
+      allRecords: [],
+      // 聊天记录id
+      recordId: null,
+      // 选中的角色聊天记录
+      historyRes: [],
+      recordsIndex: null,
       showDetail: false,
       showConfig: false,
       showInfo: false,
@@ -292,16 +340,14 @@ export default {
         radio3: null,
       },
       // 模型列表
-      modelList: []
+      modelList: [],
+      value1: '',
+      //音频地址
+      audioUrl: ''
     }
   },
   mounted() {
-    console.log(this.$refs.Header, 'this.$refs.Header');
-    this.getModelList()
-    if (this.$route.query.characterId) {
-      this.showInfo = true
-      this.getDetail(this.$route.query.characterId)
-    }
+    this.init()
   },
   watch: {
     returnMessage: {
@@ -315,6 +361,141 @@ export default {
     }
   },
   methods: {
+    async init() {
+      // 获取用户所有聊天记录
+      await this.getChatCharacterRecords()
+      // 如果用角色id获取角色详情
+      if (this.$route.query.characterId) {
+        this.showInfo = true
+        await this.getDetail(this.$route.query.characterId)
+      } else {
+        this.allRecords[0].open = true
+        await this.getDetail(this.allRecords[0].id)
+      }
+      // 查找当前角色是否有对话记录
+      this.searchHistory(this.info.id)
+      // 获取模型列表
+      this.getModelList()
+    },
+    async clickDialogue(value1, value2, index) {
+      this.audioUrl = ""
+      this.messageOptions = []
+      this.returnMessage = []
+      // 点击聊天记录切换聊天
+      if (this.$route.query.characterId) {
+        this.$route.query.characterId = value1.id
+      }
+      await this.getDetail(value1.id)
+
+      this.recordId = value2.id
+      this.getChatRecord(value2.id)
+      this.recordsIndex = index
+    },
+    handleCommand(value, value1, value2) {
+      console.log(value, 'value');
+      if (value == 1) {
+        this.editDialogueTitle(value1, value2)
+      } else if (value == 2) {
+        this.$confirm('此操作将永久删除该聊天记录, 是否继续?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          this.clearRecord(value1, value2)
+        })
+      }
+    },
+    // 修改聊天记录标题
+    editDialogueTitle(value1, value2) {
+      this.$prompt('新标题名称', '修改标题', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+      }).then(({ value }) => {
+        let params = {
+          chatTitle: value,
+          id: value2.id
+        }
+        updateTitleApi(params).then(res => {
+          console.log(res, '修改标题');
+          this.$message({
+            type: 'success',
+            message: '修改成功!'
+          });
+          this.getChatCharacterRecords()
+        })
+      })
+    },
+    clearRecord(value1, value2) {
+      let params = {
+        characterId: value1.id,
+        recordId: value2.id
+      }
+      clearRecordApi(params).then(res => {
+        this.$message({
+          type: 'success',
+          message: '删除成功!'
+        });
+        this.getChatCharacterRecords()
+      })
+    },
+    getChatRecord(recordId) {
+      let params = {
+        characterId: this.info.id,
+        recordId: recordId
+      }
+      getChatRecordApi(params).then(res => {
+        console.log(res, '查询到的聊天记录');
+        let array = res.data
+        // this.returnMessage
+        this.historyRes = res.data
+        for (let i = 0; i < array.length; i++) {
+          const element = array[i];
+          this.returnMessage = [...this.returnMessage, ...JSON.parse(element.history)]
+        }
+      })
+    },
+    searchHistory(characterId) {
+      // 查看对话记录是否有选中的角色
+      // console.log('查看对话记录是否有选中的角色');
+      console.log(this.allRecords, 'searchHistory');
+      let flg = false
+      for (let i = 0; i < this.allRecords.length; i++) {
+        const element = this.allRecords[i];
+        // 如果有则选中角色的最新一条记录并获取对话聊天记录
+        if (element.id == characterId) {
+          flg = true
+          console.log(element, '查看对话记录是否有选中的角色');
+          element.open = true
+          this.recordsIndex = element.chatCharacterList.length - 1
+          let recordId = element.chatCharacterList[element.chatCharacterList.length - 1].id
+          this.recordId = recordId
+          this.getChatRecord(recordId)
+        }
+      }
+      // 如果没有聊天记录,则增加开场聊天引导
+      if (!flg) {
+        this.getGuidance(this.info.id)
+      }
+    },
+    async getChatCharacterRecords() {
+      let res = await getChatCharacterRecordsApi()
+      console.log(res, '聊天记录');
+      for (let i = 0; i < res.data.length; i++) {
+        const element = res.data[i];
+        element.open = false
+      }
+      this.allRecords = res.data
+    },
+    // 新增对话记录
+    async addChat() {
+      let params = {
+        characterId: this.info.id,
+        chatTitle: ""
+      }
+      let res = await addChatApi(params)
+      console.log(res, '新增对话记录');
+      return res.data
+    },
     getModelList() {
       getModelListApi().then(res => {
         console.log(res, '模型列表');
@@ -325,22 +506,24 @@ export default {
       console.log(this.$refs.input, 'this.$refs.input');
       this.$refs.input.focus()
     },
-    getDetail(id) {
-      detailApi(id).then(res => {
-        console.log(res, '角色详情');
-        this.info = res.data
-        this.configForm.radio2 = this.info.modelId
-        let HistoryMessage = JSON.parse(localStorage.getItem(`[userId:${123},aiId:${this.info.id}]`));
-        if (HistoryMessage) {
-          this.returnMessage = HistoryMessage
-        } else {
-          this.returnMessage.push({
-            role: 'assistant',
-            content: this.info.firstContent
-          })
-          localStorage.setItem(`[userId:${123},aiId:${this.info.id}]`, JSON.stringify(this.returnMessage));
-        }
-        this.getGuidance(id)
+    async getDetail(id) {
+      let res = await detailApi(id)
+      console.log(res, '角色详情');
+      this.info = res.data
+      this.configForm.radio2 = this.info.modelId
+      // let HistoryMessage = JSON.parse(localStorage.getItem(`[userId:${123},aiId:${this.info.id}]`));
+      // if (HistoryMessage) {
+      //   this.returnMessage = HistoryMessage
+      // } else {
+      //   this.returnMessage.push({
+      //     role: 'assistant',
+      //     content: this.info.firstContent
+      //   })
+      //   localStorage.setItem(`[userId:${123},aiId:${this.info.id}]`, JSON.stringify(this.returnMessage));
+      // }
+      this.returnMessage.push({
+        role: 'assistant',
+        content: this.info.firstContent
       })
     },
     getGuidance(id) {
@@ -352,11 +535,28 @@ export default {
     goBack() {
       this.$router.back()
     },
-    newStart() {
-      // 清空历史数据
-      localStorage.removeItem(`[userId:${123},aiId:${this.info.id}]`);
+    async newStart() {
+      // 新业务,创建一个新的对话记录
+      this.recordId = await this.addChat()
+      for (let i = 0; i < this.allRecords.length; i++) {
+        const element = this.allRecords[i];
+        if (element.id == this.info.id) {
+          element.chatCharacterList.push({
+            chatTitle: "常规聊天",
+            createTime: '',
+            id: this.recordId
+          })
+          this.recordsIndex = element.chatCharacterList.length - 1
+        } 
+      }
+      
       this.returnMessage = []
       this.getDetail(this.info.id)
+      // 废弃》》
+      // 清空历史数据
+      // localStorage.removeItem(`[userId:${123},aiId:${this.info.id}]`);
+      // this.returnMessage = []
+      // this.getDetail(this.info.id)
     },
     Enterkey(e) {
       if (e.keyCode == 13) {
@@ -430,11 +630,15 @@ export default {
       if (!this.content) {
         return
       }
+      // 如果没有对话记录id,新增对话记录
+      if (!this.recordId) {
+        this.recordId = await this.addChat()
+      }
       this.returnMessage.push({
         role: 'user',
         content: this.content
       })
-      let HistoryMessage = JSON.parse(localStorage.getItem(`[userId:${123},aiId:${this.info.id}]`));
+      let HistoryMessage = this.returnMessage
       // 历史记录取最新的20条传给后端
       if (HistoryMessage && HistoryMessage.length > 20) {
         HistoryMessage = HistoryMessage.slice(HistoryMessage.length - 1, 20)
@@ -443,13 +647,15 @@ export default {
         historyMessage: HistoryMessage || [],
         characterId: this.info.id,
         prompt: this.content,
-        modelId: this.configForm.radio2
+        modelId: this.configForm.radio2,
+        recordId: this.recordId
       }
       // 新增一条ai信息
       this.returnMessage.push({
         role: 'assistant',
         content: ''
       })
+      
       // 清空输入框的值
       this.content = ''
 
@@ -473,16 +679,11 @@ export default {
           //   this.$message.error('错误')
           //   this.returnMessage.splice(this.returnMessage.length - 1, 1)
           // }
+          // 每次对话完刷新聊天记录
+          this.getChatCharacterRecords()
+          this.returnMessage = []
+          this.getChatRecord(this.recordId)
           console.log('结束');
-          // 消息全部显示后存到历史localStorage
-          let HistoryMessage = []
-          // 历史只存100条,长度超出截取最新的
-          if (this.returnMessage.length > 100) {
-            HistoryMessage = JSON.stringify(this.returnMessage.slice(this.returnMessage.length - 100, 100))
-          } else {
-            HistoryMessage = JSON.stringify(this.returnMessage)
-          }
-          localStorage.setItem(`[userId:${123},aiId:${this.info.id}]`, HistoryMessage);
           break;
         }
         
@@ -498,7 +699,6 @@ export default {
         //   this.returnMessage.splice(this.returnMessage.length - 1, 1)
         //   break;
         // }
-        
       }
     },
     addMessage(value) {
@@ -561,8 +761,14 @@ export default {
     },
     // 播放聊天语音
     playAudio(value, index) {
-      // console.log(value, 'value');
+      console.log(index, 'index');
+      console.log(this.historyRes, 'this.historyRes');
+      let history = this.historyRes[(index - 1) / 2]
+      console.log(history, 'history');
       this.audioPlayIndex = index
+      this.audioUrl = history.voiceFilePosition
+      console.log(this.audioUrl, 'this.audioUrl');
+      this.$refs.audio.volume = 1
       this.$refs.audio.play()
     },
     audioEnd() {
@@ -592,26 +798,25 @@ export default {
       padding: 8px;
       border-radius: 15px;
       border: solid 1px var(--color1);
-      >.photo {
-        >img {
-          border-radius: 50%;
+      margin-bottom: 10px;
+      .top {
+        >.photo {
+          >img {
+            width: 48px;
+            height: 48px;
+            border-radius: 50%;
+          }
         }
-      }
-      >.info {
-        width: calc(100% - 60px);
-        >div {
-          overflow: hidden; /* 确保超出容器的文本被裁剪 */
-          white-space: nowrap; /* 确保文本在一行内显示 */
-          text-overflow: ellipsis; /* 使用省略号表示文本超出 */
+        >.info {
+          width: calc(100% - 60px - 16px);
+          >div {
+            overflow: hidden; /* 确保超出容器的文本被裁剪 */
+            white-space: nowrap; /* 确保文本在一行内显示 */
+            text-overflow: ellipsis; /* 使用省略号表示文本超出 */
+          }
         }
       }
     }
-    .historyBox:hover {
-      background: var(--color1);
-    }
-    >.historyActive {
-      background: var(--color1);
-    }
   }
   >.chatInfo {
     .chatInfo_photo {
@@ -654,7 +859,7 @@ export default {
     flex-direction: column;
     height: 100%;
     width: 100%;
-    padding: 55px 25px 25px 25px;
+    padding: 55px 25px 170px 25px;
     >.chatBoxTitle {
       color: #fff;
       margin-top: 15px;
@@ -666,7 +871,20 @@ export default {
     }
   }
 }
+.historyActive {
+  background: var(--color1);
+}
+.dialogue:hover {
+  background: var(--color1);
+  .dialogueIconBg {
+    display: flex;
+  }
+}
 
+.dialogueIconBg {
+  display: none;
+  background: linear-gradient(90deg, rgba(0, 0, 0, 0) 10%, var(--color1) 50%, var(--color1) 100%);
+}
 .messages {
   height: calc(100% - 68px );
   overflow-y: auto;
@@ -756,6 +974,7 @@ export default {
 .planeColor {
   color: var(--bg-color1);
 }
+
 </style>
 <style scoped>
   .loadingMessage >>> .el-loading-parent--relative {