Compare commits

..

No commits in common. "85c56ebf8991d65b3d5df57c55cd7a710f8b2666" and "6a6b8abf9fe169cca698c0ddd1f55bb89ca2608d" have entirely different histories.

15 changed files with 265 additions and 290 deletions

View File

@ -1,65 +1,63 @@
<template> <template>
<full-page :customScroll="empty"> <view class="medical-case-form">
<view class="medical-case-form"> <view class="form-container">
<view class="form-container"> <!-- 动态渲染表单字段 -->
<!-- 动态渲染表单字段 --> <view
<view v-for="field in currentFields"
v-for="field in currentFields" :key="field.key"
:key="field.key" class="form-item"
class="form-item" :class="{ required: field.required }"
:class="{ required: field.required }" >
<view class="item-label">{{ field.label }}</view>
<!-- 日期选择器 -->
<picker
v-if="field.type === 'date'"
mode="date"
:value="formData[field.key]"
@change="onDateChange(field.key, $event)"
:disabled="!isEditing"
> >
<view class="item-label">{{ field.label }}</view> <view class="picker-value">
<!-- 日期选择器 --> {{ formData[field.key] || "暂无" }}
<picker </view>
v-if="field.type === 'date'" </picker>
mode="date"
:value="formData[field.key]"
@change="onDateChange(field.key, $event)"
:disabled="!isEditing"
>
<view class="picker-value">
{{ formData[field.key] || "暂无" }}
</view>
</picker>
<!-- 多行文本 --> <!-- 多行文本 -->
<textarea <textarea
v-else-if="field.type === 'textarea'" v-else-if="field.type === 'textarea'"
class="item-textarea" class="item-textarea"
v-model="formData[field.key]" v-model="formData[field.key]"
placeholder="请输入" placeholder="请输入"
:disabled="!isEditing" :disabled="!isEditing"
/> />
<!-- 单行文本 --> <!-- 单行文本 -->
<input <input
v-else v-else
class="item-input" class="item-input"
v-model="formData[field.key]" v-model="formData[field.key]"
placeholder="暂无" placeholder="暂无"
:disabled="!isEditing" :disabled="!isEditing"
/> />
</view>
</view>
<view class="footer-buttons">
<view class="btn-regenerate" @click="handleRegenerate">
<text class="btn-text">重新生成</text>
</view>
<view class="btn-save" @click="handleSave">
<text class="btn-text">保存至档案</text>
</view>
</view> </view>
</view> </view>
</full-page>
<view class="footer-buttons">
<view class="btn-regenerate" @click="handleRegenerate">
<text class="btn-text">重新生成</text>
</view>
<view class="btn-save" @click="handleSave">
<text class="btn-text">保存至档案</text>
</view>
</view>
</view>
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from "vue"; import { ref, computed, onMounted } from "vue";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import useAccountStore from "@/store/account"; import useAccountStore from "@/store/account";
import FullPage from "@/components/full-page.vue";
import api from "@/utils/api.js"; import api from "@/utils/api.js";
const caseType = ref(""); const caseType = ref("");
const formData = ref({}); const formData = ref({});
@ -73,7 +71,7 @@ const CASE_TYPE_NAMES = {
outpatient: "门诊病历", outpatient: "门诊病历",
inhospital: "住院病历", inhospital: "住院病历",
physicalExaminationTemplate: "体检记录", physicalExaminationTemplate: "体检记录",
preConsultationRecord: "预问诊记录", preConsultation: "预问诊记录",
}; };
// //
@ -187,7 +185,7 @@ const FIELD_CONFIG = {
label: FIELD_LABELS.examination, label: FIELD_LABELS.examination,
type: "textarea", type: "textarea",
required: false, required: false,
}, }
], ],
physicalExaminationTemplate: [ physicalExaminationTemplate: [
{ {
@ -200,7 +198,7 @@ const FIELD_CONFIG = {
key: "inspectSummary", key: "inspectSummary",
label: FIELD_LABELS.inspectSummary, label: FIELD_LABELS.inspectSummary,
type: "textarea", type: "textarea",
required: true, required: false,
}, },
{ {
key: "positiveFind", key: "positiveFind",
@ -209,12 +207,12 @@ const FIELD_CONFIG = {
required: false, required: false,
}, },
], ],
preConsultationRecord: [ preConsultation: [
{ {
key: "consultationDate", key: "consultationDate",
label: FIELD_LABELS.consultationDate, label: FIELD_LABELS.consultationDate,
type: "date", type: "date",
required: true, required: false,
}, },
{ {
key: "chiefComplaint", key: "chiefComplaint",
@ -232,7 +230,7 @@ const FIELD_CONFIG = {
key: "pastMedicalHistory", key: "pastMedicalHistory",
label: FIELD_LABELS.pastMedicalHistory, label: FIELD_LABELS.pastMedicalHistory,
type: "textarea", type: "textarea",
required: false, required: true,
}, },
], ],
}; };

View File

@ -367,7 +367,7 @@ function normalizeMedicalType(raw) {
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate'; if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
if (s === 'outPatient') return 'outpatient'; if (s === 'outPatient') return 'outpatient';
if (s === 'inHospital') return 'inhospital'; if (s === 'inHospital') return 'inhospital';
if (s === 'preConsultationRecord') return 'preConsultationRecord'; if (s === 'preConsultation') return 'preConsultationRecord';
if (s === 'preConsultationRecord') return 'preConsultationRecord'; if (s === 'preConsultationRecord') return 'preConsultationRecord';
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate'; if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
return s; return s;

View File

@ -30,7 +30,7 @@ export const VISIT_RECORD_TEMPLATES = [
], ],
}, },
{ {
templateType: 'preConsultationRecord', templateType: 'preConsultation',
templateName: '预问诊记录', templateName: '预问诊记录',
service: { timeTitle: 'consultDate', timeName: '问诊日期' }, service: { timeTitle: 'consultDate', timeName: '问诊日期' },
templateList: [ templateList: [

View File

@ -20,7 +20,7 @@ const ALIAS_MAP = {
inspectTime: 'inspectDate', inspectTime: 'inspectDate',
inspectSummary: 'summary', inspectSummary: 'summary',
}, },
preConsultationRecord: { preConsultation: {
presentIllnessHistory: 'presentIllness', presentIllnessHistory: 'presentIllness',
pastMedicalHistory: 'pastHistory', pastMedicalHistory: 'pastHistory',
}, },

View File

@ -124,7 +124,7 @@ function normalizeMedicalType(raw) {
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate'; if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
if (s === 'outPatient') return 'outpatient'; if (s === 'outPatient') return 'outpatient';
if (s === 'inHospital') return 'inhospital'; if (s === 'inHospital') return 'inhospital';
if (s === 'preConsultationRecord') return 'preConsultationRecord'; if (s === 'preConsultation') return 'preConsultationRecord';
if (s === 'preConsultationRecord') return 'preConsultationRecord'; if (s === 'preConsultationRecord') return 'preConsultationRecord';
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate'; if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
return s; return s;

View File

@ -106,7 +106,7 @@ function normalizeMedicalType(raw) {
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate'; if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
if (s === 'outPatient') return 'outpatient'; if (s === 'outPatient') return 'outpatient';
if (s === 'inHospital') return 'inhospital'; if (s === 'inHospital') return 'inhospital';
if (s === 'preConsultationRecord') return 'preConsultationRecord'; if (s === 'preConsultation') return 'preConsultationRecord';
if (s === 'preConsultationRecord') return 'preConsultationRecord'; if (s === 'preConsultationRecord') return 'preConsultationRecord';
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate'; if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
return s; return s;

View File

@ -858,7 +858,7 @@ function normalizeMedicalType(raw) {
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate'; if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
if (s === 'outPatient') return 'outpatient'; if (s === 'outPatient') return 'outpatient';
if (s === 'inHospital') return 'inhospital'; if (s === 'inHospital') return 'inhospital';
if (s === 'preConsultationRecord') return 'preConsultationRecord'; if (s === 'preConsultation') return 'preConsultationRecord';
if (s === 'preConsultationRecord') return 'preConsultationRecord'; if (s === 'preConsultationRecord') return 'preConsultationRecord';
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate'; if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
return s; return s;

View File

@ -6,21 +6,28 @@ $text-color-sub: #999;
$primary-color: #0877F1; $primary-color: #0877F1;
.chat-page { .chat-page {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh;
background-color: #f5f5f5; background-color: #f5f5f5;
overflow: hidden; overflow: hidden;
} }
/* 患者信息栏样式 */ /* 患者信息栏样式 - 固定在顶部 */
.patient-info-bar { .patient-info-bar {
position: relative; position: fixed;
top: 0;
left: 0;
right: 0;
background: #fff; background: #fff;
border-bottom: 1rpx solid #f0f0f0; border-bottom: 1rpx solid #f0f0f0;
padding: 20rpx 32rpx; padding: 20rpx 32rpx;
z-index: 10; z-index: 10;
flex-shrink: 0; /* 防止被压缩 */ flex-shrink: 0;
} }
.patient-info-content { .patient-info-content {
@ -84,10 +91,14 @@ $primary-color: #0877F1;
} }
.chat-content { .chat-content {
flex: 1; position: fixed;
top: 100rpx; /* 患者信息栏高度,根据实际调整 */
left: 0;
right: 0;
bottom: 200rpx; /* 输入框高度,根据实际调整 */
box-sizing: border-box; box-sizing: border-box;
overflow-x: hidden; overflow-x: hidden;
min-height: 0; overflow-y: auto;
} }
.chat-content-compressed { .chat-content-compressed {
@ -352,19 +363,31 @@ $primary-color: #0877F1;
} }
.input-section { .input-section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff; background: #fff;
border-top: 1rpx solid #e0e0e0;
position: relative;
z-index: 200; z-index: 200;
padding-bottom: 40rpx;
flex-shrink: 0; flex-shrink: 0;
transform: translateZ(0); /* 开启硬件加速,提升性能 */
display: flex;
flex-direction: column;
}
.ai-assistant-slot {
width: 100%;
background: #fff;
} }
.input-toolbar { .input-toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 16rpx 20rpx; padding: 12rpx 20rpx;
padding-bottom: env(safe-area-inset-bottom);
gap: 12rpx; gap: 12rpx;
border-top: 1rpx solid #e0e0e0;
background: #fff;
} }
.voice-toggle-btn { .voice-toggle-btn {
@ -487,7 +510,8 @@ $primary-color: #0877F1;
justify-content: flex-start; justify-content: flex-start;
background: #fff; background: #fff;
border-top: 1rpx solid #eee; border-top: 1rpx solid #eee;
padding: 20rpx 0 40rpx 60rpx; padding: 20rpx 0 20rpx 60rpx;
padding-bottom: env(safe-area-inset-bottom);
gap: 40rpx 50rpx; gap: 40rpx 50rpx;
flex-wrap: wrap; flex-wrap: wrap;
background-color: #f5f5f5; background-color: #f5f5f5;
@ -517,7 +541,7 @@ $primary-color: #0877F1;
background-color: white; background-color: white;
border-top: 1rpx solid #e0e0e0; border-top: 1rpx solid #e0e0e0;
padding: 16rpx; padding: 16rpx;
margin-bottom: 40rpx; margin-bottom: env(safe-area-inset-bottom);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
@ -662,7 +686,7 @@ $primary-color: #0877F1;
width: 100%; width: 100%;
max-height: 80vh; max-height: 80vh;
padding: 20rpx; padding: 20rpx;
padding-bottom: calc(20rpx + 40rpx); padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
@ -794,7 +818,7 @@ $primary-color: #0877F1;
background-color: white; background-color: white;
width: auto; width: auto;
padding: 32rpx 20rpx 48rpx 20rpx; padding: 32rpx 20rpx 48rpx 20rpx;
padding-bottom: calc(48rpx + 40rpx); padding-bottom: calc(48rpx + env(safe-area-inset-bottom));
text-align: center; text-align: center;
margin: 0 auto; margin: 0 auto;
position: relative; position: relative;

View File

@ -12,6 +12,19 @@
button.loading && button.loadingText ? button.loadingText : button.text button.loading && button.loadingText ? button.loadingText : button.text
}}</text> }}</text>
</view> </view>
<!-- 病历类型选择弹窗 -->
<medical-case-type-selector
ref="typeSelectorRef"
@select="handleCaseTypeSelect"
/>
<!-- 进度显示弹窗 -->
<medical-case-progress
ref="progressRef"
@regenerate="handleRegenerateFromProgress"
@next="handleNextFromProgress"
/>
</view> </view>
</template> </template>
@ -19,6 +32,8 @@
import { ref, onMounted, onUnmounted } from "vue"; import { ref, onMounted, onUnmounted } from "vue";
import request from "@/utils/http.js"; import request from "@/utils/http.js";
import api from "@/utils/api.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({ const props = defineProps({
groupId: { groupId: {
@ -41,24 +56,11 @@ const props = defineProps({
type: String, type: String,
default: "", default: "",
}, },
typeSelectorRef: {
type: Object,
default: null,
},
progressRef: {
type: Object,
default: null,
},
}); });
const emit = defineEmits([ const emit = defineEmits(["streamText", "clearInput", "generatingStateChange"]);
"streamText",
"clearInput",
"generatingStateChange",
"caseTypeSelect",
"regenerateFromProgress",
"nextFromProgress",
]);
const typeSelectorRef = ref(null);
const progressRef = ref(null);
const isGenerating = ref(false); const isGenerating = ref(false);
const buttons = ref([ const buttons = ref([
@ -215,22 +217,143 @@ const handleAIAssistant = (button) => {
// //
const handleSupplementRecord = (button) => { const handleSupplementRecord = (button) => {
props.typeSelectorRef?.open(); typeSelectorRef.value?.open();
}; };
// //
const handleCaseTypeSelect = async (type) => { const handleCaseTypeSelect = async (type) => {
emit("caseTypeSelect", 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);
}; };
// //
const handleRegenerateFromProgress = (data) => { const handleRegenerateFromProgress = (data) => {
emit("regenerateFromProgress", data); const type = { id: data.caseType };
handleCaseTypeSelect(type);
}; };
// //
const handleNextFromProgress = (data) => { const handleNextFromProgress = (data) => {
emit("nextFromProgress", 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)
)}`,
});
}; };
// //

View File

@ -13,7 +13,7 @@
<view class="input-area"> <view class="input-area">
<textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..." <textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..."
@confirm="sendTextMessage" @focus="handleInputFocus" @input="handleInput" @confirm="sendTextMessage" @focus="handleInputFocus" @input="handleInput"
:auto-height="true" :show-confirm-bar="false" :hold-keyboard="true" :cursor-spacing="40" :auto-height="true" :show-confirm-bar="false" :hold-keyboard="true"
ref="textareaRef" ref="textareaRef"
/> />
<input v-else class="voice-input-btn" :class="{ recording: isRecording }" @touchstart="startRecord" <input v-else class="voice-input-btn" :class="{ recording: isRecording }" @touchstart="startRecord"
@ -176,9 +176,9 @@ const sendTextMessage = async () => {
const textToSend = inputText.value; const textToSend = inputText.value;
inputText.value = ""; inputText.value = "";
await sendMessage("text", textToSend); await sendMessage("text", textToSend);
// //
nextTick(() => { nextTick(() => {
// focus // focus

View File

@ -73,7 +73,7 @@ const CASE_TYPE_NAMES = {
outpatient: "门诊病历", outpatient: "门诊病历",
inhospital: "住院病历", inhospital: "住院病历",
physicalExaminationTemplate: "体检记录", physicalExaminationTemplate: "体检记录",
preConsultationRecord: "预问诊记录", preConsultation: "预问诊记录",
}; };
const FIELD_LABELS = { const FIELD_LABELS = {

View File

@ -43,7 +43,7 @@ const caseTypes = [
name: "体检记录", name: "体检记录",
}, },
{ {
id: "preConsultationRecord", id: "preConsultation",
name: "预问诊记录", name: "预问诊记录",
}, },
]; ];

View File

@ -165,30 +165,12 @@
:patientAccountId="chatInfo.userID || ''" :patientAccountId="chatInfo.userID || ''"
:patientId="patientId" :patientId="patientId"
:corpId="corpId" :corpId="corpId"
:typeSelectorRef="typeSelectorRef"
:progressRef="progressRef"
@streamText="handleStreamText" @streamText="handleStreamText"
@clearInput="handleClearInput" @clearInput="handleClearInput"
@generatingStateChange="handleGeneratingStateChange" @generatingStateChange="handleGeneratingStateChange"
@caseTypeSelect="handleCaseTypeSelect"
@regenerateFromProgress="handleRegenerateFromProgress"
@nextFromProgress="handleNextFromProgress"
/> />
</template> </template>
</ChatInput> </ChatInput>
<!-- 病历类型选择弹窗 -->
<MedicalCaseTypeSelector
ref="typeSelectorRef"
@select="handleCaseTypeSelect"
/>
<!-- 进度显示弹窗 -->
<MedicalCaseProgress
ref="progressRef"
@regenerate="handleRegenerateFromProgress"
@next="handleNextFromProgress"
/>
</view> </view>
</template> </template>
@ -224,9 +206,6 @@ import SystemMessage from "./components/system-message.vue";
import ConsultAccept from "./components/consult-accept.vue"; import ConsultAccept from "./components/consult-accept.vue";
import RejectReasonModal from "./components/reject-reason-modal.vue"; import RejectReasonModal from "./components/reject-reason-modal.vue";
import AIAssistantButtons from "./components/ai-assistant-buttons.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; const timChatManager = globalTimChatManager;
@ -304,8 +283,6 @@ const { initIMAfterLogin } = useAccountStore();
// //
const chatInputRef = ref(null); const chatInputRef = ref(null);
const aiAssistantRef = ref(null); const aiAssistantRef = ref(null);
const typeSelectorRef = ref(null);
const progressRef = ref(null);
const isGenerating = ref(false); const isGenerating = ref(false);
const groupId = ref(""); const groupId = ref("");
@ -1006,142 +983,6 @@ const handleGeneratingStateChange = (generating) => {
isGenerating.value = 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({ defineExpose({
sendCommonPhrase, sendCommonPhrase,

View File

@ -2,8 +2,7 @@
<view class="flex flex-col justify-center h-full bg-white"> <view class="flex flex-col justify-center h-full bg-white">
<view v-if="list.length === 0" class="w-full"> <view v-if="list.length === 0" class="w-full">
<empty-data text="暂无团队" /> <empty-data text="暂无团队" />
<view class="mt-10 mx-auto w-100 text-base text-primary border-auto leading-normal py-5 text-center rounded" <view class="mt-10 mx-auto w-100 text-base text-primary border-auto leading-normal py-5 text-center rounded">
@click="toCreateTeam()">
创建团队 创建团队
</view> </view>
</view> </view>
@ -88,10 +87,6 @@ const indicator = computed(() => ({
})) }))
const team = computed(() => list.value[current.value] || null); const team = computed(() => list.value[current.value] || null);
function toCreateTeam() {
uni.navigateTo({ url: '/pages/work/team/edit/team-edit' })
}
function toggle(val) { function toggle(val) {
const num = current.value + val; const num = current.value + val;
if (num > 0) { if (num > 0) {

View File

@ -180,29 +180,28 @@ export async function sendArticleMessage(article, options = {}) {
url: article.url || '', url: article.url || '',
messageType: 'article', messageType: 'article',
}; };
if (options.articleId && options.userId && options.customerId && options.corpId) {
const params = {
articleId: options.articleId,
userId: options.userId,
customerId: options.customerId,
corpId: options.corpId,
uniqueRecord: 'YES'
};
if (options.teamId) {
params.teamId = options.teamId;
}
const res = await api('addArticleSendRecord', params);
if (!res || !res.success) {
toast('发送文章失败');
return
}
customMessageData.sendId = res.data;
}
// 发送自定义消息 // 发送自定义消息
const result = await globalTimChatManager.sendCustomMessage(customMessageData); const result = await globalTimChatManager.sendCustomMessage(customMessageData);
if (result?.success) { if (result?.success) {
// 记录文章发送记录(异步,不阻塞)
if (options.articleId && options.userId && options.customerId && options.corpId) {
const params = {
articleId: options.articleId,
userId: options.userId,
customerId: options.customerId,
corpId: options.corpId,
uniqueRecord: 'YES'
};
if (options.teamId) {
params.teamId = options.teamId;
}
api('addArticleSendRecord', params).catch((err) => {
console.error('记录文章发送失败:', err);
});
}
// 写入服务记录留痕(异步,不阻塞) // 写入服务记录留痕(异步,不阻塞)
if (canWriteServiceRecord(options)) { if (canWriteServiceRecord(options)) {
const base = normalizeServiceRecordBase(options); const base = normalizeServiceRecordBase(options);
@ -288,13 +287,8 @@ export async function sendSurveyMessage(survey, options = {}) {
} }
); );
// 构建自定义消息
const customMessageData = buildSurveyMessage(survey, surveyLink); const customMessageData = buildSurveyMessage(survey, surveyLink);
customMessageData.name = options.customerName;
customMessageData.memberId = options.customerId;
customMessageData.corpId = options.corpId;
customMessageData.answerId = answerId;
customMessageData.surveryId = survey.surveryId;
customMessageData.isSystem = survey.createBy === 'system';
// 发送自定义消息 - 直接传递消息对象,不再进行额外序列化 // 发送自定义消息 - 直接传递消息对象,不再进行额外序列化
const result = await globalTimChatManager.sendCustomMessage(customMessageData); const result = await globalTimChatManager.sendCustomMessage(customMessageData);