ykt-wxapp/pages/message/components/ai-assistant-buttons.vue

315 lines
6.8 KiB
Vue
Raw Permalink Normal View History

2026-01-29 18:03:40 +08:00
<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: "",
},
2026-02-02 13:27:48 +08:00
patientId: {
type: String,
default: "",
},
2026-01-29 18:03:40 +08:00
corpId: {
type: String,
default: "",
},
customerId: {
type: String,
default: "",
},
2026-02-12 14:12:01 +08:00
typeSelectorRef: {
type: Object,
default: null,
},
progressRef: {
type: Object,
default: null,
},
2026-01-29 18:03:40 +08:00
});
2026-02-12 14:12:01 +08:00
const emit = defineEmits([
"streamText",
"clearInput",
"generatingStateChange",
"caseTypeSelect",
"regenerateFromProgress",
"nextFromProgress",
]);
2026-01-29 18:03:40 +08:00
2026-02-08 13:53:22 +08:00
const isGenerating = ref(false);
2026-01-29 18:03:40 +08:00
const buttons = ref([
{
id: "followUp",
text: "追问病情",
loadingText: "AI分析中正在为您生成追问建议…",
icon: "/static/icon/zhuiwen.png",
loading: false,
},
2026-01-30 10:28:34 +08:00
// {
// id: "aiAssistant",
// text: "开启AI助手",
// icon: "/static/icon/kaiqiAI.png",
// loading: false,
// },
2026-01-29 18:03:40 +08:00
{
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");
2026-02-08 13:53:22 +08:00
isGenerating.value = true;
emit("generatingStateChange", true);
2026-01-29 18:03:40 +08:00
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);
2026-02-08 13:53:22 +08:00
isGenerating.value = false;
emit("generatingStateChange", false);
2026-01-29 18:03:40 +08:00
}
}, speed);
}, 100);
};
// 处理AI助手
const handleAIAssistant = (button) => {
uni.showToast({
title: "AI助手功能开发中",
icon: "none",
});
};
// 处理补充病历
const handleSupplementRecord = (button) => {
2026-02-12 14:12:01 +08:00
props.typeSelectorRef?.open();
2026-01-29 18:03:40 +08:00
};
// 处理病历类型选择
const handleCaseTypeSelect = async (type) => {
2026-02-12 14:12:01 +08:00
emit("caseTypeSelect", type);
2026-01-29 18:44:34 +08:00
};
// 处理从进度弹窗点击重新生成
const handleRegenerateFromProgress = (data) => {
2026-02-12 14:12:01 +08:00
emit("regenerateFromProgress", data);
2026-01-29 18:44:34 +08:00
};
// 处理从进度弹窗点击下一步
const handleNextFromProgress = (data) => {
2026-02-12 14:12:01 +08:00
emit("nextFromProgress", data);
2026-01-29 18:44:34 +08:00
};
2026-01-29 18:03:40 +08:00
// 监听重新生成事件
onMounted(() => {
uni.$on("regenerateMedicalCase", handleRegenerateMedicalCase);
});
onUnmounted(() => {
uni.$off("regenerateMedicalCase", handleRegenerateMedicalCase);
});
const handleRegenerateMedicalCase = (data) => {
const type = { id: data.caseType };
handleCaseTypeSelect(type);
};
2026-02-08 13:53:22 +08:00
// 暴露生成状态给父组件
defineExpose({
isGenerating,
});
2026-01-29 18:03:40 +08:00
</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>