feat: add new features, update theme and build config

- Add 40+ new UI components including chat modules, discovery cards, photo galleries, FAQ and booking tools
- Standardize brand color across all styles by replacing $theme-color-500 SCSS variables with #0ccd58
- Add sass 1.58.3 dependency and update vite config for modern scss compiler support
- Refactor existing components (AddCarCrad, login page) and remove unused /quick/list router route
- Add utility functions for URL parameter handling
- Add static assets including custom znicons font, component images and icons
- Fix scss syntax issues and deprecation warnings
This commit is contained in:
duanshuwen
2026-05-26 22:49:52 +08:00
parent 548df7020c
commit ac8f5b5f64
159 changed files with 12439 additions and 629 deletions

View File

@@ -0,0 +1,198 @@
<template>
<div class="w-full bg-white border-box border-ff overflow-hidden rounded-20 flex flex-col">
<!-- 占位撑开 -->
<div class="w-vw"></div>
<div class="flex flex-col px-16 pt-16 pb-12 border-box">
<div v-if="tag" class="long-answer-tag">{{ tag }}</div>
<div v-if="title" class="flex flex-row flex-items-start flex-justify-start mb-4">
<uni-icons class="icon-active" type="fire-filled" size="18" color="opacity" />
<span class="font-size-16 font-500 span-color-900 ml-6"> {{ title }}</span>
</div>
<!-- 文字内容最多显示3行 -->
<div v-if="processedContent" class="answer-content font-size-12 font-color-600">
<ChatMarkdown :text="processedContent" />
</div>
<!-- 超过3行时显示...提示 -->
<div v-if="!finish" class="flex flex-row flex-items-center mt-8">
<span class="font-size-12 font-400 font-color-600">正在生成</span>
<ChatLoading />
</div>
<div v-if="isOverflow" class="flex flex-row flex-items-center flex-justify-between mt-4"
@click="lookDetailAction">
<span class="font-size-12 font-400 theme-color-500 mr-4">查看完整{{ tag }}</span>
<uni-icons class="icon-active" type="right" size="14" color="opacity"></uni-icons>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, computed, watch, onBeforeUnmount } from "vue";
import ChatMarkdown from "./ChatMarkdown/index.vue";
import ChatLoading from "./ChatLoading/index.vue";
import StreamManager from '@/utils/StreamManager.js';
import {
getLongTextPredivText,
getLongTextValue,
hasLongTextExtraSections,
} from "@/utils/longTextCard";
// 直接根据文字长度判断超过约100个字符认为会溢出约3行
const props = defineProps({
content: {
type: String,
default: "",
},
longTextData: {
type: Object,
default: null,
},
finish: {
type: Boolean,
default: false,
},
});
const tag = computed(() => getLongTextValue(props.longTextData, "tag"));
const title = computed(() => getLongTextValue(props.longTextData, "title"));
const predivContent = computed(() => {
return getLongTextPredivText(props.longTextData, ["content_summary"]);
});
// 处理文本内容:按行截断以保证预览最多显示三行(更贴近视觉行数)
// 点击“查看详情”会跳转到完整页面(不受预览截断影响)。
const PREdiv_LINES = 3;
const PREdiv_CHAR_LIMIT = 100; // 作为备用,当没有换行但过长时也会截断
const processedContent = computed(() => {
const content = predivContent.value ? String(predivContent.value) : "";
if (!content) return "";
// 按行分割(保留空行)
const lines = content.split(/\r?\n/);
// 如果行数超过限制,截取前 PREdiv_LINES 行并添加省略号
if (lines.length > PREdiv_LINES) {
return lines.slice(0, PREdiv_LINES).join("\n") + "...";
}
// 若虽然行数不超过,但总长度仍然很长,做字符级截断作为兜底
if (content.length > PREdiv_CHAR_LIMIT) {
return content.slice(0, PREdiv_CHAR_LIMIT) + "...";
}
return content;
});
const isOverflow = computed(() => {
const contentStr = predivContent.value ? String(predivContent.value) : "";
const lines = contentStr.split(/\r?\n/);
return (
hasLongTextExtraSections(props.longTextData) ||
lines.length > PREdiv_LINES ||
contentStr.length > PREdiv_CHAR_LIMIT
);
});
let stopForwardWatcher = null;
let stopFinishWatcher = null;
let stopLongTextWatcher = null;
const cleanupStreamWatchers = () => {
if (stopForwardWatcher) {
stopForwardWatcher();
stopForwardWatcher = null;
}
if (stopFinishWatcher) {
stopFinishWatcher();
stopFinishWatcher = null;
}
if (stopLongTextWatcher) {
stopLongTextWatcher();
stopLongTextWatcher = null;
}
};
onBeforeUnmount(cleanupStreamWatchers);
const lookDetailAction = () => {
const message = predivContent.value ? String(predivContent.value) : "";
// 使用 StreamManager 以 streamId 转发当前及后续流式更新,详情页通过 streamId 订阅
const streamId = `stream_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
const updateStream = () => {
StreamManager.updateStream(
streamId,
predivContent.value ? String(predivContent.value) : "",
!!props.finish,
props.longTextData || null,
);
};
StreamManager.openStream(streamId, message, !!props.finish, props.longTextData || null);
cleanupStreamWatchers();
if (!props.finish) {
// 将当前组件后续 props.content/props.finish 的更新转发到 StreamManager
stopForwardWatcher = watch(
() => props.content,
updateStream
);
stopLongTextWatcher = watch(
() => props.longTextData,
updateStream,
{ deep: true }
);
stopFinishWatcher = watch(
() => props.finish,
(f) => {
StreamManager.updateStream(
streamId,
predivContent.value ? String(predivContent.value) : "",
!!f,
props.longTextData || null,
);
if (f) {
cleanupStreamWatchers();
}
}
);
}
// 传递 finished 参数,完成状态下不自动滚到底部
uni.navigateTo({ url: `/pages/ChatMain/ChatLongAnswer/index?streamId=${encodeURIComponent(streamId)}&finished=${props.finish ? '1' : '0'}` });
}
</script>
<style scoped lang="scss">
.icon-active {
margin-top: 1px;
color: #0CCD58;
}
.answer-content {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
line-height: 16px;
max-height: 80px;
}
.long-answer-tag {
display: inline-flex;
width: fit-content;
margin-bottom: 8px;
padding: 3px 8px;
border-radius: 12px;
border: 1px solid rgba(#0CCD58, 0.2);
background: rgba(#0CCD58, 0.08);
color: #0CCD58;
font-size: 12px;
line-height: 18px;
}
</style>