Files
xk-admin/apps/web-antd/src/components/wx-user-patient/WxUserPatientDrawer.vue
李琦 92149f43a6 fix:
1. 优化了审方pc的效果
2. 就诊人信息优化
2026-07-18 14:16:25 +08:00

358 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
/**
* 微信用户 + 就诊人档案抽屉
* 展示小程序用户基础信息、就诊人信息及挂号/处方/订单记录(跨医生)
*/
import { h, ref } from 'vue';
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
import {
Avatar,
Button,
Descriptions,
Empty,
Spin,
Table,
Tabs,
Tag,
message,
} from 'ant-design-vue';
import SensitiveText from '#/components/sensitive-text/SensitiveText.vue';
import { resolveAvatarUrl } from '#/views/system/store-input/utils/inputUser';
import PrescriptionDetail from '#/views/doctor/doctor-reception/components/PrescriptionDetail.vue';
import OrderDetail from '#/views/business/order/product-order/components/detail.vue';
import {
getUserPatientOrderListApi,
getUserPatientPrescriptionListApi,
getUserPatientProfileDetailApi,
getUserPatientRegisterListApi,
} from './api';
import VisitTypeTag from './VisitTypeTag.vue';
defineOptions({ name: 'WxUserPatientDrawer' });
const defaultAvatar = '/img/user-default-avatar.png';
const upId = ref(0);
const loading = ref(false);
const activeTab = ref('info');
const profile = ref<Record<string, any> | null>(null);
const registerList = ref<any[]>([]);
const prescriptionList = ref<any[]>([]);
const orderList = ref<any[]>([]);
const registerLoading = ref(false);
const prescriptionLoading = ref(false);
const orderLoading = ref(false);
const [PrescriptionDetailModal, PrescriptionDetailModalApi] = useVbenModal({
connectedComponent: PrescriptionDetail,
});
const [OrderDetailModal, OrderDetailModalApi] = useVbenModal({
connectedComponent: OrderDetail,
});
const [Drawer, drawerApi] = useVbenDrawer({
footer: false,
onOpenChange(isOpen: boolean) {
if (!isOpen) {
profile.value = null;
registerList.value = [];
prescriptionList.value = [];
orderList.value = [];
return;
}
const data = drawerApi.getData<{ upId?: number; patientName?: string }>();
upId.value = Number(data?.upId || 0);
activeTab.value = 'info';
if (upId.value > 0) {
void loadDetail();
}
},
});
/** 解析头像 URL */
function avatarSrc(raw?: string) {
const s = String(raw ?? '').trim();
if (!s) return defaultAvatar;
return resolveAvatarUrl(s) || defaultAvatar;
}
async function loadDetail() {
loading.value = true;
try {
profile.value = await getUserPatientProfileDetailApi(upId.value);
} catch (e) {
console.error(e);
message.error('获取用户信息失败');
} finally {
loading.value = false;
}
}
async function loadRegisterList() {
registerLoading.value = true;
try {
const res = await getUserPatientRegisterListApi(upId.value);
registerList.value = res?.items ?? [];
} catch (e) {
console.error(e);
message.error('获取挂号记录失败');
} finally {
registerLoading.value = false;
}
}
async function loadPrescriptionList() {
prescriptionLoading.value = true;
try {
const res = await getUserPatientPrescriptionListApi(upId.value);
prescriptionList.value = res?.items ?? [];
} catch (e) {
console.error(e);
message.error('获取处方记录失败');
} finally {
prescriptionLoading.value = false;
}
}
async function loadOrderList() {
orderLoading.value = true;
try {
const res = await getUserPatientOrderListApi(upId.value);
orderList.value = res?.items ?? [];
} catch (e) {
console.error(e);
message.error('获取订单记录失败');
} finally {
orderLoading.value = false;
}
}
/** Tab 切换时懒加载对应列表 */
function onTabChange(key: string | number) {
const k = String(key);
activeTab.value = k;
if (k === 'register' && registerList.value.length === 0) {
void loadRegisterList();
} else if (k === 'prescription' && prescriptionList.value.length === 0) {
void loadPrescriptionList();
} else if (k === 'order' && orderList.value.length === 0) {
void loadOrderList();
}
}
function openPrescription(id: number) {
PrescriptionDetailModalApi.setData({ values: id });
PrescriptionDetailModalApi.open();
}
function openOrder(id: number) {
OrderDetailModalApi.setData({ id });
OrderDetailModalApi.open();
}
const registerColumns = [
{ title: '订单编号', dataIndex: 'order_no', key: 'order_no' },
{ title: '医生', dataIndex: 'doctor', key: 'doctor' },
{ title: '诊所', dataIndex: 'store', key: 'store' },
{
title: '费用',
dataIndex: 'price',
key: 'price',
customRender: ({ text }: { text: number }) =>
`¥${Number(text || 0).toFixed(2)}`,
},
{
title: '支付',
dataIndex: 'is_pay',
key: 'is_pay',
customRender: ({ text }: { text: number }) =>
h(Tag, { color: text === 1 ? 'green' : 'red' }, () =>
text === 1 ? '已支付' : '未支付',
),
},
{ title: '状态', dataIndex: 'status_text', key: 'status_text' },
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at' },
];
const prescriptionColumns = [
{
title: '处方编号',
dataIndex: 'prescription_no',
key: 'prescription_no',
customRender: ({ text, record }: { text: string; record: any }) =>
h(
Button,
{ type: 'link', onClick: () => openPrescription(record.id) },
() => text,
),
},
{
title: '就诊类型',
dataIndex: 'is_online',
key: 'is_online',
customRender: ({ text }: { text: number }) =>
h(VisitTypeTag, { isOnline: text }),
},
{ title: '医生', dataIndex: 'doctor', key: 'doctor' },
{ title: '诊所', dataIndex: 'store', key: 'store' },
{ title: '诊断', dataIndex: 'clinical_diagnose', key: 'clinical_diagnose' },
{
title: '状态',
dataIndex: 'status',
key: 'status',
customRender: ({ text }: { text: number }) => {
const map: Record<number, string> = {
0: '待审核',
1: '已通过',
2: '未通过',
3: '无需审核',
};
return map[text] || '未知';
},
},
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at' },
];
const orderColumns = [
{
title: '订单号',
dataIndex: 'order_no',
key: 'order_no',
customRender: ({ text, record }: { text: string; record: any }) =>
h(Button, { type: 'link', onClick: () => openOrder(record.id) }, () => text),
},
{
title: '就诊类型',
dataIndex: 'is_online',
key: 'is_online',
customRender: ({ text }: { text: number }) =>
h(VisitTypeTag, { isOnline: text }),
},
{ title: '医生', dataIndex: 'doctor', key: 'doctor' },
{ title: '诊所', dataIndex: 'store', key: 'store' },
{
title: '实付',
dataIndex: 'total_pay_price',
key: 'total_pay_price',
customRender: ({ text }: { text: number }) =>
`¥${Number(text || 0).toFixed(2)}`,
},
{
title: '支付',
dataIndex: 'is_pay',
key: 'is_pay',
customRender: ({ text }: { text: number }) =>
h(Tag, { color: text === 1 ? 'green' : 'red' }, () =>
text === 1 ? '已支付' : '未支付',
),
},
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at' },
];
</script>
<template>
<Drawer class="w-[720px]" title="微信用户 / 就诊人">
<PrescriptionDetailModal />
<OrderDetailModal />
<Spin :spinning="loading">
<template v-if="profile">
<!-- 小程序用户 -->
<div class="mb-4 flex items-center gap-3 rounded-lg bg-gray-50 p-3 dark:bg-slate-800">
<Avatar :size="48" :src="avatarSrc(profile.user?.avatarurl)" />
<div class="min-w-0">
<div class="text-sm font-medium">
{{ profile.user?.nickname || '—' }}
</div>
<div class="text-xs text-gray-500">
用户 ID{{ profile.user?.id ?? '—' }}
</div>
</div>
</div>
<Tabs :active-key="activeTab" @change="onTabChange">
<Tabs.TabPane key="info" tab="就诊人信息">
<Descriptions :column="2" bordered size="small">
<Descriptions.Item label="姓名">
{{ profile.patient?.name || '—' }}
</Descriptions.Item>
<Descriptions.Item label="性别">
{{
profile.patient?.sex === 1
? '男'
: profile.patient?.sex === 2
? '女'
: '—'
}}
</Descriptions.Item>
<Descriptions.Item label="年龄">
{{ profile.patient?.age ? `${profile.patient.age}` : '—' }}
</Descriptions.Item>
<Descriptions.Item label="手机号">
<SensitiveText
v-if="profile.patient"
:record="profile.patient"
field="mobile"
/>
<span v-else></span>
</Descriptions.Item>
<Descriptions.Item label="身份证" :span="2">
<SensitiveText
v-if="profile.patient"
:record="profile.patient"
field="id_card"
/>
<span v-else></span>
</Descriptions.Item>
</Descriptions>
</Tabs.TabPane>
<Tabs.TabPane key="register" tab="挂号记录">
<Spin :spinning="registerLoading">
<Table
v-if="registerList.length"
:columns="registerColumns"
:data-source="registerList"
:pagination="false"
row-key="id"
size="small"
/>
<Empty v-else description="暂无挂号记录" />
</Spin>
</Tabs.TabPane>
<Tabs.TabPane key="prescription" tab="处方记录">
<Spin :spinning="prescriptionLoading">
<Table
v-if="prescriptionList.length"
:columns="prescriptionColumns"
:data-source="prescriptionList"
:pagination="false"
row-key="id"
size="small"
/>
<Empty v-else description="暂无处方记录" />
</Spin>
</Tabs.TabPane>
<Tabs.TabPane key="order" tab="订单记录">
<Spin :spinning="orderLoading">
<Table
v-if="orderList.length"
:columns="orderColumns"
:data-source="orderList"
:pagination="false"
row-key="id"
size="small"
/>
<Empty v-else description="暂无订单记录" />
</Spin>
</Tabs.TabPane>
</Tabs>
</template>
<Empty v-else-if="!loading" description="暂无数据" />
</Spin>
</Drawer>
</template>