1. 更新框架
2. 修复框架更新后导致的问题
3. 消息系统升级
This commit is contained in:
李琦
2026-08-10 20:08:50 +08:00
parent f0a33a628b
commit b82754e36b
33 changed files with 5202 additions and 886 deletions

View File

@@ -1,38 +1,93 @@
<script lang="ts" setup>
import { ref } from 'vue';
/**
* 医生预填审核弹窗(列表页)
* 打开时按 ID 拉取详情,避免只显示通过/拒绝而看不到预填资料
*/
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message, Radio, RadioGroup, Textarea } from 'ant-design-vue';
import {
Avatar,
Descriptions,
DescriptionsItem,
Image,
Radio,
RadioGroup,
Spin,
Textarea,
message,
} from 'ant-design-vue';
import { auditDoctorInput } from '../api';
import SensitiveText from '#/components/sensitive-text/SensitiveText.vue';
import { auditDoctorInput, getDoctorInputDetail } from '../api';
import {
inputUserDisplayName,
normalizeInputUser,
resolveAvatarUrl,
} from '../../store-input/utils/inputUser';
const auditStatus = ref<number>(1);
const auditRemark = ref<string>('');
const currentRow = ref<any>(null);
const detail = ref<Record<string, any> | null>(null);
const gridApi = ref<any>(null);
const loading = ref(false);
const detailLoading = ref(false);
const display = computed(() => detail.value || currentRow.value || null);
function typeText(type: number) {
if (type === 1) return '中医医生';
if (type === 2) return '西医医生';
return '-';
}
function signTypeText(signType: number) {
if (signType === 1) return '电子签名';
if (signType === 2) return '手写签名';
return '-';
}
function imgSrc(url?: string | null) {
return resolveAvatarUrl(url) || String(url || '');
}
async function loadDetail(id: number) {
detailLoading.value = true;
detail.value = null;
try {
const res = await getDoctorInputDetail(id);
detail.value = res || null;
normalizeInputUser(detail.value);
} catch {
message.error('加载预填详情失败');
} finally {
detailLoading.value = false;
}
}
/**
* 提交医生预填审核结果
* 通过 onConfirm 注册,与 Vben Modal 确认按钮对接
*/
const handleAudit = async () => {
if (!currentRow.value) {
const id = Number(display.value?.id || currentRow.value?.id || 0);
if (!id) {
message.error('数据错误');
return;
}
loading.value = true;
modalApi.setState({ confirmLoading: true });
try {
await auditDoctorInput({
id: currentRow.value.id,
id,
status: auditStatus.value,
audit_remark: auditRemark.value,
});
message.success('审核成功');
gridApi.value?.reload();
gridApi.value?.reload?.();
gridApi.value?.query?.();
modalApi.close();
auditStatus.value = 1;
auditRemark.value = '';
@@ -52,13 +107,20 @@ const [ModalComponent, modalApi] = useVbenModal({
modalApi.close();
},
onConfirm: handleAudit,
onOpenChange(isOpen: boolean) {
if (isOpen) {
const { values, gridApi: api } = modalApi.getData<Record<string, any>>();
currentRow.value = values;
gridApi.value = api;
auditStatus.value = 1;
auditRemark.value = '';
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
detail.value = null;
currentRow.value = null;
return;
}
const { values, gridApi: api } = modalApi.getData<Record<string, any>>() || {};
currentRow.value = values;
gridApi.value = api;
auditStatus.value = 1;
auditRemark.value = '';
const id = Number(values?.id || 0);
if (id > 0) {
await loadDetail(id);
}
},
});
@@ -67,25 +129,121 @@ const [ModalComponent, modalApi] = useVbenModal({
<template>
<ModalComponent
title="审核医生预填"
class="w-[500px]"
class="w-[820px] max-w-[96vw]"
:confirm-loading="loading"
>
<div class="space-y-4 p-4">
<div>
<div class="mb-2 font-semibold">审核结果:</div>
<RadioGroup v-model:value="auditStatus">
<Radio :value="1">审核通过</Radio>
<Radio :value="2">审核拒绝</Radio>
</RadioGroup>
<Spin :spinning="detailLoading">
<div v-if="display" class="space-y-4 p-2">
<Descriptions
:column="{ xxl: 2, xl: 2, lg: 2, md: 1, sm: 1, xs: 1 }"
bordered
size="small"
>
<DescriptionsItem label="预填 ID">#{{ display.id }}</DescriptionsItem>
<DescriptionsItem label="医生姓名">{{ display.name || '-' }}</DescriptionsItem>
<DescriptionsItem label="手机号">
<SensitiveText :record="display" field="mobile" />
</DescriptionsItem>
<DescriptionsItem label="身份证">
<SensitiveText :record="display" field="idcard" />
</DescriptionsItem>
<DescriptionsItem label="身份">{{ typeText(Number(display.type)) }}</DescriptionsItem>
<DescriptionsItem label="签名类型">
{{ signTypeText(Number(display.sign_type)) }}
</DescriptionsItem>
<DescriptionsItem label="科室">{{ display.depart?.name || '-' }}</DescriptionsItem>
<DescriptionsItem label="职称">{{ display.titleInfo?.name || '-' }}</DescriptionsItem>
<DescriptionsItem label="录入人">
<div class="flex items-center gap-2">
<Avatar
v-if="resolveAvatarUrl(display.inputUser?.avatar)"
:src="resolveAvatarUrl(display.inputUser?.avatar)"
:size="28"
/>
<span>{{ inputUserDisplayName(display.inputUser) }}</span>
</div>
</DescriptionsItem>
<DescriptionsItem label="擅长" :span="2">{{ display.good_at || '-' }}</DescriptionsItem>
<DescriptionsItem label="简介" :span="2">{{ display.intro || '-' }}</DescriptionsItem>
</Descriptions>
<div class="flex flex-wrap gap-3">
<div v-if="display.avatar" class="cert">
<div class="cert-label">头像</div>
<Image :src="imgSrc(display.avatar)" :width="80" :height="80" :preview="true" />
</div>
<div v-if="display.qualification" class="cert">
<div class="cert-label">资格证书</div>
<Image
:src="imgSrc(display.qualification)"
:width="80"
:height="80"
:preview="true"
/>
</div>
<div v-if="display.practicing" class="cert">
<div class="cert-label">执业证书</div>
<Image
:src="imgSrc(display.practicing)"
:width="80"
:height="80"
:preview="true"
/>
</div>
<div v-if="display.title" class="cert">
<div class="cert-label">职称证书</div>
<Image :src="imgSrc(display.title)" :width="80" :height="80" :preview="true" />
</div>
<div v-if="display.card_up" class="cert">
<div class="cert-label">身份证正面</div>
<Image :src="imgSrc(display.card_up)" :width="80" :height="80" :preview="true" />
</div>
<div v-if="display.card_down" class="cert">
<div class="cert-label">身份证反面</div>
<Image :src="imgSrc(display.card_down)" :width="80" :height="80" :preview="true" />
</div>
<div v-if="display.sign_image" class="cert">
<div class="cert-label">签名</div>
<Image :src="imgSrc(display.sign_image)" :width="80" :height="80" :preview="true" />
</div>
</div>
<div>
<div class="mb-2 font-semibold">审核结果:</div>
<RadioGroup v-model:value="auditStatus">
<Radio :value="1">审核通过</Radio>
<Radio :value="2">审核拒绝</Radio>
</RadioGroup>
</div>
<div>
<div class="mb-2 font-semibold">审核备注:</div>
<Textarea
v-model:value="auditRemark"
:rows="4"
placeholder="请输入审核备注(可选)"
/>
</div>
</div>
<div>
<div class="mb-2 font-semibold">审核备注:</div>
<Textarea
v-model:value="auditRemark"
:rows="4"
placeholder="请输入审核备注(可选)"
/>
<div v-else-if="!detailLoading" class="p-8 text-center text-muted-foreground">
暂无预填数据
</div>
</div>
</Spin>
</ModalComponent>
</template>
<style scoped>
.cert {
display: flex;
flex-direction: column;
gap: 6px;
padding: 8px;
border-radius: 10px;
border: 1px solid hsl(var(--border));
background: hsl(var(--card, var(--background)));
}
.cert-label {
font-size: 12px;
color: hsl(var(--muted-foreground));
}
</style>