ChatH5.vue 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574
  1. <template>
  2. <div class="chat bg-white">
  3. <div class="flex flex-col items-center justify-start h-screen"
  4. style="height: 100vh; height: calc(var(--vh, 1vh) * 100);">
  5. <!-- <img class="absolute h-full" :src="baseApi + info.picture" alt=""> -->
  6. <div class="top bg-white px-2 flex justify-between">
  7. <el-button class="tm_button" type="info" icon="el-icon-arrow-left" circle @click="goBack"></el-button>
  8. <div v-if="sceneId" class=" text-sm" style="max-width: 50%;">已进入场景:{{ sceneInfo.sceneName }}</div>
  9. <div class="flex">
  10. <el-button class="tm_button" type="info" circle>
  11. <i class="fa-solid fa-volume-high text-sm"></i>
  12. <!-- <i class="fa-solid fa-volume-xmark text-sm"></i> -->
  13. </el-button>
  14. <el-dropdown trigger="click" @command="rtHandleCommand">
  15. <el-button class="tm_button ml-2" type="info" icon="el-icon-more" circle></el-button>
  16. <el-dropdown-menu slot="dropdown">
  17. <el-dropdown-item command="1" icon="el-icon-warning-outline">查看详情</el-dropdown-item>
  18. </el-dropdown-menu>
  19. </el-dropdown>
  20. </div>
  21. </div>
  22. <audio ref="audio" muted v-show="false" :src="audioUrl" autoplay="" @ended="audioEnd" controls></audio>
  23. <div class="chatContent" :style="`background-image: url(${baseApi + sceneInfo.background})`">
  24. <!-- <div class="leftImg">
  25. <img :src="baseApi + info.picture" alt="">
  26. <div class="aiInfo">
  27. <span class="photo">
  28. <img :src="baseApi + info.picture">
  29. </span>
  30. <div class="info">
  31. <div class="name">{{ info.characterName }}</div>
  32. <div class="tags">
  33. <span class="tag" v-for="(item, index) in info.labelArr" :key="index">
  34. {{ item }}
  35. </span>
  36. </div>
  37. <div class="infoContent">{{ info.prologue }}</div>
  38. </div>
  39. </div>
  40. </div> -->
  41. <div class="chat-box relative " :class="sceneId && 'mask'">
  42. <div class="messages" ref="messages">
  43. <template v-for="(item, index) in returnMessage ">
  44. <div v-if="item.role == 'assistant'" class="text-white mb-4 flex" :key="index">
  45. <!-- ai返回的信息 -->
  46. <div class="pt-2 photo">
  47. <img :src="baseApi + info.picture" class="rounded-full w-12 h-12 object-cover" @click="toDetail"
  48. @error="$AIPohotoError" />
  49. </div>
  50. <div class="message fex-1 ml-2 mt-3 p-2 rounded-r-md rounded-bl-md text-base">
  51. <div v-show="!item.content" class="loadingMessage">
  52. <div v-loading="!item.content" element-loading-background="rgba(0, 0, 0, 0.0)"></div>
  53. </div>
  54. <p style="white-space: pre-line;" v-show="item.content">{{ item.content }} </p>
  55. </div>
  56. </div>
  57. <!-- 用户发送的信息 -->
  58. <div v-if="item.role == 'user'" class="text-white mb-4 flex me" :key="index">
  59. <div class="pt-2 photo">
  60. <img src="@/assets/images/default_avatar_user.png" @error="$userPohotoError"
  61. class="rounded-full w-12 h-12 object-cover" />
  62. </div>
  63. <div class="message2 fex-1 mr-2 mt-3 p-2 rounded-l-md rounded-br-md text-base">
  64. <p>{{ item.content }}</p>
  65. </div>
  66. </div>
  67. </template>
  68. </div>
  69. </div>
  70. </div>
  71. <div class="fixed bottom-0 bg-white w-full px-2 pt-2 pb-4 border-t border-gray-100">
  72. <!-- <div class="flex items-center text-gray-400 text-xs mb-4 ">
  73. <span>@Leon S Kennedy - Resident Evil</span>
  74. </div> -->
  75. <div class="flex">
  76. <el-button class="bgColor1 mButton" @click="showBottomPopup = true" style="margin-right: 5px;" type="primary"
  77. round icon="el-icon-plus"></el-button>
  78. <input type="text" placeholder="输入消息..." class="flex-1 py-2 px-4 rounded-l-lg text-sm focus:outline-none"
  79. style="background: #ffffff0f; border: 1px solid #5b5b5e;" v-model="content" @keydown="Enterkey" />
  80. <button @click.prevent="getStreamChatWithWeb" class="bgColor1 text-white px-4 rounded-r-lg text-sm"
  81. style="width: 60px;">
  82. 发送
  83. </button>
  84. </div>
  85. </div>
  86. <el-dialog :visible.sync="showBottomPopup" :show-close="false" width="100%" class="phoneBottomPopup">
  87. <transition name="el-zoom-in-bottom">
  88. <div class="popupContent popup1" v-show="showBottomPopup">
  89. <div class="configForm w-full flex flex-col overflow-y-auto" style="height: calc(100% - 75px)">
  90. <div class="row">
  91. <p class="text-base mt-3">选择模型</p>
  92. <p class="text-sm text-gray-200">每个模型可能有不同的效果,仅对当前对话有影响</p>
  93. <div class="flex items-start py-3 border-b border-gray-200">
  94. <el-radio-group class="radio" v-model="configForm.radio2" @input="formModelChange">
  95. <el-radio-button v-for="(item, index) in modelList" :key="index" :label="item.id">{{ item.model
  96. }}</el-radio-button>
  97. </el-radio-group>
  98. </div>
  99. </div>
  100. </div>
  101. <div class="bottom px-2">
  102. <div v-if="!sceneId" class="button text-white" @click="() => {
  103. this.showBottomPopup = false
  104. this.newStart()
  105. }
  106. ">
  107. <i class="icon iconfont icon-xinduihua"></i>
  108. <div>新的对话</div>
  109. </div>
  110. <div v-if="sceneId" class="button text-white" @click="() => {
  111. this.showBottomPopup = false
  112. this.handleCommand3(2)
  113. }
  114. ">
  115. <i class="icon iconfont icon-xinduihua"></i>
  116. <div>新建场景对话</div>
  117. </div>
  118. <div v-if="sceneId" class="button text-white" @click="() => {
  119. this.showBottomPopup = false
  120. this.handleCommand3(3)
  121. }
  122. ">
  123. <i class="icon iconfont icon-xinduihua"></i>
  124. <div>返回常规对话</div>
  125. </div>
  126. <div class="button text-white" @click="() => {
  127. this.showBottomPopup = false
  128. this.showHistory()
  129. }
  130. ">
  131. <i class="icon iconfont icon-lishi"></i>
  132. <div>历史对话</div>
  133. </div>
  134. </div>
  135. </div>
  136. </transition>
  137. </el-dialog>
  138. <el-dialog :visible.sync="showBottomPopup2" :show-close="false" width="100%" class="phoneBottomPopup">
  139. <transition name="el-zoom-in-bottom">
  140. <div class="popupContent popup2" v-show="showBottomPopup2">
  141. <div class=" w-full text-center py-2 text-base font-semibold">历史对话</div>
  142. <div class="historyRow py-2 cursor-pointer" v-for="( item, index ) in historyList " :key="index" @click="() => {
  143. showBottomPopup2 = false
  144. clickDialogue(jsHistory, item, index)
  145. }
  146. ">
  147. <div class="flex justify-between">
  148. <div class="name text-base">{{ item.chatTitle }}</div>
  149. <div class="time">{{ item.createTime }}</div>
  150. </div>
  151. <!-- <div class="info">
  152. {{ item. }}
  153. </div> -->
  154. </div>
  155. </div>
  156. </transition>
  157. </el-dialog>
  158. </div>
  159. </div>
  160. </template>
  161. <script>
  162. import Cookies from 'js-cookie'
  163. import {
  164. streamChatWithWebApi,
  165. getGuidanceApi,
  166. getModelListApi,
  167. addChatApi,
  168. getChatCharacterRecordsApi,
  169. getChatRecordApi,
  170. updateTitleApi,
  171. clearRecordApi,
  172. queryVoiceListApi,
  173. updateRecordApi,
  174. deleteRecordApi
  175. } from "@/api/chat.js"
  176. import { detailApi } from "@/api/detail.js"
  177. import { queryUserBalanceApi } from "@/api/user.js"
  178. import { Message, MessageBox, Notification, Loading } from 'element-ui'
  179. import 'vue-awesome/icons/paper-plane'
  180. import 'vue-awesome/icons/fire'
  181. export default {
  182. components: {
  183. },
  184. data() {
  185. return {
  186. // 对话请求超时参数
  187. timeOut: null,
  188. showBottomPopup: false,
  189. showBottomPopup2: false,
  190. jsHistory: null,
  191. historyList: [],
  192. // 聊天等待
  193. chatLoading: false,
  194. // 是否全屏
  195. fullScreen: false,
  196. // 角色所有聊天列表
  197. allRecords: [],
  198. // 聊天记录id
  199. recordId: null,
  200. // 选中的角色聊天记录
  201. historyRes: [],
  202. recordsIndex: null,
  203. showDetail: true,
  204. showConfig: false,
  205. messageOptions: [],
  206. info: {},
  207. messageLoading: false,
  208. returnMessage: [],
  209. content: '',
  210. audioPlayIndex: null,
  211. configForm: {
  212. radio1: null,
  213. radio2: null,
  214. radio3: null,
  215. },
  216. // 对话接口是否返回错误状态
  217. resError: false,
  218. // 模型列表
  219. modelList: [],
  220. setting: {
  221. value1: false,
  222. value2: false,
  223. },
  224. //音频相关↓
  225. //音频地址
  226. audioUrl: '',
  227. audioUrlArr: [],
  228. audioUrlArrIndx: 0,
  229. videoLoop: null,
  230. videoLoopTime: 0,
  231. loopPlay: false,
  232. //场景下拉菜单
  233. dropdown3Show: false,
  234. // 场景对话相关
  235. sceneId: null,
  236. sceneInfo: {},
  237. }
  238. },
  239. computed: {
  240. canChat() {
  241. console.log(this.info, 'this.info');
  242. if (!this.sceneId) {
  243. if (this.info.isDelete == 1) {
  244. console.log(1);
  245. return false
  246. } else {
  247. console.log(2);
  248. return true
  249. }
  250. } else {
  251. if (this.sceneInfo.isDelete == 1) {
  252. return false
  253. } else {
  254. return true
  255. }
  256. }
  257. },
  258. notChatSend() {
  259. console.log(this.audioPlayIndex, 'this.audioPlayIndex>>>>>>>>>>>');
  260. // 对话结束切语音已经播放完毕
  261. // return this.chatLoading != false || this.audioPlayIndex != null
  262. return this.chatLoading != false
  263. },
  264. showNodata() {
  265. return this.allRecords.length == 0 && !this.$route.query.characterId
  266. }
  267. },
  268. mounted() {
  269. this.init()
  270. },
  271. watch: {
  272. returnMessage: {
  273. handler(newVal, lodVal) {
  274. this.$nextTick(() => {
  275. let messages = this.$refs.messages
  276. messages.scrollTop = messages.scrollHeight
  277. })
  278. },
  279. deep: true
  280. }
  281. },
  282. methods: {
  283. async init() {
  284. // 获取模型列表
  285. this.getModelList()
  286. // 获取用户所有聊天记录
  287. await this.getChatCharacterRecords()
  288. // 如果用角色id获取角色详情
  289. if (this.$route.query.characterId) {
  290. if (this.$route.query.sceneId) {
  291. this.sceneId = this.$route.query.sceneId
  292. }
  293. await this.getDetail(this.$route.query.characterId)
  294. } else {
  295. this.allRecords[0].open = true
  296. await this.getDetail(this.allRecords[0].id)
  297. }
  298. // 查找当前角色是否有对话记录
  299. this.searchHistory(this.info.id)
  300. },
  301. rtHandleCommand(value) {
  302. // console.log(value);
  303. if (value == 1) {
  304. this.toDetail()
  305. }
  306. },
  307. toDetail() {
  308. this.$router.push({
  309. path: '/detailH5',
  310. query: {
  311. id: this.info.id
  312. }
  313. })
  314. },
  315. // 显示历史聊天记录
  316. showHistory() {
  317. this.showBottomPopup2 = true
  318. },
  319. formModelChange() {
  320. // 在浏览器缓存用户的配置
  321. Cookies.set(`userConfig_${this.$store.state.user.userId}_${this.info.id}`, JSON.stringify(this.configForm))
  322. },
  323. // 全屏
  324. clickFullScreen() {
  325. this.fullScreen = !this.fullScreen
  326. },
  327. // 点击聊天记录切换聊天
  328. async clickDialogue(value1, value2, index) {
  329. console.log(value2, 'value2');
  330. let _this = this
  331. this.audioUrl = ""
  332. this.messageOptions = []
  333. this.returnMessage = []
  334. if (this.$route.query.characterId) {
  335. this.$route.query.characterId = value1.id
  336. }
  337. if (!value2) {
  338. return
  339. }
  340. // 如果点击的是场景聊天
  341. if (value2.sceneId) {
  342. this.sceneId = value2.sceneId
  343. await this.getDetail(value1.id)
  344. // 从角色详情中获取场景列表
  345. let sceneList = this.info.sceneList
  346. // 获取当前聊天对应的场景
  347. for (let i = 0; i < sceneList.length; i++) {
  348. const element = sceneList[i];
  349. if (element.sceneId == value2.sceneId) {
  350. _this.sceneInfo = element
  351. }
  352. }
  353. } else {
  354. this.sceneId = null
  355. this.sceneInfo = {}
  356. await this.getDetail(value1.id)
  357. }
  358. this.recordId = value2.id
  359. this.getChatRecord(value2.id)
  360. this.recordsIndex = index
  361. },
  362. // 场景下拉菜单
  363. handleCommand3(value) {
  364. if (value == 1) {
  365. } else if (value == 2) {
  366. // 清空聊天记录
  367. this.returnMessage = []
  368. // 新建场景对话
  369. this.newStart()
  370. // 增加开场白
  371. this.returnMessage.push({
  372. role: 'assistant',
  373. content: this.sceneInfo.sceneWelcome
  374. })
  375. } else if (value == 3) {
  376. // 回到常规对话
  377. // 遍历当前聊天记录是否有常规对话
  378. let messages = null
  379. for (let i = 0; i < this.allRecords.length; i++) {
  380. const element = this.allRecords[i];
  381. if (element.id == this.info.id) {
  382. messages = element
  383. }
  384. }
  385. console.log(messages, 'messages');
  386. let flag = false
  387. for (let i = 0; i < messages.chatCharacterList.length; i++) {
  388. const element = messages.chatCharacterList[i];
  389. if (!element.sceneId) {
  390. flag = true
  391. this.clickDialogue(messages, element, i)
  392. break
  393. }
  394. }
  395. // 如果聊天记录里没有常规对话,则新增一个常规对话
  396. if (!flag) {
  397. this.sceneId = null
  398. this.sceneInfo = {}
  399. this.newStart()
  400. }
  401. }
  402. },
  403. // 聊天记录列表-角色下拉菜单
  404. handleCommand2(value, value1) {
  405. if (value == 2) {
  406. this.$confirm('此操作将永久删除该角色所有聊天记录, 是否继续?', '提示', {
  407. confirmButtonText: '确定',
  408. cancelButtonText: '取消',
  409. type: 'warning'
  410. }).then(() => {
  411. this.clearRecord(value1)
  412. })
  413. }
  414. },
  415. // 聊天记录列表-角色-聊天记录下拉菜单
  416. handleCommand(value, value1, value2) {
  417. console.log(value, 'value');
  418. if (value == 1) {
  419. this.editDialogueTitle(value1, value2)
  420. } else if (value == 2) {
  421. this.$confirm('此操作将永久删除该聊天记录, 是否继续?', '提示', {
  422. confirmButtonText: '确定',
  423. cancelButtonText: '取消',
  424. type: 'warning'
  425. }).then(() => {
  426. this.clearRecord(value1, value2)
  427. })
  428. }
  429. },
  430. // 聊天记录操作相关↓
  431. handleCommand4(value1, value2, value3) {
  432. console.log(value1, 'value1');
  433. console.log(value2, 'value2');
  434. if (value1 == 1) {
  435. // 编辑
  436. let height = this.$refs[`message${value3}`][0].offsetHeight
  437. let width = this.$refs[`message${value3}`][0].offsetWidth
  438. value2.edit = true
  439. this.$nextTick(() => {
  440. this.$refs[`messageEditInput${value3}`][0].$el.children[0].style.height = height + 'px'
  441. this.$refs[`messageEdit${value3}`][0].style.width = width + 'px'
  442. })
  443. } else if (value1 == 2) {
  444. // 删除
  445. this.$confirm('此操作将永久删除该聊天记录, 是否继续?', '提示', {
  446. confirmButtonText: '确定',
  447. cancelButtonText: '取消',
  448. type: 'warning'
  449. }).then(() => {
  450. this.deleteRecord(value2, value3)
  451. })
  452. } else if (value1 == 3) {
  453. // 重新生成最后一句对话
  454. this.resetChat(value2, value3)
  455. }
  456. },
  457. // 重新生成最后一条聊天记录
  458. resetChat(value, index) {
  459. let params = {
  460. recordId: value.id,
  461. role: value.role,
  462. }
  463. deleteRecordApi(params).then(res => {
  464. this.returnMessage.splice(index, 1)
  465. this.getStreamChatWithWeb()
  466. })
  467. },
  468. // 编辑单条聊天记录-取消
  469. editCancel(value, index) {
  470. value.edit = false
  471. value.editContent = value.content
  472. },
  473. // 编辑单条聊天记录-保存
  474. editSave(value, index) {
  475. console.log(value, 'value');
  476. let params = {
  477. content: value.editContent,
  478. recordId: value.id,
  479. role: value.role,
  480. }
  481. updateRecordApi(params).then(res => {
  482. console.log(res, 'ressss');
  483. value.edit = false
  484. this.getChatRecord(this.recordId)
  485. })
  486. },
  487. // 删除单条聊天记录
  488. deleteRecord(value, index) {
  489. let params = {
  490. recordId: value.id,
  491. role: value.role,
  492. }
  493. deleteRecordApi(params).then(res => {
  494. this.$message({
  495. message: '删除成功',
  496. type: 'success'
  497. });
  498. this.getChatRecord(this.recordId)
  499. })
  500. },
  501. // 聊天记录新增对话
  502. async newStart_juese(value) {
  503. console.log(value, 'value');
  504. this.audioUrl = ""
  505. this.sceneId = null
  506. this.sceneInfo = {}
  507. this.messageOptions = []
  508. this.returnMessage = []
  509. // 点击聊天记录切换聊天
  510. if (this.$route.query.characterId) {
  511. this.$route.query.characterId = value.id
  512. }
  513. await this.getDetail(value.id)
  514. this.recordId = await this.addChat()
  515. for (let i = 0; i < this.allRecords.length; i++) {
  516. const element = this.allRecords[i];
  517. if (element.id == this.info.id) {
  518. element.chatCharacterList.push({
  519. chatTitle: "常规聊天",
  520. createTime: '',
  521. id: this.recordId
  522. })
  523. this.recordsIndex = element.chatCharacterList.length - 1
  524. }
  525. }
  526. },
  527. // 修改聊天记录标题
  528. editDialogueTitle(value1, value2) {
  529. this.$prompt('新标题名称', '修改标题', {
  530. confirmButtonText: '确定',
  531. cancelButtonText: '取消',
  532. }).then(({ value }) => {
  533. let params = {
  534. chatTitle: value,
  535. id: value2.id
  536. }
  537. updateTitleApi(params).then(res => {
  538. console.log(res, '修改标题');
  539. this.$message({
  540. type: 'success',
  541. message: '修改成功!'
  542. });
  543. this.getChatCharacterRecords()
  544. })
  545. })
  546. },
  547. clearRecord(value1, value2) {
  548. let params = {
  549. characterId: value1.id,
  550. }
  551. if (value2) {
  552. params.recordId = value2.id
  553. }
  554. clearRecordApi(params).then(res => {
  555. this.$message({
  556. type: 'success',
  557. message: '删除成功!'
  558. });
  559. this.getChatCharacterRecords()
  560. })
  561. },
  562. async getChatRecord(recordId) {
  563. let params = {
  564. characterId: this.info.id,
  565. recordId: recordId
  566. }
  567. getChatRecordApi(params).then(res => {
  568. console.log(res, '查询到的聊天记录');
  569. let array = res.data
  570. // this.returnMessage
  571. this.historyRes = res.data
  572. if (this.returnMessage.length > 1) {
  573. this.returnMessage = this.returnMessage.splice(0, 1)
  574. }
  575. for (let i = 0; i < array.length; i++) {
  576. const element = array[i];
  577. let history = JSON.parse(element.history)
  578. history.map(item => {
  579. item.id = element.id
  580. item.uuid = element.uuid
  581. item.edit = false
  582. item.editContent = item.content
  583. if (item.role == 'assistant') {
  584. item.voiceFilePosition = element.voiceFilePosition
  585. }
  586. })
  587. this.returnMessage = [...this.returnMessage, ...history]
  588. }
  589. this.configForm.radio2 = this.modelList[0].id
  590. this.modelList.map(item => {
  591. if (item.id == array[array.length - 1].modelId) {
  592. this.configForm.radio2 = item.id
  593. }
  594. })
  595. })
  596. },
  597. searchHistory(characterId) {
  598. // 查看对话记录是否有选中的角色
  599. // console.log('查看对话记录是否有选中的角色');
  600. let flg = false
  601. for (let i = 0; i < this.allRecords.length; i++) {
  602. const element = this.allRecords[i];
  603. // 如果有则选中角色的最新一条记录并获取对话聊天记录
  604. if (element.id == characterId) {
  605. // 当前角色的聊天记录列表
  606. this.historyList = element.chatCharacterList
  607. this.jsHistory = element
  608. console.log(element, '查看对话记录是否有选中的角色');
  609. flg = true
  610. element.open = true
  611. // 二级对话判断当前是普通对话还是场景对话
  612. // 取对应的最后一条消息记录
  613. let chatCharacterList = element.chatCharacterList
  614. let chatCharacter = null
  615. let index = null
  616. for (let i = 0; i < chatCharacterList.length; i++) {
  617. const element2 = chatCharacterList[i];
  618. if (this.$route.query.sceneId == element2.sceneId) {
  619. chatCharacter = element2
  620. index = i
  621. break;
  622. } else if (!this.$route.query.sceneId && !element2.sceneId) {
  623. chatCharacter = element2
  624. index = i
  625. break;
  626. }
  627. }
  628. // 如果有聊天记录则点击
  629. if (chatCharacter) {
  630. this.clickDialogue(element, chatCharacter, index)
  631. } else {
  632. // 如果没有场景聊天记录则新增场景聊天
  633. // 从角色详情中获取场景列表
  634. let sceneList = this.info.sceneList
  635. // 获取当前聊天对应的场景
  636. for (let i = 0; i < sceneList.length; i++) {
  637. const element = sceneList[i];
  638. if (element.sceneId == this.$route.query.sceneId) {
  639. this.sceneInfo = element
  640. }
  641. }
  642. }
  643. // this.recordsIndex = element.chatCharacterList.length - 1
  644. // let recordId = element.chatCharacterList[element.chatCharacterList.length - 1].id
  645. // this.recordId = recordId
  646. // this.getChatRecord(recordId)
  647. }
  648. }
  649. // 如果没有聊天记录,则增加开场聊天引导
  650. if (!flg) {
  651. this.getGuidance(this.info.id)
  652. }
  653. },
  654. async getChatCharacterRecords() {
  655. let res = await getChatCharacterRecordsApi()
  656. console.log(res, '聊天记录');
  657. if (res.data) {
  658. for (let i = 0; i < res.data.length; i++) {
  659. const element = res.data[i];
  660. if (this.info.id == element.id) {
  661. element.open = true
  662. } else {
  663. element.open = false
  664. }
  665. }
  666. this.allRecords = res.data
  667. }
  668. },
  669. // 新增对话记录
  670. async addChat() {
  671. let params = {
  672. characterId: this.info.id,
  673. chatTitle: ""
  674. }
  675. if (this.sceneId) {
  676. params.sceneId = this.sceneId
  677. }
  678. let res = await addChatApi(params)
  679. console.log(res, '新增对话记录');
  680. return res.data
  681. },
  682. getModelList() {
  683. getModelListApi().then(res => {
  684. console.log(res, '模型列表');
  685. this.modelList = res.data
  686. })
  687. },
  688. inputBoxClick() {
  689. this.$refs.input.focus()
  690. },
  691. async getDetail(id) {
  692. let res = await detailApi(id)
  693. console.log(res, '角色详情');
  694. this.info = res.data
  695. // 模型选中角色默认模型
  696. this.configForm.radio2 = this.info.modelId
  697. // 如果用户有设置过模型使用用户设置模型
  698. let userConfig = Cookies.get(`userConfig_${this.$store.state.user.userId}_${this.info.id}`)
  699. if (userConfig) {
  700. userConfig = JSON.parse(userConfig)
  701. }
  702. if (userConfig && userConfig.radio2) {
  703. this.configForm.radio2 = userConfig.radio2
  704. }
  705. // let HistoryMessage = JSON.parse(localStorage.getItem(`[userId:${123},aiId:${this.info.id}]`));
  706. // if (HistoryMessage) {
  707. // this.returnMessage = HistoryMessage
  708. // } else {
  709. // this.returnMessage.push({
  710. // role: 'assistant',
  711. // content: this.info.firstContent
  712. // })
  713. // localStorage.setItem(`[userId:${123},aiId:${this.info.id}]`, JSON.stringify(this.returnMessage));
  714. // }
  715. if (this.sceneId) {
  716. console.log(this.sceneId);
  717. let sceneList = this.info.sceneList
  718. for (let i = 0; i < sceneList.length; i++) {
  719. const element = sceneList[i];
  720. if (element.sceneId == this.sceneId) {
  721. this.sceneInfo = element
  722. this.configForm.radio2 = element.modelId
  723. this.returnMessage.push({
  724. role: 'assistant',
  725. content: element.sceneWelcome
  726. })
  727. }
  728. }
  729. } else {
  730. this.returnMessage.push({
  731. role: 'assistant',
  732. content: this.info.firstContent
  733. })
  734. }
  735. },
  736. getGuidance(id) {
  737. getGuidanceApi(id).then(res => {
  738. console.log(res, '聊天引导');
  739. this.messageOptions = res.data
  740. })
  741. },
  742. goBack() {
  743. this.$router.back()
  744. },
  745. async newStart() {
  746. // 新业务,创建一个新的对话记录
  747. this.recordId = await this.addChat()
  748. for (let i = 0; i < this.allRecords.length; i++) {
  749. const element = this.allRecords[i];
  750. if (element.id == this.info.id) {
  751. if (this.sceneId) {
  752. element.chatCharacterList.push({
  753. chatTitle: "场景聊天",
  754. createTime: '',
  755. id: this.recordId,
  756. sceneIcon: true
  757. })
  758. } else {
  759. element.chatCharacterList.push({
  760. chatTitle: "常规聊天",
  761. createTime: '',
  762. id: this.recordId
  763. })
  764. }
  765. this.recordsIndex = element.chatCharacterList.length - 1
  766. }
  767. }
  768. this.returnMessage = []
  769. this.getDetail(this.info.id)
  770. // 废弃》》
  771. // 清空历史数据
  772. // localStorage.removeItem(`[userId:${123},aiId:${this.info.id}]`);
  773. // this.returnMessage = []
  774. // this.getDetail(this.info.id)
  775. },
  776. Enterkey(e) {
  777. if (e.keyCode == 13) {
  778. console.log(this.notChatSend, 'this.notChatSend');
  779. if (this.notChatSend) {
  780. return
  781. }
  782. if (!this.content) {
  783. return
  784. }
  785. this.getStreamChatWithWeb()
  786. }
  787. },
  788. // 前往创建场景
  789. toCreateScene() {
  790. this.$router.push({
  791. name: 'CreateScene',
  792. query: {
  793. characterId: this.info.id,
  794. }
  795. })
  796. },
  797. async getStreamChatWithWeb_old() {
  798. this.messageLoading = true
  799. if (!this.content) {
  800. return
  801. }
  802. this.returnMessage.push({
  803. role: 'user',
  804. content: this.content
  805. })
  806. let HistoryMessage = JSON.parse(localStorage.getItem(`[userId:${123},aiId:${this.info.id}]`));
  807. // 历史记录取最新的20条传给后端
  808. if (HistoryMessage && HistoryMessage.length > 20) {
  809. HistoryMessage = HistoryMessage.slice(HistoryMessage.length - 1, 20)
  810. }
  811. let params = {
  812. historyMessage: HistoryMessage || [],
  813. characterId: this.info.id,
  814. prompt: JSON.parse(JSON.stringify(this.content))
  815. }
  816. // 清空输入框的值
  817. this.content = ''
  818. // 新增一条ai信息
  819. this.returnMessage.push({
  820. role: 'assistant',
  821. content: ''
  822. })
  823. let res = await streamChatWithWebApi(params)
  824. console.log(res.data.message.content, 'res');
  825. let content = res.data.message.content
  826. let index = 0
  827. let xing = 1
  828. let messageInterval = setInterval(() => {
  829. if (index > content.length - 1) {
  830. console.log('结束');
  831. // 消息全部显示后存到历史localStorage
  832. let HistoryMessage = []
  833. // 历史只存100条,长度超出截取最新的
  834. if (this.returnMessage.length > 100) {
  835. HistoryMessage = JSON.stringify(this.returnMessage.slice(this.returnMessage.length - 100, 100))
  836. } else {
  837. HistoryMessage = JSON.stringify(this.returnMessage)
  838. }
  839. localStorage.setItem(`[userId:${123},aiId:${this.info.id}]`, HistoryMessage);
  840. clearInterval(messageInterval)
  841. return
  842. }
  843. console.log(content[index], 'content[index]');
  844. if (content[index] == "*") {
  845. if (xing == 1) {
  846. xing = 2
  847. this.returnMessage[this.returnMessage.length - 1].content += "("
  848. } else if (xing == 2) {
  849. xing = 1
  850. this.returnMessage[this.returnMessage.length - 1].content += ")"
  851. }
  852. } else {
  853. this.returnMessage[this.returnMessage.length - 1].content += content[index]
  854. }
  855. index += 1
  856. }, 50)
  857. },
  858. async getStreamChatWithWeb() {
  859. if (this.chatLoading) {
  860. return
  861. }
  862. this.audioUrl = []
  863. this.audioUrlArrIndx = 0
  864. this.textCache = ''
  865. this.resError = false
  866. this.messageLoading = true
  867. this.chatLoading = true
  868. // 初始化定时器和audio的状态
  869. if (this.videoLoop) {
  870. clearInterval(this.videoLoop)
  871. this.videoLoop = null
  872. this.audioUrl = ''
  873. this.$refs.audio.currentTime = 0;
  874. this.$refs.audio.pause()
  875. }
  876. // 如果没有对话记录id,新增对话记录
  877. if (!this.recordId) {
  878. this.recordId = await this.addChat()
  879. }
  880. if (this.content) {
  881. this.returnMessage.push({
  882. role: 'user',
  883. content: this.content
  884. })
  885. }
  886. let HistoryMessage = JSON.parse(JSON.stringify(this.returnMessage))
  887. console.log(HistoryMessage, 'HistoryMessage1');
  888. HistoryMessage = HistoryMessage.splice(1, HistoryMessage.length)
  889. console.log(HistoryMessage, 'HistoryMessage2');
  890. // 历史记录取最新的20条传给后端
  891. if (HistoryMessage && HistoryMessage.length > 20) {
  892. HistoryMessage = HistoryMessage.slice(HistoryMessage.length - 1, 20)
  893. }
  894. let params = {
  895. historyMessage: HistoryMessage,
  896. characterId: this.info.id,
  897. prompt: this.content,
  898. modelId: this.configForm.radio2,
  899. recordId: this.recordId,
  900. }
  901. // 如果当前是场景对话则需要传sceneId
  902. if (this.sceneId) {
  903. params.sceneId = this.sceneId
  904. }
  905. // 新增一条ai信息
  906. this.returnMessage.push({
  907. role: 'assistant',
  908. content: '',
  909. voiceFilePosition: '',
  910. })
  911. // 清空输入框的值
  912. this.content = ''
  913. this.timeOut = 'start'
  914. let res = await streamChatWithWebApi(params)
  915. console.log(res, 'res');
  916. this.messageLoading = false
  917. if (res.status != 200) {
  918. this.$message.error(res.statusText)
  919. this.returnMessage.splice(this.returnMessage.length - 1, 2)
  920. this.chatLoading = false
  921. return
  922. }
  923. const reader = res.body.getReader()
  924. const decoder = new TextDecoder()
  925. while (1) {
  926. const { done, value } = await reader.read()
  927. // console.log(done, 'done');
  928. if (done) {
  929. console.log(value, '结束value');
  930. this.chatLoading = false
  931. // if (typeof(value) == "undefined") {
  932. // this.$message.error('错误')
  933. // this.returnMessage.splice(this.returnMessage.length - 1, 1)
  934. // }
  935. // 每次对话完刷新聊天记录
  936. this.getChatCharacterRecords()
  937. let params = {
  938. characterId: this.info.id,
  939. recordId: this.recordId
  940. }
  941. let historyRes = await getChatRecordApi(params)
  942. this.historyRes = historyRes.data
  943. // 如果开启自动播放,则查询并播放语音
  944. // this.audioUrl = ""
  945. // if (!this.resError) {
  946. // }
  947. console.log('结束');
  948. if (this.timeOut) {
  949. clearTimeout(this.timeOut)
  950. this.timeOut = null
  951. }
  952. break;
  953. }
  954. //txt就是一个一个的字 然后添加到页面上就可以了
  955. const txt = decoder.decode(value).split('data:')
  956. // const txt = decoder.decode(value)
  957. if (this.timeOut) {
  958. clearTimeout(this.timeOut)
  959. this.timeOut = setTimeout(() => {
  960. this.returnMessage[this.returnMessage.length - 1].content = 'AI智能累得打起了小盹儿,咱们让他缓一缓,再来试试!'
  961. this.chatLoading = false
  962. }, 10000)
  963. }
  964. // console.log(txt, 'txt');
  965. this.addMessage(txt)
  966. // let data = JSON.parse(txt).message.content
  967. // console.log(txt, 'txt');
  968. // if (this.isJSON(txt) && JSON.parse(txt).code != 200) {
  969. // let data = JSON.parse(txt)
  970. // this.$message.error(data.msg)
  971. // this.returnMessage.splice(this.returnMessage.length - 1, 1)
  972. // break;
  973. // }
  974. }
  975. },
  976. addMessage(text) {
  977. for (let i = 0; i < text.length; i++) {
  978. const element = text[i];
  979. if (this.isJSON(element)) {
  980. let value = JSON.parse(element)
  981. if (value.code == 401) {
  982. this.returnMessage.splice(this.returnMessage.length - 1, 1)
  983. this.noLogin(element)
  984. this.chatLoading = false
  985. break;
  986. }
  987. if (value.code == 500) {
  988. // console.log(value, 'JSON.parse(element)');
  989. this.returnMessage.splice(this.returnMessage.length - 2, 2)
  990. // this.$message.error(value.content || 'AI智能没有理解你的意思,换个说法试试吧!')
  991. this.returnMessage[this.returnMessage.length - 1].content = 'AI智能可能没有理解你的意思,换个说法试试吧!'
  992. this.resError = true
  993. this.chatLoading = false
  994. break;
  995. }
  996. // 获取语音id
  997. if (value.code == -1) {
  998. this.returnMessage[this.returnMessage.length - 1].uuid = value.content
  999. // 如果开启了自动播放开始轮巡查询语音
  1000. if (this.setting.value1) {
  1001. this.audioUrlArrIndx = 0
  1002. this.videoLoopTime = 0
  1003. this.audioUrlArr = []
  1004. this.loopPlay = false
  1005. this.audioPlayIndex = this.returnMessage.length - 1
  1006. this.loopGetVoice(value.content, this.returnMessage.length - 1)
  1007. }
  1008. break;
  1009. }
  1010. if (!value.done) {
  1011. let txt = value.content
  1012. this.returnMessage[this.returnMessage.length - 1].content += txt
  1013. } else {
  1014. // 对话流结束
  1015. // 临时插入的ai对话记录和用户记录插入聊天id
  1016. this.returnMessage[this.returnMessage.length - 1].id = value.content
  1017. this.returnMessage[this.returnMessage.length - 2].id = value.content
  1018. }
  1019. }
  1020. }
  1021. },
  1022. noLogin(value) {
  1023. let _this = this
  1024. MessageBox.confirm(
  1025. value.content,
  1026. "系统提示",
  1027. {
  1028. confirmButtonText: "前往登录",
  1029. cancelButtonText: "取消",
  1030. type: "warning",
  1031. }
  1032. )
  1033. .then(() => {
  1034. // isRelogin.show = false;
  1035. this.$store.dispatch("LogOut").then(() => {
  1036. // location.href = "/";
  1037. _this.$refs.Header.showLogin()
  1038. });
  1039. })
  1040. .catch(() => {
  1041. // isRelogin.show = false;
  1042. });
  1043. },
  1044. isJSON(str) {
  1045. if (typeof str == 'string') {
  1046. try {
  1047. JSON.parse(str);
  1048. return true;
  1049. } catch (e) {
  1050. // console.log(e);
  1051. return false;
  1052. }
  1053. }
  1054. },
  1055. messageOptionClick(value) {
  1056. this.content = value
  1057. this.getStreamChatWithWeb()
  1058. },
  1059. // 播放聊天语音
  1060. initAudio(index) {
  1061. // 从聊天记录获取id
  1062. console.log(this.returnMessage[index], 'swssssssss');
  1063. let id = this.returnMessage[index].id
  1064. let history = this.historyRes.map((item) => {
  1065. if (item.id == id) {
  1066. return item
  1067. }
  1068. })
  1069. console.log(history, 'history');
  1070. return
  1071. this.loopGetVoice(history.id, index)
  1072. },
  1073. playAudio(index) {
  1074. if (index) {
  1075. this.audioPlayIndex = index
  1076. }
  1077. // this.audioUrlArrIndx = 0
  1078. console.log(this.audioUrlArrIndx, 'this.audioUrlArrIndx');
  1079. console.log(this.audioUrlArr, 'this.audioUrlArr');
  1080. // console.log(audioUrlArr[this.audioUrlArrIndx].voiceAddress, 'audioUrlArr[this.audioUrlArrIndx].voiceAddress');
  1081. // 如果当前播放的语音地址为空,则等待2秒后重新获取
  1082. if (!this.audioUrlArr[this.audioUrlArrIndx].voiceAddress && this.videoLoopTime <= 50) {
  1083. setTimeout(() => {
  1084. this.playAudio(index)
  1085. }, 2000)
  1086. return
  1087. }
  1088. // 如果当前播放的语音地址为false,则跳过当前语音
  1089. if (this.audioUrlArr[this.audioUrlArrIndx].voiceAddress == 'false' && this.audioUrlArrIndx < this.audioUrlArr.length - 1) {
  1090. this.audioUrlArrIndx += 1
  1091. this.playAudio(index)
  1092. return
  1093. } else if (this.audioUrlArrIndx > this.audioUrlArr.length - 1) {
  1094. this.audioUrl = ''
  1095. this.audioUrlArrIndx = 0
  1096. this.videoLoopTime = 0
  1097. this.audioUrlArr = []
  1098. return
  1099. }
  1100. this.audioUrl = this.audioUrlArr[this.audioUrlArrIndx].voiceAddress
  1101. console.log(this.audioUrl, 'this.audioUrl');
  1102. setTimeout(() => {
  1103. if (this.audioUrl) {
  1104. this.$refs.audio.play()
  1105. }
  1106. }, 1000)
  1107. },
  1108. async historyGetVoice(value, index) {
  1109. console.log(value, 'value');
  1110. if (!value.uuid) {
  1111. return
  1112. }
  1113. let params = {
  1114. uuid: value.uuid
  1115. }
  1116. let fileRes = await queryVoiceListApi(params)
  1117. console.log(fileRes.data, 'file>>>');
  1118. this.audioUrlArr = fileRes.data.voices
  1119. this.audioUrlArrIndx = 0
  1120. this.videoLoopTime = 0
  1121. this.loopPlay = false
  1122. this.playAudio(index)
  1123. },
  1124. async loopGetVoice(id, index) {
  1125. this.videoLoopTime += 1
  1126. // 获取语音文件
  1127. let params = {
  1128. uuid: id
  1129. }
  1130. let fileRes = await queryVoiceListApi(params)
  1131. console.log(fileRes.data, 'file>>>');
  1132. let voices = fileRes.data.voices
  1133. let _continue = fileRes.data.continue
  1134. this.apiHaveVideo = _continue
  1135. // 判断语音文件是否获取完毕
  1136. let trueLength = 0
  1137. for (let i = 0; i < voices.length; i++) {
  1138. const element = voices[i];
  1139. if (element.voiceAddress) {
  1140. trueLength += 1
  1141. }
  1142. }
  1143. if (!_continue && trueLength == voices.length) {
  1144. if (this.videoLoop) {
  1145. clearTimeout(this.videoLoop)
  1146. this.videoLoop = null
  1147. }
  1148. // this.returnMessage[index].voiceFilePosition = fileRes.data.voiceFilePosition
  1149. // 有语音文件则开始播放
  1150. if (voices.length > 0) {
  1151. this.audioUrlArr = voices
  1152. console.log(this.audioUrlArr, '进入播放流程1');
  1153. if (this.$refs.audio.paused || this.audioUrlArrIndx > this.audioUrlArr.length - 1) {
  1154. if (this.loopPlay == false) {
  1155. this.playAudio(index)
  1156. this.loopPlay = true
  1157. }
  1158. }
  1159. }
  1160. } else {
  1161. if (voices.length > 0) {
  1162. this.audioUrlArr = voices
  1163. console.log(this.audioUrlArr, '进入播放流程2');
  1164. if (this.$refs.audio.paused || this.audioUrlArrIndx > this.audioUrlArr.length - 1) {
  1165. if (this.loopPlay == false) {
  1166. this.playAudio(index)
  1167. this.loopPlay = true
  1168. }
  1169. }
  1170. }
  1171. // 没有语音文件则开启定时器每两秒查询一次
  1172. if (this.videoLoopTime > 50) {
  1173. // 如果调用次数超过30次则停止
  1174. return
  1175. }
  1176. this.videoLoop = setTimeout(() => {
  1177. this.loopGetVoice(id, index)
  1178. }, 2000)
  1179. }
  1180. },
  1181. audioEnd() {
  1182. this.audioUrlArrIndx += 1
  1183. console.log(this.audioUrlArrIndx, 'audioUrlArrIndx');
  1184. if (this.audioUrlArrIndx < this.audioUrlArr.length && this.videoLoopTime <= 50) {
  1185. // 如果当前播放的语音地址为空,则等待2秒后重新获取
  1186. if (!this.audioUrlArr[this.audioUrlArrIndx].voiceAddress) {
  1187. setTimeout(() => {
  1188. this.playAudio()
  1189. }, 2000)
  1190. return
  1191. }
  1192. // 如果当前播放的语音地址为false,则跳过当前语音
  1193. if (this.audioUrlArr[this.audioUrlArrIndx].voiceAddress == 'false' && this.audioUrlArrIndx < this.audioUrlArr.length - 1) {
  1194. this.audioUrlArrIndx += 1
  1195. this.playAudio()
  1196. return
  1197. }
  1198. this.audioUrl = this.audioUrlArr[this.audioUrlArrIndx].voiceAddress
  1199. setTimeout(() => {
  1200. if (this.audioUrl) {
  1201. this.$refs.audio.play()
  1202. }
  1203. }, 100)
  1204. } else {
  1205. this.audioUrlArrIndx = []
  1206. this.audioUrlArrIndx = 0
  1207. console.log('播放结束');
  1208. this.audioPlayIndex = null
  1209. }
  1210. },
  1211. ChangeSettingValue1(value) {
  1212. console.log(value, 'value');
  1213. if (value) {
  1214. queryUserBalanceApi().then(res => {
  1215. console.log(res, '用户余额');
  1216. if (res.data <= 0) {
  1217. this.setting.value1 = false
  1218. this.$message({
  1219. message: '余额不足,无法开启次功能',
  1220. type: 'error'
  1221. });
  1222. }
  1223. })
  1224. }
  1225. },
  1226. // 场景业务相关↓
  1227. async sceneChange(item) {
  1228. console.log(item, 'item');
  1229. this.$message({
  1230. message: `已进入场景${item.sceneName}`,
  1231. type: 'success'
  1232. });
  1233. // 当用户选中场景
  1234. this.sceneInfo = item
  1235. // 切换当前场景id
  1236. this.sceneId = item.sceneId
  1237. // 情况聊天记录
  1238. this.returnMessage = []
  1239. // 新建场景对话
  1240. this.newStart()
  1241. // 增加开场白
  1242. // this.returnMessage.push({
  1243. // role: 'assistant',
  1244. // content: item.sceneWelcome
  1245. // })
  1246. }
  1247. }
  1248. }
  1249. </script>
  1250. <style lang="scss" scoped>
  1251. .chat {
  1252. min-height: 100vh;
  1253. }
  1254. .top {
  1255. position: relative;
  1256. width: 100%;
  1257. height: 50px;
  1258. display: flex;
  1259. align-items: center;
  1260. }
  1261. .chatContent {
  1262. // margin-top: 30px;
  1263. background-size: cover;
  1264. background-position: center;
  1265. background-repeat: no-repeat;
  1266. display: flex;
  1267. height: calc(100% - 50px - 63px);
  1268. justify-content: center;
  1269. width: 100vw;
  1270. // margin-bottom: 63px;
  1271. >.leftImg {
  1272. border-radius: 16px 16px 16px 16px;
  1273. height: 95%;
  1274. position: relative;
  1275. min-width: 380px;
  1276. >img {
  1277. border: 6px solid;
  1278. border-image: linear-gradient(180deg, #b48733, #e8cf97, #b48733) 6 6;
  1279. height: 99%;
  1280. margin-right: 5px;
  1281. }
  1282. >.aiInfo {
  1283. background-color: #ffffff1a;
  1284. border-radius: 24px 24px 24px 24px;
  1285. bottom: -70px;
  1286. display: flex;
  1287. height: 193px;
  1288. justify-content: center;
  1289. left: 50%;
  1290. position: absolute;
  1291. transform: translate(-50%, -50%);
  1292. width: 100%;
  1293. >.photo {
  1294. width: 64px;
  1295. height: 64px;
  1296. font-size: 18px;
  1297. position: absolute;
  1298. top: -20px;
  1299. box-sizing: border-box;
  1300. margin: 0;
  1301. padding: 0;
  1302. color: #fff;
  1303. line-height: 1.5714285714285714;
  1304. list-style: none;
  1305. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
  1306. display: inline-flex;
  1307. justify-content: center;
  1308. align-items: center;
  1309. overflow: hidden;
  1310. white-space: nowrap;
  1311. text-align: center;
  1312. vertical-align: middle;
  1313. background: rgba(0, 0, 0, 0.25);
  1314. border: 1px solid transparent;
  1315. border-radius: 50%;
  1316. }
  1317. >.info {
  1318. padding-top: 50px;
  1319. width: 80%;
  1320. >.name {
  1321. color: #fff;
  1322. font-size: 22px;
  1323. font-style: normal;
  1324. font-weight: 700;
  1325. line-height: 31px;
  1326. text-align: center;
  1327. text-transform: none;
  1328. }
  1329. >.tags {
  1330. padding-bottom: 8px;
  1331. text-align: center;
  1332. >.tag {
  1333. margin-right: 4px;
  1334. background: #ffffff1a;
  1335. border-radius: 4px 4px 4px 4px;
  1336. color: #fff;
  1337. font-size: 12px;
  1338. font-weight: 500;
  1339. line-height: 17px;
  1340. border-color: transparent;
  1341. }
  1342. }
  1343. >.infoContent {
  1344. color: #fff;
  1345. font-size: 16px;
  1346. font-style: normal;
  1347. font-weight: 500;
  1348. height: 80px;
  1349. line-height: 22px;
  1350. overflow-y: auto;
  1351. text-align: center;
  1352. text-transform: none;
  1353. }
  1354. }
  1355. }
  1356. }
  1357. >.chat-box {
  1358. position: relative;
  1359. // background: linear-gradient(rgba(255, 255, 255, 0) 75%, rgb(34, 34, 34));
  1360. background-position: 50%;
  1361. background-repeat: no-repeat;
  1362. background-size: auto;
  1363. // border: 1px solid #635677 !important;
  1364. box-sizing: border-box;
  1365. // color: #fff !important;
  1366. display: flex;
  1367. flex-direction: column;
  1368. height: 100%;
  1369. // margin: 0 auto;
  1370. width: 100%;
  1371. padding: 10px;
  1372. }
  1373. >.mask {
  1374. background: rgba(0, 0, 0, 0.5);
  1375. }
  1376. }
  1377. .messages {
  1378. // height: calc(100% - 68px );
  1379. overflow-y: auto;
  1380. }
  1381. .messages::-webkit-scrollbar {
  1382. width: 0px;
  1383. }
  1384. .message {
  1385. background: var(--bg-color3);
  1386. // border: 1px solid #5b5b5e;
  1387. max-width: calc(100% - 72px);
  1388. // flex: 1;
  1389. min-width: 80px;
  1390. height: max-content;
  1391. }
  1392. .message2 {
  1393. color: #000;
  1394. background: rgba(200, 200, 200, 0.6);
  1395. // border: 1px solid #5b5b5e;
  1396. max-width: calc(100% - 72px);
  1397. // flex: 1;
  1398. min-width: 80px;
  1399. height: max-content;
  1400. }
  1401. .photo {
  1402. width: 56px;
  1403. }
  1404. .me {
  1405. flex-direction: row-reverse;
  1406. }
  1407. .loadingMessage {
  1408. position: relative;
  1409. // display: flex;
  1410. // justify-content: center;
  1411. width: 60px;
  1412. height: 32px;
  1413. }
  1414. .popup1 {
  1415. .configForm {
  1416. margin-bottom: 75px;
  1417. }
  1418. .bottom {
  1419. position: absolute;
  1420. bottom: 0;
  1421. height: 75px;
  1422. padding-bottom: 15px;
  1423. background: var(--bg-color5);
  1424. width: calc(100% - 30px);
  1425. overflow-x: auto;
  1426. .button {
  1427. height: 100%;
  1428. width: 80px;
  1429. display: inline-flex;
  1430. flex-direction: column;
  1431. justify-content: center;
  1432. align-items: center;
  1433. font-size: 11px;
  1434. .icon {
  1435. width: 34px;
  1436. height: 34px;
  1437. background: #6a6a6a;
  1438. display: flex;
  1439. justify-content: center;
  1440. align-items: center;
  1441. border-radius: 50%;
  1442. font-size: 18px;
  1443. margin-bottom: 10px;
  1444. }
  1445. }
  1446. .button:not(:first-child) {
  1447. margin-left: 20px;
  1448. }
  1449. }
  1450. }
  1451. .popup2 {
  1452. height: 100%;
  1453. }
  1454. </style>
  1455. <style scoped>
  1456. .loadingMessage>>>.el-loading-parent--relative {
  1457. width: 100%;
  1458. height: 100%;
  1459. }
  1460. .loadingMessage>>>.el-loading-spinner {
  1461. display: flex;
  1462. justify-content: center;
  1463. }
  1464. .mButton {
  1465. border: solid 0px;
  1466. }
  1467. .tm_button {
  1468. width: 26px;
  1469. height: 26px;
  1470. font-size: 20px;
  1471. padding: 0px;
  1472. }
  1473. .phoneBottomPopup>>>.el-dialog {
  1474. margin-top: 0 !important;
  1475. position: absolute;
  1476. bottom: 0;
  1477. min-height: 60%;
  1478. background: rgba(255, 255, 255, 0);
  1479. }
  1480. .phoneBottomPopup>>>.el-dialog__header {
  1481. padding: 0;
  1482. }
  1483. .phoneBottomPopup>>>.el-dialog__body {
  1484. color: #fff;
  1485. padding: 0;
  1486. width: 100%;
  1487. height: 100%;
  1488. position: absolute;
  1489. bottom: 0;
  1490. }
  1491. .phoneBottomPopup .popupContent {
  1492. background: var(--bg-color5);
  1493. color: #fff;
  1494. width: 100%;
  1495. height: 100%;
  1496. padding: 10px 15px;
  1497. position: relative;
  1498. }
  1499. .radio>>>.el-radio-button {
  1500. margin-bottom: 5px;
  1501. margin-right: 10px;
  1502. /* border-left: solid 1px #DCDFE6; */
  1503. }
  1504. .radio>>>.el-radio-button__inner {
  1505. border-left: solid 1px #DCDFE6;
  1506. background-color: var(--bg-color5);
  1507. border-radius: 4px;
  1508. color: #fff;
  1509. }
  1510. .radio>>>.el-radio-button__orig-radio:checked+.el-radio-button__inner {
  1511. border-left: solid 1px var(--bg-color1);
  1512. background-color: var(--bg-color1);
  1513. border-color: var(--bg-color1);
  1514. -webkit-box-shadow: -1px 0 0 0 var(--bg-color1);
  1515. box-shadow: -1px 0 0 0 var(--bg-color1);
  1516. }
  1517. </style>