chore: initialize qiming workspace repository
This commit is contained in:
@@ -0,0 +1,506 @@
|
||||
<template>
|
||||
<view class="msg-root">
|
||||
<text v-if="msg.error_msg != null" class="error-msg">
|
||||
{{ msg.error_msg }}
|
||||
</text>
|
||||
<template v-else>
|
||||
<!-- AI 思考模式 -->
|
||||
<view class="think-container" v-if="!!msg.thinkContent || !!msg.think">
|
||||
<view class="think-header">
|
||||
<view class="think-header-left" @tap="changeThinkContent">
|
||||
<text class="iconfont icon-BulbOutlined thinking-icon"></text>
|
||||
<text class="thinking-title">
|
||||
{{
|
||||
isThinkingFinished
|
||||
? t("Mobile.Chat.thought")
|
||||
: t("Mobile.Chat.thinking")
|
||||
}}
|
||||
</text>
|
||||
<text
|
||||
class="iconfont icon-a-Chevrondown expand-icon"
|
||||
:class="{ 'is-expanded': !hideThinkContent }"
|
||||
></text>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="think-content"
|
||||
:class="{ 'hide-think-content': hideThinkContent }"
|
||||
>
|
||||
<view class="text-box">
|
||||
<mp-html
|
||||
:content="msg.thinkContent || msg.think"
|
||||
:markdown="true"
|
||||
:selectable="true"
|
||||
:container-style="`font-size: 28rpx; color: #888; line-height: 1.6;`"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- AI 答复内容 -->
|
||||
<view class="answer-container">
|
||||
<!-- AI校验通过后,需要水平滚动表格时,需要设置 scroll-table="true" -->
|
||||
<mp-html
|
||||
:content="processedText"
|
||||
:processing-list="msg.processingList"
|
||||
:conversation-id="msg.chat_id"
|
||||
:markdown="true"
|
||||
:selectable="true"
|
||||
:container-style="`width: 100%;`"
|
||||
:tag-style="tagStyle"
|
||||
:scroll-table="true"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
<image
|
||||
v-if="
|
||||
isConversationActive && isLastMsg && taskStatus !== TaskStatus.EXECUTING
|
||||
"
|
||||
class="icon-loading-image"
|
||||
src="@/static/assets/icon_loading.svg"
|
||||
:show-menu-by-longpress="false"
|
||||
@longtap.stop.prevent
|
||||
@contextmenu.stop.prevent="true"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<msg-tool-bar
|
||||
v-if="
|
||||
((!isConversationActive || !isLastMsg) && (msg.body || msg.text)) ||
|
||||
msg.state === 3
|
||||
"
|
||||
:msg="msg"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="uts" setup>
|
||||
import { MsgItem } from "@/uni_modules/uni-ai-x/sdk";
|
||||
import msgToolBar from "@/uni_modules/uni-ai-x/components/msg-tool-bar.uvue";
|
||||
import mpHtml from "@/subpackages/uni_modules/mp-html/components/mp-html/mp-html.vue";
|
||||
import {
|
||||
replaceMathBracket,
|
||||
groupMarkdownContainers,
|
||||
} from "@/utils/markdown.uts";
|
||||
import { TaskStatus } from "@/types/enums/agent";
|
||||
import { useI18n } from "@/utils/i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
msg: MsgItem;
|
||||
// 会话是否正在进行中(有消息正在处理)
|
||||
isConversationActive?: boolean;
|
||||
taskStatus?: TaskStatus;
|
||||
// 是否是最后一条消息
|
||||
isLastMsg?: boolean;
|
||||
}>();
|
||||
|
||||
const msg = ref<MsgItem>(props.msg as MsgItem); // 转化内部状态为响应式
|
||||
const isLastMsg = computed(() => props.isLastMsg);
|
||||
const isConversationActive = computed(() => props.isConversationActive);
|
||||
|
||||
// 思考内容是否隐藏(默认隐藏)
|
||||
const hideThinkContent = ref<boolean>(true);
|
||||
|
||||
// 思考是否已完成:如果正文已经有内容,或者会话已结束,或者不是最后一条消息,则认为思考已完成
|
||||
const isThinkingFinished = computed((): boolean => {
|
||||
return (
|
||||
msg.value.body.trim().length > 0 ||
|
||||
!props.isConversationActive ||
|
||||
!props.isLastMsg
|
||||
);
|
||||
});
|
||||
|
||||
// 标签自定义样式,解决长文本换行问题
|
||||
const tagStyle = {
|
||||
p: "word-break: break-all; overflow-wrap: break-word;",
|
||||
li: "word-break: break-all; overflow-wrap: break-word;",
|
||||
div: "word-break: break-all; overflow-wrap: break-word;",
|
||||
span: "word-break: break-all; overflow-wrap: break-word;",
|
||||
};
|
||||
|
||||
const emit = defineEmits<{
|
||||
changeThinkContent: [hideThinkContent: boolean];
|
||||
}>();
|
||||
|
||||
const getRenderKey = (...args: any[]) => {
|
||||
return msg.value.chat_id + "-" + args.join("-");
|
||||
};
|
||||
|
||||
const genImageHtml = (src: string) => {
|
||||
return `<img src="${src}" />`;
|
||||
};
|
||||
|
||||
const changeThinkContent = () => {
|
||||
hideThinkContent.value = !hideThinkContent.value;
|
||||
emit("changeThinkContent", hideThinkContent.value);
|
||||
};
|
||||
|
||||
// 添加updateMessage方法,支持动态更新消息内容
|
||||
const updateMessage = (newData: any) => {
|
||||
try {
|
||||
// 使用响应式数据而不是直接操作props
|
||||
if (newData) {
|
||||
const updatedMsg = {
|
||||
...msg.value,
|
||||
...newData,
|
||||
};
|
||||
msg.value = updatedMsg;
|
||||
// console.log('更新消息成功:', newData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("updateMessage 执行失败:", error);
|
||||
}
|
||||
};
|
||||
// 处理文本内容,应用数学公式格式转换及组件合并
|
||||
const processedText = computed(() => {
|
||||
if (!msg.value || (msg.value.body || "").trim().length === 0) {
|
||||
return "";
|
||||
}
|
||||
const groupedText = groupMarkdownContainers(msg.value.body);
|
||||
return replaceMathBracket(groupedText);
|
||||
});
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
updateMessage,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.msg-root {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
// #ifdef WEB || MP-WEIXIN
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
// #endif
|
||||
padding: 0 32rpx;
|
||||
min-height: 90rpx;
|
||||
|
||||
.think-container {
|
||||
width: 100%;
|
||||
// margin-bottom: 24rpx;
|
||||
|
||||
.think-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 8rpx 0;
|
||||
|
||||
.think-header-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.thinking-icon {
|
||||
font-size: 36rpx;
|
||||
color: #999;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.thinking-title {
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
margin-left: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&.is-expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.think-content {
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
padding-top: 8rpx;
|
||||
|
||||
&.hide-think-content {
|
||||
height: 0;
|
||||
padding-top: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.text-box {
|
||||
border-left: 4rpx solid #efefef;
|
||||
padding-left: 20rpx;
|
||||
background-color: rgba(245, 245, 245, 0.3);
|
||||
border-radius: 0 8rpx 8rpx 0;
|
||||
padding-top: 8rpx;
|
||||
padding-bottom: 8rpx;
|
||||
|
||||
.text {
|
||||
font-size: 28rpx;
|
||||
color: #888;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.answer-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
color: #ff4d4f;
|
||||
font-size: 28rpx;
|
||||
line-height: 52rpx;
|
||||
}
|
||||
|
||||
.loging-icon {
|
||||
margin-top: 16rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
.stop-text {
|
||||
color: #aaa;
|
||||
font-size: 28rpx;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
.need-try {
|
||||
color: #ff4d4f;
|
||||
font-size: 28rpx;
|
||||
padding: 16rpx;
|
||||
}
|
||||
.error-msg,
|
||||
.stop-text,
|
||||
.need-try {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
color: #15171f;
|
||||
font-weight: 400;
|
||||
line-height: 56rpx;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
overflow-wrap: anywhere;
|
||||
max-width: 100%;
|
||||
}
|
||||
.strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.del {
|
||||
text-decoration-line: line-through;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.space {
|
||||
height: 10px;
|
||||
}
|
||||
.link {
|
||||
color: #0066cc !important;
|
||||
word-break: break-all;
|
||||
overflow-wrap: anywhere;
|
||||
// #ifdef WEB
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
.paragraph-box {
|
||||
display: block;
|
||||
.text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 400;
|
||||
line-height: 56rpx;
|
||||
// 自动换行并显示 \n
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
overflow-wrap: anywhere;
|
||||
max-width: 100%;
|
||||
/* #ifdef H5 */
|
||||
width: auto;
|
||||
word-break: break-all;
|
||||
/* #endif */
|
||||
}
|
||||
}
|
||||
.image-box {
|
||||
width: 100%;
|
||||
}
|
||||
.blockquote-box {
|
||||
padding: 5px;
|
||||
background: #f5f5f5;
|
||||
border-left: 4px solid #ddd;
|
||||
}
|
||||
.container-box {
|
||||
width: 100%;
|
||||
}
|
||||
.heading-box {
|
||||
.text {
|
||||
color: #15171f;
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
line-height: 56rpx;
|
||||
}
|
||||
.depth-1 {
|
||||
font-size: 50rpx;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
|
||||
.depth-2 {
|
||||
font-size: 42rpx;
|
||||
line-height: 56rpx;
|
||||
}
|
||||
|
||||
.depth-3 {
|
||||
font-size: 34rpx;
|
||||
line-height: 56rpx;
|
||||
}
|
||||
|
||||
.depth-4 {
|
||||
font-size: 30rpx;
|
||||
line-height: 56rpx;
|
||||
}
|
||||
|
||||
.depth-5 {
|
||||
font-size: 26rpx;
|
||||
line-height: 56rpx;
|
||||
}
|
||||
|
||||
.depth-6 {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
line-height: 56rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.hr {
|
||||
height: 2rpx;
|
||||
background: #ddd;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
.br {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
padding-left: 10rpx;
|
||||
.list-content {
|
||||
width: 100%;
|
||||
}
|
||||
.text {
|
||||
line-height: 56rpx;
|
||||
}
|
||||
.list-item-content-box {
|
||||
flex: 1;
|
||||
.list {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
.list-item-br {
|
||||
width: 100%;
|
||||
}
|
||||
.list-item-index {
|
||||
width: 40rpx;
|
||||
&.unordered {
|
||||
font-size: 28rpx;
|
||||
color: #000;
|
||||
}
|
||||
&.task {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-inline-image-box {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
.list-content {
|
||||
width: 100%;
|
||||
}
|
||||
.text {
|
||||
line-height: 56rpx;
|
||||
}
|
||||
.list-item-content-box {
|
||||
flex: 1;
|
||||
.list {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
.list-item-br {
|
||||
width: 100%;
|
||||
}
|
||||
.list-item-index {
|
||||
width: 40rpx;
|
||||
&.unordered {
|
||||
font-size: 28rpx;
|
||||
color: #000;
|
||||
}
|
||||
&.task {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hr-box {
|
||||
background-color: #ddd;
|
||||
height: 2rpx;
|
||||
}
|
||||
|
||||
.tool-call-box {
|
||||
width: 100%;
|
||||
margin: 10rpx 0;
|
||||
}
|
||||
|
||||
.table-box,
|
||||
.code-box,
|
||||
.hr-box,
|
||||
.paragraph-box,
|
||||
.blockquote-box,
|
||||
.heading-box,
|
||||
.list-box,
|
||||
.loging-icon,
|
||||
.math-box {
|
||||
width: 100%;
|
||||
margin: 10rpx 0;
|
||||
}
|
||||
.paragraph-box {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.space-box {
|
||||
height: 30rpx;
|
||||
}
|
||||
|
||||
.codespan {
|
||||
color: #666;
|
||||
background-color: #f5f5f5;
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 10rpx;
|
||||
font-size: 28rpx;
|
||||
word-break: break-all;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.icon-loading-image {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-right: 10rpx;
|
||||
-webkit-touch-callout: none !important;
|
||||
-webkit-user-select: none !important;
|
||||
user-select: none !important;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user