From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/views/ai/chat/index/index.vue | 630 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 630 insertions(+), 0 deletions(-)
diff --git a/src/views/ai/chat/index/index.vue b/src/views/ai/chat/index/index.vue
new file mode 100644
index 0000000..8a4deee
--- /dev/null
+++ b/src/views/ai/chat/index/index.vue
@@ -0,0 +1,630 @@
+<template>
+ <el-container class="absolute flex-1 top-0 left-0 h-full w-full">
+ <!-- 宸︿晶锛氬璇濆垪琛� -->
+ <ConversationList
+ :active-id="activeConversationId?.toString() || ''"
+ ref="conversationListRef"
+ @on-conversation-create="handleConversationCreateSuccess"
+ @on-conversation-click="handleConversationClick"
+ @on-conversation-clear="handleConversationClear"
+ @on-conversation-delete="handlerConversationDelete"
+ />
+ <!-- 鍙充晶锛氬璇濊鎯� -->
+ <el-container class="bg-[var(--el-bg-color)]">
+ <el-header
+ class="flex flex-row items-center justify-between bg-[var(--el-bg-color-page)] shadow-[0_0_0_0_var(--el-border-color-light)]"
+ >
+ <div class="text-18px font-bold">
+ {{ activeConversation?.title ? activeConversation?.title : '瀵硅瘽' }}
+ <span v-if="activeMessageList.length">({{ activeMessageList.length }})</span>
+ </div>
+ <div class="flex w-300px flex-row justify-end" v-if="activeConversation">
+ <el-button type="primary" bg plain size="small" @click="openChatConversationUpdateForm">
+ <span v-html="activeConversation?.modelName"></span>
+ <Icon icon="ep:setting" class="ml-10px" />
+ </el-button>
+ <el-button size="small" class="p-10px" @click="handlerMessageClear">
+ <Icon
+ icon="heroicons-outline:archive-box-x-mark"
+ color="var(--el-text-color-placeholder)"
+ />
+ </el-button>
+ <el-button size="small" class="p-10px">
+ <Icon icon="ep:download" color="var(--el-text-color-placeholder)" />
+ </el-button>
+ <el-button size="small" class="p-10px" @click="handleGoTopMessage">
+ <Icon icon="ep:top" color="var(--el-text-color-placeholder)" />
+ </el-button>
+ </div>
+ </el-header>
+
+ <!-- main锛氭秷鎭垪琛� -->
+ <el-main class="m-0 p-0 relative h-full w-full">
+ <div>
+ <div class="absolute top-0 bottom-0 left-0 right-0 overflow-y-hidden p-0 m-0">
+ <!-- 鎯呭喌涓�锛氭秷鎭姞杞戒腑 -->
+ <MessageLoading v-if="activeMessageListLoading" />
+ <!-- 鎯呭喌浜岋細鏃犺亰澶╁璇濇椂 -->
+ <MessageNewConversation
+ v-if="!activeConversation"
+ @on-new-conversation="handleConversationCreate"
+ />
+ <!-- 鎯呭喌涓夛細娑堟伅鍒楄〃涓虹┖ -->
+ <MessageListEmpty
+ v-if="!activeMessageListLoading && messageList.length === 0 && activeConversation"
+ @on-prompt="doSendMessage"
+ />
+ <!-- 鎯呭喌鍥涳細娑堟伅鍒楄〃涓嶄负绌� -->
+ <MessageList
+ v-if="!activeMessageListLoading && messageList.length > 0 && activeConversation"
+ ref="messageRef"
+ :conversation="activeConversation"
+ :list="messageList"
+ @on-delete-success="handleMessageDelete"
+ @on-edit="handleMessageEdit"
+ @on-refresh="handleMessageRefresh"
+ />
+ </div>
+ </div>
+ </el-main>
+
+ <!-- 搴曢儴 -->
+ <el-footer class="flex flex-col !h-auto !p-0">
+ <!-- TODO @鑺嬭壙锛氳繖鍧楄鎯冲姙娉曡縼绉讳笅锛� -->
+ <form
+ class="mt-10px mx-20px mb-20px py-9px px-10px flex flex-col h-auto rounded-10px"
+ style="border: 1px solid var(--el-border-color)"
+ >
+ <textarea
+ class="h-80px border-none box-border resize-none py-0 px-2px overflow-auto focus:outline-none"
+ v-model="prompt"
+ @keydown="handleSendByKeydown"
+ @input="handlePromptInput"
+ @compositionstart="onCompositionstart"
+ @compositionend="onCompositionend"
+ placeholder="闂垜浠讳綍闂...锛圫hift+Enter 鎹㈣锛屾寜涓� Enter 鍙戦�侊級"
+ >
+ </textarea>
+ <div class="flex justify-between pb-0 pt-5px">
+ <div class="flex items-center">
+ <MessageFileUpload v-model="uploadFiles" :limit="5" :max-size="10" class="mr-10px" />
+ <el-switch v-model="enableContext" />
+ <span class="ml-5px mr-15px text-14px text-#8f8f8f">涓婁笅鏂�</span>
+ <el-switch v-model="enableWebSearch" />
+ <span class="ml-5px text-14px text-#8f8f8f">鑱旂綉鎼滅储</span>
+ </div>
+ <el-button
+ type="primary"
+ size="default"
+ @click="handleSendByButton"
+ :loading="conversationInProgress"
+ v-if="conversationInProgress == false"
+ >
+ {{ conversationInProgress ? '杩涜涓�' : '鍙戦��' }}
+ </el-button>
+ <el-button
+ type="danger"
+ size="default"
+ @click="stopStream()"
+ v-if="conversationInProgress == true"
+ >
+ 鍋滄
+ </el-button>
+ </div>
+ </form>
+ </el-footer>
+ </el-container>
+
+ <!-- 鏇存柊瀵硅瘽 Form -->
+ <ConversationUpdateForm
+ ref="conversationUpdateFormRef"
+ @success="handleConversationUpdateSuccess"
+ />
+ </el-container>
+</template>
+
+<script setup lang="ts">
+import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
+import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation'
+import ConversationList from './components/conversation/ConversationList.vue'
+import ConversationUpdateForm from './components/conversation/ConversationUpdateForm.vue'
+import MessageList from './components/message/MessageList.vue'
+import MessageListEmpty from './components/message/MessageListEmpty.vue'
+import MessageLoading from './components/message/MessageLoading.vue'
+import MessageNewConversation from './components/message/MessageNewConversation.vue'
+import MessageFileUpload from './components/message/MessageFileUpload.vue'
+
+/** AI 鑱婂ぉ瀵硅瘽 鍒楄〃 */
+defineOptions({ name: 'AiChat' })
+
+const route = useRoute() // 璺敱
+const message = useMessage() // 娑堟伅寮圭獥
+
+// 鑱婂ぉ瀵硅瘽
+const conversationListRef = ref()
+const activeConversationId = ref<number | null>(null) // 閫変腑鐨勫璇濈紪鍙�
+const activeConversation = ref<ChatConversationVO | null>(null) // 閫変腑鐨� Conversation
+const conversationInProgress = ref(false) // 瀵硅瘽鏄惁姝e湪杩涜涓�傜洰鍓嶅彧鏈夈�愬彂閫併�戞秷鎭椂锛屼細鏇存柊涓� true锛岄伩鍏嶅垏鎹㈠璇濄�佸垹闄ゅ璇濈瓑鎿嶄綔
+
+// 娑堟伅鍒楄〃
+const messageRef = ref()
+const activeMessageList = ref<ChatMessageVO[]>([]) // 閫変腑瀵硅瘽鐨勬秷鎭垪琛�
+const activeMessageListLoading = ref<boolean>(false) // activeMessageList 鏄惁姝e湪鍔犺浇涓�
+const activeMessageListLoadingTimer = ref<any>() // activeMessageListLoading Timer 瀹氭椂鍣ㄣ�傚鏋滃姞杞介�熷害寰堝揩锛屽氨涓嶈繘鍏ュ姞杞戒腑
+// 娑堟伅婊氬姩
+const textSpeed = ref<number>(50) // Typing speed in milliseconds
+const textRoleRunning = ref<boolean>(false) // Typing speed in milliseconds
+
+// 鍙戦�佹秷鎭緭鍏ユ
+const isComposing = ref(false) // 鍒ゆ柇鐢ㄦ埛鏄惁鍦ㄨ緭鍏�
+const conversationInAbortController = ref<any>() // 瀵硅瘽杩涜涓� abort 鎺у埗鍣�(鎺у埗 stream 瀵硅瘽)
+const inputTimeout = ref<any>() // 澶勭悊杈撳叆涓洖杞︾殑瀹氭椂鍣�
+const prompt = ref<string>() // prompt
+const enableContext = ref<boolean>(true) // 鏄惁寮�鍚笂涓嬫枃
+const enableWebSearch = ref<boolean>(false) // 鏄惁寮�鍚仈缃戞悳绱�
+const uploadFiles = ref<string[]>([]) // 涓婁紶鐨勬枃浠� URL 鍒楄〃
+// 鎺ユ敹 Stream 娑堟伅
+const receiveMessageFullText = ref('')
+const receiveMessageDisplayedText = ref('')
+
+// =========== 銆愯亰澶╁璇濄�戠浉鍏� ===========
+
+/** 鑾峰彇瀵硅瘽淇℃伅 */
+const getConversation = async (id: number | null) => {
+ if (!id) {
+ return
+ }
+ const conversation: ChatConversationVO = await ChatConversationApi.getChatConversationMy(id)
+ if (!conversation) {
+ return
+ }
+ activeConversation.value = conversation
+ activeConversationId.value = conversation.id
+}
+
+/**
+ * 鐐瑰嚮鏌愪釜瀵硅瘽
+ *
+ * @param conversation 閫変腑鐨勫璇�
+ * @return 鏄惁鍒囨崲鎴愬姛
+ */
+const handleConversationClick = async (conversation: ChatConversationVO) => {
+ // 瀵硅瘽杩涜涓紝涓嶅厑璁稿垏鎹�
+ if (conversationInProgress.value) {
+ message.alert('瀵硅瘽涓紝涓嶅厑璁稿垏鎹�!')
+ return false
+ }
+
+ // 鏇存柊閫変腑鐨勫璇� id
+ activeConversationId.value = conversation.id
+ activeConversation.value = conversation
+ // 鍒锋柊 message 鍒楄〃
+ await getMessageList()
+ // 婊氬姩搴曢儴
+ scrollToBottom(true)
+ // 娓呯┖杈撳叆妗�
+ prompt.value = ''
+ // 娓呯┖鏂囦欢鍒楄〃
+ uploadFiles.value = []
+ return true
+}
+
+/** 鍒犻櫎鏌愪釜瀵硅瘽*/
+const handlerConversationDelete = async (delConversation: ChatConversationVO) => {
+ // 鍒犻櫎鐨勫璇濆鏋滄槸褰撳墠閫変腑鐨勶紝閭d箞灏遍噸缃�
+ if (activeConversationId.value === delConversation.id) {
+ await handleConversationClear()
+ }
+}
+/** 娓呯┖閫変腑鐨勫璇� */
+const handleConversationClear = async () => {
+ // 瀵硅瘽杩涜涓紝涓嶅厑璁稿垏鎹�
+ if (conversationInProgress.value) {
+ message.alert('瀵硅瘽涓紝涓嶅厑璁稿垏鎹�!')
+ return false
+ }
+ activeConversationId.value = null
+ activeConversation.value = null
+ activeMessageList.value = []
+}
+
+/** 淇敼鑱婂ぉ瀵硅瘽 */
+const conversationUpdateFormRef = ref()
+const openChatConversationUpdateForm = async () => {
+ conversationUpdateFormRef.value.open(activeConversationId.value)
+}
+const handleConversationUpdateSuccess = async () => {
+ // 瀵硅瘽鏇存柊鎴愬姛锛屽埛鏂版渶鏂颁俊鎭�
+ await getConversation(activeConversationId.value)
+}
+
+/** 澶勭悊鑱婂ぉ瀵硅瘽鐨勫垱寤烘垚鍔� */
+const handleConversationCreate = async () => {
+ // 鍒涘缓瀵硅瘽
+ await conversationListRef.value.createConversation()
+}
+/** 澶勭悊鑱婂ぉ瀵硅瘽鐨勫垱寤烘垚鍔� */
+const handleConversationCreateSuccess = async () => {
+ // 鍒涘缓鏂扮殑瀵硅瘽锛屾竻绌鸿緭鍏ユ
+ prompt.value = ''
+ // 娓呯┖鏂囦欢鍒楄〃
+ uploadFiles.value = []
+}
+
+// =========== 銆愭秷鎭垪琛ㄣ�戠浉鍏� ===========
+
+/** 鑾峰彇娑堟伅 message 鍒楄〃 */
+const getMessageList = async () => {
+ try {
+ if (activeConversationId.value === null) {
+ return
+ }
+ // Timer 瀹氭椂鍣紝濡傛灉鍔犺浇閫熷害寰堝揩锛屽氨涓嶈繘鍏ュ姞杞戒腑
+ activeMessageListLoadingTimer.value = setTimeout(() => {
+ activeMessageListLoading.value = true
+ }, 60)
+
+ // 鑾峰彇娑堟伅鍒楄〃
+ activeMessageList.value = await ChatMessageApi.getChatMessageListByConversationId(
+ activeConversationId.value
+ )
+
+ // 婊氬姩鍒版渶涓嬮潰
+ await nextTick()
+ await scrollToBottom()
+ } finally {
+ // time 瀹氭椂鍣紝濡傛灉鍔犺浇閫熷害寰堝揩锛屽氨涓嶈繘鍏ュ姞杞戒腑
+ if (activeMessageListLoadingTimer.value) {
+ clearTimeout(activeMessageListLoadingTimer.value)
+ }
+ // 鍔犺浇缁撴潫
+ activeMessageListLoading.value = false
+ }
+}
+
+/**
+ * 娑堟伅鍒楄〃
+ *
+ * 鍜� {@link #getMessageList()} 鐨勫樊寮傛槸锛屾妸 systemMessage 鑰冭檻杩涘幓
+ */
+const messageList = computed(() => {
+ if (activeMessageList.value.length > 0) {
+ return activeMessageList.value
+ }
+ // 娌℃湁娑堟伅鏃讹紝濡傛灉鏈� systemMessage 鍒欏睍绀哄畠
+ if (activeConversation.value?.systemMessage) {
+ return [
+ {
+ id: 0,
+ conversationId: activeConversation.value.id || 0,
+ type: 'system',
+ userId: '',
+ roleId: '',
+ model: 0,
+ modelId: 0,
+ content: activeConversation.value.systemMessage,
+ tokens: 0,
+ createTime: new Date(),
+ roleAvatar: '',
+ userAvatar: ''
+ } as ChatMessageVO
+ ]
+ }
+ return []
+})
+
+/** 澶勭悊鍒犻櫎 message 娑堟伅 */
+const handleMessageDelete = () => {
+ if (conversationInProgress.value) {
+ message.alert('鍥炵瓟涓紝涓嶈兘鍒犻櫎!')
+ return
+ }
+ // 鍒锋柊 message 鍒楄〃
+ getMessageList()
+}
+
+/** 澶勭悊 message 娓呯┖ */
+const handlerMessageClear = async () => {
+ if (!activeConversationId.value) {
+ return
+ }
+ try {
+ // 纭鎻愮ず
+ await message.delConfirm('纭娓呯┖瀵硅瘽娑堟伅锛�')
+ // 娓呯┖瀵硅瘽
+ await ChatMessageApi.deleteByConversationId(activeConversationId.value)
+ // 鍒锋柊 message 鍒楄〃
+ activeMessageList.value = []
+ } catch {}
+}
+
+/** 鍥炲埌 message 鍒楄〃鐨勯《閮� */
+const handleGoTopMessage = () => {
+ messageRef.value.handlerGoTop()
+}
+
+// =========== 銆愬彂閫佹秷鎭�戠浉鍏� ===========
+
+/** 澶勭悊鏉ヨ嚜 keydown 鐨勫彂閫佹秷鎭� */
+const handleSendByKeydown = async (event) => {
+ // 鍒ゆ柇鐢ㄦ埛鏄惁鍦ㄨ緭鍏�
+ if (isComposing.value) {
+ return
+ }
+ // 杩涜涓笉鍏佽鍙戦��
+ if (conversationInProgress.value) {
+ return
+ }
+ const content = prompt.value?.trim() as string
+ if (event.key === 'Enter') {
+ if (event.shiftKey) {
+ // 鎻掑叆鎹㈣
+ prompt.value += '\r\n'
+ event.preventDefault() // 闃叉榛樿鐨勬崲琛岃涓�
+ } else {
+ // 鍙戦�佹秷鎭�
+ await doSendMessage(content)
+ event.preventDefault() // 闃叉榛樿鐨勬彁浜よ涓�
+ }
+ }
+}
+
+/** 澶勭悊鏉ヨ嚜銆愬彂閫併�戞寜閽殑鍙戦�佹秷鎭� */
+const handleSendByButton = () => {
+ doSendMessage(prompt.value?.trim() as string)
+}
+
+/** 澶勭悊 prompt 杈撳叆鍙樺寲 */
+const handlePromptInput = (event) => {
+ // 闈炶緭鍏ユ硶 杈撳叆璁剧疆涓� true
+ if (!isComposing.value) {
+ // 鍥炶溅 event data 鏄� null
+ if (event.data == null) {
+ return
+ }
+ isComposing.value = true
+ }
+ // 娓呯悊瀹氭椂鍣�
+ if (inputTimeout.value) {
+ clearTimeout(inputTimeout.value)
+ }
+ // 閲嶇疆瀹氭椂鍣�
+ inputTimeout.value = setTimeout(() => {
+ isComposing.value = false
+ }, 400)
+}
+// TODO @鑺嬭壙锛氭槸涓嶆槸鍙互閫氳繃 @keydown.enter銆丂keydown.shift.enter 鏉ュ疄鐜帮紝鍥炶溅鍙戦�併�乻hift+鍥炶溅鎹㈣锛涗富瑕佺湅鐪嬶紝鏄笉鏄彲浠ョ畝鍖� isComposing 鐩稿叧鐨勯�昏緫
+const onCompositionstart = () => {
+ isComposing.value = true
+}
+const onCompositionend = () => {
+ // console.log('杈撳叆缁撴潫...')
+ setTimeout(() => {
+ isComposing.value = false
+ }, 200)
+}
+
+/** 鐪熸鎵ц銆愬彂閫併�戞秷鎭搷浣� */
+const doSendMessage = async (content: string) => {
+ // 鏍¢獙
+ if (content.length < 1) {
+ message.error('鍙戦�佸け璐ワ紝鍘熷洜锛氬唴瀹逛负绌猴紒')
+ return
+ }
+ if (activeConversationId.value == null) {
+ message.error('杩樻病鍒涘缓瀵硅瘽锛屼笉鑳藉彂閫�!')
+ return
+ }
+
+ // 鍑嗗闄勪欢 URL 鏁扮粍
+ const attachmentUrls = [...uploadFiles.value]
+
+ // 娓呯┖杈撳叆妗嗗拰鏂囦欢鍒楄〃
+ prompt.value = ''
+ uploadFiles.value = []
+
+ // 鎵ц鍙戦��
+ await doSendMessageStream({
+ conversationId: activeConversationId.value,
+ content: content,
+ attachmentUrls: attachmentUrls
+ } as ChatMessageVO)
+}
+
+/** 鐪熸鎵ц銆愬彂閫併�戞秷鎭搷浣� */
+const doSendMessageStream = async (userMessage: ChatMessageVO) => {
+ // 鍒涘缓 AbortController 瀹炰緥锛屼互渚夸腑姝㈣姹�
+ conversationInAbortController.value = new AbortController()
+ // 鏍囪瀵硅瘽杩涜涓�
+ conversationInProgress.value = true
+ // 璁剧疆涓虹┖
+ receiveMessageFullText.value = ''
+
+ try {
+ // 1.1 鍏堟坊鍔犱袱涓亣鏁版嵁锛岀瓑 stream 杩斿洖鍐嶆浛鎹�
+ activeMessageList.value.push({
+ id: -1,
+ conversationId: activeConversationId.value,
+ type: 'user',
+ content: userMessage.content,
+ attachmentUrls: userMessage.attachmentUrls || [],
+ createTime: new Date()
+ } as ChatMessageVO)
+ activeMessageList.value.push({
+ id: -2,
+ conversationId: activeConversationId.value,
+ type: 'assistant',
+ content: '鎬濊�冧腑...',
+ reasoningContent: '',
+ createTime: new Date()
+ } as ChatMessageVO)
+ // 1.2 婊氬姩鍒版渶涓嬮潰
+ await nextTick()
+ await scrollToBottom() // 搴曢儴
+ // 1.3 寮�濮嬫粴鍔�
+ textRoll()
+
+ // 2. 鍙戦�� event stream
+ let isFirstChunk = true // 鏄惁鏄涓�涓� chunk 娑堟伅娈�
+ await ChatMessageApi.sendChatMessageStream(
+ userMessage.conversationId,
+ userMessage.content,
+ conversationInAbortController.value,
+ enableContext.value,
+ enableWebSearch.value,
+ async (res) => {
+ const { code, data, msg } = JSON.parse(res.data)
+ if (code !== 0) {
+ message.alert(`瀵硅瘽寮傚父! ${msg}`)
+ // 濡傛灉鏈帴鏀跺埌娑堟伅锛屽垯杩涜鍒犻櫎
+ if (receiveMessageFullText.value === '') {
+ activeMessageList.value.pop()
+ }
+ return
+ }
+
+ // 濡傛灉鍐呭涓虹┖锛屽氨涓嶅鐞嗐��
+ if (data.receive.content === '' && !data.receive.reasoningContent) {
+ return
+ }
+
+ // 棣栨杩斿洖闇�瑕佹坊鍔犱竴涓� message 鍒伴〉闈紝鍚庨潰鐨勯兘鏄洿鏂�
+ if (isFirstChunk) {
+ isFirstChunk = false
+ // 寮瑰嚭涓や釜鍋囨暟鎹�
+ activeMessageList.value.pop()
+ activeMessageList.value.pop()
+ // 鏇存柊杩斿洖鐨勬暟鎹�
+ activeMessageList.value.push(data.send)
+ data.send.attachmentUrls = userMessage.attachmentUrls
+ activeMessageList.value.push(data.receive)
+ }
+
+ // 澶勭悊 reasoningContent
+ if (data.receive.reasoningContent) {
+ const lastMessage = activeMessageList.value[activeMessageList.value.length - 1]
+ lastMessage.reasoningContent =
+ lastMessage.reasoningContent + data.receive.reasoningContent
+ }
+
+ // 澶勭悊姝e父鍐呭
+ if (data.receive.content !== '') {
+ receiveMessageFullText.value = receiveMessageFullText.value + data.receive.content
+ }
+ // 婊氬姩鍒版渶涓嬮潰
+ await scrollToBottom()
+ },
+ (error: any) => {
+ // 寮傚父鎻愮ず锛屽苟鍋滄娴�
+ message.alert(`瀵硅瘽寮傚父锛乣)
+ stopStream()
+ // 闇�瑕佹姏鍑哄紓甯革紝绂佹閲嶈瘯
+ throw error
+ },
+ () => {
+ stopStream()
+ },
+ userMessage.attachmentUrls
+ )
+ } catch {}
+}
+
+/** 鍋滄 stream 娴佸紡璋冪敤 */
+const stopStream = async () => {
+ // tip锛氬鏋� stream 杩涜涓殑 message锛屽氨闇�瑕佽皟鐢� controller 缁撴潫
+ if (conversationInAbortController.value) {
+ conversationInAbortController.value.abort()
+ }
+ // 璁剧疆涓� false
+ conversationInProgress.value = false
+}
+
+/** 缂栬緫 message锛氳缃负 prompt锛屽彲浠ュ啀娆$紪杈� */
+const handleMessageEdit = (message: ChatMessageVO) => {
+ prompt.value = message.content
+}
+
+/** 鍒锋柊 message锛氬熀浜庢寚瀹氭秷鎭紝鍐嶆鍙戣捣瀵硅瘽 */
+const handleMessageRefresh = (message: ChatMessageVO) => {
+ doSendMessage(message.content)
+}
+
+// ============== 銆愭秷鎭粴鍔ㄣ�戠浉鍏� =============
+
+/** 婊氬姩鍒� message 搴曢儴 */
+const scrollToBottom = async (isIgnore?: boolean) => {
+ await nextTick()
+ if (messageRef.value) {
+ messageRef.value.scrollToBottom(isIgnore)
+ }
+}
+
+/** 鑷彁婊氬姩鏁堟灉 */
+const textRoll = async () => {
+ let index = 0
+ try {
+ // 鍙兘鎵ц涓�娆�
+ if (textRoleRunning.value) {
+ return
+ }
+ // 璁剧疆鐘舵��
+ textRoleRunning.value = true
+ receiveMessageDisplayedText.value = ''
+ const task = async () => {
+ // 璋冩暣閫熷害
+ const diff =
+ (receiveMessageFullText.value.length - receiveMessageDisplayedText.value.length) / 10
+ if (diff > 5) {
+ textSpeed.value = 10
+ } else if (diff > 2) {
+ textSpeed.value = 30
+ } else if (diff > 1.5) {
+ textSpeed.value = 50
+ } else {
+ textSpeed.value = 100
+ }
+ // 瀵硅瘽缁撴潫锛屽氨鎸� 30 鐨勯�熷害
+ if (!conversationInProgress.value) {
+ textSpeed.value = 10
+ }
+
+ if (index < receiveMessageFullText.value.length) {
+ receiveMessageDisplayedText.value += receiveMessageFullText.value[index]
+ index++
+
+ // 鏇存柊 message
+ const lastMessage = activeMessageList.value[activeMessageList.value.length - 1]
+ lastMessage.content = receiveMessageDisplayedText.value
+ // 婊氬姩鍒颁綇涓嬮潰
+ await scrollToBottom()
+ // 閲嶆柊璁剧疆浠诲姟
+ timer = setTimeout(task, textSpeed.value)
+ } else {
+ // 涓嶆槸瀵硅瘽涓彲浠ョ粨鏉�
+ if (!conversationInProgress.value) {
+ textRoleRunning.value = false
+ clearTimeout(timer)
+ } else {
+ // 閲嶆柊璁剧疆浠诲姟
+ timer = setTimeout(task, textSpeed.value)
+ }
+ }
+ }
+ let timer = setTimeout(task, textSpeed.value)
+ } catch {}
+}
+
+/** 鍒濆鍖� **/
+onMounted(async () => {
+ // 濡傛灉鏈� conversationId 鍙傛暟锛屽垯榛樿閫変腑
+ if (route.query.conversationId) {
+ const id = route.query.conversationId as unknown as number
+ activeConversationId.value = id
+ await getConversation(id)
+ }
+
+ // 鑾峰彇鍒楄〃鏁版嵁
+ activeMessageListLoading.value = true
+ await getMessageList()
+})
+</script>
--
Gitblit v1.8.0