<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>
|
</view>
|
|
<view class="form-grid">
|
<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
|
type="text"
|
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>
|
</view>
|
|
<view class="form-grid">
|
<view class="form-item">
|
<text class="item-label">民族</text>
|
<picker
|
mode="selector"
|
:range="nationList"
|
:value="nationIndex"
|
@change="onNationChange"
|
>
|
<view class="picker">
|
<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="idCardTypeList"
|
:value="idCardTypeIndex"
|
@change="onIdCardTypeChange"
|
>
|
<view class="picker">
|
<text>{{ currentIdCardTypeText || "请选择证件类型" }}</text>
|
<text class="icon-arrow">›</text>
|
</view>
|
</picker>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label required">证件号码</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">
|
<view
|
v-for="gender in genderOptions"
|
:key="gender.value"
|
class="option-item"
|
:class="{ active: form.sex == gender.value }"
|
@click="form.sex = gender.value"
|
>
|
<text class="radio-dot"></text>
|
<text class="option-label">{{ gender.label }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">出生日期</text>
|
<picker
|
mode="date"
|
:value="form.birthday"
|
@change="onBirthdayChange"
|
>
|
<view class="picker">
|
<text>{{ form.birthday || "选择出生日期" }}</text>
|
<text class="icon-arrow">›</text>
|
</view>
|
</picker>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">年龄</text>
|
<u-input
|
v-model="ageDisplay"
|
placeholder="自动计算"
|
disabled
|
:disabledColor="disabledColor"
|
border="none"
|
/>
|
</view>
|
</view>
|
</view>
|
|
<!-- 医疗信息卡片 -->
|
<view class="form-section">
|
<view class="section-header">
|
<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"
|
/>
|
</view>
|
<view class="form-item">
|
<text class="item-label">GCS评分</text>
|
<u-input
|
v-model="form.gcsScore"
|
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
|
v-model="form.diagnosisname"
|
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 bloodTypeOptions"
|
:key="bloodType.value"
|
class="radio-item"
|
@click="form.bloodType = bloodType.value"
|
>
|
<view
|
class="radio-dot"
|
:class="{ active: form.bloodType == bloodType.value }"
|
></view>
|
<text class="radio-label">{{ bloodType.label }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">Rh(D)</text>
|
<view class="radio-group horizontal">
|
<view
|
v-for="rh in rhOptions"
|
:key="rh.value"
|
class="radio-item"
|
@click="form.rhYin = rh.value"
|
>
|
<view
|
class="radio-dot"
|
:class="{ active: form.rhYin == rh.value }"
|
></view>
|
<text class="radio-label">{{ rh.label }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view class="form-item full-width">
|
<text class="item-label">传染病</text>
|
<view class="checkbox-group single-line">
|
<view
|
v-for="disease in infectiousList"
|
:key="disease.value"
|
class="checkbox-item"
|
:class="{ active: isInfectiousSelected(disease.value) }"
|
@click="toggleInfectious(disease.value)"
|
>
|
<view
|
class="checkbox-box"
|
:class="{ active: isInfectiousSelected(disease.value) }"
|
>
|
<text
|
v-if="isInfectiousSelected(disease.value)"
|
class="checkbox-check"
|
>✓</text
|
>
|
</view>
|
<text class="checkbox-label">{{ disease.label }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view class="form-item full-width">
|
<text class="item-label">其他</text>
|
<u-input
|
v-model="form.infectiousOther"
|
placeholder="请输入其他传染病"
|
border="none"
|
/>
|
</view>
|
</view>
|
</view>
|
|
<!-- 联系信息卡片 -->
|
<view class="form-section">
|
<view class="section-header">
|
<view class="section-icon">📞</view>
|
<text class="section-title">联系信息</text>
|
</view>
|
|
<view class="form-grid">
|
<view class="form-item">
|
<text class="item-label">ICU评估医生</text>
|
<u-input
|
v-model="form.icuDoctor"
|
placeholder="请输入ICU评估医生"
|
border="none"
|
/>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">ICU医生电话</text>
|
<u-input
|
v-model="form.icuDoctorPhone"
|
placeholder="请输入ICU医生电话"
|
type="number"
|
border="none"
|
/>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">上报信息员</text>
|
<u-input
|
v-model="form.infoName"
|
placeholder="请输入信息员"
|
border="none"
|
/>
|
</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="currentTime"
|
disabled
|
:disabledColor="disabledColor"
|
border="none"
|
/>
|
</view>
|
</view>
|
</view>
|
|
<!-- 操作按钮 -->
|
<view class="action-buttons">
|
<u-button class="btn secondary" @click="handleCancel">取消</u-button>
|
<u-button class="btn secondary" @click="resetForm">重置表单</u-button>
|
<u-button
|
class="btn primary"
|
:disabled="!isFormValid || loading"
|
@click="handleSubmit"
|
>
|
{{ 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-datetime-picker
|
:show="showDatePicker"
|
v-model="birthdayValue"
|
mode="date"
|
@confirm="onDateConfirm"
|
@cancel="showDatePicker = false"
|
title="选择出生日期"
|
></u-datetime-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 { useUserStore } from "@/stores/user";
|
import { useDictMapper } from "@/utils/useDictMapper";
|
|
// ==================== 字典数据 ====================
|
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) => {
|
return 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 userStore = useUserStore();
|
const isEditMode = ref(false);
|
const currentId = ref(null);
|
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 idCardError = ref("");
|
const attachments = ref([]);
|
|
// ==================== 表单数据 ====================
|
const form = ref({
|
caseNo: "",
|
treatmenthospitalname: "",
|
toHospital: "",
|
coordinatorName: "",
|
coordinatorNo: "",
|
treatmentdeptname: "",
|
deptName: "",
|
name: "",
|
nation: "",
|
nationality: "中国",
|
idcardtype: "",
|
idcardno: "",
|
sex: "",
|
birthday: "",
|
age: "",
|
ageunit: "",
|
inpatientno: "",
|
gcsScore: "",
|
diagnosisname: "",
|
bloodType: "",
|
rhYin: "",
|
infoName: "",
|
phone: "",
|
icuDoctor: "",
|
icuDoctorPhone: "",
|
reportername: "",
|
reporterno: "",
|
reporterphone: "",
|
reporttime: "",
|
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 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 bloodTypeOptions = [
|
{ label: "A型", value: "A型" },
|
{ label: "B型", value: "B型" },
|
{ label: "O型", value: "O型" },
|
{ label: "AB型", value: "AB型" },
|
];
|
|
const rhOptions = [
|
{ label: "阳性", value: "1" },
|
{ label: "阴性", value: "0" },
|
];
|
|
// ==================== 计算属性 ====================
|
const isFormValid = computed(() => {
|
return form.value.name && form.value.diagnosisname && form.value.toHospital;
|
});
|
|
const ageDisplay = computed(() => {
|
if (!form.value.age || !form.value.ageunit) {
|
return "自动计算";
|
}
|
return `${form.value.age}${form.value.ageunit}`;
|
});
|
|
const currentNationText = computed(() => {
|
if (nationIndex.value >= 0 && nationList.value[nationIndex.value]) {
|
return nationList.value[nationIndex.value];
|
}
|
return 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 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 generateDonorNo = () => {
|
// 如有需要可在此生成编号
|
};
|
|
// 民族选择
|
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 onBirthdayChange = (e) => {
|
form.value.birthday = e.detail.value;
|
calculateAge();
|
};
|
|
// 日期选择器确认
|
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 calculateAge = () => {
|
if (!form.value.birthday) {
|
form.value.age = "";
|
form.value.ageunit = "";
|
return;
|
}
|
|
const birthDate = new Date(form.value.birthday);
|
const today = new Date();
|
|
if (isNaN(birthDate.getTime())) {
|
form.value.age = "";
|
form.value.ageunit = "";
|
return;
|
}
|
|
if (birthDate > today) {
|
uni.showToast({ title: "出生日期不能是未来日期", icon: "none" });
|
form.value.age = "";
|
form.value.ageunit = "";
|
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();
|
|
let ageValue, ageUnit;
|
|
if (years >= 1) {
|
let actualYears = years;
|
if (months < 0 || (months == 0 && days < 0)) {
|
actualYears = years - 1;
|
}
|
ageValue = actualYears.toString();
|
ageUnit = "岁";
|
} else if (daysDiff >= 30) {
|
let totalMonths = years * 12 + months;
|
if (days < 0) {
|
totalMonths--;
|
}
|
ageValue = Math.max(1, totalMonths).toString();
|
ageUnit = "个月";
|
} else {
|
ageValue = Math.max(1, daysDiff).toString();
|
ageUnit = "天";
|
}
|
|
form.value.age = ageValue;
|
form.value.ageunit = ageUnit;
|
};
|
|
// 身份证验证
|
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 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())) return;
|
|
form.value.birthday = `${year}-${month.padStart(2, "0")}-${day.padStart(
|
2,
|
"0",
|
)}`;
|
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,
|
});
|
};
|
|
// GCS评分验证
|
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) => {
|
return selectedInfectious.value.includes(value);
|
};
|
|
// 文本域样式
|
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;";
|
};
|
|
// 附件处理
|
const handleBaseUpload = (file) => {
|
attachments.value.push(file);
|
};
|
|
const handleFilesUpdate = (files) => {
|
attachments.value = files.map((file) => ({
|
...file,
|
}));
|
};
|
|
const handlePreview = (file) => {
|
const fullUrl = file.url.startsWith("http")
|
? file.url
|
: (file.url.startsWith("/") ? "" : "/") + file.url;
|
|
if (file.type.includes("image")) {
|
uni.previewImage({
|
urls: attachments.value
|
.filter((f) => f.type.includes("image"))
|
.map((f) =>
|
f.url.startsWith("http")
|
? f.url
|
: (f.url.startsWith("/") ? "" : "/") + f.url,
|
),
|
current: fullUrl,
|
});
|
} else if (file.type.includes("pdf")) {
|
uni.downloadFile({
|
url: fullUrl,
|
success: (res) => {
|
uni.openDocument({
|
filePath: res.tempFilePath,
|
fileType: "pdf",
|
showMenu: true,
|
});
|
},
|
fail: () => {
|
uni.showToast({ title: "打开文件失败", icon: "none" });
|
},
|
});
|
} else {
|
uni.showToast({ title: "暂不支持此文件类型预览", icon: "none" });
|
}
|
};
|
|
// 表单操作
|
const handleCancel = () => {
|
uni.navigateBack();
|
};
|
|
const resetForm = () => {
|
uni.showModal({
|
title: "确认重置",
|
content: "确定要清空所有已填写的内容吗?",
|
success: (res) => {
|
if (res.confirm) {
|
Object.keys(form.value).forEach((key) => {
|
if (!["id", "caseNo"].includes(key)) {
|
form.value[key] = "";
|
}
|
});
|
form.value.nationality = "中国";
|
form.value.reportStatus = "1";
|
form.value.terminationCase = 0;
|
|
nationIndex.value = -1;
|
idCardTypeIndex.value = -1;
|
selectedInfectious.value = [];
|
attachments.value = [];
|
|
uni.showToast({ title: "表单已重置", icon: "success" });
|
}
|
},
|
});
|
};
|
|
const handleSubmit = async () => {
|
if (!isFormValid.value) {
|
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,
|
fileName: file.name,
|
type: file.type,
|
})),
|
isTransport: form.value.isTransport,
|
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) {
|
uni.showToast({
|
title: isEditMode.value ? "修改成功" : "上报成功",
|
icon: "success",
|
});
|
setTimeout(() => {
|
uni.navigateBack();
|
}, 1500);
|
} else {
|
throw new Error(res.msg || "操作失败");
|
}
|
} catch (error) {
|
uni.showToast({
|
title: error.message || (isEditMode.value ? "修改失败" : "上报失败"),
|
icon: "none",
|
});
|
} finally {
|
loading.value = false;
|
}
|
};
|
|
// 加载案例数据
|
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) {
|
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;
|
}
|
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 (res.data.annexfilesList) {
|
attachments.value = res.data.annexfilesList.map((item) => ({
|
url: item.path,
|
name: item.fileName,
|
type: item.type || "",
|
}));
|
}
|
} else {
|
throw new Error(res.msg || "数据加载失败");
|
}
|
} catch (error) {
|
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;
|
generateDonorNo();
|
}
|
|
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; // 给按钮留出空间
|
}
|
|
.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;
|
|
&.full-width {
|
grid-column: 1 / -1;
|
}
|
}
|
|
.item-label {
|
font-size: 28rpx;
|
color: #1d1d1f;
|
font-weight: 500;
|
margin-bottom: 12rpx;
|
|
&.required::after {
|
content: "*";
|
color: #ff4757;
|
margin-left: 4rpx;
|
}
|
}
|
|
// 自定义u-input样式
|
:deep(.u-input) {
|
border: 2rpx solid #e5e5e7 !important;
|
border-radius: 12rpx !important;
|
padding: 20rpx 24rpx !important;
|
background: #fff !important;
|
}
|
|
:deep(.u-textarea) {
|
border: 2rpx solid #e5e5e7 !important;
|
border-radius: 12rpx !important;
|
padding: 20rpx 24rpx !important;
|
background: #fff !important;
|
}
|
|
.radio-group {
|
display: flex;
|
gap: 40rpx;
|
|
&.horizontal {
|
flex-wrap: wrap;
|
gap: 20rpx;
|
}
|
}
|
|
.radio-item {
|
display: flex;
|
align-items: center;
|
gap: 16rpx;
|
}
|
|
.radio-dot {
|
width: 32rpx;
|
height: 32rpx;
|
border: 2rpx solid #e5e5e7;
|
border-radius: 50%;
|
position: relative;
|
|
&.active {
|
border-color: #0f95b0;
|
|
&::after {
|
content: "";
|
position: absolute;
|
top: 50%;
|
left: 50%;
|
transform: translate(-50%, -50%);
|
width: 16rpx;
|
height: 16rpx;
|
background: #0f95b0;
|
border-radius: 50%;
|
}
|
}
|
}
|
|
.radio-label {
|
font-size: 28rpx;
|
color: #1d1d1f;
|
}
|
|
.action-buttons {
|
position: fixed;
|
bottom: 0;
|
left: 0;
|
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;
|
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;
|
display: flex;
|
align-items: center;
|
gap: 8rpx;
|
padding: 8rpx 16rpx;
|
border-radius: 20rpx;
|
}
|
|
.radio-item .radio-dot.active {
|
border-color: #0f95b0;
|
|
&::after {
|
background: #0f95b0;
|
}
|
}
|
}
|
|
/* 如果需要固定高度,可以这样设置 */
|
.checkbox-group.single-line {
|
min-height: 60rpx;
|
align-items: center;
|
}
|
|
/* 在移动端优化触摸区域 */
|
.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;
|
}
|
}
|
</style>
|