更新
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
<div v-else class="chat-content">
|
||||
<UIKitProvider language="zh-CN" theme="light">
|
||||
<div class="chat-layout">
|
||||
<ConversationList class="conversation-list" />
|
||||
<!-- 嵌入式问诊窗口仅与当前患者会话,不展示全局会话列表 -->
|
||||
<Chat class="chat-area">
|
||||
<MessageList :conversationID="conversationId" />
|
||||
<MessageInput>
|
||||
@@ -129,7 +129,6 @@ import {
|
||||
} from '@/utils/tuicall-error'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import {
|
||||
ConversationList,
|
||||
Chat,
|
||||
MessageList,
|
||||
MessageInput,
|
||||
@@ -183,6 +182,8 @@ const BIND_ROOM_MAX_ATTEMPTS = 40
|
||||
const BIND_ROOM_RETRY_MS = 100
|
||||
|
||||
const localCallRecorder = new CallLocalRecorder()
|
||||
/** 与 server .env [trtc] ISLOCHOSTVOD 一致:由 getCallSignature 下发,false 时不做浏览器本地录制 */
|
||||
const isLochostVodEnabled = ref(false)
|
||||
const localRecordingBadge = ref(false)
|
||||
let localRecordingRound = 0
|
||||
let startLocalRecTimer: ReturnType<typeof setTimeout> | null = null
|
||||
@@ -199,6 +200,10 @@ function clearLocalRecordingStartTimer() {
|
||||
}
|
||||
}
|
||||
|
||||
function syncLochostVodFromSignature(res: { isLochostVod?: boolean }) {
|
||||
isLochostVodEnabled.value = Boolean(res?.isLochostVod)
|
||||
}
|
||||
|
||||
function finalizeLocalRecordingAndAttach(): Promise<void> {
|
||||
if (finalizeLocalRecordingPromise) {
|
||||
return finalizeLocalRecordingPromise
|
||||
@@ -206,6 +211,9 @@ function finalizeLocalRecordingAndAttach(): Promise<void> {
|
||||
finalizeLocalRecordingPromise = (async () => {
|
||||
clearLocalRecordingStartTimer()
|
||||
localRecordingBadge.value = false
|
||||
if (!isLochostVodEnabled.value) {
|
||||
return
|
||||
}
|
||||
localCallRecorder.beginStopNow()
|
||||
const id = recordingDiagnosisIdSnapshot ?? diagnosisId.value
|
||||
try {
|
||||
@@ -621,8 +629,12 @@ function setupCallRoomBinding() {
|
||||
if (next === CALL_STATUS_CONNECTED) {
|
||||
void tryBindRoomIfReady()
|
||||
clearLocalRecordingStartTimer()
|
||||
if (!isLochostVodEnabled.value) {
|
||||
previousCallStatus = next
|
||||
return
|
||||
}
|
||||
const roundAtSchedule = localRecordingRound
|
||||
|
||||
|
||||
// 立即尝试启动录制,不要等待 1200ms
|
||||
const attemptStartRecording = (retryCount = 0) => {
|
||||
if (roundAtSchedule !== localRecordingRound) {
|
||||
@@ -889,6 +901,7 @@ watch(() => activeConversation.value, async (newConversation) => {
|
||||
patient_id: newConversation.userProfile.userID.replace("patient_", ""),
|
||||
diagnosis_id: diagnosisId.value
|
||||
})
|
||||
syncLochostVodFromSignature(res)
|
||||
patientUserId.value = res.patientUserId
|
||||
assistant_ids.value = res.assistant_id
|
||||
console.log('会话切换后获取到 assistant_id:', assistant_ids.value, '完整响应:', res)
|
||||
@@ -922,6 +935,7 @@ const open = async (data: { patientId: number; patientName: string; diagnosisId?
|
||||
patient_id: data.patientId,
|
||||
diagnosis_id: data.diagnosisId || 0
|
||||
})
|
||||
syncLochostVodFromSignature(res)
|
||||
assistant_ids.value = res.assistant_id
|
||||
console.log('后端返回的签名数据:', res)
|
||||
console.log('assistant_id:', assistant_ids.value)
|
||||
@@ -1062,6 +1076,7 @@ const startGroupVideoCall = async () => {
|
||||
patient_id: patientId.value!,
|
||||
diagnosis_id: diagnosisId.value
|
||||
})
|
||||
syncLochostVodFromSignature(res)
|
||||
assistant_ids.value = res.assistant_id
|
||||
console.log('重新获取 assistant_id:', assistant_ids.value)
|
||||
}
|
||||
@@ -1344,13 +1359,9 @@ defineExpose({ open })
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.conversation-list {
|
||||
width: 285px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chat-area {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
@@ -227,7 +227,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const handlePreview = (url: string) => {
|
||||
previewUrl.value = url
|
||||
previewUrl.value = props.excludeDomain ? getImageUrl(url) : url
|
||||
showPreview.value = true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<span v-if="disabled" class="medicine-name-readonly">{{ modelValue || '—' }}</span>
|
||||
<el-select
|
||||
v-else
|
||||
:model-value="modelValue"
|
||||
class="medicine-name-select w-full"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="搜索药材名称或拼音首字母"
|
||||
:remote-method="remoteMethod"
|
||||
:loading="loading"
|
||||
clearable
|
||||
@visible-change="onVisibleChange"
|
||||
@update:model-value="onUpdate"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.name" />
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { medicineLists } from '@/api/medicine'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{ disabled: false }
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
const options = ref<{ id: number; name: string }[]>([])
|
||||
|
||||
function ensureCurrentInOptions() {
|
||||
const v = (props.modelValue || '').trim()
|
||||
if (!v) {
|
||||
return
|
||||
}
|
||||
if (!options.value.some((o) => o.name === v)) {
|
||||
options.value = [{ id: 0, name: v }, ...options.value]
|
||||
}
|
||||
}
|
||||
|
||||
const remoteMethod = async (query: string) => {
|
||||
loading.value = true
|
||||
try {
|
||||
const q = (query || '').trim()
|
||||
const res = await medicineLists({
|
||||
name: q || undefined,
|
||||
page_no: 1,
|
||||
page_size: 100,
|
||||
status: 1
|
||||
})
|
||||
options.value = res.lists || []
|
||||
ensureCurrentInOptions()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
options.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onVisibleChange = (open: boolean) => {
|
||||
if (open && options.value.length === 0) {
|
||||
remoteMethod('')
|
||||
}
|
||||
}
|
||||
|
||||
const onUpdate = (val: string) => {
|
||||
emit('update:modelValue', val || '')
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
ensureCurrentInOptions()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.medicine-name-readonly {
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
.medicine-name-select.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -52,7 +52,7 @@
|
||||
<el-input v-model="formData.tongue" placeholder="舌象" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<!-- <el-col :span="12">
|
||||
<el-form-item label="舌象详情" prop="tongue_image">
|
||||
<el-input v-model="formData.tongue_image" placeholder="舌象详情" />
|
||||
</el-form-item>
|
||||
@@ -61,7 +61,7 @@
|
||||
<el-form-item label="脉象详情" prop="pulse_condition">
|
||||
<el-input v-model="formData.pulse_condition" placeholder="脉象详情" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
<el-col :span="24">
|
||||
<el-form-item label="临床诊断" prop="clinical_diagnosis">
|
||||
<el-input
|
||||
@@ -93,28 +93,29 @@
|
||||
<span class="ml-2 text-gray-500">元</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-for="(herb, index) in formData.herbs" :key="index" class="herb-item">
|
||||
<el-form-item :label="`中药${index + 1}`">
|
||||
<div class="flex gap-2 items-center flex-wrap">
|
||||
<el-input
|
||||
v-model="herb.name"
|
||||
placeholder="药品名称"
|
||||
style="width: 160px"
|
||||
/>
|
||||
<div class="herbs-editor-grid">
|
||||
<div
|
||||
v-for="(herb, index) in formData.herbs"
|
||||
:key="'herb-' + index"
|
||||
class="herb-editor-card"
|
||||
>
|
||||
<div class="herb-editor-card__title">中药 {{ index + 1 }}</div>
|
||||
<MedicineNameSelect v-model="herb.name" class="herb-editor-card__select" />
|
||||
<div class="herb-editor-card__row">
|
||||
<el-input-number
|
||||
v-model="herb.dosage"
|
||||
:min="0.1"
|
||||
:step="1"
|
||||
:precision="1"
|
||||
placeholder="剂量(克)"
|
||||
style="width: 120px"
|
||||
class="herb-editor-card__dose"
|
||||
/>
|
||||
<span class="text-gray-500">克</span>
|
||||
<span class="text-gray-500 shrink-0">克</span>
|
||||
<el-button type="danger" size="small" link @click="handleRemoveHerb(index)">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -547,6 +548,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MedicineNameSelect from '@/components/medicine-name-select/index.vue'
|
||||
import feedback from '@/utils/feedback'
|
||||
import { prescriptionAdd, prescriptionDetail, prescriptionGetByAppointment, prescriptionVoid, tcmDiagnosisDetail, prescriptionLibraryLists } from '@/api/tcm'
|
||||
import { getDictData } from '@/api/app'
|
||||
@@ -1253,9 +1255,42 @@ defineExpose({
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.herb-item {
|
||||
.herbs-editor-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.herb-editor-card {
|
||||
min-width: 0;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.herb-editor-card__title {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.herb-editor-card__select {
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.herb-editor-card__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.herb-editor-card__dose {
|
||||
flex: 1;
|
||||
min-width: 88px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user