ykt-wxapp/pages/message/components/ai-assistant-buttons.vue
2026-01-29 18:03:40 +08:00

357 lines
8.3 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="ai-assistant-buttons">
<view
v-for="button in buttons"
:key="button.id"
class="ai-button"
:class="{ loading: button.loading }"
@click="handleButtonClick(button)"
>
<image class="button-icon" :src="button.icon" mode="aspectFit" />
<text class="button-text">{{
button.loading && button.loadingText ? button.loadingText : button.text
}}</text>
</view>
<!-- 病历类型选择弹窗 -->
<medical-case-type-selector
ref="typeSelectorRef"
@select="handleCaseTypeSelect"
/>
<!-- 进度显示弹窗 -->
<medical-case-progress ref="progressRef" />
</view>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import request from "@/utils/http.js";
import api from "@/utils/api.js";
import MedicalCaseTypeSelector from "./medical-case-type-selector.vue";
import MedicalCaseProgress from "./medical-case-progress.vue";
const props = defineProps({
groupId: {
type: String,
required: true,
},
patientAccountId: {
type: String,
default: "",
},
corpId: {
type: String,
default: "",
},
customerId: {
type: String,
default: "",
},
});
const emit = defineEmits(["streamText", "clearInput"]);
const typeSelectorRef = ref(null);
const progressRef = ref(null);
const buttons = ref([
{
id: "followUp",
text: "追问病情",
loadingText: "AI分析中正在为您生成追问建议…",
icon: "/static/icon/zhuiwen.png",
loading: false,
},
{
id: "aiAssistant",
text: "开启AI助手",
icon: "/static/icon/kaiqiAI.png",
loading: false,
},
{
id: "supplementRecord",
text: "补充病历",
icon: "/static/icon/buchong.png",
loading: false,
},
]);
// 处理按钮点击
const handleButtonClick = async (button) => {
if (button.loading) return;
switch (button.id) {
case "followUp":
await handleFollowUpInquiry(button);
break;
case "aiAssistant":
handleAIAssistant(button);
break;
case "supplementRecord":
handleSupplementRecord(button);
break;
}
};
// 处理追问病情
const handleFollowUpInquiry = async (button) => {
try {
button.loading = true;
// 如果没有提供患者账号ID先获取群组信息
let finalPatientAccountId = props.patientAccountId;
if (!finalPatientAccountId) {
// 从聊天记录中获取患者账号ID非当前用户的发送者
const chatRecordsResult = await api("getChatRecordsByGroupId", {
groupID: props.groupId,
count: 5,
});
if (
chatRecordsResult.success &&
chatRecordsResult.data &&
chatRecordsResult.data.records
) {
const records = chatRecordsResult.data.records;
// 找到第一个非当前用户的发送者作为患者账号
const currentUserId = uni.getStorageSync("openid") || "";
const patientMessage = records.find(
(record) =>
record.From_Account && record.From_Account !== currentUserId
);
if (patientMessage) {
finalPatientAccountId = patientMessage.From_Account;
}
}
}
if (!finalPatientAccountId) {
uni.showToast({
title: "无法获取患者信息",
icon: "none",
});
button.loading = false;
return;
}
// 调用追问病情接口不显示全局loading
const result = await request(
{
url: "/getYoucanData/im",
data: {
type: "followUpInquiry",
groupId: props.groupId,
patientAccountId: finalPatientAccountId,
corpId: props.corpId,
},
},
false
); // 第二个参数false表示不显示loading
if (result.success && result.data && result.data.suggestion) {
// 流式输出文本到输入框
streamTextToInput(result.data.suggestion);
} else {
uni.showToast({
title: result.message || "获取追问建议失败",
icon: "none",
});
}
} catch (error) {
console.error("追问病情失败:", error);
uni.showToast({
title: "操作失败,请重试",
icon: "none",
});
} finally {
button.loading = false;
}
};
// 流式输出文本到输入框
const streamTextToInput = (text) => {
if (!text) return;
// 先清空输入框
emit("clearInput");
let currentIndex = 0;
const speed = 50; // 每个字符的延迟时间(毫秒)
// 延迟一小段时间后开始流式输出,确保清空操作完成
setTimeout(() => {
const streamInterval = setInterval(() => {
if (currentIndex < text.length) {
const char = text[currentIndex];
emit("streamText", char);
currentIndex++;
} else {
clearInterval(streamInterval);
}
}, speed);
}, 100);
};
// 处理AI助手
const handleAIAssistant = (button) => {
uni.showToast({
title: "AI助手功能开发中",
icon: "none",
});
};
// 处理补充病历
const handleSupplementRecord = (button) => {
typeSelectorRef.value?.open();
};
// 处理病历类型选择
const handleCaseTypeSelect = async (type) => {
try {
// 打开进度弹窗
progressRef.value?.open(type.id);
// 模拟进度更新
progressRef.value?.updateProgress(20);
// 调用补充病历接口
const result = await request({
url: "/getYoucanData/im",
data: {
type: "supplementMedicalCase",
groupId: props.groupId,
patientAccountId: props.patientAccountId || props.customerId,
corpId: props.corpId,
caseType: type.id,
},
});
progressRef.value?.updateProgress(60);
if (result.success && result.data) {
const { detectedInfo, formData } = result.data;
// 显示检测到的信息
if (detectedInfo && detectedInfo.length > 0) {
detectedInfo.forEach((info) => {
progressRef.value?.addDetectedInfo(info);
});
}
progressRef.value?.updateProgress(80);
progressRef.value?.setGenerating(true);
// 模拟生成结构化病历
setTimeout(() => {
progressRef.value?.updateProgress(100);
// 延迟后关闭进度弹窗并跳转
setTimeout(() => {
progressRef.value?.close();
// 跳转到病历填写页面
uni.navigateTo({
url: `/pages/case/medical-case-form?caseType=${
type.id
}&customerId=${
props.customerId || props.patientAccountId
}&groupId=${props.groupId}&formData=${encodeURIComponent(
JSON.stringify(formData || {})
)}`,
});
}, 500);
}, 1000);
} else {
progressRef.value?.close();
uni.showToast({
title: result.message || "生成病历失败",
icon: "none",
});
}
} catch (error) {
console.error("补充病历失败:", error);
progressRef.value?.close();
uni.showToast({
title: "操作失败,请重试",
icon: "none",
});
}
};
// 监听重新生成事件
onMounted(() => {
uni.$on("regenerateMedicalCase", handleRegenerateMedicalCase);
});
onUnmounted(() => {
uni.$off("regenerateMedicalCase", handleRegenerateMedicalCase);
});
const handleRegenerateMedicalCase = (data) => {
const type = { id: data.caseType };
handleCaseTypeSelect(type);
};
</script>
<style scoped lang="scss">
.ai-assistant-buttons {
display: flex;
align-items: center;
gap: 16rpx;
padding: 16rpx 24rpx;
background-color: #f8f9fa;
border-bottom: 1rpx solid #e5e5e5;
.ai-button {
display: flex;
align-items: center;
gap: 8rpx;
padding: 12rpx 20rpx;
background-color: #ffffff;
border: 1rpx solid #e0e0e0;
border-radius: 40rpx;
transition: all 0.3s ease;
&.loading {
opacity: 0.8;
pointer-events: none;
.loading-icon {
width: 32rpx;
height: 32rpx;
border: 3rpx solid #e0e0e0;
border-top-color: #1890ff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
}
&:active {
background-color: #f0f0f0;
transform: scale(0.98);
}
.button-icon {
width: 32rpx;
height: 32rpx;
}
.button-text {
font-size: 28rpx;
color: #333333;
white-space: nowrap;
}
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>