feat: 消息问答的调整
This commit is contained in:
@@ -15,11 +15,10 @@
|
|||||||
<view class="area-msg-list-content" v-for="item in chatMsgList" :key="item.msgId" :id="item.msgId">
|
<view class="area-msg-list-content" v-for="item in chatMsgList" :key="item.msgId" :id="item.msgId">
|
||||||
<template v-if="item.msgType === MessageRole.AI">
|
<template v-if="item.msgType === MessageRole.AI">
|
||||||
<ChatCardAI class="flex flex-justify-start" :key="`ai-${item.msgId}-${item.msg ? item.msg.length : 0}`"
|
<ChatCardAI class="flex flex-justify-start" :key="`ai-${item.msgId}-${item.msg ? item.msg.length : 0}`"
|
||||||
:text="item.finish && item.componentName ? '' : item.msg || ''" :isLoading="item.isLoading">
|
:text="item.componentName && item.componentName === CompName.longTextCard ? '' : item.msg || ''" :isLoading="item.isLoading">
|
||||||
<template #content v-if="item.toolCall || item.componentName">
|
<template #content v-if="item.toolCall || item.componentName && item.componentName === CompName.longTextCard">
|
||||||
<AnswerComponent v-if="
|
<AnswerComponent v-if=" item.componentName === CompName.longTextCard
|
||||||
item.componentName === CompName.longTextCard
|
" :text="(item.componentMsg || item.msg)" :title="item.title" :finish="item.finish" />
|
||||||
" :text="item.msg" :title="item.title" />
|
|
||||||
<QuickBookingComponent v-if="
|
<QuickBookingComponent v-if="
|
||||||
item.toolCall && item.toolCall.componentName === CompName.quickBookingCard
|
item.toolCall && item.toolCall.componentName === CompName.quickBookingCard
|
||||||
" />
|
" />
|
||||||
@@ -545,11 +544,29 @@ const handleWebSocketMessage = (data) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 防护:确保 aiMsgIndex 有效
|
||||||
|
if (aiMsgIndex < 0 || aiMsgIndex >= chatMsgList.value.length) {
|
||||||
|
console.error('无效的 aiMsgIndex:', aiMsgIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// replyMessageId
|
// replyMessageId
|
||||||
if(data.replyMessageId) {
|
if(data.replyMessageId) {
|
||||||
chatMsgList.value[aiMsgIndex].replyMessageId = data.replyMessageId;
|
chatMsgList.value[aiMsgIndex].replyMessageId = data.replyMessageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果服务端在分片中就带来了 componentName,提前记录并将已缓存的 msg 转移到 componentMsg
|
||||||
|
const aiItem = chatMsgList.value[aiMsgIndex];
|
||||||
|
if (data.componentName) {
|
||||||
|
aiItem.componentName = data.componentName;
|
||||||
|
if (data.componentName === CompName.longTextCard) {
|
||||||
|
if (aiItem.msg && aiItem.msg.length > 0) {
|
||||||
|
aiItem.componentMsg = (aiItem.componentMsg || "") + aiItem.msg;
|
||||||
|
aiItem.msg = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 确保消息内容是字符串类型
|
// 确保消息内容是字符串类型
|
||||||
if (data.content && typeof data.content !== "string") {
|
if (data.content && typeof data.content !== "string") {
|
||||||
try {
|
try {
|
||||||
@@ -561,13 +578,24 @@ const handleWebSocketMessage = (data) => {
|
|||||||
|
|
||||||
// 直接拼接内容到对应 AI 消息
|
// 直接拼接内容到对应 AI 消息
|
||||||
if (data.content) {
|
if (data.content) {
|
||||||
if (chatMsgList.value[aiMsgIndex].isLoading) {
|
// 如果该条消息属于 longTextCard,使用 componentMsg 存储内容并保持 ChatCardAI 的 text 为空
|
||||||
|
const isLongText = aiItem.componentName === CompName.longTextCard || data.componentName === CompName.longTextCard;
|
||||||
|
if (isLongText) {
|
||||||
|
if (aiItem.isLoading) {
|
||||||
|
aiItem.componentMsg = (aiItem.componentMsg || "") + data.content;
|
||||||
|
aiItem.isLoading = false;
|
||||||
|
} else {
|
||||||
|
aiItem.componentMsg = (aiItem.componentMsg || "") + data.content;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (aiItem.isLoading) {
|
||||||
// 首次收到内容:替换“加载中”文案并取消 loading 状态(恢复原始渲染逻辑)
|
// 首次收到内容:替换“加载中”文案并取消 loading 状态(恢复原始渲染逻辑)
|
||||||
chatMsgList.value[aiMsgIndex].msg = data.content;
|
aiItem.msg = data.content;
|
||||||
chatMsgList.value[aiMsgIndex].isLoading = false;
|
aiItem.isLoading = false;
|
||||||
} else {
|
} else {
|
||||||
// 后续流式内容追加
|
// 后续流式内容追加
|
||||||
chatMsgList.value[aiMsgIndex].msg += data.content;
|
aiItem.msg += data.content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nextTick(() => scrollToBottom());
|
nextTick(() => scrollToBottom());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,25 @@
|
|||||||
<view class="w-full bg-white border-box border-ff overflow-hidden rounded-20 flex flex-col">
|
<view class="w-full bg-white border-box border-ff overflow-hidden rounded-20 flex flex-col">
|
||||||
<!-- 占位撑开 -->
|
<!-- 占位撑开 -->
|
||||||
<view class="w-vw"></view>
|
<view class="w-vw"></view>
|
||||||
<view class="flex flex-col p-16 border-box">
|
<view class="flex flex-col p-16 border-box border-left-4">
|
||||||
<view class="flex flex-row flex-items-start flex-justify-start">
|
<view v-if="title" class="flex flex-row flex-items-start flex-justify-start mb-8">
|
||||||
<uni-icons class="icon-active" type="fire-filled" size="18" color="opacity" />
|
<uni-icons class="icon-active" type="fire-filled" size="18" color="opacity" />
|
||||||
<text class="font-size-16 font-500 text-color-900 ml-6">游玩划重点</text>
|
<text class="font-size-16 font-500 text-color-900 ml-6"> {{ title }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 文字内容,最多显示3行 -->
|
<!-- 文字内容,最多显示3行 -->
|
||||||
<view class="answer-content font-size-12 font-color-600 mt-8">
|
<view class="answer-content font-size-12 font-color-600">
|
||||||
<ChatMarkdown :key="textKey" :text="processedText" />
|
<ChatMarkdown :key="textKey" :text="processedText" />
|
||||||
</view>
|
</view>
|
||||||
<!-- 超过3行时显示...提示 -->
|
<!-- 超过3行时显示...提示 -->
|
||||||
<view class="flex flex-row flex-items-center mt-8" v-if="isOverflow" @click="lookDetailAction">
|
<view v-if="!finish" class="flex flex-row flex-items-center mt-8">
|
||||||
|
<text class="font-size-12 font-400 font-color-600">正在生成</text>
|
||||||
|
<DotLoading />
|
||||||
|
</view>
|
||||||
|
<view v-if="isOverflow && finish" class="flex flex-row flex-items-center mt-8" @click="lookDetailAction">
|
||||||
<text class="font-size-12 font-400 theme-color-500 mr-4">查看详情</text>
|
<text class="font-size-12 font-400 theme-color-500 mr-4">查看详情</text>
|
||||||
<uni-icons class="icon-active" type="right" size="14" color="opacity"></uni-icons>
|
<uni-icons class="icon-active" type="right" size="14" color="opacity"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -25,6 +30,7 @@
|
|||||||
import { defineProps, computed, ref, watch } from "vue";
|
import { defineProps, computed, ref, watch } from "vue";
|
||||||
|
|
||||||
import ChatMarkdown from "../../chat/ChatMarkdown/index.vue";
|
import ChatMarkdown from "../../chat/ChatMarkdown/index.vue";
|
||||||
|
import DotLoading from "../../loading/DotLoading.vue";
|
||||||
|
|
||||||
const isOverflow = ref(false)
|
const isOverflow = ref(false)
|
||||||
|
|
||||||
@@ -37,7 +43,11 @@ const props = defineProps({
|
|||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
}
|
},
|
||||||
|
finish: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 用于强制重新渲染的key
|
// 用于强制重新渲染的key
|
||||||
@@ -102,5 +112,9 @@ const lookDetailAction = () => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
|
max-height: 80px;
|
||||||
|
}
|
||||||
|
.border-left-4 {
|
||||||
|
border-left: 4px solid $theme-color-500;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="bg-F5F7FA flex flex-col h-screen overflow-hidden">
|
<view class="bg-F5F7FA flex flex-col h-screen overflow-hidden">
|
||||||
<TopNavBar title="详情" backgroundColor="transparent" />
|
<TopNavBar :title="title" backgroundColor="transparent" />
|
||||||
<view class="flex-full overflow-hidden scroll-y p-12 border-box">
|
<view class="flex-full overflow-hidden scroll-y p-12 border-box">
|
||||||
<ChatMarkdown :text="answerText" />
|
<ChatMarkdown :text="answerText" />
|
||||||
</view>
|
</view>
|
||||||
@@ -20,9 +20,13 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const answerText = ref(props.answerText || "");
|
const answerText = ref(props.answerText || "");
|
||||||
|
const title = ref("");
|
||||||
|
|
||||||
onLoad(({ message = "" }) => {
|
onLoad(({ message = "" }) => {
|
||||||
answerText.value = decodeURIComponent(message);
|
answerText.value = decodeURIComponent(message);
|
||||||
|
if (answerText.value.length > 6) {
|
||||||
|
title.value = answerText.value.substring(0, 6) + "...";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
Reference in New Issue
Block a user