|
@@ -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 {
|