Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
水玉婷
ai-wechat
Commits
68fb0503
Commit
68fb0503
authored
Jan 22, 2026
by
水玉婷
Browse files
feat:思考添加动画效果
parent
8c585125
Changes
3
Hide whitespace changes
Inline
Side-by-side
src/views/components/AiChat.vue
View file @
68fb0503
...
@@ -53,14 +53,21 @@
...
@@ -53,14 +53,21 @@
<div v-else v-html="item.content" class="message-inner-box"></div>
<div v-else v-html="item.content" class="message-inner-box"></div>
<!-- 思考过程框 -->
<!-- 思考过程框 -->
<div v-if="item.hasThinkBox" class="think-box-wrapper">
<div v-if="item.hasThinkBox" class="think-box-wrapper">
<div class="think-box-toggle" @click="toggleThinkBox(index, i)">
<div
:
class="
['
think-box-toggle
', item.thinkBoxExpanded ? 'expanded' : 'collapsed']
" @click="toggleThinkBox(index, i)">
<component
<component
:is="item.thinkBoxExpanded ? UpOutlined : DownOutlined"
:is="item.thinkBoxExpanded ? UpOutlined : DownOutlined"
class="toggle-icon"
class="toggle-icon"
/>
/>
<span v-if="item.thinkingTimeText" class="thinking-text">{{ item.thinkingTimeText }}</span>
<div v-if="item.thinkingTimeText" class="thinking-loading">
<span class="thinking-text">{{ item.thinkingTimeText }}</span>
<div v-if="item.thinkingTimeText === '正在思考中'" class="loading-dots">
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</div>
</div>
</div>
</div>
<div
v-if="item.thinkBoxExpanded" class="think-box-content
"
<div
:class="['think-box-content', item.thinkBoxExpanded ? 'expanded' : 'collapsed']
"
v-html="templateService.generateThinkingTemplate(item.thinkContent || '')"></div>
v-html="templateService.generateThinkingTemplate(item.thinkContent || '')"></div>
</div>
</div>
</template>
</template>
...
@@ -326,17 +333,17 @@ const validateMessageParams = (type: MessageType, params: MessageParams): boolea
...
@@ -326,17 +333,17 @@ const validateMessageParams = (type: MessageType, params: MessageParams): boolea
// 统一发送消息函数
// 统一发送消息函数
const sendMessage = async (type: MessageType = 'text', params: MessageParams = {}) => {
const sendMessage = async (type: MessageType = 'text', params: MessageParams = {}) => {
// 如果消息文本为空且是文本类型,则延迟1秒后模拟折线图消息进行测试
// 如果消息文本为空且是文本类型,则延迟1秒后模拟折线图消息进行测试
if (type === 'text' && !messageText.value.trim() && !params.message) {
//
if (type === 'text' && !messageText.value.trim() && !params.message) {
loading.value = true;
//
loading.value = true;
console.log('📊 检测到空消息,1秒后发送折线图测试...');
//
console.log('📊 检测到空消息,1秒后发送折线图测试...');
setTimeout(() => {
//
setTimeout(() => {
simulateLineChartMessage();
//
simulateLineChartMessage();
loading.value = false;
//
loading.value = false;
}, 1000);
//
}, 1000);
return;
//
return;
}
//
}
loading.value = true;
loading.value = true;
...
@@ -589,63 +596,63 @@ onBeforeUnmount(() => {
...
@@ -589,63 +596,63 @@ onBeforeUnmount(() => {
});
});
// 模拟折线图消息
// 模拟折线图消息
const simulateLineChartMessage = () => {
//
const simulateLineChartMessage = () => {
console.log('模拟折线图消息');
//
console.log('模拟折线图消息');
// 使用真实医院数据创建折线图数据,包含多个维度和指标
//
// 使用真实医院数据创建折线图数据,包含多个维度和指标
const lineChartData = {
//
const lineChartData = {
title:"2023年各医院月度运营数据汇总",
//
title:"2023年各医院月度运营数据汇总",
dimFields: ['月份', '地区', '医院类型', '科室'],
//
dimFields: ['月份', '地区', '医院类型', '科室'],
indexFields: ['用户总数', '费用汇总', '平均住院天数', '手术台数', '门诊量'],
//
indexFields: ['用户总数', '费用汇总', '平均住院天数', '手术台数', '门诊量'],
rows: [
//
rows: [
{ '月份': '1月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 100, '费用汇总': 5000, '平均住院天数': 7.2, '手术台数': 15, '门诊量': 1200 },
//
{ '月份': '1月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 100, '费用汇总': 5000, '平均住院天数': 7.2, '手术台数': 15, '门诊量': 1200 },
{ '月份': '2月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 150, '费用汇总': 6000, '平均住院天数': 6.8, '手术台数': 18, '门诊量': 1400 },
//
{ '月份': '2月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 150, '费用汇总': 6000, '平均住院天数': 6.8, '手术台数': 18, '门诊量': 1400 },
{ '月份': '3月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 120, '费用汇总': 5500, '平均住院天数': 7.5, '手术台数': 16, '门诊量': 1300 },
//
{ '月份': '3月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 120, '费用汇总': 5500, '平均住院天数': 7.5, '手术台数': 16, '门诊量': 1300 },
{ '月份': '4月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 180, '费用汇总': 8000, '平均住院天数': 6.5, '手术台数': 22, '门诊量': 1600 },
//
{ '月份': '4月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 180, '费用汇总': 8000, '平均住院天数': 6.5, '手术台数': 22, '门诊量': 1600 },
{ '月份': '5月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 200, '费用汇总': 9000, '平均住院天数': 6.2, '手术台数': 25, '门诊量': 1800 },
//
{ '月份': '5月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 200, '费用汇总': 9000, '平均住院天数': 6.2, '手术台数': 25, '门诊量': 1800 },
{ '月份': '6月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 250, '费用汇总': 11000, '平均住院天数': 5.8, '手术台数': 30, '门诊量': 2000 },
//
{ '月份': '6月', '地区': '北京', '医院类型': '三甲医院', '科室': '内科', '用户总数': 250, '费用汇总': 11000, '平均住院天数': 5.8, '手术台数': 30, '门诊量': 2000 },
{ '月份': '1月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 80, '费用汇总': 8000, '平均住院天数': 8.5, '手术台数': 45, '门诊量': 800 },
//
{ '月份': '1月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 80, '费用汇总': 8000, '平均住院天数': 8.5, '手术台数': 45, '门诊量': 800 },
{ '月份': '2月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 120, '费用汇总': 9500, '平均住院天数': 8.2, '手术台数': 52, '门诊量': 900 },
//
{ '月份': '2月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 120, '费用汇总': 9500, '平均住院天数': 8.2, '手术台数': 52, '门诊量': 900 },
{ '月份': '3月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 100, '费用汇总': 8500, '平均住院天数': 8.8, '手术台数': 48, '门诊量': 850 },
//
{ '月份': '3月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 100, '费用汇总': 8500, '平均住院天数': 8.8, '手术台数': 48, '门诊量': 850 },
{ '月份': '4月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 150, '费用汇总': 12000, '平均住院天数': 7.8, '手术台数': 60, '门诊量': 1100 },
//
{ '月份': '4月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 150, '费用汇总': 12000, '平均住院天数': 7.8, '手术台数': 60, '门诊量': 1100 },
{ '月份': '5月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 180, '费用汇总': 14000, '平均住院天数': 7.5, '手术台数': 68, '门诊量': 1200 },
//
{ '月份': '5月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 180, '费用汇总': 14000, '平均住院天数': 7.5, '手术台数': 68, '门诊量': 1200 },
{ '月份': '6月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 220, '费用汇总': 16000, '平均住院天数': 7.2, '手术台数': 75, '门诊量': 1400 },
//
{ '月份': '6月', '地区': '北京', '医院类型': '三甲医院', '科室': '外科', '用户总数': 220, '费用汇总': 16000, '平均住院天数': 7.2, '手术台数': 75, '门诊量': 1400 },
{ '月份': '1月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 80, '费用汇总': 4000, '平均住院天数': 6.8, '手术台数': 12, '门诊量': 1000 },
//
{ '月份': '1月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 80, '费用汇总': 4000, '平均住院天数': 6.8, '手术台数': 12, '门诊量': 1000 },
{ '月份': '2月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 120, '费用汇总': 5000, '平均住院天数': 6.5, '手术台数': 15, '门诊量': 1200 },
//
{ '月份': '2月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 120, '费用汇总': 5000, '平均住院天数': 6.5, '手术台数': 15, '门诊量': 1200 },
{ '月份': '3月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 100, '费用汇总': 4500, '平均住院天数': 7.0, '手术台数': 13, '门诊量': 1100 },
//
{ '月份': '3月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 100, '费用汇总': 4500, '平均住院天数': 7.0, '手术台数': 13, '门诊量': 1100 },
{ '月份': '4月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 150, '费用汇总': 7000, '平均住院天数': 6.2, '手术台数': 18, '门诊量': 1400 },
//
{ '月份': '4月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 150, '费用汇总': 7000, '平均住院天数': 6.2, '手术台数': 18, '门诊量': 1400 },
{ '月份': '5月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 180, '费用汇总': 8000, '平均住院天数': 5.9, '手术台数': 22, '门诊量': 1600 },
//
{ '月份': '5月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 180, '费用汇总': 8000, '平均住院天数': 5.9, '手术台数': 22, '门诊量': 1600 },
{ '月份': '6月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 220, '费用汇总': 10000, '平均住院天数': 5.6, '手术台数': 28, '门诊量': 1800 },
//
{ '月份': '6月', '地区': '上海', '医院类型': '三甲医院', '科室': '内科', '用户总数': 220, '费用汇总': 10000, '平均住院天数': 5.6, '手术台数': 28, '门诊量': 1800 },
{ '月份': '1月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 60, '费用汇总': 2500, '平均住院天数': 5.5, '手术台数': 8, '门诊量': 600 },
//
{ '月份': '1月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 60, '费用汇总': 2500, '平均住院天数': 5.5, '手术台数': 8, '门诊量': 600 },
{ '月份': '2月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 90, '费用汇总': 3200, '平均住院天数': 5.2, '手术台数': 10, '门诊量': 700 },
//
{ '月份': '2月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 90, '费用汇总': 3200, '平均住院天数': 5.2, '手术台数': 10, '门诊量': 700 },
{ '月份': '3月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 80, '费用汇总': 2800, '平均住院天数': 5.8, '手术台数': 9, '门诊量': 650 },
//
{ '月份': '3月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 80, '费用汇总': 2800, '平均住院天数': 5.8, '手术台数': 9, '门诊量': 650 },
{ '月份': '4月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 120, '费用汇总': 4500, '平均住院天数': 5.0, '手术台数': 12, '门诊量': 800 },
//
{ '月份': '4月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 120, '费用汇总': 4500, '平均住院天数': 5.0, '手术台数': 12, '门诊量': 800 },
{ '月份': '5月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 150, '费用汇总': 5500, '平均住院天数': 4.8, '手术台数': 15, '门诊量': 900 },
//
{ '月份': '5月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 150, '费用汇总': 5500, '平均住院天数': 4.8, '手术台数': 15, '门诊量': 900 },
{ '月份': '6月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 180, '费用汇总': 6500, '平均住院天数': 4.5, '手术台数': 18, '门诊量': 1000 },
//
{ '月份': '6月', '地区': '上海', '医院类型': '二甲医院', '科室': '内科', '用户总数': 180, '费用汇总': 6500, '平均住院天数': 4.5, '手术台数': 18, '门诊量': 1000 },
{ '月份': '1月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 60, '费用汇总': 3000, '平均住院天数': 6.0, '手术台数': 10, '门诊量': 800 },
//
{ '月份': '1月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 60, '费用汇总': 3000, '平均住院天数': 6.0, '手术台数': 10, '门诊量': 800 },
{ '月份': '2月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 90, '费用汇总': 4000, '平均住院天数': 5.8, '手术台数': 12, '门诊量': 900 },
//
{ '月份': '2月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 90, '费用汇总': 4000, '平均住院天数': 5.8, '手术台数': 12, '门诊量': 900 },
{ '月份': '3月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 80, '费用汇总': 3500, '平均住院天数': 6.2, '手术台数': 11, '门诊量': 850 },
//
{ '月份': '3月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 80, '费用汇总': 3500, '平均住院天数': 6.2, '手术台数': 11, '门诊量': 850 },
{ '月份': '4月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 120, '费用汇总': 6000, '平均住院天数': 5.5, '手术台数': 15, '门诊量': 1100 },
//
{ '月份': '4月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 120, '费用汇总': 6000, '平均住院天数': 5.5, '手术台数': 15, '门诊量': 1100 },
{ '月份': '5月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 150, '费用汇总': 7000, '平均住院天数': 5.2, '手术台数': 18, '门诊量': 1200 },
//
{ '月份': '5月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 150, '费用汇总': 7000, '平均住院天数': 5.2, '手术台数': 18, '门诊量': 1200 },
{ '月份': '6月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 180, '费用汇总': 9000, '平均住院天数': 4.9, '手术台数': 22, '门诊量': 1400 }
//
{ '月份': '6月', '地区': '广州', '医院类型': '三甲医院', '科室': '内科', '用户总数': 180, '费用汇总': 9000, '平均住院天数': 4.9, '手术台数': 22, '门诊量': 1400 }
],
//
],
// chartType: 'line' // 设置默认图表类型为折线图
//
// chartType: 'line' // 设置默认图表类型为折线图
};
//
};
// 正确的SSE消息格式:status 3(图表数据),type 2(表格数据)
//
// 正确的SSE消息格式:status 3(图表数据),type 2(表格数据)
const simulatedMessage = {
//
const simulatedMessage = {
message: lineChartData,
//
message: lineChartData,
status: 3, // 图表数据
//
status: 3, // 图表数据
type: 2 // 表格数据
//
type: 2 // 表格数据
};
//
};
// 调用handleSSEMessage处理模拟消息
//
// 调用handleSSEMessage处理模拟消息
handleSSEMessage(simulatedMessage);
//
handleSSEMessage(simulatedMessage);
};
//
};
</script>
</script>
<style lang="less" scoped>
<style lang="less" scoped>
@import './style.less';
@import './style.less';
...
...
src/views/components/style.less
View file @
68fb0503
...
@@ -325,25 +325,113 @@ li {
...
@@ -325,25 +325,113 @@ li {
cursor: pointer;
cursor: pointer;
font-size: 14px;
font-size: 14px;
text-decoration: none;
text-decoration: none;
display: flex;
align-items: center;
align-items: center;
gap: 4px;
gap: 8px;
padding: 4px 8px;
border-radius: 4px;
transition: all 0.2s ease;
.toggle-icon {
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
transform: rotate(0deg);
}
// 展开状态下的图标旋转
&.expanded .toggle-icon {
transform: rotate(180deg);
}
.thinking-loading {
display: flex;
align-items: center;
gap: 8px;
}
.loading-dots {
display: flex;
gap: 4px;
.dot {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: @primary-color;
animation: loading-dots 1.4s ease-in-out infinite both;
&:nth-child(1) {
animation-delay: -0.32s;
}
&:nth-child(2) {
animation-delay: -0.16s;
}
&:nth-child(3) {
animation-delay: 0s;
}
}
}
.thinking-text {
.thinking-text {
margin-left: 6px;
color: @gray-7;
color: @gray-7;
font-size: 13px;
font-size: 13px;
transition: color 0.3s ease;
font-weight: 500;
}
}
// Loading dots动画
@keyframes loading-dots {
0%, 80%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
40% {
transform: scale(1.2);
opacity: 1;
}
}
}
}
:deep(.think-box-content) {
:deep(.think-box-content) {
margin-top: 8px;
margin-top: 8px;
overflow: hidden;
max-height: 0;
opacity: 0;
transform: translateY(-10px);
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.4s ease-in-out,
transform 0.4s ease-in-out,
margin-top 0.4s ease-in-out;
// 展开状态
&.expanded {
max-height: 20000px; // 设置足够大的高度
opacity: 1;
transform: translateY(0);
margin-top: 8px;
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); // 平滑展开
}
// 折叠状态
&.collapsed {
max-height: 0;
opacity: 0;
transform: translateY(-10px);
margin-top: 0;
transition-timing-function: cubic-bezier(0.55, 0.085, 0.68, 0.53); // 平滑折叠
}
.think-content {
.think-content {
font-size: 14px;
font-size: 14px;
color: @gray-5;
color: @gray-5;
padding: 12px 0;
opacity: 0;
transform: translateY(5px);
transition: opacity 0.3s ease-in-out 0.1s, transform 0.3s ease-in-out 0.1s;
}
&.expanded .think-content {
opacity: 1;
transform: translateY(0);
}
}
}
}
...
...
src/views/components/utils/contentTemplateService.ts
View file @
68fb0503
...
@@ -425,7 +425,7 @@ export class ContentTemplateService {
...
@@ -425,7 +425,7 @@ export class ContentTemplateService {
hasThinkBox
:
true
,
hasThinkBox
:
true
,
thinkBoxExpanded
:
false
,
thinkBoxExpanded
:
false
,
thinkingTime
:
0
,
thinkingTime
:
0
,
thinkingTimeText
:
'
正在思考中
……
'
,
thinkingTimeText
:
'
正在思考中
'
,
});
});
}
else
{
}
else
{
// 如果已有思考内容块,确保hasThinkBox为true
// 如果已有思考内容块,确保hasThinkBox为true
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment