diff --git a/.cursor/rules/Code-Standards.md b/.cursor/rules/Code-Standards.md new file mode 100644 index 00000000..9e5c2433 --- /dev/null +++ b/.cursor/rules/Code-Standards.md @@ -0,0 +1,10 @@ +--- +description: +alwaysApply: true +--- + +1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则 +2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于2行 +3. 有封装好的方法、组件需要复用,不要重复造轮子 +4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show) +5. 数据库的(created_at、updated_at、deleted_at)统一使用时间戳,不要使用字符串,并且我在查询器中一级格式化成字符串了,无需再次格式化 diff --git a/apps/web-antd/src/views/business/order/product-order/components/cells/OrderUserInfoCell.vue b/apps/web-antd/src/views/business/order/product-order/components/cells/OrderUserInfoCell.vue new file mode 100644 index 00000000..fbf9dd5a --- /dev/null +++ b/apps/web-antd/src/views/business/order/product-order/components/cells/OrderUserInfoCell.vue @@ -0,0 +1,148 @@ + + + + + + + + + + 下单用户: + + + {{ row.user?.nickname || '—' }} + + + ID:{{ row.user?.id ?? '—' }} + + + + + + + + + 医生: + + + {{ row.doctor.name }} + + + + + + + + + + + + — + + + + + + + + 就诊人: + + + {{ row.patient || '—' }} + + + {{ formatPatientAge(row) }} + + + + + + + diff --git a/apps/web-antd/src/views/business/order/product-order/config/table.ts b/apps/web-antd/src/views/business/order/product-order/config/table.ts index 02785f04..d6c34657 100644 --- a/apps/web-antd/src/views/business/order/product-order/config/table.ts +++ b/apps/web-antd/src/views/business/order/product-order/config/table.ts @@ -26,10 +26,10 @@ export const gridOptions: VxeGridProps = { slots: { default: 'order-store' }, }, { - field: 'user.avatarUrl', - title: '下单用户信息', - slots: { default: 'avatar' }, - width: 100, + field: 'order_user_info', + title: '用户/医生/就诊人', + slots: { default: 'order-user-info' }, + width: 210, }, { field: 'express_info', diff --git a/apps/web-antd/src/views/business/order/product-order/index.vue b/apps/web-antd/src/views/business/order/product-order/index.vue index 9d3f3d14..9e739c91 100644 --- a/apps/web-antd/src/views/business/order/product-order/index.vue +++ b/apps/web-antd/src/views/business/order/product-order/index.vue @@ -35,8 +35,10 @@ import { getOrderPriceAdjustConfig } from '#/api/order/priceAdjust'; import type { QuickDiscountOption } from '#/utils/pricePercentAdjust'; import { normalizeQuickOptions } from '#/utils/pricePercentAdjust'; import PrescriptionDetail from '#/views/doctor/doctor-reception/components/PrescriptionDetail.vue'; +import DoctorCardModal from '#/views/doctor/doctor/components/DoctorCardModal.vue'; import DetailModal from './components/detail.vue'; +import OrderUserInfoCell from './components/cells/OrderUserInfoCell.vue'; import FormModalDemo from './components/modal.vue'; import ProductOrderExportModal from './components/ProductOrderExportModal.vue'; import Refund from './components/refund.vue'; @@ -198,6 +200,25 @@ const [PrescriptionDetailModal, PrescriptionDetailModalApi] = useVbenModal({ connectedComponent: PrescriptionDetail, }); +const [DoctorCardModals, DoctorCardModalApi] = useVbenModal({ + connectedComponent: DoctorCardModal, +}); + +/** 从订单列表以只读模式打开医生档案 */ +function showOrderDoctorCard(row: Record) { + const doctor = row.doctor; + if (!doctor?.id && !row.su_id && !doctor?.su_id) { + message.warning('该订单无关联医生'); + return; + } + DoctorCardModalApi.setData({ + id: doctor?.id, + su_id: row.su_id ?? doctor?.su_id, + readonly: true, + }); + DoctorCardModalApi.open(); +} + const [TraceDrawer, traceDrawerApi] = useVbenDrawer({ connectedComponent: OrderTraceDrawer, }); @@ -501,6 +522,7 @@ const openOrderAmountVerify = () => { + @@ -547,10 +569,8 @@ const openOrderAmountVerify = () => { - - - {{ row.user.nickname }} - ID:{{ row.user.id }} + + diff --git a/apps/web-antd/src/views/doctor/doctor/api/index.ts b/apps/web-antd/src/views/doctor/doctor/api/index.ts index f9f3e58e..90c4919b 100644 --- a/apps/web-antd/src/views/doctor/doctor/api/index.ts +++ b/apps/web-antd/src/views/doctor/doctor/api/index.ts @@ -66,6 +66,16 @@ export async function updateDoctorSubjectsBindApi(data: Record) { return requestClient.post(`${prefix}update-subjects-bind`, data); } +/** doctor_id 为 su_id,更新科室绑定 */ +export async function updateDoctorDepartBindApi(data: Record) { + return requestClient.post(`${prefix}update-depart-bind`, data); +} + +/** doctor_id 为 su_id,更新职称绑定 */ +export async function updateDoctorTitleBindApi(data: Record) { + return requestClient.post(`${prefix}update-title-bind`, data); +} + /** * 新增医生 * @param data diff --git a/apps/web-antd/src/views/doctor/doctor/components/BindDepartModal.vue b/apps/web-antd/src/views/doctor/doctor/components/BindDepartModal.vue new file mode 100644 index 00000000..860d5b4b --- /dev/null +++ b/apps/web-antd/src/views/doctor/doctor/components/BindDepartModal.vue @@ -0,0 +1,94 @@ + + + + + + + + + diff --git a/apps/web-antd/src/views/doctor/doctor/components/BindTitleModal.vue b/apps/web-antd/src/views/doctor/doctor/components/BindTitleModal.vue new file mode 100644 index 00000000..9a9197f8 --- /dev/null +++ b/apps/web-antd/src/views/doctor/doctor/components/BindTitleModal.vue @@ -0,0 +1,92 @@ + + + + + + + + + diff --git a/apps/web-antd/src/views/doctor/doctor/components/DoctorCardModal.vue b/apps/web-antd/src/views/doctor/doctor/components/DoctorCardModal.vue index ab76b045..cf9d0e27 100644 --- a/apps/web-antd/src/views/doctor/doctor/components/DoctorCardModal.vue +++ b/apps/web-antd/src/views/doctor/doctor/components/DoctorCardModal.vue @@ -45,6 +45,8 @@ const gridApi = ref(); const doctorId = ref(0); const suId = ref(0); const hideStoresTab = ref(false); +/** 只读模式:从订单页等场景打开时禁止编辑与保存 */ +const readonly = ref(false); const loading = ref(false); const activeTab = ref('overview'); const cardData = ref(null); @@ -67,6 +69,8 @@ const userStore = useUserStore(); const canEditProfile = computed(() => canEditDoctorServiceUser(userStore.userInfo), ); +/** 是否允许编辑(权限 + 非只读) */ +const canEdit = computed(() => canEditProfile.value && !readonly.value); const savingServiceUser = ref(false); @@ -177,7 +181,7 @@ async function loadCard() { } async function saveOverview() { - if (!canEditProfile.value) return; + if (!canEdit.value) return; savingOverview.value = true; try { const res = await updateDoctor({ @@ -198,7 +202,7 @@ async function saveOverview() { } async function saveServiceUser() { - if (!canEditProfile.value || !suId.value) return; + if (!canEdit.value || !suId.value) return; savingServiceUser.value = true; try { const res = await updateDoctorServiceUserApi({ @@ -220,7 +224,7 @@ async function saveServiceUser() { } async function saveCredentials() { - if (!canEditProfile.value) return; + if (!canEdit.value) return; savingCredentials.value = true; try { await updateDoctorCredentialsApi({ @@ -363,6 +367,7 @@ const [Modal, modalApi] = useVbenModal({ doctorId.value = data?.id ?? data?.values?.id ?? 0; suId.value = data?.su_id ?? data?.values?.su_id ?? 0; hideStoresTab.value = !!data?.hideStoresTab; + readonly.value = !!data?.readonly; activeTab.value = 'overview'; prescriptionList.value = []; patientList.value = []; @@ -460,7 +465,7 @@ const [Modal, modalApi] = useVbenModal({ 医生头像 @@ -469,18 +474,18 @@ const [Modal, modalApi] = useVbenModal({ - + - + - + - + - + 保存基本信息 @@ -498,14 +503,14 @@ const [Modal, modalApi] = useVbenModal({ 账号头像 - + @@ -567,48 +572,60 @@ const [Modal, modalApi] = useVbenModal({ 资质证书管理 请上传清晰、无遮挡的证件扫描件或照片 - + 保存全部资质 - - + + + 未上传 - + + + 未上传 - + + + 未上传 - + + + 未上传 - + + + 未上传 - + + + 未上传 - + {{ credentialsForm.sign_type === 2 ? '手写扫描签名' : '电子认证签名' }} @@ -670,7 +687,7 @@ const [Modal, modalApi] = useVbenModal({ 🖥️ 未开通 PC 端管理权限 开通后,医生可使用电脑端登录系统进行高级管理操作,如查看完整报表等。 - 立即开通后台账号 + 立即开通后台账号 diff --git a/apps/web-antd/src/views/doctor/doctor/components/cells/DoctorBasicInfoCell.vue b/apps/web-antd/src/views/doctor/doctor/components/cells/DoctorBasicInfoCell.vue new file mode 100644 index 00000000..649775c5 --- /dev/null +++ b/apps/web-antd/src/views/doctor/doctor/components/cells/DoctorBasicInfoCell.vue @@ -0,0 +1,95 @@ + + + + + + + + {{ row.name || '-' }} + + + + + 手机 + + + + wxID + {{ row.su_id ?? '-' }} + + + + + + diff --git a/apps/web-antd/src/views/doctor/doctor/components/cells/DoctorProfessionalInfoCell.vue b/apps/web-antd/src/views/doctor/doctor/components/cells/DoctorProfessionalInfoCell.vue new file mode 100644 index 00000000..372202ad --- /dev/null +++ b/apps/web-antd/src/views/doctor/doctor/components/cells/DoctorProfessionalInfoCell.vue @@ -0,0 +1,115 @@ + + + + + + + 科室 + + {{ row.depart?.name || '绑定' }} + + + + 职称 + + {{ row.title?.name || '绑定' }} + + + + 诊疗科目 + + {{ row.subjects?.name || '绑定' }} + + + + 所属诊所 + + {{ formatStores(row.stores) || '绑定' }} + + + + + + + diff --git a/apps/web-antd/src/views/doctor/doctor/config/table.ts b/apps/web-antd/src/views/doctor/doctor/config/table.ts index 6668db7e..64c48744 100644 --- a/apps/web-antd/src/views/doctor/doctor/config/table.ts +++ b/apps/web-antd/src/views/doctor/doctor/config/table.ts @@ -29,27 +29,24 @@ export const gridOptions: VxeGridProps = { }, columns: [ { type: 'checkbox', width: 60 }, - { field: 'id', align: 'left', title: 'ID', width: 100 }, - { field: 'name', align: 'left', title: '名称', slots: { default: 'name' } }, - { field: 'mobile', align: 'left', title: '手机号', slots: { default: 'mobile' } }, + { field: 'id', align: 'left', title: 'ID', width: 80 }, { - field: 'avatar', + field: 'doctor_basic_info', align: 'left', - title: '头像', - slots: { default: 'avatar' }, - width: 130, + title: '基础信息', + width: 220, + slots: { default: 'doctor_basic_info' }, }, - { field: 'su_id', title: 'wxID' }, - { field: 'stores', title: '所属诊所', slots: { default: 'stores' } }, { - field: 'subjects', - title: '诊疗科目', - slots: { default: 'subjects' }, - minWidth: 120, + field: 'doctor_professional_info', + align: 'left', + title: '执业信息', + width: 280, + slots: { default: 'doctor_professional_info' }, }, { field: 'service_user', title: '审核状态', slots: { default: 'service-user' } }, - { field: 'created_at', title: '注册时间' }, - { type: 'html', title: '操作', width: 340, slots: { default: 'action' } }, + { field: 'created_at', title: '注册时间', width: 160 }, + { type: 'html', title: '操作', width: 260, slots: { default: 'action' } }, ], keepSource: true, pagerConfig: {}, diff --git a/apps/web-antd/src/views/doctor/doctor/index.vue b/apps/web-antd/src/views/doctor/doctor/index.vue index ff2c1c7c..02060e94 100644 --- a/apps/web-antd/src/views/doctor/doctor/index.vue +++ b/apps/web-antd/src/views/doctor/doctor/index.vue @@ -5,15 +5,18 @@ import { ref } from 'vue'; import { Page, useVbenModal } from '@vben/common-ui'; -import { Button, Image, message, Tag } from 'ant-design-vue'; +import { Button, message, Tag } from 'ant-design-vue'; import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { TableAction } from '#/components/table-action'; -import SensitiveText from '#/components/sensitive-text/SensitiveText.vue'; import { auditApi, openPcWindowsApi, refuseApi } from './api'; +import BindDepart from './components/BindDepartModal.vue'; import BindStore from './components/BindStoreModal.vue'; import BindSubjects from './components/BindSubjectsModal.vue'; +import BindTitle from './components/BindTitleModal.vue'; +import DoctorBasicInfoCell from './components/cells/DoctorBasicInfoCell.vue'; +import DoctorProfessionalInfoCell from './components/cells/DoctorProfessionalInfoCell.vue'; import FormModalDemo from './components/modal.vue'; import DoctorCardModal from './components/DoctorCardModal.vue'; import { formOptions } from './config/search'; @@ -52,13 +55,20 @@ const [BindSubjectsModal, BindSubjectsModalApi] = useVbenModal({ connectedComponent: BindSubjects, }); +const [BindDepartModal, BindDepartModalApi] = useVbenModal({ + connectedComponent: BindDepart, +}); + +const [BindTitleModal, BindTitleModalApi] = useVbenModal({ + connectedComponent: BindTitle, +}); + const [DoctorCardModals, DoctorCardModalApi] = useVbenModal({ connectedComponent: DoctorCardModal, }); const showModal = (data = {}, isUpdate = false) => { formModalApi.setData({ - // 表单值 values: data, update: isUpdate, gridApi, @@ -66,15 +76,16 @@ const showModal = (data = {}, isUpdate = false) => { formModalApi.open(); }; -const showBindStoreModal = (data: number) => { +/** 打开绑定诊所弹窗 */ +const showBindStoreModal = (row: Record) => { BindStoreModalApi.setData({ - // 表单值 - values: data, + values: row, gridApi, }); BindStoreModalApi.open(); }; +/** 打开绑定诊疗科目弹窗 */ const showBindSubjectsModal = (row: Record) => { BindSubjectsModalApi.setData({ values: row, @@ -83,6 +94,24 @@ const showBindSubjectsModal = (row: Record) => { BindSubjectsModalApi.open(); }; +/** 打开编辑科室弹窗 */ +const showBindDepartModal = (row: Record) => { + BindDepartModalApi.setData({ + values: row, + gridApi, + }); + BindDepartModalApi.open(); +}; + +/** 打开编辑职称弹窗 */ +const showBindTitleModal = (row: Record) => { + BindTitleModalApi.setData({ + values: row, + gridApi, + }); + BindTitleModalApi.open(); +}; + const showDoctorCard = (row: Record) => { DoctorCardModalApi.setData({ id: row.id, @@ -119,6 +148,8 @@ const refuse = (id: number) => { + + @@ -128,22 +159,10 @@ const refuse = (id: number) => { label: '新增', type: 'primary', icon: 'ant-design:plus-outlined', - // auth: ['超级医生', 'sys:user:save'], onClick: showModal.bind(null), }, ]" - :drop-down-actions="[ - // { - // label: '删除', - // icon: 'ant-design:delete-outlined', - // ifShow: hasTopTableDropDownActions, - // // auth: ['超级医生', 'sys:user:save'], - // popConfirm: { - // title: '确定删除吗?', - // confirm: deleteApi.bind(null, false), - // }, - // }, - ]" + :drop-down-actions="[]" > @@ -153,24 +172,17 @@ const refuse = (id: number) => { - - + + - - - {{ row.name }} - - - - - - - - {{ item?.store?.name }} - - - - {{ row.subjects?.name ?? '-' }} + + 已通过 @@ -203,30 +215,13 @@ const refuse = (id: number) => { type: 'link', icon: 'uil:edit', size: 'small', - // auth: ['doctor', 'sys:role:detail'], - onClick: showModal.bind(null, row), - }, - { - label: '绑定诊所', - type: 'link', - icon: 'uil:edit', - size: 'small', - // auth: ['doctor', 'sys:role:detail'], - onClick: showBindStoreModal.bind(null, row), - }, - { - label: '绑定诊疗科目', - type: 'link', - icon: 'uil:edit', - size: 'small', - onClick: showBindSubjectsModal.bind(null, row), + onClick: showModal.bind(null, row, true), }, { label: '开通PC端', type: 'link', icon: 'uil:edit', size: 'small', - // auth: ['doctor', 'sys:role:detail'], onClick: openPcWindows.bind(null, row.id), }, ]" @@ -237,7 +232,6 @@ const refuse = (id: number) => { icon: 'uil:edit', size: 'small', ifShow: row.service_user?.status === 1, - // auth: ['doctor', 'sys:role:detail'], onClick: audit.bind(null, row.id), }, { @@ -246,7 +240,6 @@ const refuse = (id: number) => { icon: 'uil:edit', size: 'small', ifShow: row.service_user?.status === 1, - // auth: ['doctor', 'sys:role:detail'], onClick: refuse.bind(null, row.id), }, ]"
请上传清晰、无遮挡的证件扫描件或照片
开通后,医生可使用电脑端登录系统进行高级管理操作,如查看完整报表等。
- {{ item?.store?.name }} -