Files
YGChatCS/src/pages/index/components/module/AnswerComponent/index.vue

149 lines
4.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="w-full bg-white border-box border-ff overflow-hidden rounded-20 flex flex-col">
<!-- 占位撑开 -->
<view class="w-vw"></view>
<view class="flex flex-col p-16 border-box border-left-4">
<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" />
<text class="font-size-16 font-500 text-color-900 ml-6"> {{ title }}</text>
</view>
<!-- 文字内容最多显示3行 -->
<view class="answer-content font-size-12 font-color-600">
<ChatMarkdown :key="textKey" :text="processedText" />
</view>
<!-- 超过3行时显示...提示 -->
<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" class="flex flex-row flex-items-center mt-8" @click="lookDetailAction">
<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>
</view>
</view>
</view>
</template>
<script setup>
import { defineProps, computed, ref, watch, onBeforeUnmount } from "vue";
import ChatMarkdown from "../../chat/ChatMarkdown/index.vue";
import DotLoading from "../../loading/DotLoading.vue";
import StreamManager from '@/utils/StreamManager.js';
const isOverflow = ref(false)
// 直接根据文字长度判断超过约100个字符认为会溢出约3行
const props = defineProps({
title: {
type: String,
default: "",
},
text: {
type: String,
default: "",
},
finish: {
type: Boolean,
default: false,
},
});
// 用于强制重新渲染的key
const textKey = ref(0);
// 处理文本内容:按行截断以保证预览最多显示三行(更贴近视觉行数)
// 点击“查看详情”会跳转到完整页面(不受预览截断影响)。
const PREVIEW_LINES = 3;
const PREVIEW_CHAR_LIMIT = 100; // 作为备用,当没有换行但过长时也会截断
const processedText = computed(() => {
const txt = props.text ? String(props.text) : "";
if (!txt) return "";
// 按行分割(保留空行)
const lines = txt.split(/\r?\n/);
// 如果行数超过限制,截取前 PREVIEW_LINES 行并添加省略号
if (lines.length > PREVIEW_LINES) {
return lines.slice(0, PREVIEW_LINES).join("\n") + "...";
}
// 若虽然行数不超过,但总长度仍然很长,做字符级截断作为兜底
if (txt.length > PREVIEW_CHAR_LIMIT) {
return txt.slice(0, PREVIEW_CHAR_LIMIT) + "...";
}
return txt;
});
// 监听 text 变化:更新 textKey 并同步 isOverflow合并为单一响应函数避免冗余
watch(
() => props.text,
(newText, oldText) => {
const textStr = newText ? String(newText) : "";
const lines = textStr.split(/\r?\n/);
isOverflow.value = lines.length > PREVIEW_LINES || textStr.length > PREVIEW_CHAR_LIMIT;
if (newText !== oldText) {
textKey.value++;
}
},
{ immediate: true }
);
const lookDetailAction = () => {
const message = props.text ? String(props.text) : "";
// 使用 StreamManager 以 streamId 转发当前及后续流式更新,详情页通过 streamId 订阅
const streamId = `stream_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;
StreamManager.openStream(streamId, message, !!props.finish);
// 将当前组件后续 props.text/props.finish 的更新转发到 StreamManager
const stopForward = watch(
() => props.text,
(v) => {
StreamManager.updateStream(streamId, v ? String(v) : "", !!props.finish);
}
);
const stopFinishWatcher = watch(
() => props.finish,
(f) => {
StreamManager.updateStream(streamId, props.text ? String(props.text) : "", !!f);
if (f) {
stopForward();
stopFinishWatcher();
}
}
);
// 清理:组件卸载时停止转发(若仍存在)
onBeforeUnmount(() => {
try {
stopForward && stopForward();
stopFinishWatcher && stopFinishWatcher();
} catch (e) {}
});
uni.navigateTo({ url: `/pages/long-answer/index?streamId=${encodeURIComponent(streamId)}` });
}
</script>
<style scoped lang="scss">
.icon-active {
margin-top: 1px;
color: $theme-color-500;
}
.answer-content {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
line-height: 16px;
max-height: 80px;
}
.border-left-4 {
border-left: 4px solid $theme-color-500;
}
</style>