| | |
| | | <template> |
| | | <view class="case-report-container"> |
| | | <!-- 表单内容 --> |
| | | |
| | | <view class="form-content"> |
| | | <view class="page-header"> |
| | | <text class="page-title">{{ |
| | | isEditMode ? "修改案例" : "上报案例" |
| | | }}</text> |
| | | </view> |
| | | <!-- 基本信息卡片 --> |
| | | |
| | | <!-- 案例基本信息卡片 --> |
| | | <view class="form-section"> |
| | | <view class="section-header"> |
| | | <view class="section-icon">📋</view> |
| | | <text class="section-title">捐献案例基本信息</text> |
| | | <text class="section-title">案例基本信息</text> |
| | | </view> |
| | | |
| | | <view class="form-grid"> |
| | | <!-- 修改后:治疗医院输入框 --> |
| | | <!-- <view class="form-item"> |
| | | <text class="item-label required">治疗医院</text> |
| | | <u-input |
| | | v-model="form.treatmenthospitalname" |
| | | placeholder="请输入治疗医院名称" |
| | | maxlength="100" |
| | | class="custom-input" |
| | | /> |
| | | </view> --> |
| | | |
| | | <!-- 新增:上报医院 --> |
| | | <view class="form-item"> |
| | | <text class="item-label">案例编号</text> |
| | | <u-input |
| | | v-model="form.caseNo" |
| | | placeholder="系统自动生成" |
| | | disabled |
| | | :disabledColor="disabledColor" |
| | | /> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="item-label">上报医院</text> |
| | | <u-input |
| | | v-model="form.toHospital" |
| | | placeholder="请输入上报医院" |
| | | maxlength="100" |
| | | class="custom-input" |
| | | /> |
| | | </view> |
| | | |
| | | <!-- 新增:部门名称 --> |
| | | <view class="form-item"> |
| | | <text class="item-label">科室名称</text> |
| | | <u-input |
| | | v-model="form.deptName" |
| | | placeholder="请输入上报科室" |
| | | maxlength="50" |
| | | class="custom-input" |
| | | /> |
| | | </view> |
| | | |
| | | <view class="form-item"> |
| | | <text class="item-label required">患者姓名</text> |
| | | <u-input |
| | |
| | | v-model="form.name" |
| | | placeholder="请输入姓名" |
| | | maxlength="20" |
| | | class="custom-input" |
| | | /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 个人信息卡片 --> |
| | | <!-- 个人信息卡片(捐献人信息) --> |
| | | <view class="form-section"> |
| | | <view class="section-header"> |
| | | <view class="section-icon">👤</view> |
| | | <text class="section-title">捐献人信息</text> |
| | | <text class="section-title">个人信息</text> |
| | | </view> |
| | | |
| | | <view class="form-grid"> |
| | | <!-- 改造后的民族选择 --> |
| | | <view class="form-item"> |
| | | <text class="item-label">民族</text> |
| | | <picker |
| | | mode="selector" |
| | | :range="nationLabels" |
| | | :range="nationList" |
| | | :value="nationIndex" |
| | | @change="onNationChange" |
| | | > |
| | | <view class="picker"> |
| | | <text>{{ currentNation }}</text> |
| | | <text>{{ currentNationText || "请选择民族" }}</text> |
| | | <text class="icon-arrow">›</text> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | |
| | | <view class="form-item"> |
| | | <text class="item-label">国籍</text> |
| | | <u-input |
| | | type="text" |
| | | v-model="form.nationality" |
| | | placeholder="请输入国籍" |
| | | class="custom-input" |
| | | /> |
| | | </view> |
| | | |
| | | <!-- 改造后的证件类型选择 --> |
| | | <view class="form-item"> |
| | | <text class="item-label">证件类型</text> |
| | | <picker |
| | | mode="selector" |
| | | :range="idCardTypeLabels" |
| | | :range="idCardTypeList" |
| | | :value="idCardTypeIndex" |
| | | @change="onIdCardTypeChange" |
| | | > |
| | | <view class="picker"> |
| | | <text>{{ currentIdCardType }}</text> |
| | | <text>{{ currentIdCardTypeText || "请选择证件类型" }}</text> |
| | | <text class="icon-arrow">›</text> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | |
| | | <view class="form-item"> |
| | | <text class="item-label required">证件号码</text> |
| | | <text class="item-label">证件号码</text> |
| | | <u-input |
| | | type="idcard" |
| | | v-model="form.idcardno" |
| | | placeholder="请输入证件号码" |
| | | maxlength="18" |
| | | class="custom-input" |
| | | @blur="onIdCardChange" |
| | | /> |
| | | <text class="error-text" v-if="idCardError">{{ idCardError }}</text> |
| | | </view> |
| | | |
| | | <!-- 改造后的性别选择 --> |
| | | <view class="form-item"> |
| | | <text class="item-label">性别</text> |
| | | <view class="radio-options"> |
| | |
| | | v-for="gender in genderOptions" |
| | | :key="gender.value" |
| | | class="option-item" |
| | | :class="{ active: form.sex === gender.value }" |
| | | :class="{ active: form.sex == gender.value }" |
| | | @click="form.sex = gender.value" |
| | | > |
| | | <text class="radio-dot"></text> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 改造后的出生日期选择 --> |
| | | <view class="form-item"> |
| | | <text class="item-label">出生日期</text> |
| | | <picker |
| | |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | |
| | | <!-- ===== 修改:年龄改为可手动输入 + 单位选择 ===== --> |
| | | <view class="form-item"> |
| | | <text class="item-label">年龄</text> |
| | | <view style="display: flex; gap: 10rpx; align-items: center"> |
| | | <u-input |
| | | v-model="form.age" |
| | | placeholder="输入或自动计算" |
| | | type="number" |
| | | style="flex: 1" |
| | | /> |
| | | <picker |
| | | mode="selector" |
| | | :range="ageUnitList" |
| | | :value="ageUnitIndex" |
| | | @change="onAgeUnitChange" |
| | | style="flex: 0 0 160rpx" |
| | | > |
| | | <view |
| | | class="picker" |
| | | style=" |
| | | height: 86rpx; |
| | | background: #fafafa; |
| | | border-radius: 12rpx; |
| | | padding: 0 16rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | border: 2rpx solid #e5e5e7; |
| | | " |
| | | > |
| | | <text>{{ currentAgeUnitText || "单位" }}</text> |
| | | <text class="icon-arrow">›</text> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | </view> |
| | | <!-- ===== 修改结束 ===== --> |
| | | <view class="form-item"> |
| | | <text class="item-label">联系电话</text> |
| | | <u-input |
| | | v-model="ageDisplay" |
| | | placeholder="自动计算" |
| | | disabled |
| | | :disabledColor="disabledColor" |
| | | border="none" |
| | | v-model="form.phone" |
| | | placeholder="请输入联系电话" |
| | | type="number" |
| | | /> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="item-label">学历</text> |
| | | <picker |
| | | mode="selector" |
| | | :range="educationList" |
| | | :value="educationIndex" |
| | | @change="onEducationChange" |
| | | > |
| | | <view class="picker"> |
| | | <text>{{ currentEducationText || "请选择学历" }}</text> |
| | | <text class="icon-arrow">›</text> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="item-label">职业</text> |
| | | <picker |
| | | mode="selector" |
| | | :range="occupationList" |
| | | :value="occupationIndex" |
| | | @change="onOccupationChange" |
| | | > |
| | | <view class="picker"> |
| | | <text>{{ currentOccupationText || "请选择职业" }}</text> |
| | | <text class="icon-arrow">›</text> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="item-label">籍贯</text> |
| | | <u-input v-model="form.nativeplace" placeholder="请输入籍贯" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 地址信息卡片 --> |
| | | <view class="form-section"> |
| | | <view class="section-header"> |
| | | <view class="section-icon">📍</view> |
| | | <text class="section-title">地址信息</text> |
| | | </view> |
| | | |
| | | <view class="address-block"> |
| | | <text class="address-label">现住地址</text> |
| | | <area-select v-model="residenceAddress" @change="onResidenceChange" /> |
| | | <u-input |
| | | class="address-detail-input" |
| | | v-model="form.residenceaddress" |
| | | placeholder="请输入详细地址" |
| | | border="none" |
| | | /> |
| | | </view> |
| | | |
| | | <view class="address-block"> |
| | | <text class="address-label">户籍地址</text> |
| | | <area-select v-model="registerAddress" @change="onRegisterChange" /> |
| | | <u-input |
| | | class="address-detail-input" |
| | | v-model="form.registeraddress" |
| | | placeholder="请输入详细地址" |
| | | border="none" |
| | | /> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | <view class="section-icon">🏥</view> |
| | | <text class="section-title">医疗信息</text> |
| | | </view> |
| | | |
| | | <view class="form-grid"> |
| | | <view class="form-item"> |
| | | <text class="item-label">住院号</text> |
| | | <u-input |
| | | v-model="form.inpatientno" |
| | | placeholder="请输入住院号" |
| | | border="none" |
| | | /> |
| | | <u-input v-model="form.inpatientno" placeholder="请输入住院号" /> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="item-label">GCS评分</text> |
| | |
| | | type="number" |
| | | maxlength="1" |
| | | placeholder="≤7" |
| | | border="none" |
| | | @blur="handleGcsBlur" |
| | | /> |
| | | </view> |
| | | |
| | | <view class="form-item full-width"> |
| | | <text class="item-label required">疾病诊断</text> |
| | | <u-textarea |
| | |
| | | placeholder="请输入疾病诊断名称" |
| | | count |
| | | maxlength="200" |
| | | :customStyle="textareaStyle(!form.diagnosisname)" |
| | | /> |
| | | </view> |
| | | <!-- <view class="form-item"> |
| | | <text class="item-label">是否需要转运</text> |
| | | <view class="radio-group horizontal"> |
| | | <view |
| | | v-for="bloodType in isTransportOptions" |
| | | :key="bloodType.value" |
| | | class="radio-item" |
| | | @click="form.isTransport = bloodType.value" |
| | | > |
| | | <view |
| | | class="radio-dot" |
| | | :class="{ active: form.isTransport === bloodType.value }" |
| | | ></view> |
| | | <text class="radio-label">{{ bloodType.label }}</text> |
| | | </view> |
| | | </view> |
| | | </view> --> |
| | | <view class="form-item"> |
| | | <text class="item-label">血型</text> |
| | | <view class="radio-group horizontal"> |
| | |
| | | > |
| | | <view |
| | | class="radio-dot" |
| | | :class="{ active: form.bloodType === bloodType.value }" |
| | | :class="{ active: form.bloodType == bloodType.value }" |
| | | ></view> |
| | | <text class="radio-label">{{ bloodType.label }}</text> |
| | | </view> |
| | |
| | | > |
| | | <view |
| | | class="radio-dot" |
| | | :class="{ active: form.rhYin === rh.value }" |
| | | :class="{ active: form.rhYin == rh.value }" |
| | | ></view> |
| | | <text class="radio-label">{{ rh.label }}</text> |
| | | </view> |
| | |
| | | <text class="item-label">传染病</text> |
| | | <view class="checkbox-group single-line"> |
| | | <view |
| | | v-for="disease in infectiousDiseaselist" |
| | | v-for="disease in infectiousList" |
| | | :key="disease.value" |
| | | class="checkbox-item" |
| | | :class="{ active: isInfectiousSelected(disease.value) }" |
| | | @click="toggleInfectious(disease.value)" |
| | | > |
| | | <view |
| | |
| | | <u-input |
| | | v-model="form.infectiousOther" |
| | | placeholder="请输入其他传染病" |
| | | border="none" |
| | | /> |
| | | </view> |
| | | </view> |
| | |
| | | <view class="section-icon">📞</view> |
| | | <text class="section-title">联系信息</text> |
| | | </view> |
| | | |
| | | <view class="form-grid"> |
| | | <view class="form-item"> |
| | | <!-- <view class="form-item"> |
| | | <text class="item-label">ICU评估医生</text> |
| | | <u-input |
| | | v-model="form.icuDoctor" |
| | | placeholder="请输入ICU评估医生" |
| | | border="none" |
| | | /> |
| | | <u-input v-model="form.icuDoctor" placeholder="请输入ICU评估医生" /> |
| | | </view> |
| | | |
| | | <view class="form-item"> |
| | | <text class="item-label">ICU医生电话</text> |
| | | <u-input |
| | | v-model="form.icuDoctorPhone" |
| | | placeholder="请输入ICU医生电话" |
| | | type="number" |
| | | border="none" |
| | | /> |
| | | </view> |
| | | <u-input v-model="form.icuDoctorPhone" placeholder="请输入ICU医生电话" type="number" /> |
| | | </view> --> |
| | | <view class="form-item"> |
| | | <text class="item-label">上报信息员</text> |
| | | <u-input |
| | | v-model="form.infoName" |
| | | placeholder="请输入信息员" |
| | | border="none" |
| | | /> |
| | | <u-input v-model="form.infoName" placeholder="请输入信息员" /> |
| | | </view> |
| | | |
| | | <view class="form-item"> |
| | | <text class="item-label">联系电话</text> |
| | | <u-input |
| | | v-model="form.phone" |
| | | placeholder="请输入联系电话" |
| | | type="number" |
| | | border="none" |
| | | /> |
| | | </view> |
| | | |
| | | <!-- <view class="form-item"> |
| | | <text class="item-label">报告人</text> |
| | | <u-input |
| | | v-model="form.reporterno" |
| | | placeholder="请选择报告人" |
| | | readonly |
| | | border="none" |
| | | @click="showReporterPicker = true" |
| | | > |
| | | <template #suffix> |
| | | <u-icon name="arrow-down" color="#86868b"></u-icon> |
| | | </template> |
| | | </u-input> |
| | | </view> --> |
| | | |
| | | <!-- ===== 修改:报告时间改为可手动选择 ===== --> |
| | | <view class="form-item"> |
| | | <text class="item-label">报告时间</text> |
| | | <u-input |
| | | v-model="currentTime" |
| | | disabled |
| | | :disabledColor="disabledColor" |
| | | border="none" |
| | | <view |
| | | class="picker" |
| | | @click="showReportTimePicker = true" |
| | | style=" |
| | | height: 86rpx; |
| | | background: #fafafa; |
| | | border-radius: 12rpx; |
| | | padding: 0 24rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | border: 2rpx solid #e5e5e7; |
| | | " |
| | | > |
| | | <text>{{ form.reporttime || "选择报告时间" }}</text> |
| | | <text class="icon-arrow">›</text> |
| | | </view> |
| | | <u-datetime-picker |
| | | :show="showReportTimePicker" |
| | | v-model="reportTimeValue" |
| | | mode="datetime" |
| | | @confirm="onReportTimeConfirm" |
| | | @cancel="showReportTimePicker = false" |
| | | title="选择报告时间" |
| | | /> |
| | | </view> |
| | | <!-- ===== 修改结束 ===== --> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 附件上传组件(假设已实现) --> |
| | | <attachment-upload |
| | | ref="attachment" |
| | | :files="attachments" |
| | | :readonly="isReadonly" |
| | | :maxCount="5" |
| | | @update:files="handleFilesUpdate" |
| | | @upload-base="handleBaseUpload" |
| | | @preview="handlePreview" |
| | | /> |
| | | |
| | | <!-- 操作按钮 --> |
| | | <view class="action-buttons"> |
| | |
| | | {{ loading ? "提交中..." : isEditMode ? "保存修改" : "提交上报" }} |
| | | </u-button> |
| | | </view> |
| | | <attachment-upload |
| | | ref="attachment" |
| | | :files="attachments" |
| | | :readonly="isReadonly" |
| | | :maxCount="5" |
| | | @update:files="handleFilesUpdate" |
| | | @upload-base="handleBaseUpload" |
| | | @preview="handlePreview" |
| | | /> |
| | | </view> |
| | | |
| | | <!-- 科室选择器 --> |
| | | <u-picker |
| | | :show="selectShow" |
| | | :columns="pickerColumns" |
| | | keyName="label" |
| | | @confirm="onConfirm" |
| | | @cancel="selectShow = false" |
| | | title="请选择科室" |
| | | ref="uPicker" |
| | | ></u-picker> |
| | | |
| | | <!-- 医疗机构选择器 --> |
| | | <u-picker |
| | | :show="showHospitalPicker" |
| | | :columns="[hospitalOptions]" |
| | | keyName="label" |
| | | @confirm="onHospitalConfirm" |
| | | @cancel="showHospitalPicker = false" |
| | | title="请选择医疗机构" |
| | | ></u-picker> |
| | | |
| | | <!-- 民族选择器 --> |
| | | <u-picker |
| | | :show="showNationPicker" |
| | | :columns="[nationOptions]" |
| | | keyName="label" |
| | | @confirm="onNationConfirm" |
| | | @cancel="showNationPicker = false" |
| | | title="请选择民族" |
| | | ></u-picker> |
| | | |
| | | <!-- 证件类型选择器 --> |
| | | <!-- <u-picker |
| | | :show="showIdCardTypePicker" |
| | | :columns="[idCardTypeOptions]" |
| | | keyName="label" |
| | | @confirm="onIdCardTypeConfirm" |
| | | @cancel="showIdCardTypePicker = false" |
| | | title="请选择证件类型" |
| | | ></u-picker> --> |
| | | |
| | | <!-- 日期选择器 --> |
| | | <u-datetime-picker |
| | | :show="showDatePicker" |
| | | v-model="birthdayValue" |
| | | mode="date" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDatePicker = false" |
| | | title="选择出生日期" |
| | | ></u-datetime-picker> |
| | | |
| | | <!-- 报告人选择器 --> |
| | | <u-picker |
| | | :show="showReporterPicker" |
| | | :columns="[reporterOptions]" |
| | | keyName="label" |
| | | @confirm="onReporterConfirm" |
| | | @cancel="showReporterPicker = false" |
| | | title="请选择报告人" |
| | | ></u-picker> |
| | | |
| | | <!-- 加载状态 --> |
| | | <u-loading-icon :show="loading" text="提交中..."></u-loading-icon> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, watch } from "vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import attachmentUpload from "@/components/attachment"; |
| | | import attachmentUpload from "@/components/attachment"; // 根据实际路径调整 |
| | | import AreaSelect from "@/components/AreaSelect"; // 引入省市区组件 |
| | | import { useUserStore } from "@/stores/user"; |
| | | import { useDict } from "@/utils/dict"; |
| | | import { useDictMapper } from "@/utils/useDictMapper"; |
| | | |
| | | const dict = ref({}); |
| | | // ==================== 字典数据 ==================== |
| | | const requiredDictTypes = [ |
| | | "sys_IDType", |
| | | "sys_user_sex", |
| | | "sys_Nation", |
| | | "sys_BloodType", |
| | | "sys_Infectious", |
| | | "sys_AgeUnit", // 年龄单位(已存在) |
| | | "sys_education", |
| | | "sys_occupation", |
| | | ]; |
| | | |
| | | const { dictData, loading: dictLoading } = useDictMapper(requiredDictTypes); |
| | | const getDictList = (dictType) => dictData.value[dictType] || []; |
| | | |
| | | const nationList = computed(() => |
| | | getDictList("sys_Nation").map((item) => item.label), |
| | | ); |
| | | const idCardTypeList = computed(() => |
| | | getDictList("sys_IDType").map((item) => item.label), |
| | | ); |
| | | const infectiousList = computed(() => getDictList("sys_Infectious")); |
| | | const educationList = computed(() => |
| | | getDictList("sys_education").map((item) => item.label), |
| | | ); |
| | | const occupationList = computed(() => |
| | | getDictList("sys_occupation").map((item) => item.label), |
| | | ); |
| | | // 年龄单位列表 |
| | | const ageUnitList = computed(() => |
| | | getDictList("sys_AgeUnit").map((item) => item.label), |
| | | ); |
| | | |
| | | // ==================== 状态管理 ==================== |
| | | const userStore = useUserStore(); |
| | | const isEditMode = ref(false); |
| | | const currentId = ref(null); |
| | | const selectedInfectious = ref([]); // 存储选中的传染病值数组 |
| | | const selectedInfectious = ref([]); |
| | | const loading = ref(false); |
| | | const currentTime = ref(""); |
| | | const disabledColor = ref("#f5f5f7"); |
| | | const isReadonly = ref(false); |
| | | const showDatePicker = ref(false); |
| | | const birthdayValue = ref(0); |
| | | const nationIndex = ref(-1); |
| | | const idCardTypeIndex = ref(-1); |
| | | const educationIndex = ref(-1); |
| | | const occupationIndex = ref(-1); |
| | | const idCardError = ref(""); |
| | | const attachments = ref([]); |
| | | |
| | | // 表单数据 |
| | | // 年龄单位索引 |
| | | const ageUnitIndex = ref(-1); |
| | | |
| | | // 报告时间选择器 |
| | | const showReportTimePicker = ref(false); |
| | | const reportTimeValue = ref(Date.now()); |
| | | |
| | | // 地址数据 |
| | | const residenceAddress = ref({ sheng: "", shi: "", qu: "" }); |
| | | const registerAddress = ref({ sheng: "", shi: "", qu: "" }); |
| | | |
| | | // ==================== 表单数据 ==================== |
| | | const form = ref({ |
| | | caseNo: "", |
| | | treatmenthospitalname: "", |
| | |
| | | coordinatorName: "", |
| | | coordinatorNo: "", |
| | | treatmentdeptname: "", |
| | | deptName: "", |
| | | name: "", |
| | | nation: "", |
| | | nationality: "中国", |
| | | idcardtype: "", |
| | | idcardno: "", |
| | | rhYin: "1", |
| | | sex: "", |
| | | birthday: "", |
| | | age: "", |
| | | ageunit: "", // 新增:年龄单位 |
| | | ageunit: "", |
| | | inpatientno: "", |
| | | gcsScore: "", |
| | | diagnosisname: "", |
| | | bloodType: "", |
| | | rhYin: "", |
| | | infoName: "", |
| | | phone: "", |
| | | icuDoctor: "", |
| | | icuDoctorPhone: "", |
| | | reportername: "", |
| | | reporterno: "", |
| | | reporterphone: "", // 报告者联系电话 |
| | | reporterphone: "", |
| | | reporttime: "", |
| | | contactperson: "", // 新增:联系人(协调员) |
| | | education: "", // 新增:学历 |
| | | illnessoverview: "", // 新增:病情概况 |
| | | infectious: "", // 传染病情况 |
| | | infectiousOther: "", // 传染病其他 |
| | | isTransport: "", // 新增:是否需要转运,默认1不需要 |
| | | nativeplace: "", // 新增:籍贯 |
| | | occupation: "", // 新增:职业 |
| | | patientstate: "", // 新增:病人状况 |
| | | registeraddress: "", // 新增:户籍地址 |
| | | registerprovince: "", // 新增:户籍地址省编号 |
| | | registerprovincename: "", // 新增:户籍地址省名称 |
| | | registercityname: "", // 新增:市名称 |
| | | registertownname: "", // 新增:所属街道(镇)名称 |
| | | registercommunityname: "", // 新增:社区(村)名称 |
| | | residenceaddress: "", // 新增:现住地址 |
| | | residenceprovince: "", // 新增:现住地址省代码 |
| | | residenceprovincename: "", // 新增:现住地址省名称 |
| | | residencecountycode: "", // 新增:所属区域编号 |
| | | residencecountyname: "", // 新增:所属区域名称 |
| | | residencetownname: "", // 新增:所属街道(镇)名称 |
| | | residencecommunity: "", // 新增:社区(村)编号 |
| | | residencecommunityname: "", // 新增:社区(村)名称 |
| | | remark: "", // 新增:备注 |
| | | reportStatus: "1", // 新增:上报状态,默认1已上报 |
| | | terminationCase: 0, // 新增:是否终止案例,默认0开启 |
| | | annexfilesList: [], // 附件文件地址集合 |
| | | contactperson: "", |
| | | education: "", |
| | | illnessoverview: "", |
| | | infectious: "", |
| | | infectiousOther: "", |
| | | isTransport: "", |
| | | nativeplace: "", |
| | | occupation: "", |
| | | patientstate: "", |
| | | registeraddress: "", |
| | | registerprovince: "", |
| | | registerprovincename: "", |
| | | registercityname: "", |
| | | registertownname: "", |
| | | registercommunityname: "", |
| | | residenceaddress: "", |
| | | residenceprovince: "", |
| | | residenceprovincename: "", |
| | | residencecountycode: "", |
| | | residencecountyname: "", |
| | | residencetownname: "", |
| | | residencecommunity: "", |
| | | residencecommunityname: "", |
| | | remark: "", |
| | | reportStatus: "1", |
| | | terminationCase: 0, |
| | | annexfilesList: [], |
| | | }); |
| | | |
| | | // 选择器状态 |
| | | const attachments = ref([]); |
| | | const infectiousDiseaselist = ref([]); |
| | | const nationLabel = ref([]); |
| | | // ==================== 选项数据 ==================== |
| | | const genderOptions = computed(() => { |
| | | const sexDict = getDictList("sys_user_sex"); |
| | | if (sexDict.length) |
| | | return sexDict.map((item) => ({ label: item.label, value: item.value })); |
| | | return [ |
| | | { label: "男", value: "1" }, |
| | | { label: "女", value: "2" }, |
| | | ]; |
| | | }); |
| | | |
| | | const isReadonly = ref(false); |
| | | const id = ref(null); |
| | | const selectShow = ref(false); |
| | | const showHospitalPicker = ref(false); |
| | | const showNationPicker = ref(false); |
| | | const showIdCardTypePicker = ref(false); |
| | | const showDatePicker = ref(false); |
| | | const showReporterPicker = ref(false); |
| | | const birthdayValue = ref(0); |
| | | |
| | | // 字典数据选项 |
| | | const hospitalOptions = ref([ |
| | | { label: "青岛镜湖医院", value: "qdhospital1" }, |
| | | { label: "青岛科大医院", value: "qdhospital2" }, |
| | | { label: "青岛大学附属医院", value: "qdhospital3" }, |
| | | { label: "青岛市立医院", value: "qdhospital4" }, |
| | | ]); |
| | | |
| | | const pickerColumns = ref([ |
| | | [ |
| | | { label: "神经外科", value: "neurosurgery" }, |
| | | { label: "心血管内科", value: "cardiology" }, |
| | | { label: "重症医学科", value: "icu" }, |
| | | { label: "急诊科", value: "emergency" }, |
| | | { label: "神经内科", value: "neurology" }, |
| | | { label: "呼吸内科", value: "respiratory" }, |
| | | { label: "消化内科", value: "gastroenterology" }, |
| | | { label: "肾内科", value: "nephrology" }, |
| | | ], |
| | | ]); |
| | | |
| | | const nationOptions = ref([ |
| | | { label: "汉族", value: "han" }, |
| | | { label: "回族", value: "hui" }, |
| | | { label: "满族", value: "man" }, |
| | | { label: "蒙古族", value: "menggu" }, |
| | | ]); |
| | | |
| | | const idCardTypeOptions = ref([ |
| | | { label: "居民身份证", value: "idcard" }, |
| | | { label: "护照", value: "passport" }, |
| | | { label: "军官证", value: "officer" }, |
| | | ]); |
| | | |
| | | const genderOptions = ref([ |
| | | { label: "男", value: "1" }, |
| | | { label: "女", value: "2" }, |
| | | ]); |
| | | |
| | | const bloodTypeOptions = ref([ |
| | | const bloodTypeOptions = [ |
| | | { label: "A型", value: "A型" }, |
| | | { label: "B型", value: "B型" }, |
| | | { label: "O型", value: "O型" }, |
| | | { label: "AB型", value: "AB型" }, |
| | | ]); |
| | | const isTransportOptions = ref([ |
| | | { label: "需要", value: "2" }, |
| | | { label: "不需要", value: "1" }, |
| | | ]); |
| | | const rhOptions = ref([ |
| | | ]; |
| | | |
| | | const rhOptions = [ |
| | | { label: "阳性", value: "1" }, |
| | | { label: "阴性", value: "0" }, |
| | | ]); |
| | | ]; |
| | | |
| | | const reporterOptions = ref([ |
| | | { label: "张医生", value: "doctor1" }, |
| | | { label: "李医生", value: "doctor2" }, |
| | | ]); |
| | | |
| | | // 状态管理 |
| | | const loading = ref(false); |
| | | const currentTime = ref(""); |
| | | const disabledColor = ref("#f5f5f7"); |
| | | |
| | | // 计算属性 |
| | | const isFormValid = computed(() => { |
| | | return ( |
| | | form.value.name && |
| | | // form.value.idcardno && |
| | | form.value.diagnosisname && |
| | | form.value.toHospital |
| | | ); |
| | | }); |
| | | // 选择器索引 |
| | | const hospitalIndex = ref(-1); |
| | | const deptIndex = ref(-1); |
| | | const nationIndex = ref(-1); |
| | | const idCardTypeIndex = ref(-1); |
| | | |
| | | // 计算属性 - 标签数组 |
| | | const hospitalLabels = computed(() => |
| | | hospitalOptions.value.map((item) => item.label), |
| | | ); |
| | | const ageDisplay = computed(() => { |
| | | if (!form.value.age || !form.value.ageunit) { |
| | | return "自动计算"; |
| | | } |
| | | return `${form.value.age}${form.value.ageunit}`; |
| | | }); |
| | | const deptLabels = computed(() => { |
| | | return pickerColumns.value[0].map((item) => item.label); |
| | | }); |
| | | const nationLabels = computed(() => |
| | | nationLabel.value.map((item) => item.label), |
| | | ); |
| | | const idCardTypeLabels = computed(() => |
| | | idCardTypeOptions.value.map((item) => item.label), |
| | | // ==================== 计算属性 ==================== |
| | | const isFormValid = computed( |
| | | () => form.value.name && form.value.diagnosisname && form.value.toHospital, |
| | | ); |
| | | |
| | | // 计算属性 - 当前选中显示文本 |
| | | const currentHospital = computed(() => { |
| | | return hospitalIndex.value >= 0 |
| | | ? hospitalLabels.value[hospitalIndex.value] |
| | | : "请选择医疗机构"; |
| | | // 年龄显示(用于回显单位文本) |
| | | const currentAgeUnitText = computed(() => { |
| | | if (ageUnitIndex.value >= 0 && ageUnitList.value[ageUnitIndex.value]) { |
| | | return ageUnitList.value[ageUnitIndex.value]; |
| | | } |
| | | if (form.value.ageunit) { |
| | | const found = getDictList("sys_AgeUnit").find( |
| | | (item) => item.value === form.value.ageunit, |
| | | ); |
| | | return found ? found.label : form.value.ageunit; |
| | | } |
| | | return ""; |
| | | }); |
| | | |
| | | const currentDept = computed(() => { |
| | | return deptIndex.value >= 0 |
| | | ? deptLabels.value[deptIndex.value] |
| | | : "请选择科室"; |
| | | const currentNationText = computed(() => { |
| | | if (nationIndex.value >= 0 && nationList.value[nationIndex.value]) |
| | | return nationList.value[nationIndex.value]; |
| | | return form.value.nation || ""; |
| | | }); |
| | | |
| | | const currentNation = computed(() => { |
| | | return nationIndex.value >= 0 |
| | | ? nationLabels.value[nationIndex.value] |
| | | : form.value.nation |
| | | ? form.value.nation |
| | | : "请选择民族"; |
| | | const currentIdCardTypeText = computed(() => { |
| | | if (idCardTypeIndex.value >= 0 && idCardTypeList.value[idCardTypeIndex.value]) |
| | | return idCardTypeList.value[idCardTypeIndex.value]; |
| | | if (form.value.idcardtype) { |
| | | const found = getDictList("sys_IDType").find( |
| | | (item) => item.value == form.value.idcardtype, |
| | | ); |
| | | return found ? found.label : form.value.idcardtype; |
| | | } |
| | | return ""; |
| | | }); |
| | | |
| | | const currentIdCardType = computed(() => { |
| | | return idCardTypeIndex.value >= 0 |
| | | ? idCardTypeLabels.value[idCardTypeIndex.value] |
| | | : "请选择证件类型"; |
| | | }); |
| | | const onNationChange = (e) => { |
| | | const index = parseInt(e.detail.value); |
| | | nationIndex.value = index; |
| | | form.value.nation = nationOptions.value[index].label; |
| | | console.log(form.value.nation, "form.value.nation"); |
| | | }; |
| | | |
| | | const onIdCardTypeChange = (e) => { |
| | | const index = parseInt(e.detail.value); |
| | | idCardTypeIndex.value = index; |
| | | form.value.idcardtype = idCardTypeOptions.value[index].value; |
| | | console.log(form.value.idcardtype, "form.value.idcardtype"); |
| | | }; |
| | | |
| | | const onBirthdayChange = (e) => { |
| | | form.value.birthday = e.detail.value; |
| | | calculateAge(); |
| | | }; |
| | | const onDateConfirm = (e) => { |
| | | const date = new Date(e.value); |
| | | |
| | | // 格式化日期为 YYYY-MM-DD |
| | | form.value.birthday = `${date.getFullYear()}-${(date.getMonth() + 1) |
| | | .toString() |
| | | .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`; |
| | | |
| | | calculateAge(); |
| | | showDatePicker.value = false; |
| | | }; |
| | | |
| | | // 身份证验证 |
| | | const idCardError = ref(""); |
| | | const validateIdCard = () => { |
| | | const idCard = form.value.idcardno; |
| | | console.log(idCard, "当前输入"); |
| | | |
| | | // if (!idCard) { |
| | | // idCardError.value = "请输入证件号码"; |
| | | // return false; |
| | | // } |
| | | |
| | | if (idCard.length !== 18) { |
| | | idCardError.value = "身份证号码必须是18位"; |
| | | return false; |
| | | const currentEducationText = computed(() => { |
| | | if (educationIndex.value >= 0 && educationList.value[educationIndex.value]) |
| | | return educationList.value[educationIndex.value]; |
| | | if (form.value.education) { |
| | | const found = getDictList("sys_education").find( |
| | | (item) => item.value == form.value.education, |
| | | ); |
| | | return found ? found.label : form.value.education; |
| | | } |
| | | |
| | | // 验证前17位必须是数字 |
| | | const reg = /^\d{17}(\d|X|x)$/; |
| | | if (!reg.test(idCard)) { |
| | | idCardError.value = "身份证号码格式不正确"; |
| | | return false; |
| | | } |
| | | // 验证出生日期 |
| | | const birthStr = idCard.substring(6, 14); |
| | | const year = birthStr.substring(0, 4); |
| | | const month = birthStr.substring(4, 6); |
| | | const day = birthStr.substring(6, 8); |
| | | |
| | | const birthDate = new Date(`${year}-${month}-${day}`); |
| | | if (isNaN(birthDate.getTime())) { |
| | | idCardError.value = "身份证号码中的出生日期无效"; |
| | | return false; |
| | | } |
| | | console.log(3); |
| | | |
| | | // 验证日期范围(1900年至今) |
| | | const currentYear = new Date().getFullYear(); |
| | | const birthYear = parseInt(year, 10); |
| | | if (birthYear < 1900 || birthYear > currentYear) { |
| | | idCardError.value = "出生年份不在有效范围内"; |
| | | return false; |
| | | } |
| | | console.log(4); |
| | | |
| | | // 验证校验码(第18位) |
| | | if (!validateIdCardCheckCode(idCard)) { |
| | | idCardError.value = "身份证号码校验失败"; |
| | | return false; |
| | | } |
| | | console.log(5); |
| | | |
| | | idCardError.value = ""; |
| | | return true; |
| | | }; |
| | | // 身份证号变化处理 |
| | | const onIdCardChange = (value) => { |
| | | // 验证身份证格式 |
| | | if (validateIdCard()) { |
| | | // 尝试提取出生日期 |
| | | extractBirthdayFromIdCard(); |
| | | } |
| | | }; |
| | | const handleGcsBlur = () => { |
| | | const val = Number(form.value.gcsScore); |
| | | |
| | | if (!val && val !== 0) return; |
| | | console.log(val); |
| | | |
| | | if (val > 7) { |
| | | uni.showToast({ |
| | | title: "GCS评分不能大于7", |
| | | icon: "none", |
| | | }); |
| | | form.value.gcsScore = ""; |
| | | } |
| | | |
| | | if (val < 3) { |
| | | uni.showToast({ |
| | | title: "GCS评分不能小于3", |
| | | icon: "none", |
| | | }); |
| | | form.value.gcsScore = ""; |
| | | } |
| | | }; |
| | | |
| | | // 身份证校验码验证函数 |
| | | const validateIdCardCheckCode = (idCard) => { |
| | | // 系数数组 |
| | | const coefficientArray = [ |
| | | 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, |
| | | ]; |
| | | // 校验码对应值 |
| | | const checkCodeMap = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]; |
| | | |
| | | let sum = 0; |
| | | for (let i = 0; i < 17; i++) { |
| | | sum += parseInt(idCard.charAt(i), 10) * coefficientArray[i]; |
| | | } |
| | | |
| | | const checkCode = checkCodeMap[sum % 11]; |
| | | return checkCode === idCard.charAt(17).toUpperCase(); |
| | | }; |
| | | // 从身份证号提取出生日期 |
| | | const extractBirthdayFromIdCard = () => { |
| | | const idCard = form.value.idcardno; |
| | | |
| | | if (!idCard || idCard.length !== 18) { |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // 提取出生日期部分(第7-14位:YYYYMMDD) |
| | | const birthStr = idCard.substring(6, 14); |
| | | const year = birthStr.substring(0, 4); |
| | | const month = birthStr.substring(4, 6); |
| | | const day = birthStr.substring(6, 8); |
| | | |
| | | // 验证日期是否有效 |
| | | const birthDate = new Date(`${year}-${month}-${day}`); |
| | | if (isNaN(birthDate.getTime())) { |
| | | console.log("身份证号码中的出生日期无效"); |
| | | return; |
| | | } |
| | | |
| | | // 验证日期范围(不能是未来日期) |
| | | const today = new Date(); |
| | | if (birthDate > today) { |
| | | console.log("出生日期不能是未来日期"); |
| | | return; |
| | | } |
| | | |
| | | // 验证月份和日期是否合理 |
| | | const monthNum = parseInt(month, 10); |
| | | const dayNum = parseInt(day, 10); |
| | | |
| | | if (monthNum < 1 || monthNum > 12) { |
| | | console.log("月份无效"); |
| | | return; |
| | | } |
| | | |
| | | // 检查日期是否有效(简单验证) |
| | | const daysInMonth = new Date(year, monthNum, 0).getDate(); |
| | | if (dayNum < 1 || dayNum > daysInMonth) { |
| | | console.log("日期无效"); |
| | | return; |
| | | } |
| | | |
| | | // 设置出生日期 |
| | | form.value.birthday = `${year}-${month.padStart(2, "0")}-${day.padStart( |
| | | 2, |
| | | "0", |
| | | )}`; |
| | | |
| | | // 计算年龄 |
| | | calculateAge(); |
| | | |
| | | // 从身份证提取性别(第17位,奇数为男,偶数为女) |
| | | const genderCode = parseInt(idCard.charAt(16), 10); |
| | | if (!isNaN(genderCode)) { |
| | | form.value.sex = genderCode % 2 === 1 ? "1" : "2"; |
| | | } |
| | | |
| | | uni.showToast({ |
| | | title: "已自动提取出生日期和性别", |
| | | icon: "success", |
| | | duration: 1500, |
| | | }); |
| | | } catch (error) { |
| | | console.error("解析身份证出生日期失败:", error); |
| | | } |
| | | }; |
| | | |
| | | const textareaStyle = (isError) => { |
| | | return isError |
| | | ? "border: 2rpx solid #ff4757; border-radius: 12rpx; min-height: 120rpx; padding: 20rpx 24rpx;" |
| | | : "border: 2rpx solid #e5e5e7; border-radius: 12rpx; min-height: 120rpx; padding: 20rpx 24rpx;"; |
| | | }; |
| | | |
| | | // 生命周期 |
| | | onMounted(() => { |
| | | // updateCurrentTime(); |
| | | // generateDonorNo(); |
| | | setInterval(updateCurrentTime, 1000); |
| | | return ""; |
| | | }); |
| | | |
| | | onLoad(async (options) => { |
| | | id.value = options.id; |
| | | if (options.id) { |
| | | currentId.value = options.id; |
| | | isEditMode.value = true; |
| | | await loadCaseData(options.id); |
| | | } else { |
| | | isEditMode.value = false; |
| | | generateDonorNo(); |
| | | const currentOccupationText = computed(() => { |
| | | if (occupationIndex.value >= 0 && occupationList.value[occupationIndex.value]) |
| | | return occupationList.value[occupationIndex.value]; |
| | | if (form.value.occupation) { |
| | | const found = getDictList("sys_occupation").find( |
| | | (item) => item.value == form.value.occupation, |
| | | ); |
| | | return found ? found.label : form.value.occupation; |
| | | } |
| | | console.log(userStore.userInfo, "userStoreuserInfo"); |
| | | |
| | | if (!userStore.userInfo) { |
| | | await userStore.refreshUserInfo(); |
| | | } |
| | | form.value.treatmenthospitalname = userStore.userInfo.orgName; |
| | | form.value.toHospital = userStore.userInfo.orgName; |
| | | form.value.coordinatorName = userStore.userInfo.coordinatorName; |
| | | form.value.coordinatorNo = userStore.userInfo.coordinatorNo; |
| | | form.value.infoName = userStore.userInfo.nickName; |
| | | form.value.phone = userStore.userInfo.phonenumber; |
| | | form.value.reportername = userStore.userInfo.name; |
| | | form.value.reporterno = userStore.userInfo.userId; |
| | | // 获取字典数据 |
| | | dict.value = await useDict( |
| | | "sys_IDType", |
| | | "sys_user_sex", |
| | | "sys_Nation", |
| | | "sys_BloodType", |
| | | "sys_Infectious", |
| | | "sys_AgeUnit", // 新增年龄单位 |
| | | "sys_education", // 新增学历 |
| | | "sys_occupation", // 新增职业 |
| | | ); |
| | | initOptions(); |
| | | updateCurrentTime(); |
| | | return ""; |
| | | }); |
| | | // 初始化选项数据的方法 |
| | | const initOptions = () => { |
| | | infectiousDiseaselist.value = dict.value.sys_Infectious || []; |
| | | idCardTypeOptions.value = dict.value.sys_IDType || []; |
| | | nationLabel.value = dict.value.sys_Nation || []; |
| | | |
| | | // 初始化传染病选择状态 |
| | | if (form.value.infectious) { |
| | | // 将逗号分隔的字符串转换为数组 |
| | | selectedInfectious.value = form.value.infectious |
| | | .split(",") |
| | | .filter((item) => item.trim() !== ""); |
| | | } |
| | | }; |
| | | // 切换传染病选择 |
| | | const toggleInfectious = (value) => { |
| | | const index = selectedInfectious.value.indexOf(value); |
| | | |
| | | if (index === -1) { |
| | | // 添加选中 |
| | | selectedInfectious.value.push(value); |
| | | } else { |
| | | // 移除选中 |
| | | selectedInfectious.value.splice(index, 1); |
| | | } |
| | | |
| | | // 更新表单的 infectious 字段(逗号分隔的字符串) |
| | | form.value.infectious = selectedInfectious.value.join(","); |
| | | }; |
| | | // 检查传染病是否被选中 |
| | | const isInfectiousSelected = (value) => { |
| | | return selectedInfectious.value.includes(value); |
| | | }; |
| | | |
| | | // 方法定义 |
| | | // ==================== 方法 ==================== |
| | | const updateCurrentTime = () => { |
| | | const now = new Date(); |
| | | |
| | | // 手动格式化日期和时间 |
| | | const year = now.getFullYear(); |
| | | const month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(now.getDate()).padStart(2, "0"); |
| | | const hours = String(now.getHours()).padStart(2, "0"); |
| | | const minutes = String(now.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(now.getSeconds()).padStart(2, "0"); |
| | | |
| | | currentTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | form.value.reporttime = currentTime.value; |
| | | const timeStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | currentTime.value = timeStr; |
| | | // 如果报告时间尚未手动设置(或为空),则自动填入当前时间 |
| | | if (!form.value.reporttime) { |
| | | form.value.reporttime = timeStr; |
| | | } |
| | | }; |
| | | |
| | | const generateDonorNo = () => { |
| | | const date = new Date(); |
| | | const timestamp = date.getTime().toString().slice(-6); |
| | | // form.value.caseNo = `DON${date.getFullYear()}${(date.getMonth() + 1) |
| | | // .toString() |
| | | // .padStart(2, "0")}${timestamp}`; |
| | | const generateCaseNo = () => { |
| | | // 优先使用上报时间,如果没有则使用当前时间 |
| | | const timeStr = form.value.reporttime || currentTime.value; |
| | | |
| | | // 解析时间字符串 |
| | | let year, month, day, hours, minutes; |
| | | |
| | | if (timeStr) { |
| | | // 格式:2026-07-15 14:30:00 |
| | | const parts = timeStr.split(" "); |
| | | if (parts.length >= 2) { |
| | | const dateParts = parts[0].split("-"); |
| | | const timeParts = parts[1].split(":"); |
| | | year = dateParts[0]; |
| | | month = dateParts[1]; |
| | | day = dateParts[2]; |
| | | hours = timeParts[0]; |
| | | minutes = timeParts[1]; |
| | | } else { |
| | | // 只有日期没有时间 |
| | | const dateParts = timeStr.split("-"); |
| | | year = dateParts[0]; |
| | | month = dateParts[1]; |
| | | day = dateParts[2]; |
| | | hours = "00"; |
| | | minutes = "00"; |
| | | } |
| | | } else { |
| | | // 没有上报时间,使用当前时间 |
| | | const now = new Date(); |
| | | year = now.getFullYear().toString(); |
| | | month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | day = String(now.getDate()).padStart(2, "0"); |
| | | hours = String(now.getHours()).padStart(2, "0"); |
| | | minutes = String(now.getMinutes()).padStart(2, "0"); |
| | | } |
| | | |
| | | // 格式:opo202607151430 |
| | | form.value.caseNo = `${year}${month}${day}${hours}${minutes}`; |
| | | }; |
| | | |
| | | // 民族选择 |
| | | const onNationChange = (e) => { |
| | | const index = parseInt(e.detail.value); |
| | | nationIndex.value = index; |
| | | form.value.nation = nationList.value[index]; |
| | | }; |
| | | |
| | | // 证件类型选择 |
| | | const onIdCardTypeChange = (e) => { |
| | | const index = parseInt(e.detail.value); |
| | | idCardTypeIndex.value = index; |
| | | const selectedLabel = idCardTypeList.value[index]; |
| | | const dictItem = getDictList("sys_IDType").find( |
| | | (item) => item.label == selectedLabel, |
| | | ); |
| | | form.value.idcardtype = dictItem ? dictItem.value : selectedLabel; |
| | | }; |
| | | |
| | | // 学历选择 |
| | | const onEducationChange = (e) => { |
| | | const index = parseInt(e.detail.value); |
| | | educationIndex.value = index; |
| | | const selectedLabel = educationList.value[index]; |
| | | const dictItem = getDictList("sys_education").find( |
| | | (item) => item.label == selectedLabel, |
| | | ); |
| | | form.value.education = dictItem ? dictItem.value : selectedLabel; |
| | | }; |
| | | |
| | | // 职业选择 |
| | | const onOccupationChange = (e) => { |
| | | const index = parseInt(e.detail.value); |
| | | occupationIndex.value = index; |
| | | const selectedLabel = occupationList.value[index]; |
| | | const dictItem = getDictList("sys_occupation").find( |
| | | (item) => item.label == selectedLabel, |
| | | ); |
| | | form.value.occupation = dictItem ? dictItem.value : selectedLabel; |
| | | }; |
| | | |
| | | // 年龄单位选择 |
| | | const onAgeUnitChange = (e) => { |
| | | const index = parseInt(e.detail.value); |
| | | ageUnitIndex.value = index; |
| | | const selectedLabel = ageUnitList.value[index]; |
| | | const dictItem = getDictList("sys_AgeUnit").find( |
| | | (item) => item.label === selectedLabel, |
| | | ); |
| | | form.value.ageunit = dictItem ? dictItem.value : selectedLabel; |
| | | }; |
| | | |
| | | // 出生日期 |
| | | const onBirthdayChange = (e) => { |
| | | form.value.birthday = e.detail.value; |
| | | calculateAge(); |
| | | }; |
| | | |
| | | const onDateConfirm = (e) => { |
| | | const date = new Date(e.value); |
| | | form.value.birthday = `${date.getFullYear()}-${String( |
| | | date.getMonth() + 1, |
| | | ).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`; |
| | | calculateAge(); |
| | | showDatePicker.value = false; |
| | | }; |
| | | |
| | | const calculateAge = () => { |
| | | if (!form.value.birthday) { |
| | | form.value.age = ""; |
| | | form.value.ageunit = ""; |
| | | ageUnitIndex.value = -1; |
| | | return; |
| | | } |
| | | |
| | | const birthDate = new Date(form.value.birthday); |
| | | const today = new Date(); |
| | | |
| | | // 检查日期有效性 |
| | | if (isNaN(birthDate.getTime())) { |
| | | form.value.age = ""; |
| | | form.value.ageunit = ""; |
| | | return; |
| | | } |
| | | |
| | | // 检查是否为未来日期 |
| | | if (isNaN(birthDate.getTime())) return; |
| | | if (birthDate > today) { |
| | | uni.showToast({ |
| | | title: "出生日期不能是未来日期", |
| | | icon: "none", |
| | | }); |
| | | uni.showToast({ title: "出生日期不能是未来日期", icon: "none" }); |
| | | form.value.age = ""; |
| | | form.value.ageunit = ""; |
| | | ageUnitIndex.value = -1; |
| | | return; |
| | | } |
| | | |
| | | // 计算总天数差 |
| | | const timeDiff = today.getTime() - birthDate.getTime(); |
| | | const daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); |
| | | |
| | | if (daysDiff < 0) { |
| | | // 未来日期处理 |
| | | form.value.age = ""; |
| | | form.value.ageunit = ""; |
| | | return; |
| | | } |
| | | |
| | | // 计算年、月、日 |
| | | const years = today.getFullYear() - birthDate.getFullYear(); |
| | | const months = today.getMonth() - birthDate.getMonth(); |
| | | const days = today.getDate() - birthDate.getDate(); |
| | | |
| | | const daysDiff = Math.floor((today - birthDate) / (1000 * 60 * 60 * 24)); |
| | | if (daysDiff < 0) return; |
| | | let years = today.getFullYear() - birthDate.getFullYear(); |
| | | let months = today.getMonth() - birthDate.getMonth(); |
| | | let days = today.getDate() - birthDate.getDate(); |
| | | let ageValue, ageUnit; |
| | | |
| | | if (years >= 1) { |
| | | // 大于等于1年:显示年 |
| | | let actualYears = years; |
| | | |
| | | // 处理月份和日期的边界情况 |
| | | if (months < 0 || (months === 0 && days < 0)) { |
| | | actualYears = years - 1; |
| | | } |
| | | |
| | | ageValue = actualYears.toString(); |
| | | if (months < 0 || (months === 0 && days < 0)) years--; |
| | | ageValue = years.toString(); |
| | | ageUnit = "岁"; |
| | | } else if (daysDiff >= 30) { |
| | | // 大于等于30天:显示月 |
| | | let totalMonths = years * 12 + months; |
| | | if (days < 0) { |
| | | totalMonths--; |
| | | } |
| | | ageValue = Math.max(1, totalMonths).toString(); // 确保至少1个月 |
| | | if (days < 0) totalMonths--; |
| | | ageValue = Math.max(1, totalMonths).toString(); |
| | | ageUnit = "个月"; |
| | | } else { |
| | | // 小于30天:显示天 |
| | | ageValue = Math.max(1, daysDiff).toString(); // 确保至少1天 |
| | | ageValue = Math.max(1, daysDiff).toString(); |
| | | ageUnit = "天"; |
| | | } |
| | | |
| | | form.value.age = ageValue; |
| | | form.value.ageunit = ageUnit; |
| | | // 同步单位索引 |
| | | const unitList = ageUnitList.value; |
| | | const idx = unitList.findIndex((item) => item === ageUnit); |
| | | ageUnitIndex.value = idx >= 0 ? idx : -1; |
| | | }; |
| | | |
| | | // 处理基础附件上传 |
| | | const handleBaseUpload = (file) => { |
| | | console.log("基础附件上传成功:", file); |
| | | attachments.value.push(file); |
| | | // 身份证处理 |
| | | const validateIdCard = () => { |
| | | const idCard = form.value.idcardno; |
| | | if (!idCard) { |
| | | idCardError.value = ""; |
| | | return true; |
| | | } |
| | | if (idCard.length !== 18) { |
| | | idCardError.value = "身份证号码必须是18位"; |
| | | return false; |
| | | } |
| | | const reg = /^\d{17}(\d|X|x)$/; |
| | | if (!reg.test(idCard)) { |
| | | idCardError.value = "身份证号码格式不正确"; |
| | | return false; |
| | | } |
| | | const coefficientArray = [ |
| | | 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, |
| | | ]; |
| | | const checkCodeMap = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]; |
| | | let sum = 0; |
| | | for (let i = 0; i < 17; i++) |
| | | sum += parseInt(idCard.charAt(i), 10) * coefficientArray[i]; |
| | | const checkCode = checkCodeMap[sum % 11]; |
| | | if (checkCode != idCard.charAt(17).toUpperCase()) { |
| | | idCardError.value = "身份证号码校验失败"; |
| | | return false; |
| | | } |
| | | idCardError.value = ""; |
| | | return true; |
| | | }; |
| | | |
| | | // 处理其他附件上传 |
| | | const onIdCardChange = () => { |
| | | if (validateIdCard()) extractBirthdayFromIdCard(); |
| | | }; |
| | | |
| | | const extractBirthdayFromIdCard = () => { |
| | | const idCard = form.value.idcardno; |
| | | if (!idCard || idCard.length !== 18) return; |
| | | const year = idCard.substring(6, 10); |
| | | const month = idCard.substring(10, 12); |
| | | const day = idCard.substring(12, 14); |
| | | const birthDate = new Date(`${year}-${month}-${day}`); |
| | | if (isNaN(birthDate.getTime())) return; |
| | | form.value.birthday = `${year}-${month}-${day}`; |
| | | calculateAge(); |
| | | const genderCode = parseInt(idCard.charAt(16), 10); |
| | | if (!isNaN(genderCode)) form.value.sex = genderCode % 2 === 1 ? "1" : "2"; |
| | | uni.showToast({ |
| | | title: "已自动提取出生日期和性别", |
| | | icon: "success", |
| | | duration: 1500, |
| | | }); |
| | | }; |
| | | |
| | | const handleGcsBlur = () => { |
| | | const val = Number(form.value.gcsScore); |
| | | if (!val && val !== 0) return; |
| | | if (val > 7) { |
| | | uni.showToast({ title: "GCS评分不能大于7", icon: "none" }); |
| | | form.value.gcsScore = ""; |
| | | } |
| | | if (val < 3) { |
| | | uni.showToast({ title: "GCS评分不能小于3", icon: "none" }); |
| | | form.value.gcsScore = ""; |
| | | } |
| | | }; |
| | | |
| | | // 传染病 |
| | | const toggleInfectious = (value) => { |
| | | const index = selectedInfectious.value.indexOf(value); |
| | | if (index === -1) selectedInfectious.value.push(value); |
| | | else selectedInfectious.value.splice(index, 1); |
| | | form.value.infectious = selectedInfectious.value.join(","); |
| | | }; |
| | | const isInfectiousSelected = (value) => |
| | | selectedInfectious.value.includes(value); |
| | | |
| | | // 报告时间手动选择 |
| | | const onReportTimeConfirm = (e) => { |
| | | const date = new Date(e.value); |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | const hours = String(date.getHours()).padStart(2, "0"); |
| | | const minutes = String(date.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(date.getSeconds()).padStart(2, "0"); |
| | | form.value.reporttime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | showReportTimePicker.value = false; |
| | | |
| | | // 如果是新增模式,重新生成案例编号 |
| | | if (!isEditMode.value) { |
| | | generateCaseNo(); |
| | | } |
| | | }; |
| | | |
| | | // 附件处理(示例,需根据实际附件组件调整) |
| | | const handleBaseUpload = (file) => attachments.value.push(file); |
| | | const handleFilesUpdate = (files) => { |
| | | console.log(files, "files"); |
| | | |
| | | attachments.value = files.map((file) => ({ |
| | | ...file, |
| | | // 确保只存储半路径 |
| | | // url: file.url.startsWith("http") |
| | | // ? file.url.replace(baseUrlHt, "") |
| | | // : file.url, |
| | | })); |
| | | attachments.value = files.map((file) => ({ ...file })); |
| | | }; |
| | | |
| | | // 预览文件 - 修改为使用完整URL |
| | | const handlePreview = (file) => { |
| | | const fullUrl = file.url.startsWith("http") |
| | | ? file.url |
| | | : baseUrlHt + (file.url.startsWith("/") ? "" : "/") + file.url; |
| | | |
| | | : (file.url.startsWith("/") ? "" : "/") + file.url; |
| | | if (file.type.includes("image")) { |
| | | uni.previewImage({ |
| | | urls: attachments.value |
| | |
| | | .map((f) => |
| | | f.url.startsWith("http") |
| | | ? f.url |
| | | : baseUrlHt + (f.url.startsWith("/") ? "" : "/") + f.url, |
| | | : (f.url.startsWith("/") ? "" : "/") + f.url, |
| | | ), |
| | | current: fullUrl, |
| | | }); |
| | | } else if (file.type.includes("pdf")) { |
| | | uni.downloadFile({ |
| | | url: fullUrl, |
| | | success: (res) => { |
| | | success: (res) => |
| | | uni.openDocument({ |
| | | filePath: res.tempFilePath, |
| | | fileType: "pdf", |
| | | showMenu: true, |
| | | }); |
| | | }, |
| | | fail: (err) => { |
| | | console.error("打开文档失败:", err); |
| | | uni.showToast({ title: "打开文件失败", icon: "none" }); |
| | | }, |
| | | }), |
| | | }); |
| | | } else { |
| | | uni.showToast({ title: "暂不支持此文件类型预览", icon: "none" }); |
| | | } |
| | | }; |
| | | // 选择器确认事件 |
| | | const onConfirm = (e) => { |
| | | if (e.value && e.value[0]) { |
| | | form.value.treatmentdeptname = e.value[0].label; |
| | | } |
| | | selectShow.value = false; |
| | | |
| | | // 地址变更处理 |
| | | const onResidenceChange = (val) => { |
| | | form.value.residenceprovincename = val.sheng; |
| | | form.value.residenceprovince = val.provinceCode || ""; |
| | | form.value.residencecountyname = val.shi; |
| | | form.value.residencetownname = val.qu; |
| | | // 可根据需要补充区县code |
| | | }; |
| | | const onRegisterChange = (val) => { |
| | | form.value.registerprovincename = val.sheng; |
| | | form.value.registerprovince = val.provinceCode || ""; |
| | | form.value.registercityname = val.shi; |
| | | form.value.registertownname = val.qu; |
| | | }; |
| | | |
| | | const onHospitalConfirm = (e) => { |
| | | console.log(e, "民族"); |
| | | |
| | | if (e.value && e.value[0]) { |
| | | form.value.treatmenthospitalname = e.value[0].label; |
| | | } |
| | | showHospitalPicker.value = false; |
| | | }; |
| | | |
| | | const onNationConfirm = (e) => { |
| | | if (e.value && e.value[0]) { |
| | | form.value.nation = e.value[0].label; |
| | | } |
| | | showNationPicker.value = false; |
| | | }; |
| | | |
| | | const onIdCardTypeConfirm = (e) => { |
| | | if (e.value && e.value[0]) { |
| | | form.value.idcardtype = e.value[0].value; |
| | | } |
| | | showIdCardTypePicker.value = false; |
| | | }; |
| | | |
| | | // const onDateConfirm = (e) => { |
| | | // const date = new Date(e.value); |
| | | // form.value.birthday = `${date.getFullYear()}-${(date.getMonth() + 1) |
| | | // .toString() |
| | | // .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`; |
| | | // calculateAge(); |
| | | // showDatePicker.value = false; |
| | | // }; |
| | | |
| | | const onReporterConfirm = (e) => { |
| | | if (e.value && e.value[0]) { |
| | | form.value.reporterno = e.value[0].label; |
| | | } |
| | | showReporterPicker.value = false; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // 表单操作 |
| | | const handleCancel = () => uni.navigateBack(); |
| | | |
| | | const resetForm = () => { |
| | | uni.showModal({ |
| | |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | Object.keys(form.value).forEach((key) => { |
| | | if (!["id", "caseNo"].includes(key)) { |
| | | form.value[key] = ""; |
| | | } |
| | | if (!["id", "caseNo"].includes(key)) form.value[key] = ""; |
| | | }); |
| | | |
| | | // 重置选择器索引 |
| | | hospitalIndex.value = -1; |
| | | deptIndex.value = -1; |
| | | form.value.nationality = "中国"; |
| | | form.value.reportStatus = "1"; |
| | | form.value.terminationCase = 0; |
| | | nationIndex.value = -1; |
| | | idCardTypeIndex.value = -1; |
| | | // 重置传染病选择 |
| | | educationIndex.value = -1; |
| | | occupationIndex.value = -1; |
| | | ageUnitIndex.value = -1; |
| | | selectedInfectious.value = []; |
| | | |
| | | // 重置附件 |
| | | attachments.value = []; |
| | | form.value.nationality = "中国"; |
| | | form.value.isTransport = ""; |
| | | form.value.terminationCase = 0; |
| | | form.value.reportStatus = "1"; |
| | | |
| | | residenceAddress.value = { sheng: "", shi: "", qu: "" }; |
| | | registerAddress.value = { sheng: "", shi: "", qu: "" }; |
| | | if (!isEditMode.value) { |
| | | generateDonorNo(); |
| | | // 重置报告时间为当前时间 |
| | | updateCurrentTime(); |
| | | // 根据新的上报时间生成案例编号 |
| | | generateCaseNo(); |
| | | } |
| | | |
| | | uni.showToast({ title: "表单已重置", icon: "success" }); |
| | | } |
| | | }, |
| | |
| | | |
| | | const handleSubmit = async () => { |
| | | if (!isFormValid.value) { |
| | | uni.showToast({ |
| | | title: "请填写姓名、证件号码和疾病诊断", |
| | | icon: "none", |
| | | }); |
| | | uni.showToast({ title: "请填写姓名、疾病诊断和上报医院", icon: "none" }); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | loading.value = true; |
| | | uni.showLoading({ title: isEditMode.value ? "修改中..." : "提交中..." }); |
| | | // 准备提交数据 |
| | | const submitData = { |
| | | ...form.value, |
| | | treatmenthospitalname: form.value.toHospital, |
| | | age: parseInt(form.value.age) || 0, |
| | | annexfilesList: attachments.value.map((file) => ({ |
| | | path: file.url, |
| | |
| | | terminationCase: form.value.terminationCase || 0, |
| | | reportStatus: form.value.reportStatus || "1", |
| | | }; |
| | | |
| | | let res; |
| | | if (isEditMode.value) { |
| | | // 修改接口 |
| | | res = await uni.$uapi.post( |
| | | "/project/donatebaseinforeport/edit", |
| | | submitData, |
| | | ); |
| | | } else { |
| | | // 新增接口 |
| | | res = await uni.$uapi.post( |
| | | "/project/donatebaseinforeport/add", |
| | | submitData, |
| | | ); |
| | | } |
| | | |
| | | uni.hideLoading(); |
| | | |
| | | if (res.code === 200) { |
| | | if (res.code == 200) { |
| | | uni.showToast({ |
| | | title: isEditMode.value ? "修改成功" : "上报成功", |
| | | icon: "success", |
| | | }); |
| | | |
| | | // 清空本地存储的草稿 |
| | | removeDraft(); |
| | | |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | setTimeout(() => uni.navigateBack(), 1500); |
| | | } else { |
| | | throw new Error(res.msg || "操作失败"); |
| | | } |
| | | } catch (error) { |
| | | console.error("操作失败:", error); |
| | | uni.showToast({ |
| | | title: error.message || (isEditMode.value ? "修改失败" : "上报失败"), |
| | | icon: "none", |
| | |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | const removeDraft = () => { |
| | | try { |
| | | uni.removeStorageSync('caseReportDraft'); |
| | | } catch (e) { |
| | | console.error('移除草稿失败', e); |
| | | } |
| | | }; |
| | | // 取消处理 |
| | | const handleCancel = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // 加载案例数据 |
| | | const loadCaseData = async (id) => { |
| | | try { |
| | | loading.value = true; |
| | | const res = await uni.$uapi.get( |
| | | `/project/donatebaseinforeport/getInfo/${id}`, |
| | | ); |
| | | |
| | | if (res.code) { |
| | | form.value = res.data; |
| | | if (res.data.infectious) { |
| | | if (res.data.infectious) |
| | | selectedInfectious.value = res.data.infectious |
| | | .split(",") |
| | | .filter((item) => item.trim() !== ""); |
| | | if (form.value.nation) { |
| | | const idx = nationList.value.findIndex( |
| | | (item) => item == form.value.nation, |
| | | ); |
| | | nationIndex.value = idx >= 0 ? idx : -1; |
| | | } |
| | | currentNation.value = form.value.nation; |
| | | idCardTypeIndex.value = form.value.idcardtype; |
| | | // 处理选择器索引 |
| | | // updatePickerIndexes(); |
| | | // 处理附件 |
| | | if (form.value.idcardtype) { |
| | | const dictList = getDictList("sys_IDType"); |
| | | const idx = dictList.findIndex( |
| | | (item) => item.value == form.value.idcardtype, |
| | | ); |
| | | idCardTypeIndex.value = idx >= 0 ? idx : -1; |
| | | } |
| | | if (form.value.education) { |
| | | const dictList = getDictList("sys_education"); |
| | | const idx = dictList.findIndex( |
| | | (item) => item.value == form.value.education, |
| | | ); |
| | | educationIndex.value = idx >= 0 ? idx : -1; |
| | | } |
| | | if (form.value.occupation) { |
| | | const dictList = getDictList("sys_occupation"); |
| | | const idx = dictList.findIndex( |
| | | (item) => item.value == form.value.occupation, |
| | | ); |
| | | occupationIndex.value = idx >= 0 ? idx : -1; |
| | | } |
| | | // 年龄单位回显 |
| | | if (form.value.ageunit) { |
| | | const unitList = ageUnitList.value; |
| | | const idx = unitList.findIndex((item) => item === form.value.ageunit); |
| | | ageUnitIndex.value = idx >= 0 ? idx : -1; |
| | | } |
| | | // 地址回显 |
| | | if (res.data.residenceprovincename) { |
| | | residenceAddress.value = { |
| | | sheng: res.data.residenceprovincename, |
| | | shi: res.data.residencecountyname, |
| | | qu: res.data.residencetownname, |
| | | }; |
| | | } |
| | | if (res.data.registerprovincename) { |
| | | registerAddress.value = { |
| | | sheng: res.data.registerprovincename, |
| | | shi: res.data.registercityname, |
| | | qu: res.data.registertownname, |
| | | }; |
| | | } |
| | | if (res.data.annexfilesList) { |
| | | attachments.value = res.data.annexfilesList; |
| | | attachments.value.forEach((item) => { |
| | | item.url = item.path; |
| | | item.name = item.fileName; |
| | | }); |
| | | attachments.value = res.data.annexfilesList.map((item) => ({ |
| | | url: item.path, |
| | | name: item.fileName, |
| | | type: item.type || "", |
| | | })); |
| | | } |
| | | console.log(3, "res"); |
| | | |
| | | uni.showToast({ |
| | | title: "数据加载成功", |
| | | icon: "success", |
| | | }); |
| | | } else { |
| | | throw new Error(res.msg || "数据加载失败"); |
| | | } |
| | | } catch (error) { |
| | | console.error("加载案例数据失败:", error); |
| | | uni.showToast({ |
| | | title: "数据加载失败,请重试", |
| | | icon: "none", |
| | | }); |
| | | uni.showToast({ title: "数据加载失败,请重试", icon: "none" }); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // ==================== 生命周期 ==================== |
| | | onMounted(() => { |
| | | updateCurrentTime(); |
| | | setInterval(updateCurrentTime, 1000); |
| | | }); |
| | | |
| | | onLoad(async (options) => { |
| | | if (options.id) { |
| | | currentId.value = options.id; |
| | | isEditMode.value = true; |
| | | await new Promise((resolve) => { |
| | | if (!dictLoading.value) resolve(); |
| | | else { |
| | | const stopWatch = watch(dictLoading, (val) => { |
| | | if (!val) { |
| | | stopWatch(); |
| | | resolve(); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | await loadCaseData(options.id); |
| | | } else { |
| | | isEditMode.value = false; |
| | | generateCaseNo(); |
| | | // 新增时默认填入报告时间 |
| | | updateCurrentTime(); |
| | | } |
| | | |
| | | if (!userStore.userInfo) await userStore.refreshUserInfo(); |
| | | const user = userStore.userInfo; |
| | | if (user) { |
| | | if (!form.value.treatmenthospitalname) |
| | | form.value.treatmenthospitalname = user.orgName; |
| | | if (!form.value.toHospital) form.value.toHospital = user.orgName; |
| | | if (!form.value.coordinatorName) |
| | | form.value.coordinatorName = user.coordinatorName; |
| | | if (!form.value.coordinatorNo) |
| | | form.value.coordinatorNo = user.coordinatorNo; |
| | | if (!form.value.infoName) form.value.infoName = user.nickName; |
| | | if (!form.value.phone) form.value.phone = user.phonenumber; |
| | | if (!form.value.reportername) form.value.reportername = user.name; |
| | | if (!form.value.reporterno) form.value.reporterno = user.userId; |
| | | } |
| | | updateCurrentTime(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | // 样式与原来保持一致,新增地址块样式 |
| | | .case-report-container { |
| | | min-height: 100vh; |
| | | background: linear-gradient(135deg, #f8fdff 0%, #e8f7f6 100%); |
| | |
| | | |
| | | .form-content { |
| | | padding: 30rpx; |
| | | padding-bottom: 180rpx; // 给按钮留出空间 |
| | | padding-bottom: 220rpx; |
| | | } |
| | | |
| | | .page-header { |
| | | padding: 30rpx 0; |
| | | text-align: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .page-title { |
| | | font-size: 38rpx; |
| | | font-weight: 700; |
| | | color: #1d1d1f; |
| | | letter-spacing: 2rpx; |
| | | } |
| | | |
| | | .form-section { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 30rpx; |
| | | box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06); |
| | | margin-bottom: 26rpx; |
| | | box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .section-header { |
| | | display: flex; |
| | | // align-items: center; |
| | | margin-bottom: 30rpx; |
| | | align-items: center; |
| | | margin-bottom: 28rpx; |
| | | padding-bottom: 20rpx; |
| | | border-bottom: 2rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .section-icon { |
| | | font-size: 32rpx; |
| | | margin-right: 16rpx; |
| | | font-size: 34rpx; |
| | | margin-right: 14rpx; |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 32rpx; |
| | | font-weight: 600; |
| | | font-weight: 650; |
| | | color: #1d1d1f; |
| | | } |
| | | |
| | | .form-grid { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24rpx; |
| | | gap: 28rpx; |
| | | } |
| | | |
| | | .form-item { |
| | |
| | | .item-label { |
| | | font-size: 28rpx; |
| | | color: #1d1d1f; |
| | | font-weight: 500; |
| | | font-weight: 520; |
| | | margin-bottom: 12rpx; |
| | | |
| | | &.required::after { |
| | |
| | | } |
| | | } |
| | | |
| | | // 自定义u-input样式 |
| | | :deep(.u-input) { |
| | | border: 2rpx solid #e5e5e7 !important; |
| | | border-radius: 12rpx !important; |
| | | padding: 20rpx 24rpx !important; |
| | | background: #fff !important; |
| | | } |
| | | |
| | | :deep(.u-input), |
| | | :deep(.u-textarea) { |
| | | border: 2rpx solid #e5e5e7 !important; |
| | | border-radius: 12rpx !important; |
| | | padding: 20rpx 24rpx !important; |
| | | background: #fff !important; |
| | | |
| | | &:focus-within { |
| | | border-color: #0f95b0 !important; |
| | | } |
| | | } |
| | | |
| | | .picker { |
| | | height: 86rpx; |
| | | background: #fafafa; |
| | | border-radius: 12rpx; |
| | | padding: 0 24rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | border: 2rpx solid #e5e5e7; |
| | | |
| | | &:active { |
| | | border-color: #0f95b0; |
| | | } |
| | | |
| | | .icon-arrow { |
| | | font-size: 34rpx; |
| | | color: #86868b; |
| | | transform: rotate(90deg); |
| | | } |
| | | } |
| | | |
| | | .radio-options { |
| | | display: flex; |
| | | gap: 44rpx; |
| | | |
| | | .option-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 14rpx; |
| | | |
| | | .radio-dot { |
| | | width: 34rpx; |
| | | height: 34rpx; |
| | | border: 3rpx solid #d1d1d6; |
| | | border-radius: 50%; |
| | | position: relative; |
| | | } |
| | | |
| | | .option-label { |
| | | font-size: 28rpx; |
| | | color: #1d1d1f; |
| | | } |
| | | |
| | | &.active .radio-dot { |
| | | border-color: #0f95b0; |
| | | |
| | | &::after { |
| | | content: ""; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | width: 18rpx; |
| | | height: 18rpx; |
| | | background: #0f95b0; |
| | | border-radius: 50%; |
| | | } |
| | | } |
| | | |
| | | &.active .option-label { |
| | | color: #0f95b0; |
| | | font-weight: 550; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .radio-group { |
| | |
| | | } |
| | | } |
| | | |
| | | .radio-item { |
| | | // 替换原有的 .radio-group.horizontal 相关样式 |
| | | .radio-group.horizontal { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16rpx; |
| | | } |
| | | flex-wrap: nowrap; |
| | | overflow-x: auto; |
| | | gap: 18rpx; |
| | | |
| | | .radio-dot { |
| | | width: 32rpx; |
| | | height: 32rpx; |
| | | border: 2rpx solid #e5e5e7; |
| | | border-radius: 50%; |
| | | position: relative; |
| | | .radio-item { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8rpx; |
| | | padding: 10rpx 20rpx; |
| | | border-radius: 20rpx; |
| | | background: #f5f5f7; |
| | | transition: all 0.2s; |
| | | |
| | | &.active { |
| | | border-color: #0f95b0; |
| | | |
| | | &::after { |
| | | content: ""; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | width: 16rpx; |
| | | height: 16rpx; |
| | | background: #0f95b0; |
| | | .radio-dot { |
| | | width: 36rpx; |
| | | height: 36rpx; |
| | | border: 2rpx solid #d1d1d6; |
| | | border-radius: 50%; |
| | | background-color: #fff; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.2s; |
| | | |
| | | &.active { |
| | | border-color: #0f95b0; |
| | | background-color: #0f95b0; |
| | | |
| | | // 可选:如果希望有白色对勾,取消注释下面 |
| | | // &::after { |
| | | // content: "✓"; |
| | | // font-size: 20rpx; |
| | | // color: white; |
| | | // line-height: 1; |
| | | // } |
| | | } |
| | | } |
| | | |
| | | .radio-label { |
| | | font-size: 26rpx; |
| | | color: #1d1d1f; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | // 选中时整体背景微微变色(可选) |
| | | &.active { |
| | | background: rgba(15, 149, 176, 0.1); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .radio-label { |
| | | font-size: 28rpx; |
| | | color: #1d1d1f; |
| | | .checkbox-group.single-line { |
| | | display: flex; |
| | | flex-wrap: nowrap; |
| | | overflow-x: auto; |
| | | gap: 16rpx; |
| | | |
| | | .checkbox-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8rpx; |
| | | flex-shrink: 0; |
| | | padding: 10rpx 20rpx; |
| | | border-radius: 22rpx; |
| | | background: #f5f5f7; |
| | | |
| | | &.active { |
| | | border-color: #0f95b0; |
| | | background: rgba(15, 149, 176, 0.09); |
| | | } |
| | | |
| | | .checkbox-box { |
| | | width: 30rpx; |
| | | height: 30rpx; |
| | | border: 2rpx solid #d1d1d6; |
| | | border-radius: 6rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: #fff; |
| | | |
| | | &.active { |
| | | border-color: #0f95b0; |
| | | background: #0f95b0; |
| | | } |
| | | |
| | | .checkbox-check { |
| | | font-size: 20rpx; |
| | | color: white; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | .checkbox-label { |
| | | font-size: 25rpx; |
| | | color: #1d1d1f; |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .error-text { |
| | | font-size: 23rpx; |
| | | color: #ff4757; |
| | | margin-top: 8rpx; |
| | | } |
| | | |
| | | .address-block { |
| | | margin-bottom: 32rpx; |
| | | |
| | | .address-label { |
| | | font-size: 27rpx; |
| | | color: #1d1d1f; |
| | | font-weight: 520; |
| | | margin-bottom: 12rpx; |
| | | display: block; |
| | | } |
| | | |
| | | .address-detail-input { |
| | | margin-top: 16rpx; |
| | | } |
| | | } |
| | | |
| | | .action-buttons { |
| | |
| | | right: 0; |
| | | background: #fff; |
| | | padding: 20rpx 30rpx calc(20rpx + env(safe-area-inset-bottom)); |
| | | box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.08); |
| | | z-index: 99; |
| | | box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.07); |
| | | z-index: 9; |
| | | display: flex; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | .btn { |
| | | flex: 1; |
| | | height: 80rpx; |
| | | border-radius: 16rpx; |
| | | font-size: 32rpx; |
| | | font-weight: 500; |
| | | |
| | | &.secondary { |
| | | background: #f5f5f7 !important; |
| | | color: #1d1d1f !important; |
| | | } |
| | | |
| | | &.primary { |
| | | background: linear-gradient(135deg, #0f95b0, #89c4c1) !important; |
| | | color: #fff !important; |
| | | |
| | | &:disabled { |
| | | background: #c0c0c0 !important; |
| | | opacity: 0.6; |
| | | } |
| | | } |
| | | } |
| | | .case-report-container { |
| | | min-height: 100vh; |
| | | background: linear-gradient(135deg, #f8fdff 0%, #e8f7f6 100%); |
| | | } |
| | | |
| | | .form-scroll { |
| | | height: 100vh; |
| | | } |
| | | |
| | | |
| | | |
| | | .form-section { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 30rpx; |
| | | box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06); |
| | | } |
| | | |
| | | .section-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 30rpx; |
| | | padding-bottom: 20rpx; |
| | | border-bottom: 2rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .section-icon { |
| | | font-size: 32rpx; |
| | | margin-right: 16rpx; |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 32rpx; |
| | | font-weight: 600; |
| | | color: #1d1d1f; |
| | | } |
| | | |
| | | .form-grid { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24rpx; |
| | | } |
| | | |
| | | .form-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .item-label { |
| | | font-size: 28rpx; |
| | | color: #1d1d1f; |
| | | font-weight: 500; |
| | | margin-bottom: 12rpx; |
| | | |
| | | &.required::after { |
| | | content: "*"; |
| | | color: #ff4757; |
| | | margin-left: 4rpx; |
| | | } |
| | | } |
| | | |
| | | /* 改造后的选择器样式 */ |
| | | .picker { |
| | | height: 88rpx; |
| | | background: #f5f5f7; |
| | | border-radius: 12rpx; |
| | | padding: 0 24rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | border: 2rpx solid #e5e5e7; |
| | | |
| | | text { |
| | | font-size: 28rpx; |
| | | color: #1d1d1f; |
| | | |
| | | &.icon-arrow { |
| | | font-size: 32rpx; |
| | | color: #86868b; |
| | | transform: rotate(90deg); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .page-header { |
| | | padding: 30rpx 0; |
| | | text-align: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .page-title { |
| | | font-size: 36rpx; |
| | | font-weight: 600; |
| | | color: #1d1d1f; |
| | | } |
| | | |
| | | /* 在原有样式基础上添加 */ |
| | | .form-section { |
| | | position: relative; |
| | | } |
| | | |
| | | .edit-badge { |
| | | position: absolute; |
| | | top: 30rpx; |
| | | right: 30rpx; |
| | | background: #ff6b35; |
| | | color: white; |
| | | padding: 8rpx 16rpx; |
| | | border-radius: 20rpx; |
| | | font-size: 24rpx; |
| | | } |
| | | |
| | | /* 加载状态样式 */ |
| | | .loading-overlay { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | z-index: 9999; |
| | | } |
| | | |
| | | /* 响应式调整 */ |
| | | @media (max-width: 768px) { |
| | | .form-content { |
| | | padding: 20rpx; |
| | | padding-bottom: 180rpx; |
| | | } |
| | | |
| | | .form-section { |
| | | padding: 20rpx; |
| | | } |
| | | } |
| | | |
| | | /* 改造后的单选按钮样式 */ |
| | | .radio-options { |
| | | display: flex; |
| | | gap: 40rpx; |
| | | } |
| | | |
| | | .option-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16rpx; |
| | | |
| | | .radio-dot { |
| | | width: 32rpx; |
| | | height: 32rpx; |
| | | border: 2rpx solid #e5e5e7; |
| | | border-radius: 50%; |
| | | position: relative; |
| | | } |
| | | |
| | | .option-label { |
| | | font-size: 28rpx; |
| | | color: #1d1d1f; |
| | | } |
| | | |
| | | &.active { |
| | | .radio-dot { |
| | | border-color: #0f95b0; |
| | | |
| | | &::after { |
| | | content: ""; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | width: 16rpx; |
| | | height: 16rpx; |
| | | background: #0f95b0; |
| | | border-radius: 50%; |
| | | } |
| | | } |
| | | |
| | | .option-label { |
| | | color: #0f95b0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .error-text { |
| | | font-size: 24rpx; |
| | | color: #ff4757; |
| | | margin-top: 8rpx; |
| | | } |
| | | |
| | | /* 原有样式调整 */ |
| | | :deep(.u-input) { |
| | | border: 2rpx solid #e5e5e7 !important; |
| | | border-radius: 12rpx !important; |
| | | padding: 20rpx 24rpx !important; |
| | | background: #fff !important; |
| | | } |
| | | .idcard-hint { |
| | | margin-top: 8rpx; |
| | | padding: 12rpx 16rpx; |
| | | background: #f0f9ff; |
| | | border-radius: 8rpx; |
| | | border-left: 4rpx solid #0f95b0; |
| | | } |
| | | |
| | | .hint-text { |
| | | display: block; |
| | | font-size: 24rpx; |
| | | color: #0f95b0; |
| | | margin-bottom: 4rpx; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .error-text { |
| | | font-size: 24rpx; |
| | | color: #ff4757; |
| | | margin-top: 8rpx; |
| | | } |
| | | .btn { |
| | | flex: 1; |
| | | height: 80rpx; |
| | | border-radius: 16rpx; |
| | | font-size: 32rpx; |
| | | font-weight: 500; |
| | | |
| | | &.secondary { |
| | | background: #f5f5f7 !important; |
| | | color: #1d1d1f !important; |
| | | } |
| | | |
| | | &.primary { |
| | | background: linear-gradient(135deg, #0f95b0, #89c4c1) !important; |
| | | color: #fff !important; |
| | | |
| | | &:disabled { |
| | | background: #c0c0c0 !important; |
| | | opacity: 0.6; |
| | | } |
| | | } |
| | | } |
| | | .checkbox-group { |
| | | display: flex; |
| | | flex-wrap: nowrap; /* 禁止换行 */ |
| | | gap: 20rpx; |
| | | overflow-x: auto; /* 水平滚动 */ |
| | | padding-bottom: 10rpx; /* 给滚动条留出空间 */ |
| | | -webkit-overflow-scrolling: touch; /* iOS平滑滚动 */ |
| | | |
| | | &.single-line { |
| | | display: flex; |
| | | flex-wrap: nowrap; |
| | | overflow-x: auto; |
| | | width: 100%; |
| | | |
| | | /* 隐藏滚动条,但保持滚动功能 */ |
| | | &::-webkit-scrollbar { |
| | | display: none; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .checkbox-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8rpx; |
| | | flex-shrink: 0; /* 防止选项被压缩 */ |
| | | padding: 8rpx 16rpx; |
| | | border-radius: 20rpx; |
| | | background: #f5f5f7; |
| | | transition: all 0.3s ease; |
| | | |
| | | &.active { |
| | | background: rgba(15, 149, 176, 0.1); |
| | | } |
| | | } |
| | | |
| | | .checkbox-box { |
| | | width: 28rpx; |
| | | height: 28rpx; |
| | | border: 2rpx solid #e5e5e7; |
| | | border-radius: 6rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: #fff; |
| | | transition: all 0.3s ease; |
| | | flex-shrink: 0; |
| | | |
| | | &.active { |
| | | border-color: #0f95b0; |
| | | background: #0f95b0; |
| | | } |
| | | } |
| | | |
| | | .checkbox-check { |
| | | font-size: 20rpx; |
| | | color: white; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .checkbox-label { |
| | | font-size: 24rpx; |
| | | color: #1d1d1f; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | /* 优化单选按钮组的样式,保持一致性 */ |
| | | .radio-group.horizontal { |
| | | display: flex; |
| | | flex-wrap: nowrap; |
| | | overflow-x: auto; |
| | | gap: 20rpx; |
| | | padding-bottom: 10rpx; |
| | | -webkit-overflow-scrolling: touch; |
| | | |
| | | &::-webkit-scrollbar { |
| | | display: none; |
| | | } |
| | | |
| | | .radio-item { |
| | | flex-shrink: 0; |
| | | .btn { |
| | | flex: 1; |
| | | height: 84rpx; |
| | | border-radius: 16rpx; |
| | | font-size: 31rpx; |
| | | font-weight: 530; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8rpx; |
| | | padding: 8rpx 16rpx; |
| | | border-radius: 20rpx; |
| | | } |
| | | justify-content: center; |
| | | |
| | | .radio-item .radio-dot.active { |
| | | border-color: #0f95b0; |
| | | |
| | | &::after { |
| | | background: #0f95b0; |
| | | &.secondary { |
| | | background: #f5f5f7 !important; |
| | | color: #1d1d1f !important; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* 如果需要固定高度,可以这样设置 */ |
| | | .checkbox-group.single-line { |
| | | min-height: 60rpx; |
| | | align-items: center; |
| | | } |
| | | &.primary { |
| | | background: linear-gradient(135deg, #0f95b0, #66b8b5) !important; |
| | | color: #fff !important; |
| | | |
| | | /* 在移动端优化触摸区域 */ |
| | | .checkbox-item { |
| | | min-height: 60rpx; |
| | | padding: 8rpx 20rpx; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | /* 添加选中状态的视觉反馈 */ |
| | | .checkbox-item.active { |
| | | border: 1rpx solid #0f95b0; |
| | | background: rgba(15, 149, 176, 0.08); |
| | | } |
| | | |
| | | /* 响应式调整 */ |
| | | @media (max-width: 768px) { |
| | | .checkbox-group.single-line { |
| | | gap: 12rpx; |
| | | } |
| | | |
| | | .checkbox-item { |
| | | padding: 6rpx 14rpx; |
| | | } |
| | | |
| | | .checkbox-label { |
| | | font-size: 22rpx; |
| | | &:disabled { |
| | | background: #c8c8cc !important; |
| | | opacity: 0.55; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |