From 6b713a2d6cca0bcfe9b0b02c170d1f036ffc07d7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 13 Jun 2026 15:00:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/src/views/tcm/diagnosis/edit.vue | 71 ++++++++- .../controller/tcm/DiagnosisController.php | 4 +- .../app/adminapi/logic/tcm/DiagnosisLogic.php | 143 +++++++++++++++--- .../add_diagnosis_edit_patient_basic_menu.sql | 15 ++ 4 files changed, 208 insertions(+), 25 deletions(-) create mode 100644 server/sql/1.9.20260613/add_diagnosis_edit_patient_basic_menu.sql diff --git a/admin/src/views/tcm/diagnosis/edit.vue b/admin/src/views/tcm/diagnosis/edit.vue index 3582f2b4..f405eb33 100644 --- a/admin/src/views/tcm/diagnosis/edit.vue +++ b/admin/src/views/tcm/diagnosis/edit.vue @@ -67,17 +67,37 @@ > 基本信息 + + + + + +
@@ -87,6 +107,7 @@ v-model="formData.id_card" placeholder="请输入身份证号" maxlength="18" + :disabled="!canEditPatientBasicFields" @focus="handleIdCardFocus" @blur="handleIdCardBlur" /> @@ -120,6 +141,7 @@ v-model="formData.phone" placeholder="请输入手机号" maxlength="11" + :disabled="!canEditPatientBasicFields" @focus="handlePhoneFocus" @blur="handlePhoneBlur" /> @@ -127,7 +149,7 @@ - + @@ -144,11 +166,14 @@ :max="150" placeholder="请输入年龄" class="w-full" + :disabled="!canEditPatientBasicFields" + :controls="canEditPatientBasicFields" /> +
@@ -778,8 +803,24 @@ const submitting = ref(false) /** 拥有后手机号可正常编辑,失焦不再强制脱敏 */ const hasPhonePlainPermission = computed(() => hasPermission(['tcm.diagnosis/phonePlain'])) const hasDailyRecordPermission = computed(() => hasPermission(['tcm.diagnosis/dailyRecord'])) -/** 编辑且无明文权限:只读脱敏,点击切换查看完整号 */ -const showPhoneMaskedEdit = computed(() => mode.value === 'edit' && !hasPhonePlainPermission.value) +const patientBasicLocked = ref(false) +const canEditPatientBasicFromApi = ref(true) +const latestPrescriptionOrder = ref<{ + id: number + fulfillment_status: number + fulfillment_status_text: string +} | null>(null) +const canEditPatientBasicFields = computed(() => { + if (viewOnly.value) return false + if (mode.value === 'add') return true + return canEditPatientBasicFromApi.value +}) +/** 编辑且无明文权限或基本信息锁定:只读脱敏,点击切换查看完整号 */ +const showPhoneMaskedEdit = computed(() => { + if (mode.value !== 'edit') return false + if (!canEditPatientBasicFields.value) return true + return !hasPhonePlainPermission.value +}) const phoneRevealUnlocked = ref(false) const activeTab = ref('basic') const drawerTitle = computed(() => { @@ -1167,6 +1208,9 @@ async function loadDiagnosisDetailIntoForm(id: number): Promise { originalPhone.value = data.phone || '' originalIdCard.value = data.id_card || '' + patientBasicLocked.value = !!data.patient_basic_locked + canEditPatientBasicFromApi.value = data.can_edit_patient_basic !== false + latestPrescriptionOrder.value = data.latest_prescription_order ?? null data.phone = hasPhonePlainPermission.value ? data.phone || '' : maskPhone(data.phone || '') data.id_card = maskIdCard(data.id_card || '') @@ -1178,12 +1222,19 @@ async function loadDiagnosisDetailIntoForm(id: number): Promise { formData.value.diabetes_discovery_year = y == null || y === '' ? '' : String(y) } +const resetPatientBasicLockState = () => { + patientBasicLocked.value = false + canEditPatientBasicFromApi.value = true + latestPrescriptionOrder.value = null +} + const open = async (type: string, id?: number) => { viewOnly.value = false mode.value = type visible.value = true activeTab.value = 'basic' // 重置到基本信息标签页 phoneRevealUnlocked.value = false + resetPatientBasicLockState() // 加载字典数据 await getDictOptions() @@ -1206,6 +1257,7 @@ const openViewOnly = async (id: number) => { visible.value = true activeTab.value = 'basic' phoneRevealUnlocked.value = false + resetPatientBasicLockState() await getDictOptions() await loadDiagnosisDetailIntoForm(id) } @@ -1370,6 +1422,19 @@ defineExpose({ } } + .diagnosis-patient-basic-fieldset:disabled { + opacity: 1; + :deep(.el-input__wrapper), + :deep(.el-input-number), + :deep(.el-input-number__decrease), + :deep(.el-input-number__increase), + :deep(.el-radio), + :deep(.el-radio__input) { + cursor: not-allowed; + pointer-events: none; + } + } + :deep(.el-form) { padding-bottom: 20px; font-size: 12px; diff --git a/server/app/adminapi/controller/tcm/DiagnosisController.php b/server/app/adminapi/controller/tcm/DiagnosisController.php index d1eb592f..2d1fe24a 100755 --- a/server/app/adminapi/controller/tcm/DiagnosisController.php +++ b/server/app/adminapi/controller/tcm/DiagnosisController.php @@ -87,7 +87,7 @@ class DiagnosisController extends BaseAdminController public function edit() { $params = (new DiagnosisValidate())->post()->goCheck('edit'); - $result = DiagnosisLogic::edit($params); + $result = DiagnosisLogic::edit($params, $this->adminInfo); if ($result) { return $this->success('编辑成功', [], 1, 1); } @@ -116,7 +116,7 @@ class DiagnosisController extends BaseAdminController { $params = (new DiagnosisValidate())->goCheck('id'); - $result = DiagnosisLogic::detail($params); + $result = DiagnosisLogic::detail($params, $this->adminInfo); DiagnosisLogic::markAssignRead((int) ($params['id'] ?? 0), $this->adminId); return $this->data($result); } diff --git a/server/app/adminapi/logic/tcm/DiagnosisLogic.php b/server/app/adminapi/logic/tcm/DiagnosisLogic.php index dbe8e5e1..dfdb4d12 100755 --- a/server/app/adminapi/logic/tcm/DiagnosisLogic.php +++ b/server/app/adminapi/logic/tcm/DiagnosisLogic.php @@ -28,6 +28,7 @@ use app\common\model\DiagnosisViewRecord; use app\common\model\doctor\Appointment; use app\common\model\auth\Admin; use app\common\model\auth\AdminRole; +use app\adminapi\logic\auth\AuthLogic; use app\adminapi\logic\doctor\DoctorNoteLogic; use app\adminapi\logic\doctor\AppointmentLogic; use app\adminapi\logic\tcm\TrackingNoteLogic; @@ -73,8 +74,8 @@ class DiagnosisLogic extends BaseLogic } } - // 生成患者ID - $params['patient_id'] = self::generatePatientId(); + // 新患者先占位 0,写入后 patient_id 与自增 id 对齐 + $params['patient_id'] = 0; // 处理既往史数组 if (isset($params['past_history']) && is_array($params['past_history'])) { @@ -119,6 +120,7 @@ class DiagnosisLogic extends BaseLogic } $model = Diagnosis::create($params); + $model = self::syncPatientIdWithDiagnosisId($model); // 图片写入 doctor_note 表 if (!empty($newTongueImages) || !empty($newReportFiles)) { @@ -130,7 +132,7 @@ class DiagnosisLogic extends BaseLogic } // 自动为患者创建 TRTC 账号 - self::createPatientTrtcAccount($model->id); + self::createPatientTrtcAccount($model->patient_id); return $model->id; } catch (\Exception $e) { @@ -140,16 +142,20 @@ class DiagnosisLogic extends BaseLogic } /** - * @notes 生成患者ID - * @return int + * @notes 新患者首张诊单:patient_id 与自增 id 对齐 + * @param Diagnosis $model + * @return Diagnosis */ - private static function generatePatientId(): int + private static function syncPatientIdWithDiagnosisId(Diagnosis $model): Diagnosis { - // 获取当前最大的患者ID - $maxPatientId = Diagnosis::max('id') ?? 10000000; - - // 返回下一个患者ID - return $maxPatientId + 1; + if ((int) $model->patient_id === (int) $model->id) { + return $model; + } + + Diagnosis::where('id', $model->id)->update(['patient_id' => $model->id]); + $model->patient_id = (int) $model->id; + + return $model; } /** @@ -157,9 +163,23 @@ class DiagnosisLogic extends BaseLogic * @param array $params * @return bool */ - public static function edit(array $params): bool + /** 诊单编辑:患者基本信息字段(姓名/身份证/手机/性别/年龄) */ + private const PATIENT_BASIC_FIELDS = ['patient_name', 'id_card', 'phone', 'gender', 'age']; + + public static function edit(array $params, array $adminInfo = []): bool { try { + if (!empty($params['id']) && $adminInfo !== []) { + $existing = Diagnosis::find((int) $params['id']); + if ($existing && !self::canEditPatientBasicInfo((int) $params['id'], $adminInfo)) { + if (self::patientBasicFieldsChanged($existing, $params)) { + self::setError('最近业务订单未完成,无法修改患者基本信息'); + + return false; + } + } + } + // 检查手机号是否重复(排除当前记录) if (!empty($params['phone'])) { $exists = Diagnosis::where('phone', $params['phone']) @@ -250,7 +270,7 @@ class DiagnosisLogic extends BaseLogic * @param $params * @return array */ - public static function detail($params): array + public static function detail($params, array $adminInfo = []): array { $diagnosis = Diagnosis::findOrEmpty($params['id'])->toArray(); @@ -317,7 +337,17 @@ class DiagnosisLogic extends BaseLogic $diagnosis['local_hospital_visit_date'] = $diagnosis['diagnosis_date']; } $diagnosis['is_view'] =DiagnosisViewRecord::where(['diagnosis_id'=> $diagnosis['id'], 'user_id' =>$params['user_id']??0])->find() ? 1 : 0; - + + $latestPo = self::getLatestPrescriptionOrderRowForDiagnosis((int) ($diagnosis['id'] ?? 0)); + $patientBasicLocked = $latestPo !== null && (int) ($latestPo['fulfillment_status'] ?? 0) !== 3; + $diagnosis['patient_basic_locked'] = $patientBasicLocked; + $diagnosis['can_edit_patient_basic'] = self::canEditPatientBasicInfo((int) ($diagnosis['id'] ?? 0), $adminInfo); + $diagnosis['latest_prescription_order'] = $latestPo !== null ? [ + 'id' => (int) ($latestPo['id'] ?? 0), + 'fulfillment_status' => (int) ($latestPo['fulfillment_status'] ?? 0), + 'fulfillment_status_text'=> PrescriptionOrderLogic::fulfillmentStatusLabel((int) ($latestPo['fulfillment_status'] ?? 0)), + ] : null; + return $diagnosis; } @@ -486,11 +516,11 @@ class DiagnosisLogic extends BaseLogic } /** - * 诊单下用于指派日志快照的「最新」业务单:按 create_time 最新,其次 id(避免误用更早下单但 id 更大的记录)。 + * 诊单下「最新」业务单:按 create_time 最新,其次 id(避免误用更早下单但 id 更大的记录)。 * - * @return array{creator_id: int, create_time: int}|null + * @return array|null */ - private static function getLatestPrescriptionOrderSnapshotForDiagnosis(int $diagnosisId): ?array + private static function getLatestPrescriptionOrderRowForDiagnosis(int $diagnosisId): ?array { if ($diagnosisId <= 0) { return null; @@ -500,19 +530,88 @@ class DiagnosisLogic extends BaseLogic ->whereNull('delete_time') ->order('create_time', 'desc') ->order('id', 'desc') - ->field(['creator_id', 'create_time']) + ->field(['id', 'creator_id', 'create_time', 'fulfillment_status']) ->find(); if ($row === null || $row === []) { return null; } + return $row; + } + + /** + * 诊单下用于指派日志快照的「最新」业务单:按 create_time 最新,其次 id(避免误用更早下单但 id 更大的记录)。 + * + * @return array{creator_id: int, create_time: int}|null + */ + private static function getLatestPrescriptionOrderSnapshotForDiagnosis(int $diagnosisId): ?array + { + $row = self::getLatestPrescriptionOrderRowForDiagnosis($diagnosisId); + if ($row === null) { + return null; + } + return [ 'creator_id' => (int) ($row['creator_id'] ?? 0), 'create_time' => (int) ($row['create_time'] ?? 0), ]; } + /** + * 菜单权限 tcm.diagnosis/editPatientBasic:最近业务订单未完成时仍可改患者基本信息 + */ + public static function hasEditPatientBasicPermission(array $adminInfo): bool + { + if (!empty($adminInfo['root']) && (int) $adminInfo['root'] === 1) { + return true; + } + $perms = AuthLogic::getAuthByAdminId((int) ($adminInfo['admin_id'] ?? 0)); + + return in_array('tcm.diagnosis/editPatientBasic', $perms, true); + } + + /** + * 是否允许编辑诊单患者基本信息(无业务单 / 最近一单已完成 / 具备 editPatientBasic 权限) + */ + public static function canEditPatientBasicInfo(int $diagnosisId, array $adminInfo): bool + { + $latest = self::getLatestPrescriptionOrderRowForDiagnosis($diagnosisId); + if ($latest === null) { + return true; + } + if ((int) ($latest['fulfillment_status'] ?? 0) === 3) { + return true; + } + + return self::hasEditPatientBasicPermission($adminInfo); + } + + /** + * @param Diagnosis $existing + */ + private static function patientBasicFieldsChanged($existing, array $params): bool + { + foreach (self::PATIENT_BASIC_FIELDS as $field) { + if (!array_key_exists($field, $params)) { + continue; + } + $old = $existing->{$field}; + $new = $params[$field]; + if ($field === 'age' || $field === 'gender') { + if ((int) $old !== (int) $new) { + return true; + } + continue; + } + if ((string) $old !== (string) $new) { + return true; + } + } + + return false; + } + /** * 诊单 current assistant_id 为 0 时,从最近一条指派日志推断「原医助」(如发货释放后:上一条多为 from=X、to=0)。 * - 最近一条 to_assistant_id > 0:视为上一任持有人(库未同步时的兜底) @@ -3092,8 +3191,9 @@ class DiagnosisLogic extends BaseLogic unset($params['user_id']); // 诊单表无此字段,仅用于创建 view_record $patientId = $params['patient_id'] ?? 0; - if (!$patientId) { - $params['patient_id'] = self::generatePatientId(); + $isNewPatient = !$patientId; + if ($isNewPatient) { + $params['patient_id'] = 0; } $params['status'] = 1; @@ -3123,6 +3223,9 @@ class DiagnosisLogic extends BaseLogic } $model = Diagnosis::create($params); + if ($isNewPatient) { + $model = self::syncPatientIdWithDiagnosisId($model); + } self::createPatientTrtcAccount($model->patient_id); // 图片写入 doctor_note diff --git a/server/sql/1.9.20260613/add_diagnosis_edit_patient_basic_menu.sql b/server/sql/1.9.20260613/add_diagnosis_edit_patient_basic_menu.sql new file mode 100644 index 00000000..33f1e666 --- /dev/null +++ b/server/sql/1.9.20260613/add_diagnosis_edit_patient_basic_menu.sql @@ -0,0 +1,15 @@ +-- 诊单编辑页:最近业务订单未完成时仍可修改患者基本信息(姓名/身份证/手机/性别/年龄) +-- 接口:tcm.diagnosis/edit(后端校验);前端 edit.vue 同 perms 控制表单可编辑 +SET @diag_menu_id := (SELECT id FROM zyt_system_menu WHERE perms = 'tcm.diagnosis/lists' LIMIT 1); + +INSERT INTO zyt_system_menu ( + pid, type, name, icon, sort, perms, paths, component, + selected, params, is_cache, is_show, is_disable, create_time, update_time +) +SELECT + @diag_menu_id, 'A', '未完成订单改患者信息', '', 22, + 'tcm.diagnosis/editPatientBasic', '', '', + '', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() +FROM DUAL +WHERE @diag_menu_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM zyt_system_menu WHERE perms = 'tcm.diagnosis/editPatientBasic');