diff --git a/pages/message/chat.scss b/pages/message/chat.scss
index 72447c2..782e705 100644
--- a/pages/message/chat.scss
+++ b/pages/message/chat.scss
@@ -384,7 +384,7 @@ $primary-color: #0877F1;
display: flex;
align-items: center;
padding: 12rpx 20rpx;
- padding-bottom: env(safe-area-inset-bottom);
+ padding-bottom: 60rpx;
gap: 12rpx;
border-top: 1rpx solid #e0e0e0;
background: #fff;
@@ -511,7 +511,7 @@ $primary-color: #0877F1;
background: #fff;
border-top: 1rpx solid #eee;
padding: 20rpx 0 20rpx 60rpx;
- padding-bottom: env(safe-area-inset-bottom);
+ padding-bottom: 60rpx;
gap: 40rpx 50rpx;
flex-wrap: wrap;
background-color: #f5f5f5;
@@ -541,7 +541,7 @@ $primary-color: #0877F1;
background-color: white;
border-top: 1rpx solid #e0e0e0;
padding: 16rpx;
- margin-bottom: env(safe-area-inset-bottom);
+ margin-bottom: 60rpx;
display: flex;
align-items: center;
justify-content: space-between;
@@ -686,7 +686,7 @@ $primary-color: #0877F1;
width: 100%;
max-height: 80vh;
padding: 20rpx;
- padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+ padding-bottom: 80rpx;
position: relative;
box-sizing: border-box;
margin: 0;
@@ -818,7 +818,7 @@ $primary-color: #0877F1;
background-color: white;
width: auto;
padding: 32rpx 20rpx 48rpx 20rpx;
- padding-bottom: calc(48rpx + env(safe-area-inset-bottom));
+ padding-bottom: 108rpx;
text-align: center;
margin: 0 auto;
position: relative;
diff --git a/pages/message/components/ai-assistant-buttons.vue b/pages/message/components/ai-assistant-buttons.vue
index 634cf87..768322b 100644
--- a/pages/message/components/ai-assistant-buttons.vue
+++ b/pages/message/components/ai-assistant-buttons.vue
@@ -12,19 +12,6 @@
button.loading && button.loadingText ? button.loadingText : button.text
}}
-
-
-
-
-
-
@@ -32,8 +19,6 @@
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: {
@@ -56,11 +41,24 @@ const props = defineProps({
type: String,
default: "",
},
+ typeSelectorRef: {
+ type: Object,
+ default: null,
+ },
+ progressRef: {
+ type: Object,
+ default: null,
+ },
});
-const emit = defineEmits(["streamText", "clearInput", "generatingStateChange"]);
+const emit = defineEmits([
+ "streamText",
+ "clearInput",
+ "generatingStateChange",
+ "caseTypeSelect",
+ "regenerateFromProgress",
+ "nextFromProgress",
+]);
-const typeSelectorRef = ref(null);
-const progressRef = ref(null);
const isGenerating = ref(false);
const buttons = ref([
@@ -217,143 +215,22 @@ const handleAIAssistant = (button) => {
// 处理补充病历
const handleSupplementRecord = (button) => {
- typeSelectorRef.value?.open();
+ props.typeSelectorRef?.open();
};
// 处理病历类型选择
const handleCaseTypeSelect = async (type) => {
- try {
- // 打开进度弹窗
- progressRef.value?.open(type.id);
- progressRef.value?.updateProgress(10);
-
- // 调用补充病历接口(流式处理)
- await requestWithStream({
- url: "/getYoucanData/im",
- data: {
- type: "supplementMedicalCase",
- groupId: props.groupId,
- patientAccountId: props.patientAccountId || props.customerId,
- corpId: props.corpId,
- caseType: type.id,
- },
- onProgress: (data) => {
- // 处理流式数据
- handleStreamData(data, type.id);
- },
- onComplete: (finalData) => {
- // 完成后跳转
- handleComplete(finalData, type.id);
- },
- onError: (error) => {
- progressRef.value?.close();
- uni.showToast({
- title: error.message || "生成病历失败",
- icon: "none",
- });
- },
- });
- } catch (error) {
- console.error("补充病历失败:", error);
- progressRef.value?.close();
- uni.showToast({
- title: "操作失败,请重试",
- icon: "none",
- });
- }
-};
-
-// 流式请求处理
-const requestWithStream = async ({
- url,
- data,
- onProgress,
- onComplete,
- onError,
-}) => {
- try {
- // 调用接口时不显示全局 loading(第二个参数为 false)
- const result = await request(
- {
- url,
- data,
- },
- false
- );
-
- if (result.success && result.data) {
- // 模拟流式处理(如果后端返回的是完整数据)
- const extractedData = result.data.extractedData || {};
-
- // 逐个字段动态显示(包括空值字段)
- let progressValue = 20;
- const fields = Object.entries(extractedData);
- const delay = 300; // 每个字段显示间隔
-
- for (let i = 0; i < fields.length; i++) {
- const [key, value] = fields[i];
-
- // 显示所有字段,包括空值(会在组件中显示为"暂无")
- await new Promise((resolve) => setTimeout(resolve, delay));
- onProgress({ key, value });
- progressValue += Math.floor(60 / fields.length);
- progressRef.value?.updateProgress(Math.min(progressValue, 80));
- }
-
- // 完成
- onComplete(result.data);
- } else {
- onError(new Error(result.message || "请求失败"));
- }
- } catch (error) {
- onError(error);
- }
-};
-
-// 处理流式数据
-const handleStreamData = (data, caseType) => {
- const { key, value } = data;
-
- // 添加检测到的信息
- progressRef.value?.addDetectedInfo(key, value);
-};
-
-// 处理完成
-const handleComplete = (finalData, caseType) => {
- progressRef.value?.updateProgress(90);
- progressRef.value?.setGenerating(true);
-
- // 延迟后完成
- setTimeout(() => {
- progressRef.value?.updateProgress(100);
- progressRef.value?.setGenerating(false);
-
- // 延迟后显示操作按钮(不自动跳转)
- setTimeout(() => {
- progressRef.value?.setCompleted(true, finalData);
- }, 500);
- }, 800);
+ emit("caseTypeSelect", type);
};
// 处理从进度弹窗点击重新生成
const handleRegenerateFromProgress = (data) => {
- const type = { id: data.caseType };
- handleCaseTypeSelect(type);
+ emit("regenerateFromProgress", data);
};
// 处理从进度弹窗点击下一步
const handleNextFromProgress = (data) => {
- // 根据病历类型动态构建表单数据
- const extractedData = data.data?.extractedData || {};
-
- // 跳转到病历填写页面
- uni.navigateTo({
- url: `/pages/case/ai-medical-case-form?caseType=${data.caseType}&patientId=${
- props.patientId
- }&groupId=${props.groupId}&formData=${encodeURIComponent(
- JSON.stringify(extractedData)
- )}`,
- });
+ emit("nextFromProgress", data);
};
// 监听重新生成事件
diff --git a/pages/message/components/chat-input.vue b/pages/message/components/chat-input.vue
index 515345b..e11e1a4 100644
--- a/pages/message/components/chat-input.vue
+++ b/pages/message/components/chat-input.vue
@@ -13,7 +13,7 @@
+
+
+
+
+
+
@@ -206,6 +224,9 @@ import SystemMessage from "./components/system-message.vue";
import ConsultAccept from "./components/consult-accept.vue";
import RejectReasonModal from "./components/reject-reason-modal.vue";
import AIAssistantButtons from "./components/ai-assistant-buttons.vue";
+import MedicalCaseTypeSelector from "./components/medical-case-type-selector.vue";
+import MedicalCaseProgress from "./components/medical-case-progress.vue";
+import request from "@/utils/http.js";
const timChatManager = globalTimChatManager;
@@ -283,6 +304,8 @@ const { initIMAfterLogin } = useAccountStore();
// 聊天输入组件引用
const chatInputRef = ref(null);
const aiAssistantRef = ref(null);
+const typeSelectorRef = ref(null);
+const progressRef = ref(null);
const isGenerating = ref(false);
const groupId = ref("");
@@ -983,6 +1006,142 @@ const handleGeneratingStateChange = (generating) => {
isGenerating.value = generating;
};
+// 处理病历类型选择
+const handleCaseTypeSelect = async (type) => {
+ try {
+ // 打开进度弹窗
+ progressRef.value?.open(type.id);
+ progressRef.value?.updateProgress(10);
+
+ // 调用补充病历接口(流式处理)
+ await requestWithStream({
+ url: "/getYoucanData/im",
+ data: {
+ type: "supplementMedicalCase",
+ groupId: groupId.value,
+ patientAccountId: chatInfo.value.userID || "",
+ corpId: corpId,
+ caseType: type.id,
+ },
+ onProgress: (data) => {
+ // 处理流式数据
+ handleStreamData(data, type.id);
+ },
+ onComplete: (finalData) => {
+ // 完成后跳转
+ handleComplete(finalData, type.id);
+ },
+ onError: (error) => {
+ progressRef.value?.close();
+ uni.showToast({
+ title: error.message || "生成病历失败",
+ icon: "none",
+ });
+ },
+ });
+ } catch (error) {
+ console.error("补充病历失败:", error);
+ progressRef.value?.close();
+ uni.showToast({
+ title: "操作失败,请重试",
+ icon: "none",
+ });
+ }
+};
+
+// 流式请求处理
+const requestWithStream = async ({
+ url,
+ data,
+ onProgress,
+ onComplete,
+ onError,
+}) => {
+ try {
+ // 调用接口时不显示全局 loading(第二个参数为 false)
+ const result = await request(
+ {
+ url,
+ data,
+ },
+ false
+ );
+
+ if (result.success && result.data) {
+ // 模拟流式处理(如果后端返回的是完整数据)
+ const extractedData = result.data.extractedData || {};
+
+ // 逐个字段动态显示(包括空值字段)
+ let progressValue = 20;
+ const fields = Object.entries(extractedData);
+ const delay = 300; // 每个字段显示间隔
+
+ for (let i = 0; i < fields.length; i++) {
+ const [key, value] = fields[i];
+
+ // 显示所有字段,包括空值(会在组件中显示为"暂无")
+ await new Promise((resolve) => setTimeout(resolve, delay));
+ onProgress({ key, value });
+ progressValue += Math.floor(60 / fields.length);
+ progressRef.value?.updateProgress(Math.min(progressValue, 80));
+ }
+
+ // 完成
+ onComplete(result.data);
+ } else {
+ onError(new Error(result.message || "请求失败"));
+ }
+ } catch (error) {
+ onError(error);
+ }
+};
+
+// 处理流式数据
+const handleStreamData = (data, caseType) => {
+ const { key, value } = data;
+
+ // 添加检测到的信息
+ progressRef.value?.addDetectedInfo(key, value);
+};
+
+// 处理完成
+const handleComplete = (finalData, caseType) => {
+ progressRef.value?.updateProgress(90);
+ progressRef.value?.setGenerating(true);
+
+ // 延迟后完成
+ setTimeout(() => {
+ progressRef.value?.updateProgress(100);
+ progressRef.value?.setGenerating(false);
+
+ // 延迟后显示操作按钮(不自动跳转)
+ setTimeout(() => {
+ progressRef.value?.setCompleted(true, finalData);
+ }, 500);
+ }, 800);
+};
+
+// 处理从进度弹窗点击重新生成
+const handleRegenerateFromProgress = (data) => {
+ const type = { id: data.caseType };
+ handleCaseTypeSelect(type);
+};
+
+// 处理从进度弹窗点击下一步
+const handleNextFromProgress = (data) => {
+ // 根据病历类型动态构建表单数据
+ const extractedData = data.data?.extractedData || {};
+
+ // 跳转到病历填写页面
+ uni.navigateTo({
+ url: `/pages/case/ai-medical-case-form?caseType=${data.caseType}&patientId=${
+ patientId.value
+ }&groupId=${groupId.value}&formData=${encodeURIComponent(
+ JSON.stringify(extractedData)
+ )}`,
+ });
+};
+
// 暴露方法给常用语页面调用
defineExpose({
sendCommonPhrase,