fix: 拒诊、聊天消息优化
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled

This commit is contained in:
2025-08-30 15:31:03 +08:00
parent 70bf4f3019
commit bf3d02124d
11 changed files with 253 additions and 48 deletions

View File

@@ -6,16 +6,29 @@ import { useUserStore } from '@vben/stores';
import {Avatar, Button, Image} from 'ant-design-vue';
import {getChatMessageRegisterInfoApi} from "#/views/business/chat/api";
import {getPrescriptionCheckStatusApi} from "#/views/business/order/prescription/api";
import PrescriptionDetail from '#/views/doctor/doctor-reception/components/PrescriptionDetail.vue';
import previewMedia from '../composables/useMediaPreview.ts';
import {useThemeStore} from '../stores/theme.ts';
import AudioMessage from './AudioMessage.vue';
import {receptionApi} from "#/views/doctor/doctor-reception/api";
import {useChatStore} from "#/views/business/chat/stores/chat";
import { useUserStore as chatUseUserStore } from '../stores/user';
import {sendMessage} from "#/views/business/chat/utils/request";
import RefusalOfTreatmentModal
from "#/views/doctor/doctor-reception/components/RefusalOfTreatmentModal.vue";
const chatUserStore = chatUseUserStore();
const userStore = useUserStore();
const chatStore = useChatStore();
const themeStore = useThemeStore();
const [RefusalOfTreatmentModals, RefusalOfTreatmentModalApi] = useVbenModal({
connectedComponent: RefusalOfTreatmentModal,
});
const props = defineProps({
message: {
type: Object,
@@ -52,19 +65,22 @@ const prescriptionStatusMap = {
2: '未通过',
};
// 添加挂号状态映射
// 添加挂号状态映射 0待支付1已支付待接诊2接诊中3已结束4已取消5待评价6已评价7已拒诊
const registerStatusMap = {
0: '待就诊',
1: '已缴费',
2: '已就诊',
3: '已取消',
4: '已退费'
0: '待支付',
1: '待接诊',
2: '接诊中',
3: '已结束',
4: '已取消',
5: '待评价',
6: '已评价',
7: '已拒诊',
};
// 添加支付状态映射
const payStatusMap = {
0: '未支付',
1: '已支付'
1: '已支付',
};
const checkPrescriptionStatus = (id) => {
@@ -76,26 +92,48 @@ const checkPrescriptionStatus = (id) => {
})
};
const getRegisterCardInfo = () => {
getChatMessageRegisterInfoApi({ id: props.message.content.id }).then((res) => {
// eslint-disable-next-line vue/no-mutating-props
props.message.content = res;
});
};
// 接诊操作
const acceptRegister = async (registerId, orderNo) => {
if (registerOperating.value) return;
registerOperating.value = true;
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1500));
// 更新消息状态
if (props.message.content && props.message.content.id === registerId) {
props.message.content.status = 2; // 已就诊
}
receptionApi(registerId).then(async (value) => {
// message.success('接诊成功');
// 更新消息状态
getRegisterCardInfo();
// 显示成功提示
console.log(`接诊成功 - 挂号ID: ${registerId}, 订单号: ${orderNo}`);
const messageObj = {
type: 'text',
content: '我已接诊,请简单说明您的情况~',
time: new Date().toLocaleTimeString().slice(0, 5),
senderId: `${chatUserStore.currentUser.id}`,
receiverId: chatStore.currentFriend.id,
isSent: true,
read: true,
duration: 0,
};
// 这里可以调用实际的API
// await acceptRegisterApi({ id: registerId, order_no: orderNo });
chatStore.addMessage(messageObj, chatUserStore.currentUser.id);
await sendMessage({
senderId: chatUserStore.currentUser.id,
receiverId: chatStore.currentFriend.id,
roomId: chatStore.currentFriend.room_id,
type: 'text',
content: '我已接诊,请简单说明您的情况~',
});
});
} catch (error) {
console.error('接诊失败:', error);
@@ -110,26 +148,13 @@ const rejectRegister = async (registerId, orderNo) => {
registerOperating.value = true;
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1500));
// 更新消息状态
if (props.message.content && props.message.content.id === registerId) {
props.message.content.status = 3; // 已取消
}
// 显示成功提示
console.log(`拒诊成功 - 挂号ID: ${registerId}, 订单号: ${orderNo}`);
// 这里可以调用实际的API
// await rejectRegisterApi({ id: registerId, order_no: orderNo, reason: '医生拒诊' });
} catch (error) {
console.error('拒诊失败:', error);
} finally {
registerOperating.value = false;
}
RefusalOfTreatmentModalApi.setData({
values: {
register_id: registerId,
},
});
RefusalOfTreatmentModalApi.open();
registerOperating.value = false;
};
const bubbleClasses = computed(() => {
@@ -167,6 +192,8 @@ const formatDuration = (seconds) => {
};
const formatTextWithLinks = (text) => {
console.log(text, props.message)
if (!text) return text; // 或 return text (但可能返回 undefined/null根据需求调整)
const urlRegex = /(https?:\/\/\S+)/g;
return text.replaceAll(
urlRegex,
@@ -226,6 +253,10 @@ const getSexText = (sex) => {
return sex === 1 ? '男' : '女';
};
if (props.message.type === 'register') {
getRegisterCardInfo();
}
</script>
<template>
@@ -233,6 +264,7 @@ const getSexText = (sex) => {
:class="isSent ? 'flex-row-reverse' : 'flex-row'"
class="mb-4 flex items-start gap-3"
>
<RefusalOfTreatmentModals />
<PrescriptionDetailModal />
<!-- 头像 -->
<div
@@ -358,7 +390,10 @@ const getSexText = (sex) => {
'bg-blue-100 text-blue-800': message.content.status === 1,
'bg-green-100 text-green-800': message.content.status === 2,
'bg-red-100 text-red-800': message.content.status === 3,
'bg-gray-100 text-gray-800': message.content.status === 4
'bg-gray-100 text-gray-800': message.content.status === 4,
'bg-purple-100 text-purple-800': message.content.status === 5,
'bg-indigo-100 text-indigo-800': message.content.status === 6,
'bg-pink-100 text-pink-800': message.content.status === 7,
}"
class="text-xs px-2 py-1 rounded-full font-medium"
>
@@ -424,7 +459,7 @@ const getSexText = (sex) => {
</div>
<!-- 接诊/拒诊按钮 - 只在待就诊状态显示 -->
<div v-if="message.content.status === 0" class="flex gap-2 mt-2">
<div v-if="message.content.status === 1" class="flex gap-2 mt-2">
<Button
type="primary"
size="small"

View File

@@ -263,7 +263,8 @@ export const useChatStore = defineStore('chat', () => {
if (message.type === 'image') lastMessage = '[图片]';
if (message.type === 'video') lastMessage = '[视频]';
if (message.type === 'audio') lastMessage = '[语音]';
if (message.type === 'prescription') lastMessage = '[处方]';
if (message.type === 'prescription') lastMessage = '[处方信息]';
if (message.type === 'register') lastMessage = '[挂号信息]';
if (message.type === 'video-call' || message.type === 'audio-call')
lastMessage = '[通话]';
@@ -321,6 +322,12 @@ export const useChatStore = defineStore('chat', () => {
};
try {
if (
newMessage.type === 'register' ||
messageData.type === 'prescription'
) {
newMessage.content = JSON.parse(messageData.content);
}
// await chatDB.saveMessage(newMessage, currentUserId, senderId);
if (currentFriend.value && senderId == currentFriend.value.id) {
@@ -339,6 +346,8 @@ export const useChatStore = defineStore('chat', () => {
if (newMessage.type === 'image') lastMessage = '[图片]';
if (newMessage.type === 'video') lastMessage = '[视频]';
if (newMessage.type === 'audio') lastMessage = '[语音]';
if (newMessage.type === 'prescription') lastMessage = '[处方信息]';
if (newMessage.type === 'register') lastMessage = '[挂号信息]';
if (
newMessage.type === 'video-call' ||
newMessage.type === 'audio-call'

View File

@@ -112,7 +112,7 @@ export const sendMessage = (data) => {
const apiUrl = '/im-api/send-to-user';
// 消息类型映射 - 根据用户提供的完整映射更新
const messageTypeMap = {
const messageTypeMap: any = {
text: 0,
image: 1,
audio: 2,

View File

@@ -23,7 +23,7 @@ export const gridOptions: VxeGridProps<RowType> = {
{ field: 'user_patient.name', align: 'left', title: '就诊人名称' },
{ field: 'store.name', align: 'left', title: '开方诊所' },
{ field: 'status', title: '状态', slots: { default: 'status' } },
// { field: 'created_at', title: '下单时间' },
{ field: 'created_at', title: '创建时间' },
{
type: 'html',
title: '操作',

View File

@@ -102,6 +102,7 @@ const openPrescriptionSourceModal = (id) => {
<Tag v-else-if="row.status === 2" color="red">拒绝审核</Tag>
<Tag v-else-if="row.status === 3" color="purple">无需审核</Tag>
<Tag v-else-if="row.status === 4" color="green">无需审核</Tag>
<!-- <p>{{ row.created_at }}</p>-->
</div>
</template>
<template #toolbar-tools></template>

View File

@@ -23,9 +23,10 @@ export const gridOptions: VxeGridProps<RowType> = {
{ field: 'doctor_info.depart.name', title: '科室' },
{ field: 'user_patient.name', title: '就诊人名称' },
{ field: 'store.name', title: '开方诊所' },
{ field: 'type', title: '状态', slots: { default: 'type' } },
{ field: 'prescription', title: '处方', slots: { default: 'prescription'} },
{ field: 'status', title: '状态', slots: { default: 'status' } },
// { field: 'created_at', title: '下单时间' },
{ field: 'created_at', title: '创建时间' },
// {
// type: 'html',
// title: '操作',

View File

@@ -73,6 +73,12 @@ const openPrescriptionDetail = (values) => {
</div>
</template>
<template #type="{ row }">
<div class="mt-3">
<Tag v-if="row.type === 0" color="purple">线下</Tag>
<Tag v-else-if="row.type === 1" color="green">线上</Tag>
</div>
</template>
<template #status="{ row }">
<div class="mt-3">
<!-- 状态:0待支付1已支付待接诊2接诊中3已结束4已取消5待评价6已评价7已拒诊-->
@@ -84,6 +90,7 @@ const openPrescriptionDetail = (values) => {
<Tag v-else-if="row.status === 5" color="purple">待评价</Tag>
<Tag v-else-if="row.status === 6" color="purple">已评价</Tag>
<Tag v-else-if="row.status === 7" color="red">已拒诊</Tag>
<!-- <p>{{ row.created_at }}</p>-->
</div>
</template>
<template #toolbar-tools></template>

View File

@@ -108,6 +108,20 @@ export async function getPrescriptionInfoApi(id: number) {
return requestClient.get<any>('prescription/detail', { params: { id } });
}
/**
* 获取拒接接诊列表
*/
export async function refuseReceptionListApi() {
return requestClient.get<any>(`${prefix}refuse-reception-list`);
}
/**
* 拒诊
* @param data
*/
export async function refuseReceptionApi(data: any) {
return requestClient.post<any>(`${prefix}refuse-reception`, data);
}
/**
* 检查是否有药物相冲

View File

@@ -0,0 +1,91 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message, Radio, RadioGroup } from 'ant-design-vue';
import {refuseReceptionApi, refuseReceptionListApi} from '#/views/doctor/doctor-reception/api';
const registerId = ref('');
const refuseReception = ref('');
const refuseReceptionList = ref([]);
const activeRegister = ref();
const getPatientListByReceptionApi = ref();
const updateReceptionStatusApi = ref();
const [Modal, modalApi] = useVbenModal({
fullscreenButton: false,
draggable: true,
onCancel() {
modalApi.close();
},
onConfirm: async () => {
if (registerId.value === '') {
message.error('请选择接诊单');
return;
}
if (refuseReception.value === '') {
message.error('请选择拒诊原因');
return;
}
refuseReceptionApi({
register_id: registerId.value,
refuse_reason: refuseReception.value,
}).then(() => {
if (getPatientListByReceptionApi.value) {
getPatientListByReceptionApi.value();
}
if (updateReceptionStatusApi.value) {
updateReceptionStatusApi.value(activeRegister.value);
}
modalApi.close();
message.success('拒绝成功');
});
// setUpdateDiagnosis.value(selectDiagnosisList.value);
},
onOpenChange(isOpen: boolean) {
if (isOpen) {
// 更新患者列表
getPatientListByReceptionApi.value = isOpen
? modalApi.getData()?.getPatientListByReception
: null;
// 更新患者信息
updateReceptionStatusApi.value = isOpen
? modalApi.getData()?.updateReceptionStatus
: null;
// // 当前挂号信息
// activeRegister.value = isOpen
// ? modalApi.getData()?.activeRegister
// : null;
// console.log('模态框:', activeRegister.value)
const { values } = modalApi.getData<Record<string, any>>();
if (values) {
registerId.value = values.register_id;
refuseReceptionListApi().then((res) => {
refuseReceptionList.value = res;
});
console.log(values.register_id, 'sssssssssss');
// selectDiagnosisList.value = values;
}
}
},
});
</script>
<template>
<Modal class="w-[20%]" title="拒诊原因">
挂号ID{{ registerId }}
<RadioGroup style="align-items: center" v-model:value="refuseReception">
<Radio
v-for="item in refuseReceptionList"
:key="item.key"
:value="item.value"
class="mt-3 w-full"
>
{{ item.value }}
</Radio>
</RadioGroup>
</Modal>
</template>
<style lang="scss"></style>

View File

@@ -50,6 +50,8 @@ import DiagnosisModal from './components/DiagnosisModal.vue';
import DoctorOrderModal from './components/DoctorOrderModal.vue';
import PrescriptionDetail from './components/PrescriptionDetail.vue';
import WesternModal from './components/WesternModal.vue';
import RefusalOfTreatmentModal
from "#/views/doctor/doctor-reception/components/RefusalOfTreatmentModal.vue";
interface Patient {
id: number;
@@ -298,6 +300,7 @@ const setVisible = (value, instruction = ''): void => {
visible.value = value;
}
};
/**
* 选择华女这操作
* @param patient
@@ -598,6 +601,11 @@ function updateDoctorOrder(values) {
const [PrescriptionDetailModal, PrescriptionDetailModalApi] = useVbenModal({
connectedComponent: PrescriptionDetail,
});
const [RefusalOfTreatmentModals, RefusalOfTreatmentModalApi] = useVbenModal({
connectedComponent: RefusalOfTreatmentModal,
});
const openWesternModal = () => {
// 打开西药处方模态框逻辑
WesternDrugModalApi.setData({
@@ -646,6 +654,34 @@ function reception() {
});
}
const updateReceptionStatus = () => {
receptionStatus.value = 0;
tabType.value = 0;
console.log('这', {
receptionStatus: receptionStatus.value,
tabType: tabType.value
})
}
/**
* 拒诊
*/
function refuseReception() {
RefusalOfTreatmentModalApi.setData({
values: {
register_id: localStorage.getItem(`doctorReception-id`)
},
getPatientListByReception,
updateReceptionStatus,
// 在patients中循环查询id = selectPatientId.value的
// let patient = patients.
// selectPatient(localStorage.getItem(`doctorReception-id`));
})
RefusalOfTreatmentModalApi.open();
}
/**
* 结束诊断
*/
@@ -1099,6 +1135,7 @@ watch(
<template>
<div :class="leftShow === true? 'container-box-grid':'container-box'">
<RefusalOfTreatmentModals />
<!-- 如果leftShow === true展示收起反之则显示展开-->
<div>
<Button :class="`${leftShow === true? 'w-full': 'sticky top-5 left-5'}`" type="primary" @click="leftShow = !leftShow">{{ leftShow === true ? '收起患者列表' : '展开患者列表' }}</Button>
@@ -1148,6 +1185,15 @@ watch(
>
接诊
</Button>
<Button
v-if="receptionStatus === 1"
class="send-btn"
type="primary"
danger
@click="refuseReception"
>
拒诊
</Button>
<Button
v-if="receptionStatus === 2"
class="send-btn"
@@ -2113,6 +2159,7 @@ watch(
.send-btn {
cursor: pointer;
margin-right: 10px;
float: right;
}

View File

@@ -282,12 +282,12 @@ export const infoModalFormProps: VbenFormProps = {
},
fieldName: 'sign_type',
formItemClass: 'col-span-6',
dependencies: {
show: (values) => {
return values.id == null;
},
triggerFields: ['id'],
},
// dependencies: {
// show: (values) => {
// return values.id == null;
// },
// triggerFields: ['id'],
// },
label: '签名类型',
rules: 'required',
},