ykt-wxapp/pages/message/components/ai-assistant-buttons.vue
2026-02-12 14:12:01 +08:00

315 lines
6.8 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>
</view>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import request from "@/utils/http.js";
import api from "@/utils/api.js";
const props = defineProps({
groupId: {
type: String,
required: true,
},
patientAccountId: {
type: String,
default: "",
},
patientId: {
type: String,
default: "",
},
corpId: {
type: String,
default: "",
},
customerId: {
type: String,
default: "",
},
typeSelectorRef: {
type: Object,
default: null,
},
progressRef: {
type: Object,
default: null,
},
});
const emit = defineEmits([
"streamText",
"clearInput",
"generatingStateChange",
"caseTypeSelect",
"regenerateFromProgress",
"nextFromProgress",
]);
const isGenerating = ref(false);
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");
isGenerating.value = true;
emit("generatingStateChange", true);
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);
isGenerating.value = false;
emit("generatingStateChange", false);
}
}, speed);
}, 100);
};
// 处理AI助手
const handleAIAssistant = (button) => {
uni.showToast({
title: "AI助手功能开发中",
icon: "none",
});
};
// 处理补充病历
const handleSupplementRecord = (button) => {
props.typeSelectorRef?.open();
};
// 处理病历类型选择
const handleCaseTypeSelect = async (type) => {
emit("caseTypeSelect", type);
};
// 处理从进度弹窗点击重新生成
const handleRegenerateFromProgress = (data) => {
emit("regenerateFromProgress", data);
};
// 处理从进度弹窗点击下一步
const handleNextFromProgress = (data) => {
emit("nextFromProgress", data);
};
// 监听重新生成事件
onMounted(() => {
uni.$on("regenerateMedicalCase", handleRegenerateMedicalCase);
});
onUnmounted(() => {
uni.$off("regenerateMedicalCase", handleRegenerateMedicalCase);
});
const handleRegenerateMedicalCase = (data) => {
const type = { id: data.caseType };
handleCaseTypeSelect(type);
};
// 暴露生成状态给父组件
defineExpose({
isGenerating,
});
</script>
<style scoped lang="scss">
.ai-assistant-buttons {
display: flex;
align-items: center;
gap: 16rpx;
padding: 16rpx 24rpx;
background-color: #f8f9fa;
.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>