Compare commits

...
Author SHA1 Message Date
Your Name 6b713a2d6c 更新 2026-06-13 15:00:43 +08:00
4 changed files with 208 additions and 25 deletions
+68 -3
View File
@@ -67,17 +67,37 @@
>
<!-- 基本信息 -->
<el-divider content-position="left">基本信息</el-divider>
<el-alert
v-if="mode === 'edit' && patientBasicLocked && !canEditPatientBasicFields"
type="warning"
:closable="false"
show-icon
class="mb-3"
>
<template v-if="latestPrescriptionOrder">
最近业务订单 #{{ latestPrescriptionOrder.id }} 状态为{{ latestPrescriptionOrder.fulfillment_status_text }}患者基本信息不可修改
</template>
<template v-else>
存在未完成的业务订单患者基本信息不可修改
</template>
</el-alert>
<el-form-item label="诊单ID">
<el-input v-model="formData.patient_id" disabled placeholder="系统自动生成" />
</el-form-item>
<fieldset
:disabled="!canEditPatientBasicFields"
class="diagnosis-patient-basic-fieldset border-0 min-w-0 p-0 m-0"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="姓名" prop="patient_name">
<el-input
v-model="formData.patient_name"
placeholder="请输入姓名"
:disabled="!canEditPatientBasicFields"
/>
</el-form-item>
</el-col>
@@ -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 @@
</el-col>
<el-col :span="12">
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="formData.gender">
<el-radio-group v-model="formData.gender" :disabled="!canEditPatientBasicFields">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
@@ -144,11 +166,14 @@
:max="150"
placeholder="请输入年龄"
class="w-full"
:disabled="!canEditPatientBasicFields"
:controls="canEditPatientBasicFields"
/>
</el-form-item>
</el-col>
</el-row>
</fieldset>
<!-- 生命体征 -->
@@ -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<void> {
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<void> {
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;
@@ -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);
}
+123 -20
View File
@@ -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<string, mixed>|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
@@ -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');