From 92d2ea48d343fc00d81905167d033c40200ea716 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期二, 04 三月 2025 15:56:19 +0800 Subject: [PATCH] 快速引用;去重 --- src/components/chat/smallChat/index.vue | 207 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 189 insertions(+), 18 deletions(-) diff --git a/src/components/chat/smallChat/index.vue b/src/components/chat/smallChat/index.vue index f8b0821..d19dd9c 100644 --- a/src/components/chat/smallChat/index.vue +++ b/src/components/chat/smallChat/index.vue @@ -92,7 +92,7 @@ <template v-else> <span v-if="(historyMessages[index+1].content as AssistantContent).isError" - class="flex items-center ml-4 text-danger before:content-['('] after:content-[')']" + class="flex items-center text-nowrap ml-4 text-danger before:content-['('] after:content-[')']" > {{ (historyMessages[index + 1].content as AssistantContent).value }} <el-tooltip @@ -105,7 +105,7 @@ </el-icon> </el-tooltip> </span> - <span v-else class="ml-4 text-success before:content-['('] after:content-[')']"> + <span v-else class="ml-4 text-success text-nowrap before:content-['('] after:content-[')']"> {{ (historyMessages[index + 1].content as AssistantContent).value }} </span> </template> @@ -134,25 +134,40 @@ <!-- 搴曢儴杈撳叆妗� --> <div class="p-2 border-t"> - <ChatInput v-model="inputText" @sendClick="sendClick" @toggleHistory="toggleHistory" :showHistory="showHistory" /> + <ChatInput + :isTalking="lastIsLoading" + v-model="inputText" + @sendClick="sendClick" + @toggleHistory="toggleHistory" + @stopGenClick="stopGenClick" + :showHistory="showHistory" + /> </div> </div> + <Teleport to="body"> + <WorkOrderDlg v-model="optDlgIsShow" :item="optDlgMapRow" @insert="submitDlg" @cancelSubmit="cancelSubmit"></WorkOrderDlg> + </Teleport> </div> </template> <script setup lang="ts" name="smallChat"> +import type { CancelTokenSource } from 'axios'; +import axios from 'axios'; +import { cloneDeep, defaults } from 'lodash-es'; +import { fromLonLat } from 'ol/proj'; import { computed, nextTick, onMounted, ref } from 'vue'; import ChatInput from './ChatInput.vue'; import type { ChatMessage } from './types'; import { AssistantContent } from './types'; +import WorkOrderDlg from './WorkOrderDlg.vue'; import { agentStreamByPost } from '/@/api/ai/chat'; +import { getSearchMapElement } from '/@/api/map'; +import { useDrag } from '/@/hooks/useDrag'; import { Logger } from '/@/model/logger/Logger'; import { GaoDeSourceType, gaoDeSourceTypeMap, type OLMap } from '/@/model/map/OLMap'; -import assistantPic from '/static/images/role/assistant-200x192.png'; +import { systemGlobalConfig } from '/@/stores/global'; +import { formatDate } from '/@/utils/formatTime'; import userPic from '/static/images/role/user-200x206.png'; -import { useDrag } from '/@/hooks/useDrag'; -import { cloneDeep, defaults } from 'lodash-es'; - const props = defineProps<{ olMap?: OLMap; }>(); @@ -177,7 +192,12 @@ x: 200, }, }); - +const cancelSubmit = (reason) => { + refreshAssistantMessage({ reason: reason }); +}; +const submitDlg = () => { + refreshAssistantMessage({ value: `鎴愬姛`, isError: false }); +}; const historyMessages = ref<ChatMessage[]>([]); const isInit = computed(() => historyMessages.value.length === 0); const initQuestionList = ref([ @@ -185,14 +205,32 @@ { title: '鍦板浘缂╂斁', question: '鏀惧ぇ' }, { title: '璁惧鏄鹃殣', question: '闅愯棌璁惧' }, { title: '璁惧鑱氱劍', question: '鑱氱劍璁惧' }, + { title: '鍒涘缓宸ュ崟', question: '鏉剧澶ч亾DN800鏉惧矖鑱旈�氱洃娴嬭澶囨病鏈夋暟鎹紝鍒涘缓涓�涓澶囩淮淇伐鍗曪紝璇峰強鏃舵淳浜虹淮淇��' }, ]); const chatContentRef = ref<HTMLDivElement>(null); - const getLastAssistantMessage = () => { const last = historyMessages.value[historyMessages.value.length - 1]; - const result = last.role === 'assistant' ? last : null; + const result = last?.role === 'assistant' ? last : null; return result as ChatMessage<AssistantContent>; }; + +const lastIsLoading = computed(() => { + const last = getLastAssistantMessage(); + const loading = last?.content?.isLoading ?? false; + return loading; +}); + +//#region ====================== 娣诲姞宸ュ崟 ====================== +const optDlgIsShow = ref(false); +const optDlgMapRow = ref(null); + +const openOptDlg = (row?: any) => { + optDlgMapRow.value = row; + optDlgIsShow.value = true; +}; + +const submit = () => {}; +//#endregion const mockCommand = (question: string) => { if (question === `鍒囨崲${gaoDeSourceTypeMap[GaoDeSourceType.Vector]}`) { @@ -216,10 +254,91 @@ } }; +const handleCreateWorkOrder = (formData: any) => { + openOptDlg(formData ?? {}); +}; + +const handleSwitchLayer = (formData: { layerId: string; visible: boolean }) => { + props.olMap.setLayerVisible(formData.layerId, formData.visible); + refreshAssistantMessage({ value: `鎴愬姛`, isError: false }); +}; + +const changeTheme = (formData: { themeId: string }) => { + props.olMap.setThemeById(formData.themeId); + refreshAssistantMessage({ value: `鎴愬姛`, isError: false }); +}; + +const handleQueryObject = async (formData: { objectName: string }) => { + const res = await getSearchMapElement({ + search_text: formData.objectName, + max_count: 10, + time: formatDate(new Date()), + }); + const result = res?.values ?? []; + props.olMap.clearObjectSearch(); + const features = []; + for (const item of result) { + if (!item.WKT) continue; + const feature = props.olMap.readWKT(item.WKT); + features.push(feature); + } + props.olMap.zoomToFeatures(features); + props.olMap.highlightSearch(features); + refreshAssistantMessage({ value: `鎴愬姛`, isError: false }); +}; + +const handleSearchAddress = async (formData: { address: string }) => { + const result = await props.olMap.gaodeAddressSearch(formData.address, systemGlobalConfig.value['ui.project_city']); + const pois = result?.pois ?? []; + const searchResultList = + pois + .map((item) => { + return { + name: item.name, + // address: item.address, + cityname: item.cityname, + adname: item.adname, + model: item, + isSearchObj: false, + location: item.location, + }; + }) + ?.slice(0, 5) ?? []; + + const features = []; + for (const item of searchResultList) { + if (!item.location) continue; + const [lon, lat] = item.location.split(',').map(Number); + const point = fromLonLat([lon, lat]); + const WKT = `SRID=3857;POINT(${point[0]} ${point[1]})`; + const feature = props.olMap.readWKT(WKT); + features.push(feature); + } + props.olMap.highlightSearch(features); + props.olMap.zoomToFeatures(features); + refreshAssistantMessage({ value: `鎴愬姛`, isError: false }); +}; + +const handleSetBackgroundLayer = (formData: { LayerId: string }) => { + if (!formData.LayerId) return; + props.olMap.setSourceType(formData.LayerId as GaoDeSourceType); + refreshAssistantMessage({ value: `鎴愬姛`, isError: false }); +}; +let lastAxiosSource: CancelTokenSource = null; + const startStream = (question: string) => { if (lastIsInit) { showHistory.value = false; } + + const currentSource = axios.CancelToken.source(); + lastAxiosSource = currentSource; + // if (question === '鏉剧澶ч亾DN800鏉惧矖鑱旈�氱洃娴嬭澶囨病鏈夋暟鎹紝鍒涘缓涓�涓澶囩淮淇伐鍗曪紝璇峰強鏃舵淳浜虹淮淇��') { + // setTimeout(() => { + // openOptDlg(); + // }, 400); + // return; + // } // mockCommand(question); // return; @@ -232,18 +351,57 @@ (chunkRes) => { Logger.info('agent stream response锛歕n\n' + JSON.stringify(chunkRes)); - if (chunkRes.mode === 'map') { - haveMapOperate = true; - if (chunkRes.type === 'string') { - const jsonData = JSON.parse(chunkRes.value); - handleMapCommand(jsonData); + if ( + chunkRes.type === 'string' && + ['create_work_order', 'switch_layers', 'switch_topic', 'query_address', 'query_object', 'map'].includes(chunkRes.mode) + ) { + const jsonData = JSON.parse(chunkRes.value); + + switch (chunkRes.mode) { + case 'create_work_order': + haveMapOperate = true; + handleCreateWorkOrder(jsonData); + break; + + case 'switch_layers': + haveMapOperate = true; + handleSwitchLayer(jsonData); + break; + + case 'switch_topic': + haveMapOperate = true; + changeTheme(jsonData); + break; + + case 'switch_background_layers': + haveMapOperate = true; + handleSetBackgroundLayer(jsonData); + break; + + case 'query_address': + haveMapOperate = true; + handleSearchAddress(jsonData); + break; + + case 'query_object': + haveMapOperate = true; + handleQueryObject(jsonData); + break; + case 'map': + haveMapOperate = true; + handleMapCommand(jsonData); + break; } } + if (chunkRes.mode === 'finish') { if (!haveMapOperate) { refreshAssistantMessage({ reason: `鏈瘑鍒埌鎿嶄綔锛�"${question}"` }); } } + }, + { + cancelToken: currentSource.token, } ).catch((error) => { Logger.error('agent stream error锛歕n\n' + error); @@ -273,6 +431,7 @@ const handleMapCommand = (command: any) => { if (!command) return; + // openOptDlg(); switch (command.operate) { case '鏀惧ぇ': props.olMap.zoomIn(); @@ -297,7 +456,8 @@ if (equipOverlay) { equipOverlay.isVisible = true; // 寮哄埗瑙﹀彂鏇存柊 - props.olMap.layerInfo.value = props.olMap.layerInfo.value.concat([]) + props.olMap.layerInfo.value = props.olMap.layerInfo.value.concat([]); + // props.olMap.toggleMarkerOverlayVisible(true); } break; case '闅愯棌璁惧': @@ -305,8 +465,8 @@ if (equipOverlay1) { equipOverlay1.isVisible = false; // 寮哄埗瑙﹀彂鏇存柊 - props.olMap.layerInfo.value = props.olMap.layerInfo.value.concat([]) - + props.olMap.layerInfo.value = props.olMap.layerInfo.value.concat([]); + // props.olMap.toggleMarkerOverlayVisible(false); } break; case '鑱氱劍璁惧': @@ -378,6 +538,17 @@ }); }; //#endregion + +//#region ====================== 娴佸仠姝� ====================== +const stopGenClick = () => { + lastAxiosSource?.cancel(); + const last = getLastAssistantMessage(); + if (!last) return; + last.content.isLoading = false; + last.content.isError = true; + last.content.reason = '鐢ㄦ埛鍋滄鎿嶄綔'; +}; +//#endregion onMounted(() => {}); </script> -- Gitblit v1.9.3