<template>
|
<view class="case-report-container">
|
<!-- 表单内容 -->
|
<scroll-view scroll-y class="form-scroll" :show-scrollbar="false">
|
<view class="form-content">
|
<!-- 基本信息卡片 -->
|
<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.donorno"
|
placeholder="系统自动生成"
|
disabled
|
:disabledColor="disabledColor"
|
border="none"
|
/>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">医疗机构</text>
|
<u-input
|
v-model="form.treatmenthospitalno"
|
placeholder="请选择医疗机构"
|
readonly
|
border="none"
|
@click="showHospitalPicker = 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="form.treatmentdeptname"
|
placeholder="请选择科室"
|
readonly
|
border="none"
|
@click="selectShow = true"
|
>
|
<template #suffix>
|
<u-icon name="arrow-down" color="#86868b"></u-icon>
|
</template>
|
</u-input>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label required">姓名</text>
|
<u-input
|
v-model="form.name"
|
placeholder="请输入姓名"
|
border="none"
|
:customStyle="inputStyle(!form.name)"
|
/>
|
</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.nation"
|
placeholder="请选择民族"
|
readonly
|
border="none"
|
@click="showNationPicker = 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="form.nationality"
|
placeholder="请输入国籍"
|
border="none"
|
/>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">证件类型</text>
|
<u-input
|
v-model="form.idcardtype"
|
placeholder="请选择证件类型"
|
readonly
|
border="none"
|
@click="showIdCardTypePicker = true"
|
>
|
<template #suffix>
|
<u-icon name="arrow-down" color="#86868b"></u-icon>
|
</template>
|
</u-input>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label required">证件号码</text>
|
<u-input
|
v-model="form.idcardno"
|
placeholder="请输入证件号码"
|
border="none"
|
:customStyle="inputStyle(!form.idcardno)"
|
@blur="validateIdCard"
|
/>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">性别</text>
|
<view class="radio-group">
|
<view
|
v-for="gender in genderOptions"
|
:key="gender.value"
|
class="radio-item"
|
@click="form.sex = gender.value"
|
>
|
<view
|
class="radio-dot"
|
:class="{ active: form.sex === gender.value }"
|
></view>
|
<text class="radio-label">{{ gender.label }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">出生日期</text>
|
<u-input
|
v-model="form.birthday"
|
placeholder="选择出生日期"
|
readonly
|
border="none"
|
@click="showDatePicker = 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="form.age"
|
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 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>
|
</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.infoname"
|
placeholder="请输入信息员"
|
border="none"
|
/>
|
</view>
|
|
<view class="form-item">
|
<text class="item-label">联系电话</text>
|
<u-input
|
v-model="form.infophone"
|
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>
|
</view>
|
</view>
|
|
<!-- 操作按钮 -->
|
<view class="action-buttons">
|
<u-button class="btn secondary" @click="resetForm">重置表单</u-button>
|
<u-button
|
class="btn primary"
|
:disabled="!isFormValid"
|
@click="submitForm"
|
>提交上报</u-button
|
>
|
</view>
|
<attachment-upload
|
ref="attachment"
|
:files="attachments"
|
:readonly="isReadonly"
|
:maxCount="5"
|
@update:files="handleFilesUpdate"
|
@upload-base="handleBaseUpload"
|
@preview="handlePreview"
|
/>
|
</view>
|
</scroll-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 } from "vue";
|
import { onLoad } from "@dcloudio/uni-app";
|
import attachmentUpload from "@/components/attachment";
|
import { useUserStore } from "@/stores/user";
|
|
// 表单数据
|
const form = ref({
|
donorno: "",
|
treatmenthospitalno: "",
|
treatmentdeptname: "",
|
name: "",
|
nation: "",
|
nationality: "中国",
|
idcardtype: "",
|
idcardno: "",
|
sex: "",
|
birthday: "",
|
age: "",
|
inpatientno: "",
|
diagnosisname: "",
|
bloodtype: "",
|
rhyin: "",
|
infoname: "",
|
infophone: "",
|
reporterno: "",
|
reporttime: "",
|
});
|
|
// 选择器状态
|
const attachments = ref([]);
|
const isReadonly = ref(false);
|
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([
|
{ label: "A型", value: "A" },
|
{ label: "B型", value: "B" },
|
{ label: "O型", value: "O" },
|
{ label: "AB型", value: "AB" },
|
]);
|
|
const rhOptions = ref([
|
{ label: "阳性", value: "positive" },
|
{ label: "阴性", value: "negative" },
|
]);
|
|
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;
|
});
|
|
// 样式方法
|
const inputStyle = (isError) => {
|
return isError
|
? "border: 2rpx solid #ff4757; border-radius: 12rpx;"
|
: "border: 2rpx solid #e5e5e7; border-radius: 12rpx;";
|
};
|
|
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);
|
});
|
|
onLoad((options) => {
|
if (options.edit && options.id) {
|
loadCaseData(options.id);
|
}
|
});
|
|
// 方法定义
|
const updateCurrentTime = () => {
|
const now = new Date();
|
currentTime.value = now.toLocaleString("zh-CN", {
|
year: "numeric",
|
month: "2-digit",
|
day: "2-digit",
|
hour: "2-digit",
|
minute: "2-digit",
|
});
|
form.value.reporttime = currentTime.value;
|
};
|
|
const generateDonorNo = () => {
|
const date = new Date();
|
const timestamp = date.getTime().toString().slice(-6);
|
form.value.donorno = `DON${date.getFullYear()}${(date.getMonth() + 1)
|
.toString()
|
.padStart(2, "0")}${timestamp}`;
|
};
|
|
const calculateAge = () => {
|
if (!form.value.birthday) return;
|
const birthDate = new Date(form.value.birthday);
|
const today = new Date();
|
let age = today.getFullYear() - birthDate.getFullYear();
|
const monthDiff = today.getMonth() - birthDate.getMonth();
|
|
if (
|
monthDiff < 0 ||
|
(monthDiff === 0 && today.getDate() < birthDate.getDate())
|
) {
|
age--;
|
}
|
form.value.age = age.toString();
|
};
|
|
const validateIdCard = () => {
|
if (form.value.idcardno && form.value.idcardno.length !== 18) {
|
uni.showToast({
|
title: "请输入18位身份证号码",
|
icon: "none",
|
});
|
}
|
};
|
// 处理基础附件上传
|
const handleBaseUpload = (file) => {
|
console.log("基础附件上传成功:", file);
|
};
|
|
// 处理其他附件上传
|
const handleFilesUpdate = (files) => {
|
formData.attachments = files.map((file) => ({
|
...file,
|
// 确保只存储半路径
|
url: file.url.startsWith("http")
|
? file.url.replace(baseUrlHt, "")
|
: file.url,
|
}));
|
};
|
|
// 预览文件 - 修改为使用完整URL
|
const handlePreview = (file) => {
|
const fullUrl = file.url.startsWith("http")
|
? file.url
|
: baseUrlHt + (file.url.startsWith("/") ? "" : "/") + file.url;
|
|
if (file.type.includes("image")) {
|
uni.previewImage({
|
urls: formData.attachments
|
.filter((f) => f.type.includes("image"))
|
.map((f) =>
|
f.url.startsWith("http")
|
? f.url
|
: baseUrlHt + (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: (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 onHospitalConfirm = (e) => {
|
if (e.value && e.value[0]) {
|
form.value.treatmenthospitalno = 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].label;
|
}
|
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 resetForm = () => {
|
uni.showModal({
|
title: "确认重置",
|
content: "确定要清空所有已填写的内容吗?",
|
success: (res) => {
|
if (res.confirm) {
|
Object.keys(form.value).forEach((key) => {
|
if (key !== "donorno") {
|
form.value[key] = "";
|
}
|
});
|
form.value.nationality = "中国";
|
generateDonorNo();
|
uni.showToast({ title: "表单已重置", icon: "success" });
|
}
|
},
|
});
|
};
|
|
const submitForm = async () => {
|
if (!isFormValid.value) {
|
uni.showToast({
|
title: "请填写姓名、证件号码和疾病诊断",
|
icon: "none",
|
});
|
return;
|
}
|
|
loading.value = true;
|
|
try {
|
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
uni.showToast({
|
title: "上报成功",
|
icon: "success",
|
});
|
|
setTimeout(() => {
|
uni.navigateBack();
|
}, 1500);
|
} catch (error) {
|
uni.showToast({
|
title: "上报失败,请重试",
|
icon: "none",
|
});
|
} finally {
|
loading.value = false;
|
}
|
};
|
|
const loadCaseData = (id) => {
|
// 模拟加载编辑数据
|
form.value = {
|
donorno: "DON20241216001",
|
treatmenthospitalno: "青岛镜湖医院",
|
treatmentdeptname: "神经外科",
|
name: "张三",
|
nation: "汉族",
|
nationality: "中国",
|
idcardtype: "居民身份证",
|
idcardno: "370203198510123456",
|
sex: "1",
|
birthday: "1985-10-12",
|
age: "38",
|
inpatientno: "ZY20241216001",
|
diagnosisname: "脑外伤导致脑死亡",
|
bloodtype: "A",
|
rhyin: "positive",
|
infoname: "李医生",
|
infophone: "13800138000",
|
reporterno: "张医生",
|
reporttime: currentTime.value,
|
};
|
};
|
</script>
|
<style lang="scss" scoped>
|
.case-report-container {
|
min-height: 100vh;
|
background: linear-gradient(135deg, #f8fdff 0%, #e8f7f6 100%);
|
}
|
|
.form-scroll {
|
height: 100vh;
|
}
|
|
.form-content {
|
padding: 30rpx;
|
}
|
|
.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 {
|
display: flex;
|
gap: 20rpx;
|
margin-top: 40rpx;
|
}
|
|
.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;
|
}
|
}
|
}
|
</style>
|