- 将诊断组件中家长姓名输入框占位符从"≤14 岁必填"改为"≤14岁必填" - 将传染病报告组件中家长姓名输入框占位符统一为"≤14岁必填" - 移除多余的条件判断逻辑,简化占位符显示逻辑
1225 lines
38 KiB
Vue
1225 lines
38 KiB
Vue
<template>
|
||
<div class="infectious-report-container">
|
||
<!-- 标题区 -->
|
||
<div class="report-header">
|
||
<h1 class="report-title">中华人民共和国传染病报告卡</h1>
|
||
<div class="card-number-row">
|
||
<span class="card-number-label">卡片编号:</span>
|
||
<el-input
|
||
v-model="form.cardNo"
|
||
class="card-number-input"
|
||
placeholder="系统自动生成"
|
||
disabled
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 表单区域 -->
|
||
<div class="report-form" v-loading="loading">
|
||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top">
|
||
<!-- 患者基本信息 -->
|
||
<div class="section-title">
|
||
<span class="title-text">患者基本信息</span>
|
||
</div>
|
||
|
||
<!-- 患者姓名、家长姓名、身份证号 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="8">
|
||
<el-form-item label="患者姓名" prop="patName" required>
|
||
<el-input v-model="form.patName" placeholder="请输入患者姓名" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item :label="'家长姓名' + (isChildPatient ? '' : '')" :prop="isChildPatient ? 'parentName' : ''" :required="isChildPatient">
|
||
<el-input v-model="form.parentName" placeholder="≤14岁必填" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="身份证号" prop="idNo" required>
|
||
<el-input v-model="form.idNo" placeholder="请输入身份证号" maxlength="18" @change="handleIdCardChange" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 性别、出生日期、实足年龄 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="6">
|
||
<el-form-item label="性别" prop="sex" required>
|
||
<el-radio-group v-model="form.sex">
|
||
<el-radio label="男">男</el-radio>
|
||
<el-radio label="女">女</el-radio>
|
||
<el-radio label="未知">未知</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="10">
|
||
<el-form-item label="出生日期" required>
|
||
<div class="date-inputs">
|
||
<el-input v-model="form.birthYear" placeholder="年" maxlength="4" @change="calculateAge" style="width: 80px" />
|
||
<span>年</span>
|
||
<el-input v-model="form.birthMonth" placeholder="月" maxlength="2" @change="calculateAge" style="width: 60px" />
|
||
<span>月</span>
|
||
<el-input v-model="form.birthDay" placeholder="日" maxlength="2" @change="calculateAge" style="width: 60px" />
|
||
<span>日</span>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="或 实足年龄">
|
||
<div class="age-inputs">
|
||
<el-input v-model="form.age" placeholder="年龄" style="width: 100px" />
|
||
<el-select v-model="form.ageUnit" style="width: 80px">
|
||
<el-option label="岁" value="岁" />
|
||
<el-option label="月" value="月" />
|
||
<el-option label="天" value="天" />
|
||
</el-select>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 工作单位 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="24">
|
||
<el-form-item label="工作单位(学校)">
|
||
<el-input v-model="form.workplace" placeholder="请输入工作单位" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 联系电话、紧急联系人电话 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="12">
|
||
<el-form-item label="联系电话" prop="phone" required>
|
||
<el-input v-model="form.phone" placeholder="请输入联系电话" maxlength="11" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="紧急联系人电话" prop="contactPhone" required>
|
||
<el-input v-model="form.contactPhone" placeholder="请输入紧急联系人电话" maxlength="11" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 现住地址 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="24">
|
||
<el-form-item label="现住地址" required>
|
||
<el-cascader
|
||
v-model="addressCodes"
|
||
:options="addressOptions"
|
||
:props="addressCascaderProps"
|
||
placeholder="请选择省/市/区县/街道"
|
||
clearable
|
||
style="width: 100%"
|
||
@change="handleAddressChange"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="12">
|
||
<el-input v-model="form.addressVillage" placeholder="村(居)" />
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-input v-model="form.addressHouse" placeholder="门牌号" />
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 病人属于 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="24">
|
||
<el-form-item label="病人属于" prop="patientBelong" required>
|
||
<el-radio-group v-model="form.patientBelong">
|
||
<el-radio label="1">本县区</el-radio>
|
||
<el-radio label="2">本市其他县区</el-radio>
|
||
<el-radio label="3">本省其他地市</el-radio>
|
||
<el-radio label="4">外省</el-radio>
|
||
<el-radio label="5">港澳台</el-radio>
|
||
<el-radio label="6">外籍</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 职业 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="12">
|
||
<el-form-item label="职业" prop="occupation" required>
|
||
<el-select v-model="form.occupation" placeholder="请选择职业" style="width: 100%">
|
||
<el-option v-for="item in occupationList" :key="item.value" :label="item.label" :value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="病例分类" prop="caseClass" required>
|
||
<el-radio-group v-model="form.caseClass">
|
||
<el-radio label="1">疑似病例</el-radio>
|
||
<el-radio label="2">临床诊断病例</el-radio>
|
||
<el-radio label="3">确诊病例</el-radio>
|
||
<el-radio label="4">病原携带者</el-radio>
|
||
<el-radio label="5">阳性检测结果</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 发病日期、诊断日期、死亡日期 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="8">
|
||
<el-form-item label="发病日期" prop="onsetDate" required>
|
||
<el-date-picker
|
||
v-model="form.onsetDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
style="width: 100%"
|
||
value-format="YYYY-MM-DD"
|
||
format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="诊断日期" prop="diagDate" required>
|
||
<el-date-picker
|
||
v-model="form.diagDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
style="width: 100%"
|
||
value-format="YYYY-MM-DD"
|
||
format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="死亡日期">
|
||
<el-date-picker
|
||
v-model="form.deathDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
style="width: 100%"
|
||
value-format="YYYY-MM-DD"
|
||
format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 疾病名称选择区 -->
|
||
<div class="section-title">
|
||
<span class="title-text">疾病名称(勾选或补填)</span>
|
||
</div>
|
||
|
||
<!-- 甲类传染病 -->
|
||
<div class="disease-section">
|
||
<div class="disease-title">甲类传染病</div>
|
||
<div class="disease-list">
|
||
<el-checkbox
|
||
:model-value="form.selectedClassA === '0101'"
|
||
@update:model-value="(checked) => handleClassACheckbox('0101', checked)"
|
||
>鼠疫</el-checkbox>
|
||
<el-checkbox
|
||
:model-value="form.selectedClassA === '0102'"
|
||
@update:model-value="(checked) => handleClassACheckbox('0102', checked)"
|
||
>霍乱</el-checkbox>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 乙类传染病 -->
|
||
<div class="disease-section">
|
||
<div class="disease-title">乙类传染病</div>
|
||
<div class="disease-list four-columns">
|
||
<el-checkbox
|
||
v-for="disease in classBDiseases"
|
||
:key="disease.code"
|
||
:model-value="form.selectedClassB === disease.code"
|
||
@update:model-value="(checked) => handleClassBCheckbox(disease.code, checked)"
|
||
>{{ disease.name }}</el-checkbox>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 丙类传染病 -->
|
||
<div class="disease-section">
|
||
<div class="disease-title">丙类传染病</div>
|
||
<div class="disease-list four-columns">
|
||
<el-checkbox
|
||
v-for="disease in classCDiseases"
|
||
:key="disease.code"
|
||
:model-value="form.selectedClassC === disease.code"
|
||
@update:model-value="(checked) => handleClassCCheckbox(disease.code, checked)"
|
||
>{{ disease.name }}</el-checkbox>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 其他法定管理传染病 + 分型选择 -->
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="showSubtypeSelect ? 16 : 24">
|
||
<el-form-item label="其他法定管理以及重点监测传染病">
|
||
<el-input v-model="form.otherDisease" placeholder="手动输入非列表疾病" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col v-if="showSubtypeSelect" :span="8">
|
||
<el-form-item label="分型" prop="diseaseType" :required="showSubtypeSelect">
|
||
<el-select v-model="form.diseaseType" placeholder="请选择分型" style="width: 100%">
|
||
<el-option
|
||
v-for="option in currentSubtypeOptions"
|
||
:key="option.value"
|
||
:label="option.label"
|
||
:value="option.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 报告信息区 -->
|
||
<div class="section-title">
|
||
<span class="title-text">报告信息</span>
|
||
</div>
|
||
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="8">
|
||
<el-form-item label="报告单位">
|
||
<el-input v-model="form.reportOrg" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="联系电话">
|
||
<el-input v-model="form.reportOrgPhone" placeholder="请输入联系电话" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="报告医生" required>
|
||
<el-input v-model="form.reportDoc" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="8">
|
||
<el-form-item label="填卡日期" prop="reportDate" required>
|
||
<el-date-picker
|
||
v-model="form.reportDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
style="width: 100%"
|
||
value-format="YYYY-MM-DD"
|
||
format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="订正病名">
|
||
<el-input v-model="form.correctName" placeholder="请输入订正病名" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="退卡原因">
|
||
<el-input v-model="form.withdrawReason" placeholder="请输入退卡原因" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="16" class="form-row">
|
||
<el-col :span="24">
|
||
<el-form-item label="备注">
|
||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</div>
|
||
|
||
<!-- 操作按钮区 -->
|
||
<div class="action-buttons">
|
||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">保 存</el-button>
|
||
<el-button @click="handleReset">重 置</el-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, watch, getCurrentInstance, onMounted } from 'vue';
|
||
import pcas from 'china-division/dist/pcas-code.json';
|
||
import { saveInfectiousDiseaseReport, getNextCardNo, listInfectiousCards } from '../api';
|
||
import useUserStore from '@/store/modules/user';
|
||
import { useDict } from '@/utils/dict';
|
||
|
||
const { proxy } = getCurrentInstance();
|
||
const userStore = useUserStore();
|
||
|
||
// 获取职业字典数据
|
||
const { prfs: occupationList } = useDict('prfs');
|
||
|
||
// 公共常量
|
||
const PHONE_REGEX = /^1[3-9]\d{9}$|^0\d{2,3}-?\d{7,8}$/;
|
||
const DISEASE_NAMES = {
|
||
'0203': '病毒性肝炎',
|
||
'0211': '炭疽',
|
||
'0213': '肺结核',
|
||
'0222': '梅毒',
|
||
'0224': '血吸虫病',
|
||
'0225': '疟疾'
|
||
};
|
||
|
||
// 乙类传染病列表
|
||
const classBDiseases = [
|
||
{ code: '0201', name: '传染性非典型肺炎' },
|
||
{ code: '0202', name: '艾滋病' },
|
||
{ code: '0203', name: '病毒性肝炎' },
|
||
{ code: '0204', name: '脊髓灰质炎' },
|
||
{ code: '0205', name: '人感染高致病性禽流感' },
|
||
{ code: '0206', name: '麻疹' },
|
||
{ code: '0207', name: '流行性出血热' },
|
||
{ code: '0208', name: '狂犬病' },
|
||
{ code: '0209', name: '流行性乙型脑炎' },
|
||
{ code: '0210', name: '登革热' },
|
||
{ code: '0211', name: '炭疽' },
|
||
{ code: '0212', name: '细菌性和阿米巴性痢疾' },
|
||
{ code: '0213', name: '肺结核' },
|
||
{ code: '0214', name: '伤寒和副伤寒' },
|
||
{ code: '0215', name: '流行性脑脊髓膜炎' },
|
||
{ code: '0216', name: '百日咳' },
|
||
{ code: '0217', name: '白喉' },
|
||
{ code: '0218', name: '新生儿破伤风' },
|
||
{ code: '0219', name: '猩红热' },
|
||
{ code: '0220', name: '布鲁氏菌病' },
|
||
{ code: '0221', name: '淋病' },
|
||
{ code: '0222', name: '梅毒' },
|
||
{ code: '0223', name: '钩端螺旋体病' },
|
||
{ code: '0224', name: '血吸虫病' },
|
||
{ code: '0225', name: '疟疾' },
|
||
];
|
||
|
||
// 丙类传染病列表
|
||
const classCDiseases = [
|
||
{ code: '0301', name: '流行性感冒' },
|
||
{ code: '0302', name: '流行性腮腺炎' },
|
||
{ code: '0303', name: '风疹' },
|
||
{ code: '0304', name: '急性出血性结膜炎' },
|
||
{ code: '0305', name: '麻风病' },
|
||
{ code: '0306', name: '流行性和地方性斑疹伤寒' },
|
||
{ code: '0307', name: '黑热病' },
|
||
{ code: '0308', name: '包虫病' },
|
||
{ code: '0309', name: '丝虫病' },
|
||
{ code: '0310', name: '其它感染性腹泻病' },
|
||
{ code: '0311', name: '手足口病' },
|
||
];
|
||
|
||
const formRef = ref(null);
|
||
const loading = ref(false);
|
||
const submitLoading = ref(false);
|
||
|
||
const props = defineProps({
|
||
patientInfo: {
|
||
type: Object,
|
||
default: () => ({}),
|
||
},
|
||
activeTab: {
|
||
type: String,
|
||
default: '',
|
||
},
|
||
});
|
||
|
||
const emit = defineEmits(['saved']);
|
||
|
||
// 地址级联选择器配置
|
||
const addressOptions = ref(pcas);
|
||
const addressCodes = ref([]);
|
||
const municipalities = ['北京市', '天津市', '上海市', '重庆市'];
|
||
const addressCascaderProps = {
|
||
checkStrictly: true,
|
||
value: 'code',
|
||
label: 'name',
|
||
children: 'children'
|
||
};
|
||
|
||
const form = ref({
|
||
cardNo: '',
|
||
patName: '',
|
||
parentName: '',
|
||
idNo: '',
|
||
sex: '男',
|
||
birthYear: '',
|
||
birthMonth: '',
|
||
birthDay: '',
|
||
age: '',
|
||
ageUnit: '岁',
|
||
workplace: '',
|
||
phone: '',
|
||
contactPhone: '',
|
||
addressProv: '',
|
||
addressCity: '',
|
||
addressCounty: '',
|
||
addressTown: '',
|
||
addressVillage: '',
|
||
addressHouse: '',
|
||
patientBelong: '1',
|
||
occupation: '',
|
||
caseClass: '',
|
||
onsetDate: '',
|
||
diagDate: '',
|
||
deathDate: '',
|
||
selectedDiseases: [],
|
||
selectedClassA: '',
|
||
selectedClassB: '',
|
||
selectedClassC: '',
|
||
otherDisease: '',
|
||
diseaseType: '',
|
||
reportOrg: '',
|
||
reportOrgPhone: '',
|
||
reportDoc: '',
|
||
reportDate: '',
|
||
correctName: '',
|
||
withdrawReason: '',
|
||
remark: '',
|
||
encounterId: '',
|
||
patientId: '',
|
||
diagnosisId: '',
|
||
});
|
||
|
||
// 表单验证规则
|
||
const rules = {
|
||
patName: [{ required: true, message: '请输入患者姓名', trigger: 'blur' }],
|
||
idNo: [
|
||
{ required: true, message: '请输入身份证号', trigger: 'blur' },
|
||
{ pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/, message: '请输入有效的身份证号码', trigger: 'blur' }
|
||
],
|
||
phone: [
|
||
{ required: true, message: '请输入联系电话', trigger: 'blur' },
|
||
{ pattern: PHONE_REGEX, message: '请输入有效的联系电话', trigger: 'blur' }
|
||
],
|
||
contactPhone: [
|
||
{ required: true, message: '请输入紧急联系人电话', trigger: 'blur' },
|
||
{ pattern: PHONE_REGEX, message: '请输入有效的紧急联系人电话', trigger: 'blur' }
|
||
],
|
||
occupation: [{ required: true, message: '请选择职业', trigger: 'change' }],
|
||
caseClass: [{ required: true, message: '请选择病例分类', trigger: 'change' }],
|
||
patientBelong: [{ required: true, message: '请选择病人属于', trigger: 'change' }],
|
||
onsetDate: [{ required: true, message: '请选择发病日期', trigger: 'change' }],
|
||
diagDate: [{ required: true, message: '请选择诊断日期', trigger: 'change' }],
|
||
reportDate: [{ required: true, message: '请选择填卡日期', trigger: 'change' }],
|
||
};
|
||
|
||
// 疾病类型与分型选项映射
|
||
const diseaseSubtypeMap = {
|
||
'0213': [
|
||
{ label: '涂阳', value: '涂阳' },
|
||
{ label: '仅培阳', value: '仅培阳' },
|
||
{ label: '菌阴', value: '菌阴' },
|
||
{ label: '未痰检', value: '未痰检' }
|
||
],
|
||
'0222': [
|
||
{ label: 'Ⅰ期', value: 'Ⅰ期' },
|
||
{ label: 'Ⅱ期', value: 'Ⅱ期' },
|
||
{ label: 'Ⅲ期', value: 'Ⅲ期' },
|
||
{ label: '胎传', value: '胎传' },
|
||
{ label: '隐性', value: '隐性' }
|
||
],
|
||
'0211': [
|
||
{ label: '肺炭疽', value: '肺炭疽' },
|
||
{ label: '皮肤炭疽', value: '皮肤炭疽' },
|
||
{ label: '胃肠炭疽', value: '胃肠炭疽' },
|
||
{ label: '未分型', value: '未分型' }
|
||
],
|
||
'0203': [
|
||
{ label: '甲型', value: '甲型' },
|
||
{ label: '乙型', value: '乙型' },
|
||
{ label: '丙型', value: '丙型' },
|
||
{ label: '戊型', value: '戊型' }
|
||
],
|
||
'0225': [
|
||
{ label: '间日疟', value: '间日疟' },
|
||
{ label: '恶性疟', value: '恶性疟' },
|
||
{ label: '三日疟', value: '三日疟' },
|
||
{ label: '卵形疟', value: '卵形疟' },
|
||
{ label: '未分型', value: '未分型' }
|
||
],
|
||
'0224': [
|
||
{ label: '急性', value: '急性' },
|
||
{ label: '慢性', value: '慢性' },
|
||
{ label: '晚期', value: '晚期' },
|
||
{ label: '未分型', value: '未分型' }
|
||
]
|
||
};
|
||
|
||
// 需要分型的疾病编码列表
|
||
const diseasesRequiringSubtype = ['0203', '0211', '0213', '0222', '0224', '0225'];
|
||
|
||
// 计算当前选中的疾病对应的分型选项
|
||
const currentSubtypeOptions = computed(() => {
|
||
if (!form.value.selectedClassA && !form.value.selectedClassB && !form.value.selectedClassC) {
|
||
return [];
|
||
}
|
||
const selectedDisease = form.value.selectedClassA || form.value.selectedClassB || form.value.selectedClassC;
|
||
return diseaseSubtypeMap[selectedDisease] || [];
|
||
});
|
||
|
||
// 计算是否显示分型选择
|
||
const showSubtypeSelect = computed(() => {
|
||
return (form.value.selectedClassA || form.value.selectedClassB || form.value.selectedClassC) && currentSubtypeOptions.value.length > 0;
|
||
});
|
||
|
||
// 计算是否为14岁以下儿童
|
||
const isChildPatient = computed(() => {
|
||
const age = parseInt(form.value.age);
|
||
return !isNaN(age) && form.value.ageUnit === '岁' && age <= 14;
|
||
});
|
||
|
||
// 监听疾病选择变化,自动清空分型选择
|
||
watch(() => [form.value.selectedClassA, form.value.selectedClassB, form.value.selectedClassC], (newVal, oldVal) => {
|
||
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
|
||
form.value.diseaseType = '';
|
||
}
|
||
updateSelectedDiseases();
|
||
}, { deep: true });
|
||
|
||
// 监听患者信息变化,自动填充表单
|
||
watch(() => props.patientInfo, (newVal) => {
|
||
if (newVal && Object.keys(newVal).length > 0) {
|
||
initForm();
|
||
}
|
||
}, { deep: true, immediate: true });
|
||
|
||
// 监听Tab切换,重新初始化
|
||
watch(() => props.activeTab, (newVal) => {
|
||
if (newVal === 'infectiousReport' && props.patientInfo && Object.keys(props.patientInfo).length > 0) {
|
||
initForm();
|
||
}
|
||
});
|
||
|
||
// 获取当前日期
|
||
function getCurrentDate() {
|
||
const date = new Date();
|
||
const year = date.getFullYear();
|
||
let month = date.getMonth() + 1;
|
||
let day = date.getDate();
|
||
month = month < 10 ? '0' + month : month;
|
||
day = day < 10 ? '0' + day : day;
|
||
return `${year}-${month}-${day}`;
|
||
}
|
||
|
||
// 解析出生日期
|
||
function parseBirthDate(birthDate) {
|
||
if (!birthDate) return { year: '', month: '', day: '' };
|
||
const date = new Date(birthDate);
|
||
if (Number.isNaN(date.getTime())) return { year: '', month: '', day: '' };
|
||
return {
|
||
year: date.getFullYear().toString(),
|
||
month: (date.getMonth() + 1).toString().padStart(2, '0'),
|
||
day: date.getDate().toString().padStart(2, '0'),
|
||
};
|
||
}
|
||
|
||
// 从身份证号解析出生日期和性别
|
||
function parseIdCardInfo(idNo) {
|
||
if (!idNo) return null;
|
||
idNo = idNo.trim().toUpperCase();
|
||
|
||
if (idNo.length === 18) {
|
||
const birthYear = idNo.substring(6, 10);
|
||
const birthMonth = idNo.substring(10, 12);
|
||
const birthDay = idNo.substring(12, 14);
|
||
const genderCode = idNo.substring(16, 17);
|
||
const birthDate = new Date(parseInt(birthYear), parseInt(birthMonth) - 1, parseInt(birthDay));
|
||
if (isNaN(birthDate.getTime())) return null;
|
||
return {
|
||
birthYear,
|
||
birthMonth,
|
||
birthDay,
|
||
sex: parseInt(genderCode, 10) % 2 === 1 ? '男' : '女',
|
||
birthDate: `${birthYear}-${birthMonth}-${birthDay}`
|
||
};
|
||
}
|
||
|
||
if (idNo.length === 15) {
|
||
const birthYear = '19' + idNo.substring(6, 8);
|
||
const birthMonth = idNo.substring(8, 10);
|
||
const birthDay = idNo.substring(10, 12);
|
||
const genderCode = idNo.substring(13, 14);
|
||
const birthDate = new Date(parseInt(birthYear), parseInt(birthMonth) - 1, parseInt(birthDay));
|
||
if (isNaN(birthDate.getTime())) return null;
|
||
return {
|
||
birthYear,
|
||
birthMonth,
|
||
birthDay,
|
||
sex: parseInt(genderCode, 10) % 2 === 1 ? '男' : '女',
|
||
birthDate: `${birthYear}-${birthMonth}-${birthDay}`
|
||
};
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
// 身份证号变化时自动填充
|
||
function handleIdCardChange(value) {
|
||
if (!value) return;
|
||
const info = parseIdCardInfo(value);
|
||
if (info) {
|
||
form.value.birthYear = info.birthYear;
|
||
form.value.birthMonth = info.birthMonth;
|
||
form.value.birthDay = info.birthDay;
|
||
form.value.sex = info.sex;
|
||
calculateAge();
|
||
}
|
||
}
|
||
|
||
// 计算年龄
|
||
function calculateAge() {
|
||
const { birthYear, birthMonth, birthDay } = form.value;
|
||
if (!birthYear || !birthMonth || !birthDay) return;
|
||
|
||
const birthDate = new Date(parseInt(birthYear), parseInt(birthMonth) - 1, parseInt(birthDay));
|
||
const today = new Date();
|
||
|
||
let age = today.getFullYear() - birthDate.getFullYear();
|
||
const monthDiff = today.getMonth() - birthDate.getMonth();
|
||
|
||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||
age--;
|
||
}
|
||
|
||
if (age < 0) age = 0;
|
||
|
||
form.value.age = age.toString();
|
||
form.value.ageUnit = '岁';
|
||
}
|
||
|
||
// 地址选择变化处理
|
||
function handleAddressChange(values) {
|
||
if (!values || values.length === 0) {
|
||
form.value.addressProv = '';
|
||
form.value.addressCity = '';
|
||
form.value.addressCounty = '';
|
||
form.value.addressTown = '';
|
||
return;
|
||
}
|
||
|
||
const names = [];
|
||
let currentNodes = addressOptions.value;
|
||
|
||
for (const code of values) {
|
||
const node = currentNodes?.find(n => n.code === code);
|
||
if (node) {
|
||
names.push(node.name);
|
||
currentNodes = node.children;
|
||
}
|
||
}
|
||
|
||
const provName = names[0] || '';
|
||
let cityName = names[1] || '';
|
||
const countyName = names[2] || '';
|
||
const townName = names[3] || '';
|
||
|
||
if (municipalities.includes(provName)) {
|
||
form.value.addressProv = provName;
|
||
form.value.addressCity = provName;
|
||
form.value.addressCounty = cityName;
|
||
form.value.addressTown = countyName;
|
||
} else {
|
||
form.value.addressProv = provName;
|
||
form.value.addressCity = cityName;
|
||
form.value.addressCounty = countyName;
|
||
form.value.addressTown = townName;
|
||
}
|
||
}
|
||
|
||
// 根据地址名称数组查找对应的代码数组
|
||
function findCodesByNames(names) {
|
||
const codes = [];
|
||
let currentNodes = addressOptions.value;
|
||
|
||
for (const name of names) {
|
||
if (!name) break;
|
||
const node = currentNodes?.find(n => n.name === name);
|
||
if (node) {
|
||
codes.push(node.code);
|
||
currentNodes = node.children;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
return codes;
|
||
}
|
||
|
||
// 疾病选择处理
|
||
const handleDiseaseCheckbox = (category, value, checked) => {
|
||
if (checked) {
|
||
form.value[category] = value;
|
||
} else {
|
||
form.value[category] = '';
|
||
}
|
||
updateSelectedDiseases();
|
||
};
|
||
|
||
const handleClassACheckbox = (value, checked) => {
|
||
handleDiseaseCheckbox('selectedClassA', value, checked);
|
||
};
|
||
|
||
const handleClassBCheckbox = (value, checked) => {
|
||
handleDiseaseCheckbox('selectedClassB', value, checked);
|
||
};
|
||
|
||
const handleClassCCheckbox = (value, checked) => {
|
||
handleDiseaseCheckbox('selectedClassC', value, checked);
|
||
};
|
||
|
||
const updateSelectedDiseases = () => {
|
||
const diseases = [];
|
||
if (form.value.selectedClassA) diseases.push(form.value.selectedClassA);
|
||
if (form.value.selectedClassB) diseases.push(form.value.selectedClassB);
|
||
if (form.value.selectedClassC) diseases.push(form.value.selectedClassC);
|
||
form.value.selectedDiseases = diseases;
|
||
};
|
||
|
||
// 初始化表单
|
||
async function initForm() {
|
||
const patientInfo = props.patientInfo || {};
|
||
const birthInfo = parseBirthDate(patientInfo.birthDate || patientInfo.birthday);
|
||
|
||
// 生成默认卡片编号
|
||
const orgCode = userStore.fixmedinsCode || '0000';
|
||
let cardNo = '';
|
||
try {
|
||
const res = await getNextCardNo(orgCode);
|
||
if (res.code === 200 && res.data) {
|
||
cardNo = res.data;
|
||
}
|
||
} catch (err) {
|
||
console.error('获取卡片编号失败:', err);
|
||
}
|
||
|
||
form.value = {
|
||
cardNo: cardNo,
|
||
patName: patientInfo.patientName || patientInfo.name || '',
|
||
parentName: '',
|
||
idNo: patientInfo.idCard || '',
|
||
sex: patientInfo.genderName || (patientInfo.genderEnum === 1 ? '男' : patientInfo.genderEnum === 2 ? '女' : '未知'),
|
||
birthYear: birthInfo.year,
|
||
birthMonth: birthInfo.month,
|
||
birthDay: birthInfo.day,
|
||
age: patientInfo.age || '',
|
||
ageUnit: '岁',
|
||
workplace: patientInfo.workCompany || '',
|
||
phone: patientInfo.phone || '',
|
||
contactPhone: '',
|
||
addressProv: patientInfo.addressProvince || '',
|
||
addressCity: patientInfo.addressCity || '',
|
||
addressCounty: patientInfo.addressDistrict || '',
|
||
addressTown: patientInfo.addressStreet || '',
|
||
addressVillage: '',
|
||
addressHouse: '',
|
||
patientBelong: '1',
|
||
occupation: '',
|
||
caseClass: '',
|
||
onsetDate: getCurrentDate(),
|
||
diagDate: getCurrentDate(),
|
||
deathDate: '',
|
||
selectedDiseases: [],
|
||
selectedClassA: '',
|
||
selectedClassB: '',
|
||
selectedClassC: '',
|
||
otherDisease: '',
|
||
diseaseType: '',
|
||
reportOrg: userStore.orgName || '',
|
||
reportOrgPhone: '',
|
||
reportDoc: userStore.nickName || userStore.name || '',
|
||
reportDate: getCurrentDate(),
|
||
correctName: '',
|
||
withdrawReason: '',
|
||
remark: '',
|
||
encounterId: patientInfo.encounterId || '',
|
||
patientId: patientInfo.patientId || '',
|
||
diagnosisId: '',
|
||
};
|
||
|
||
// 设置地址选择器初始值
|
||
const provName = patientInfo.addressProvince || '';
|
||
const cityName = patientInfo.addressCity || '';
|
||
const countyName = patientInfo.addressDistrict || '';
|
||
const townName = patientInfo.addressStreet || '';
|
||
|
||
let names = [];
|
||
if (municipalities.includes(provName)) {
|
||
names = [provName, countyName, townName].filter(n => n);
|
||
} else {
|
||
names = [provName, cityName, countyName, townName].filter(n => n);
|
||
}
|
||
addressCodes.value = findCodesByNames(names);
|
||
|
||
if (birthInfo.year) {
|
||
calculateAge();
|
||
}
|
||
}
|
||
|
||
// 构建提交数据
|
||
function buildSubmitData() {
|
||
const formData = form.value;
|
||
|
||
let diseaseCode = '';
|
||
let diseaseType = null;
|
||
|
||
if (formData.selectedClassA || formData.selectedClassB || formData.selectedClassC) {
|
||
diseaseCode = formData.selectedClassA || formData.selectedClassB || formData.selectedClassC;
|
||
if (formData.diseaseType) {
|
||
diseaseType = formData.diseaseType;
|
||
}
|
||
} else if (formData.otherDisease) {
|
||
diseaseCode = 'OTHER';
|
||
}
|
||
|
||
const ageUnitMap = { '岁': '1', '月': '2', '天': '3' };
|
||
|
||
const submitData = {
|
||
cardNo: formData.cardNo,
|
||
visitId: props.patientInfo?.encounterId || formData.encounterId || null,
|
||
diagId: formData.diagnosisId || null,
|
||
patId: formData.patientId || null,
|
||
idType: 1,
|
||
idNo: formData.idNo,
|
||
patName: formData.patName || '',
|
||
parentName: formData.parentName || null,
|
||
sex: formData.sex === '男' ? '1' : formData.sex === '女' ? '2' : '0',
|
||
birthday: formData.birthYear && formData.birthMonth && formData.birthDay
|
||
? `${formData.birthYear}-${formData.birthMonth}-${formData.birthDay}`
|
||
: null,
|
||
age: formData.age ? parseInt(formData.age) : null,
|
||
ageUnit: ageUnitMap[formData.ageUnit] || '1',
|
||
workplace: formData.workplace || null,
|
||
phone: formData.phone || null,
|
||
contactPhone: formData.contactPhone || null,
|
||
addressProv: formData.addressProv || '',
|
||
addressCity: formData.addressCity || '',
|
||
addressCounty: formData.addressCounty || '',
|
||
addressTown: formData.addressTown || '',
|
||
addressVillage: formData.addressVillage || '',
|
||
addressHouse: formData.addressHouse || '',
|
||
patientBelong: parseInt(formData.patientBelong) || 1,
|
||
occupation: formData.occupation || null,
|
||
diseaseCode: diseaseCode || null,
|
||
diseaseType: diseaseType || null,
|
||
otherDisease: formData.otherDisease || null,
|
||
caseClass: formData.caseClass ? parseInt(formData.caseClass) : null,
|
||
onsetDate: formData.onsetDate || null,
|
||
diagDate: formData.diagDate ? `${formData.diagDate}T00:00:00` : null,
|
||
deathDate: formData.deathDate || null,
|
||
correctName: formData.correctName || null,
|
||
withdrawReason: formData.withdrawReason || null,
|
||
reportOrg: formData.reportOrg || '',
|
||
reportOrgPhone: formData.reportOrgPhone || null,
|
||
reportDoc: formData.reportDoc || '',
|
||
reportDate: formData.reportDate || null,
|
||
cardNameCode: 1,
|
||
registrationSource: 1,
|
||
status: 0,
|
||
deptId: props.patientInfo?.deptId || null,
|
||
doctorId: userStore.userId || null,
|
||
};
|
||
|
||
return submitData;
|
||
}
|
||
|
||
// 手动验证表单
|
||
function validateFormManually() {
|
||
const errors = [];
|
||
|
||
if (!form.value.idNo) {
|
||
errors.push('请输入身份证号');
|
||
} else {
|
||
const idCardReg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
|
||
if (!idCardReg.test(form.value.idNo)) {
|
||
errors.push('请输入有效的身份证号码');
|
||
}
|
||
}
|
||
|
||
if (!form.value.phone) {
|
||
errors.push('请输入联系电话');
|
||
} else if (!PHONE_REGEX.test(form.value.phone)) {
|
||
errors.push('请输入有效的联系电话');
|
||
}
|
||
|
||
if (!form.value.contactPhone) {
|
||
errors.push('请输入紧急联系人电话');
|
||
} else if (!PHONE_REGEX.test(form.value.contactPhone)) {
|
||
errors.push('请输入有效的紧急联系人电话');
|
||
}
|
||
|
||
if (isChildPatient.value && !form.value.parentName) {
|
||
errors.push('14岁以下患者必须填写家长姓名');
|
||
}
|
||
|
||
if (!form.value.onsetDate) {
|
||
errors.push('请选择发病日期');
|
||
} else if (form.value.diagDate && new Date(form.value.onsetDate) > new Date(form.value.diagDate)) {
|
||
errors.push('发病日期不能晚于诊断日期');
|
||
}
|
||
|
||
if (!form.value.diagDate) {
|
||
errors.push('请选择诊断日期');
|
||
}
|
||
|
||
if (!form.value.reportDate) {
|
||
errors.push('请选择填卡日期');
|
||
}
|
||
|
||
if (!form.value.caseClass) {
|
||
errors.push('请选择病例分类');
|
||
}
|
||
|
||
if (!form.value.occupation) {
|
||
errors.push('请选择职业');
|
||
}
|
||
|
||
if (!form.value.patientBelong) {
|
||
errors.push('请选择病人属于');
|
||
}
|
||
|
||
if (!form.value.addressProv || !form.value.addressCity) {
|
||
errors.push('请至少选择省和市');
|
||
}
|
||
|
||
if (form.value.deathDate) {
|
||
if (form.value.diagDate && new Date(form.value.deathDate) < new Date(form.value.diagDate)) {
|
||
errors.push('死亡日期不能早于诊断日期');
|
||
}
|
||
if (new Date(form.value.deathDate) > new Date()) {
|
||
errors.push('死亡日期不能晚于当前日期');
|
||
}
|
||
}
|
||
|
||
const selectedDisease = form.value.selectedClassA || form.value.selectedClassB || form.value.selectedClassC;
|
||
if (diseasesRequiringSubtype.includes(selectedDisease) && !form.value.diseaseType) {
|
||
errors.push(`${DISEASE_NAMES[selectedDisease]}必须选择分型`);
|
||
}
|
||
|
||
if (errors.length > 0) {
|
||
proxy.$modal.msgError(errors[0]);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 提交表单
|
||
async function handleSubmit() {
|
||
try {
|
||
if (!validateFormManually()) {
|
||
return;
|
||
}
|
||
|
||
if (form.value.selectedDiseases.length === 0 && !form.value.otherDisease) {
|
||
proxy.$modal.msgError('请至少选择一种疾病或填写其他传染病');
|
||
return;
|
||
}
|
||
|
||
submitLoading.value = true;
|
||
|
||
const submitData = buildSubmitData();
|
||
|
||
const res = await saveInfectiousDiseaseReport(submitData);
|
||
|
||
if (res.code === 200) {
|
||
proxy.$modal.msgSuccess('传染病报告卡保存成功');
|
||
emit('saved');
|
||
// 重新初始化表单,获取新的卡片编号
|
||
initForm();
|
||
} else {
|
||
proxy.$modal.msgError(res.msg || '保存失败');
|
||
}
|
||
} catch (err) {
|
||
console.error('传染病报告卡保存失败:', err);
|
||
proxy.$modal.msgError(err.message || '保存失败,请检查网络连接');
|
||
} finally {
|
||
submitLoading.value = false;
|
||
}
|
||
}
|
||
|
||
// 重置表单
|
||
function handleReset() {
|
||
const { patName, idNo, sex, birthYear, birthMonth, birthDay, age, ageUnit, phone, reportOrg, reportDoc, encounterId, patientId } = form.value;
|
||
formRef.value?.resetFields();
|
||
|
||
// 保留患者基本信息
|
||
form.value.patName = patName;
|
||
form.value.idNo = idNo;
|
||
form.value.sex = sex;
|
||
form.value.birthYear = birthYear;
|
||
form.value.birthMonth = birthMonth;
|
||
form.value.birthDay = birthDay;
|
||
form.value.age = age;
|
||
form.value.ageUnit = ageUnit;
|
||
form.value.phone = phone;
|
||
form.value.reportOrg = reportOrg;
|
||
form.value.reportDoc = reportDoc;
|
||
form.value.encounterId = encounterId;
|
||
form.value.patientId = patientId;
|
||
|
||
// 重置其他字段
|
||
form.value.reportDate = getCurrentDate();
|
||
form.value.onsetDate = getCurrentDate();
|
||
form.value.diagDate = getCurrentDate();
|
||
form.value.selectedClassA = '';
|
||
form.value.selectedClassB = '';
|
||
form.value.selectedClassC = '';
|
||
form.value.diseaseType = '';
|
||
form.value.parentName = '';
|
||
form.value.contactPhone = '';
|
||
form.value.occupation = '';
|
||
form.value.caseClass = '';
|
||
form.value.patientBelong = '1';
|
||
form.value.otherDisease = '';
|
||
form.value.correctName = '';
|
||
form.value.withdrawReason = '';
|
||
form.value.remark = '';
|
||
form.value.deathDate = '';
|
||
|
||
updateSelectedDiseases();
|
||
}
|
||
|
||
// 组件挂载时初始化
|
||
onMounted(() => {
|
||
if (props.patientInfo && Object.keys(props.patientInfo).length > 0) {
|
||
initForm();
|
||
}
|
||
});
|
||
|
||
// 暴露方法供父组件调用
|
||
defineExpose({
|
||
initForm
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.infectious-report-container {
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: #fff;
|
||
padding: 16px;
|
||
}
|
||
|
||
/* 标题区样式 */
|
||
.report-header {
|
||
text-align: center;
|
||
padding-bottom: 16px;
|
||
border-bottom: 2px solid #3498db;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.report-title {
|
||
color: #2c3e50;
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
margin: 0 0 12px 0;
|
||
}
|
||
|
||
.card-number-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.card-number-label {
|
||
color: #666;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.card-number-input {
|
||
width: 240px;
|
||
}
|
||
|
||
/* 表单区域样式 */
|
||
.report-form {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding-right: 8px;
|
||
}
|
||
|
||
.section-title {
|
||
margin: 16px 0 12px 0;
|
||
padding-bottom: 8px;
|
||
border-bottom: 2px solid #3498db;
|
||
}
|
||
|
||
.title-text {
|
||
color: #2c3e50;
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.form-row {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.date-inputs {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.date-inputs span {
|
||
color: #606266;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.age-inputs {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
/* 疾病选择区样式 */
|
||
.disease-section {
|
||
margin-bottom: 12px;
|
||
padding: 12px;
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
background: #fafafa;
|
||
}
|
||
|
||
.disease-title {
|
||
color: #2c3e50;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
padding-bottom: 6px;
|
||
border-bottom: 1px solid #e4e7ed;
|
||
}
|
||
|
||
.disease-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.disease-list :deep(.el-checkbox) {
|
||
width: calc(50% - 8px);
|
||
margin-right: 0;
|
||
}
|
||
|
||
.disease-list.four-columns :deep(.el-checkbox) {
|
||
width: calc(25% - 8px);
|
||
}
|
||
|
||
/* 操作按钮区样式 */
|
||
.action-buttons {
|
||
padding: 16px 0 0;
|
||
border-top: 1px solid #e4e7ed;
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
/* 响应式调整 */
|
||
@media (max-width: 1200px) {
|
||
.disease-list.four-columns :deep(.el-checkbox) {
|
||
width: calc(33.33% - 8px);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.disease-list.four-columns :deep(.el-checkbox) {
|
||
width: calc(50% - 8px);
|
||
}
|
||
}
|
||
</style> |