<template>
|
<div class="confirmation-detail">
|
<case-basic-info :case-id="caseId" :show-attachment="true" :column="3" />
|
|
<el-card class="detail-card paper-card">
|
<!-- 基础信息 -->
|
<div slot="header" class="clearfix">
|
<span class="detail-title">捐献确认信息</span>
|
</div>
|
|
<el-form :model="form" ref="form" label-width="120px" class="paper-form">
|
<!-- <el-row :gutter="20">
|
<el-col :span="8">
|
<el-form-item label="确认时间" prop="signdate">
|
<el-date-picker
|
v-model="form.signdate"
|
type="datetime"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row> -->
|
|
<!-- 亲属信息 -->
|
<el-divider content-position="left">亲属确认信息</el-divider>
|
|
<!-- 主要亲属表单 -->
|
<div class="form-row">
|
<el-form-item label="亲属姓名" class="col-1-3">
|
<el-input
|
v-model="familyMemberList[0].name"
|
:disabled="isReadOnly"
|
placeholder="请输入亲属姓名"
|
/>
|
</el-form-item>
|
<el-form-item label="关系" class="col-1-3">
|
<el-select
|
v-model="familyMemberList[0].relation"
|
:disabled="isReadOnly"
|
placeholder="请选择关系"
|
>
|
<el-option
|
v-for="dict in dict.type.sys_FamilyRelation"
|
:key="dict.value"
|
:label="dict.label"
|
:value="dict.value"
|
/>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="身份证号" class="col-1-3">
|
<el-input
|
v-model="familyMemberList[0].idcard"
|
:disabled="isReadOnly"
|
placeholder="请输入身份证号"
|
maxlength="18"
|
/>
|
</el-form-item>
|
</div>
|
<div class="form-row">
|
<el-form-item label="住址" class="col-full">
|
<template v-if="!isReadOnly">
|
<div class="area-select-box">
|
<li_area_select v-model="familyMemberList[0].address" />
|
</div>
|
<el-input
|
v-model="familyMemberList[0].address.detail"
|
placeholder="请输入详细地址"
|
class="address-detail"
|
/>
|
</template>
|
<span v-else>{{ formatAddress(familyMemberList[0].address) }}</span>
|
</el-form-item>
|
</div>
|
<div class="form-row">
|
<el-form-item label="现居住地" class="col-full">
|
<template v-if="!isReadOnly">
|
<div class="area-select-box">
|
<li_area_select
|
v-model="familyMemberList[0].currentResidence"
|
/>
|
</div>
|
<el-input
|
v-model="familyMemberList[0].currentResidence.detail"
|
placeholder="请输入详细地址"
|
class="address-detail"
|
/>
|
</template>
|
<span v-else>{{
|
formatAddress(familyMemberList[0].currentResidence)
|
}}</span>
|
</el-form-item>
|
</div>
|
<div class="form-row">
|
<el-form-item label="手机" class="col-1-3">
|
<el-input
|
v-model="familyMemberList[0].phone"
|
:disabled="isReadOnly"
|
placeholder="请输入手机号"
|
maxlength="11"
|
/>
|
</el-form-item>
|
</div>
|
|
<!-- 亲属关系确认 -->
|
<div class="form-row">
|
<el-form-item label="亲属关系确认" class="col-full">
|
<el-checkbox-group
|
v-model="form.kinship"
|
:disabled="isReadOnly"
|
@change="syncKinshipRelatives"
|
>
|
<el-checkbox
|
v-for="dict in kinshipCheckboxOptions"
|
:key="dict.value"
|
:label="String(dict.value)"
|
>
|
{{
|
isChildLabel(dict.label) ? dict.label + "(人)" : dict.label
|
}}
|
</el-checkbox>
|
</el-checkbox-group>
|
</el-form-item>
|
</div>
|
<div class="form-row" v-if="adultChildSelected">
|
<el-form-item label="子女数量" class="col-1-3">
|
<el-input
|
v-model="form.kinshipChildrennum"
|
:disabled="isReadOnly"
|
placeholder="请输入子女数量"
|
@input="syncKinshipRelatives"
|
/>
|
</el-form-item>
|
</div>
|
|
<!-- 亲属表(由亲属关系勾选自动生成,姓名可编辑) -->
|
<el-table
|
:data="otherFamilyMemberList"
|
size="small"
|
border
|
style="margin-top: 15px;"
|
>
|
<el-table-column label="序号" width="60" align="center">
|
<template slot-scope="scope">
|
{{ scope.$index + 1 }}
|
</template>
|
</el-table-column>
|
<el-table-column label="与捐赠者关系" width="160" align="center">
|
<template slot-scope="scope">
|
{{ getKinshipLabel(scope.row.relation) }}
|
</template>
|
</el-table-column>
|
<el-table-column label="姓名" min-width="200">
|
<template slot-scope="scope">
|
<el-input
|
style="width: 160px"
|
v-model="scope.row.name"
|
:disabled="isReadOnly"
|
placeholder="请输入姓名"
|
size="small"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column
|
v-if="!isReadOnly"
|
label="操作"
|
width="80"
|
align="center"
|
>
|
<template slot-scope="scope">
|
<el-button
|
type="text"
|
size="small"
|
style="color: #f56c6c"
|
@click="handleDeleteFamilyRow(scope.$index)"
|
>删除</el-button
|
>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<div class="form-row">
|
<el-form-item label-width="100px" label="捐献决定" class="col-full">
|
<div class="decision-statement">
|
我(们)已知悉捐献的相关法律法规及规定,同意并代表捐献者做出逝世后自愿、无偿捐献决定:
|
</div>
|
<template v-if="!isReadOnly">
|
<el-checkbox-group v-model="organdecision">
|
<el-checkbox
|
v-for="item in organselection"
|
:key="item"
|
:label="item"
|
>{{ item }}
|
</el-checkbox>
|
</el-checkbox-group>
|
<el-input
|
v-if="organdecision.includes('其他')"
|
v-model="organdecisionOther"
|
placeholder="请输入其他捐献决定的具体内容"
|
class="decision-other"
|
></el-input>
|
</template>
|
<span v-else class="decision-selected">{{
|
displayOrganDecision
|
}}</span>
|
<div class="decision-statement">
|
同意上述所捐用于临床医疗、医学教学和科学研究。
|
</div>
|
</el-form-item>
|
</div>
|
|
<!-- <div class="form-row">
|
<el-form-item
|
label="家属意见备注"
|
prop="relativeRemark"
|
class="col-full"
|
>
|
<el-input
|
type="textarea"
|
:rows="3"
|
v-model="form.relativeRemark"
|
placeholder="记录家属的意见和沟通情况"
|
:disabled="isReadOnly"
|
/>
|
</el-form-item>
|
</div> -->
|
|
<!-- 人体器官获取组织与负责人签字 -->
|
<div class="form-row">
|
<el-form-item label="人体器官获取组织" class="col-half">
|
<el-input
|
v-model="form.acquisitiontissuename"
|
:disabled="isReadOnly"
|
placeholder="请输入人体器官获取组织"
|
/>
|
</el-form-item>
|
<el-form-item
|
label="负责人签字"
|
prop="responsibleusername"
|
class="col-half"
|
>
|
<el-input
|
v-model="form.responsibleusername"
|
:disabled="isReadOnly"
|
placeholder="请输入负责人签字"
|
/>
|
</el-form-item>
|
</div>
|
|
<!-- 协调员与签署日期 -->
|
<div class="form-row">
|
<el-form-item
|
label="协调员1"
|
prop="coordinatedusernameo"
|
class="col-1-3"
|
>
|
<el-input
|
v-model="form.coordinatedusernameo"
|
:disabled="isReadOnly"
|
placeholder="请输入协调员1"
|
/>
|
</el-form-item>
|
<el-form-item
|
label="协调员2"
|
prop="coordinatedusernamet"
|
class="col-1-3"
|
>
|
<el-input
|
v-model="form.coordinatedusernamet"
|
:disabled="isReadOnly"
|
placeholder="请输入协调员2"
|
/>
|
</el-form-item>
|
<el-form-item label="签署日期" prop="signdate" class="col-1-3">
|
<el-date-picker
|
clearable
|
v-model="form.signdate"
|
type="date"
|
value-format="yyyy-MM-dd"
|
placeholder="选择签署日期"
|
:disabled="isReadOnly"
|
/>
|
</el-form-item>
|
</div>
|
</el-form>
|
</el-card>
|
|
<!-- 添加这里:底部居中按钮 -->
|
|
<!-- 捐献确认阶段附件(可编辑) -->
|
<el-card class="attachment-card">
|
<div slot="header" class="clearfix">
|
<span class="detail-title">捐献确认阶段附件</span>
|
</div>
|
|
<el-tabs v-model="activeAttachmentType" type="card">
|
<el-tab-pane
|
v-for="type in confirmAttachmentTypes"
|
:key="type.value"
|
:label="type.label"
|
:name="type.value"
|
>
|
<div class="attachment-section">
|
<div class="attachment-header">
|
<span class="upload-title">{{ type.label }}</span>
|
<el-tooltip content="点击上传该类型附件" placement="top">
|
<el-button
|
v-if="!isReadOnly"
|
size="mini"
|
type="primary"
|
icon="el-icon-plus"
|
@click="openUploadDialog(type.value)"
|
>
|
添加附件
|
</el-button>
|
</el-tooltip>
|
</div>
|
|
<div class="attachment-list">
|
<el-table
|
:data="getConfirmAttachments(type.value)"
|
size="small"
|
v-loading="attachmentLoading"
|
style="width: 100%;"
|
>
|
<el-table-column label="文件名" min-width="200">
|
<template slot-scope="scope">
|
<i
|
class="el-icon-document"
|
style="color: #409EFF; margin-right: 8px;"
|
></i>
|
<span class="file-name">{{ scope.row.fileName }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="文件类型" width="90" align="center">
|
<template slot-scope="scope">
|
<el-tag size="small">{{
|
getFileType(scope.row.fileName)
|
}}</el-tag>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="文件大小" width="90" align="center">
|
<template slot-scope="scope">
|
<span>{{ formatFileSize(scope.row.fileSize) }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="上传时间" width="155" align="center">
|
<template slot-scope="scope">
|
<span>{{ formatDateTime(scope.row.uploadTime) }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="操作" width="145" align="center">
|
<template slot-scope="scope">
|
<el-button
|
size="mini"
|
type="primary"
|
@click="handlePreview(scope.row)"
|
>
|
预览
|
</el-button>
|
<el-button
|
v-if="!isReadOnly"
|
size="mini"
|
type="danger"
|
@click="handleRemoveAttachment(type.value, scope.$index)"
|
>
|
删除
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<div
|
v-if="getConfirmAttachments(type.value).length === 0"
|
class="empty-attachment"
|
>
|
<el-empty
|
:description="`暂无${type.label}附件`"
|
:image-size="80"
|
></el-empty>
|
</div>
|
</div>
|
</div>
|
</el-tab-pane>
|
</el-tabs>
|
</el-card>
|
|
<!-- 捐献者登记阶段附件(仅查看) -->
|
<el-card class="attachment-card">
|
<div slot="header" class="clearfix">
|
<span class="detail-title">捐献者登记阶段附件(仅查看)</span>
|
</div>
|
|
<el-tabs v-model="activeRegisterType" type="card">
|
<el-tab-pane
|
v-for="type in registerAttachmentTypes"
|
:key="type.value"
|
:label="type.label"
|
:name="type.value"
|
>
|
<div class="attachment-section">
|
<div class="attachment-header">
|
<span class="upload-title">{{ type.label }}</span>
|
</div>
|
|
<div class="attachment-list">
|
<el-table
|
:data="getRegisterAttachments(type.value)"
|
size="small"
|
v-loading="attachmentLoading"
|
style="width: 100%;"
|
>
|
<el-table-column label="文件名" min-width="200">
|
<template slot-scope="scope">
|
<i
|
class="el-icon-document"
|
style="color: #409EFF; margin-right: 8px;"
|
></i>
|
<span class="file-name">{{ scope.row.fileName }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="文件类型" width="90" align="center">
|
<template slot-scope="scope">
|
<el-tag size="small">{{
|
getFileType(scope.row.fileName)
|
}}</el-tag>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="文件大小" width="90" align="center">
|
<template slot-scope="scope">
|
<span>{{ formatFileSize(scope.row.fileSize) }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="上传时间" width="155" align="center">
|
<template slot-scope="scope">
|
<span>{{
|
formatDateTime(
|
scope.row.uploadTime || scope.row.createTime
|
)
|
}}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="操作" width="90" align="center">
|
<template slot-scope="scope">
|
<el-button
|
size="mini"
|
type="primary"
|
@click="handlePreview(scope.row)"
|
>
|
预览
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<div
|
v-if="getRegisterAttachments(type.value).length === 0"
|
class="empty-attachment"
|
>
|
<el-empty
|
:description="`暂无${type.label}附件`"
|
:image-size="80"
|
></el-empty>
|
</div>
|
</div>
|
</div>
|
</el-tab-pane>
|
</el-tabs>
|
</el-card>
|
<!-- 底部操作按钮 -->
|
<div style="text-align: center; margin: 30px 0;">
|
<el-button
|
v-if="!isReadOnly"
|
type="primary"
|
@click="handleSave"
|
:loading="saveLoading"
|
style="margin-right: 20px; min-width: 140px;"
|
>
|
保存确认信息
|
</el-button>
|
<el-button
|
v-if="!isReadOnly"
|
type="success"
|
@click="accomplish"
|
:loading="saveLoading"
|
style="min-width: 140px;"
|
>
|
确认完成
|
</el-button>
|
</div>
|
|
<!-- 上传对话框 -->
|
<el-dialog
|
:title="`上传${getCurrentTypeLabel}附件`"
|
:visible.sync="uploadDialogVisible"
|
width="480px"
|
:close-on-click-modal="false"
|
>
|
<el-upload
|
ref="uploadRef"
|
class="upload-demo"
|
drag
|
:action="uploadAction"
|
:headers="headers"
|
multiple
|
:file-list="tempFileList"
|
:before-upload="beforeUpload"
|
:on-change="handleFileChange"
|
:on-remove="handleTempRemove"
|
:on-success="handleUploadSuccess"
|
:auto-upload="false"
|
>
|
<i class="el-icon-upload"></i>
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
<div class="el-upload__tip" slot="tip">
|
支持上传pdf、jpg、png、doc、docx格式文件,单个文件不超过10MB
|
</div>
|
</el-upload>
|
|
<span slot="footer" class="dialog-footer">
|
<el-button @click="uploadDialogVisible = false">取消</el-button>
|
<el-button
|
type="primary"
|
@click="submitUpload"
|
:loading="uploadLoading"
|
:disabled="tempFileList.length === 0"
|
>
|
确认上传
|
</el-button>
|
</span>
|
</el-dialog>
|
|
<!-- 文件预览弹窗 -->
|
<FilePreviewDialog
|
:visible="previewVisible"
|
:file="currentPreviewFile"
|
@close="previewVisible = false"
|
@download="handleDownload"
|
/>
|
</div>
|
</template>
|
|
<script>
|
import {
|
relativesList,
|
relativesEdit,
|
relativesAdd
|
} from "@/api/businessApi";
|
import {
|
listRelativesconfirmation,
|
getRelativesconfirmation
|
} from "@/api/project/relativesconfirmation";
|
import {
|
getDonatebaseinfo,
|
updateDonatebaseinfo
|
} from "@/api/project/donatebaseinfo";
|
import FilePreviewDialog from "@/components/FilePreviewDialog";
|
import CaseBasicInfo from "@/components/CaseBasicInfo";
|
import Li_area_select from "@/components/Address";
|
import { getToken } from "@/utils/auth";
|
|
export default {
|
name: "ConfirmationDetail",
|
components: {
|
FilePreviewDialog,
|
CaseBasicInfo,
|
Li_area_select
|
},
|
dicts: ["sys_FamilyRelation", "sys_KinshipConfirm", "sys_Kinship"],
|
data() {
|
return {
|
caseId: null,
|
isEdit: false,
|
form: {
|
id: undefined,
|
infoid: undefined,
|
caseNo: "",
|
name: "",
|
sex: "",
|
age: "",
|
diagnosisname: "",
|
treatmenthospitalname: "",
|
coordinatedusernameo: "",
|
coordinatedusernamet: "",
|
responsibleusername: "",
|
acquisitiontissuename: "",
|
relativeconfirmationsign: "0",
|
signdate: "",
|
relativeconfirmationsignname: "",
|
signfamilyrelations: "",
|
relativeidcardno: "",
|
relativephone: "",
|
phone: "",
|
kinship: [],
|
kinshipChildrennum: null,
|
relativeRemark: "",
|
assessannex: "",
|
otherFamilyMembers: ""
|
},
|
// 统一亲属列表(第一条为主要亲属,其余为其他亲属)
|
familyMemberList: [
|
{
|
name: "",
|
relation: "",
|
phone: "",
|
idcard: "",
|
address: { sheng: "", shi: "", qu: "", detail: "" },
|
currentResidence: { sheng: "", shi: "", qu: "", detail: "" },
|
isMain: true
|
}
|
],
|
// 标记本次 syncKinshipRelatives 属于"回显填充"(此时允许用已有子女行数兜底数量)
|
kinshipHydrated: false,
|
|
uploadAction: process.env.VUE_APP_BASE_API + "/common/upload",
|
headers: {
|
Authorization: "Bearer " + getToken()
|
},
|
organdecision: [],
|
organdecisionOther: "",
|
organselection: [
|
"肝脏",
|
"肾脏",
|
"心脏",
|
"肺脏",
|
"胰腺",
|
"小肠",
|
"眼角膜",
|
"其他"
|
],
|
loading: false,
|
saveLoading: false,
|
infoid: null,
|
archiveData: null,
|
activeAttachmentType: "2",
|
activeRegisterType: "1",
|
attachmentLoading: false,
|
uploadDialogVisible: false,
|
uploadLoading: false,
|
tempFileList: [],
|
currentUploadType: "",
|
previewVisible: false,
|
currentPreviewFile: null,
|
// 捐献确认阶段附件(可编辑,存储于亲属确认表 assessannex)
|
confirmAttachmentTypes: [
|
{ value: "2", label: "人体器官捐献亲属确认登记表" },
|
{ value: "3", label: "捐献者及直系亲属身份证、户口簿相关证明" },
|
{ value: "4", label: "公民身故后人体器官(角膜)遗体捐献告知书" },
|
{ value: "7", label: "授权委托资料" }
|
],
|
confirmAttachmentData: {
|
"2": [],
|
"3": [],
|
"4": [],
|
"7": []
|
},
|
// 捐献者登记阶段附件(仅查看,存储于亲属确认表 assessannex)
|
registerAttachmentTypes: [
|
{ value: "1", label: "人体器官潜在捐献者登记表附件" },
|
{ value: "5", label: "证件照" },
|
{ value: "6", label: "疾病诊断治疗" }
|
],
|
registerAttachmentData: {
|
"1": [],
|
"5": [],
|
"6": []
|
}
|
};
|
},
|
computed: {
|
// 查看模式:只读,禁用表单并隐藏保存按钮
|
isReadOnly() {
|
return this.$route.query.mode === "view";
|
},
|
// 是否选中"成年子女"
|
adultChildSelected() {
|
const value = this.findAdultChildValue();
|
return value != null && (this.form.kinship || []).includes(value);
|
},
|
// 亲属关系确认字典(优先 sys_KinshipConfirm,其次 sys_Kinship)
|
kinshipDictOptions() {
|
const a = this.dict.type.sys_KinshipConfirm || [];
|
const b = this.dict.type.sys_Kinship || [];
|
return a.length ? a : b;
|
},
|
// 重排亲属关系选项:子女/成年子女排倒数第二,受托人排最后
|
orderedKinshipOptions() {
|
const list = this.kinshipDictOptions;
|
const children = list.filter(d => this.isChildLabel(d.label));
|
const trustee = list.filter(d => d.label === "受托人");
|
const others = list.filter(
|
d => !this.isChildLabel(d.label) && d.label !== "受托人"
|
);
|
return [...others, ...children, ...trustee];
|
},
|
// 渲染勾选框实际使用的选项 = 当前字典 + 历史脏数据中映射不到的值
|
// 脏值如果不在选项里,el-checkbox-group 根本没有对应复选框,视觉上就是"勾不上";
|
// 补成带"(历史数据)"标记的项后既能显示勾选,也允许用户手动取消。
|
// 独立成计算属性(不并入 orderedKinshipOptions),避免与 watch 互相触发形成循环。
|
kinshipCheckboxOptions() {
|
const options = (this.orderedKinshipOptions || []).map(d => ({
|
value: String(d.value),
|
label: d.label
|
}));
|
const extra = [];
|
(this.form.kinship || []).forEach(v => {
|
const raw = String(v);
|
if (!raw) return;
|
if (options.some(o => o.value === raw)) return;
|
if (extra.some(o => o.value === raw)) return;
|
const label = this.getKinshipLabel(raw);
|
extra.push({
|
value: raw,
|
label: label === raw ? `历史数据(${raw})` : `${label}(历史数据)`
|
});
|
});
|
return [...options, ...extra];
|
},
|
// 其他亲属列表(排除第一条主要亲属)
|
otherFamilyMemberList() {
|
return this.familyMemberList.slice(1);
|
},
|
// 捐献决定(查看模式只展示已勾选项)
|
displayOrganDecision() {
|
return (this.organdecision || [])
|
.map(item => {
|
if (item === "其他" && this.organdecisionOther) {
|
return `其他(${this.organdecisionOther})`;
|
}
|
return item;
|
})
|
.join("、");
|
},
|
getCurrentTypeLabel() {
|
const type =
|
this.confirmAttachmentTypes.find(
|
t => t.value === this.currentUploadType
|
) ||
|
this.registerAttachmentTypes.find(
|
t => t.value === this.currentUploadType
|
);
|
return type ? type.label : "";
|
}
|
},
|
watch: {
|
// 字典是异步加载的,加载完成后重新校准已保存的勾选值并重算亲属表
|
orderedKinshipOptions(val) {
|
if (!val || !val.length) return;
|
const normalized = this.normalizeKinshipValues(this.form.kinship || []);
|
if (normalized.join(",") !== (this.form.kinship || []).join(",")) {
|
this.form.kinship = normalized;
|
}
|
this.kinshipHydrated = true;
|
this.syncKinshipRelatives();
|
}
|
},
|
created() {
|
this.infoid = this.$route.query.infoid;
|
this.caseId = this.$route.query.infoid;
|
this.isEdit = this.$route.query.confirm === "true";
|
if (this.infoid) {
|
this.getDetail(this.infoid);
|
}
|
},
|
methods: {
|
async getDetail(infoid) {
|
this.loading = true;
|
try {
|
const response = await relativesList({
|
infoid,
|
pageNum: 1,
|
pageSize: 10
|
});
|
// 亲属确认完整记录(含 kinship 多选字段),用于保证勾选回显
|
let fullRecord = null;
|
try {
|
const fullRes = await listRelativesconfirmation({
|
infoid,
|
pageNum: 1,
|
pageSize: 10
|
});
|
if (
|
fullRes.code === 200 &&
|
Array.isArray(fullRes.rows) &&
|
fullRes.rows.length
|
) {
|
// 必须校验 infoid 一致,否则可能拿到全表第一条记录
|
const matched = fullRes.rows.find(
|
r => String(r.infoid) === String(infoid)
|
);
|
fullRecord = matched || fullRes.rows[0];
|
}
|
} catch (e) {
|
console.warn("获取亲属确认完整记录失败:", e);
|
}
|
|
if (response.code === 200) {
|
this.handleDetailData(response, fullRecord);
|
await this.loadArchiveInfo();
|
// 再用主键精确拉一次完整记录,确保 kinship 一定能拿到
|
await this.loadFullKinship();
|
} else {
|
this.$message.error("获取详情失败:" + (response.msg || "未知错误"));
|
}
|
} catch (error) {
|
console.error("获取捐献确认详情失败:", error);
|
this.$message.error("获取详情失败");
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
handleDetailData(response, fullRecord) {
|
let detailData = null;
|
if (response.data) {
|
if (Array.isArray(response.data)) {
|
detailData = response.data[0] || {};
|
} else if (response.data.rows && Array.isArray(response.data.rows)) {
|
detailData = response.data.rows[0] || {};
|
} else if (Array.isArray(response.data.list)) {
|
detailData = response.data.list[0] || {};
|
} else {
|
detailData = response.data;
|
}
|
} else if (response.rows && Array.isArray(response.rows)) {
|
detailData = response.rows[0] || {};
|
} else {
|
detailData = response;
|
}
|
|
// 先解析亲属大字段:勾选值可能需要从它反推
|
this.familyMemberList = [];
|
let parsedMembers = [];
|
const rawMembers =
|
detailData.otherFamilyMembers ||
|
(fullRecord && fullRecord.otherFamilyMembers) ||
|
"";
|
if (rawMembers) {
|
try {
|
parsedMembers =
|
typeof rawMembers === "string"
|
? JSON.parse(rawMembers)
|
: rawMembers || [];
|
if (!Array.isArray(parsedMembers)) parsedMembers = [];
|
} catch (e) {
|
console.warn("解析其他家属数据失败:", e);
|
parsedMembers = [];
|
}
|
}
|
|
// 亲属关系确认勾选值:完整记录 → 详情 → 从亲属表反推(三重兜底)
|
let kinshipRaw = null;
|
if (
|
fullRecord &&
|
fullRecord.kinship != null &&
|
fullRecord.kinship !== ""
|
) {
|
kinshipRaw = fullRecord.kinship;
|
} else if (detailData.kinship != null && detailData.kinship !== "") {
|
kinshipRaw = detailData.kinship;
|
}
|
let kinshipValues = this.parseKinship(kinshipRaw);
|
if (!kinshipValues.length) {
|
// 兜底:亲属表能复现,说明它存的 relation 就是当时勾选的值
|
kinshipValues = Array.from(
|
new Set(
|
parsedMembers
|
.filter(
|
m =>
|
m &&
|
m.isMain !== true &&
|
m.relation != null &&
|
m.relation !== ""
|
)
|
.map(m => String(m.relation).trim())
|
.filter(v => v !== "")
|
)
|
);
|
}
|
|
this.form = {
|
...this.form,
|
id: detailData.id || this.$route.query.id,
|
infoid: detailData.infoid || this.infoid,
|
caseNo: detailData.caseNo || "",
|
name: detailData.name || "",
|
sex: detailData.sex || "",
|
age: detailData.age || "",
|
diagnosisname: detailData.diagnosisname || "",
|
treatmenthospitalname: detailData.treatmenthospitalname || "",
|
coordinatedusernameo: detailData.coordinatedusernameo || "",
|
coordinatedusernamet: detailData.coordinatedusernamet || "",
|
responsibleusername: detailData.responsibleusername || "",
|
acquisitiontissuename: "",
|
relativeconfirmationsign: detailData.relativeconfirmationsign || "0",
|
signdate: this.normalizeSigndate(detailData.signdate),
|
relativeconfirmationsignname:
|
detailData.relativeconfirmationsignname || "",
|
signfamilyrelations: detailData.signfamilyrelations || "",
|
relativeidcardno: detailData.relativeidcardno || "",
|
relativephone: detailData.relativephone || "",
|
phone: detailData.phone || detailData.relativephone || "",
|
kinship: kinshipValues,
|
kinshipChildrennum:
|
detailData.kinshipChildrennum != null &&
|
detailData.kinshipChildrennum !== ""
|
? detailData.kinshipChildrennum
|
: fullRecord && fullRecord.kinshipChildrennum != null
|
? fullRecord.kinshipChildrennum
|
: null,
|
relativeRemark: detailData.relativeRemark || "",
|
assessannex: detailData.assessannex || ""
|
};
|
|
// 主要亲属:优先取 JSON 中标记 isMain 的项(新格式,含住址/现居住地)
|
let mainMember = (parsedMembers || []).find(m => m && m.isMain === true);
|
// 命中项在解析数组中的位置;-1 表示主亲属不来自 otherFamilyMembers
|
let mainIndex = mainMember ? parsedMembers.indexOf(mainMember) : -1;
|
if (!mainMember) {
|
const hasMain =
|
detailData.relativeconfirmationsignname ||
|
detailData.signfamilyrelations ||
|
detailData.relativeidcardno;
|
if (hasMain) {
|
// 老格式:主亲属存在专用字段里,otherFamilyMembers 只存"其他亲属"
|
mainMember = {
|
name: detailData.relativeconfirmationsignname || "",
|
relation: detailData.signfamilyrelations || "",
|
idcard: detailData.relativeidcardno || "",
|
phone: detailData.phone || detailData.relativephone || "",
|
address: "",
|
currentResidence: "",
|
isMain: true
|
};
|
} else if (parsedMembers.length) {
|
// 脏数据兜底:主亲属专用字段全空、JSON 里也没有 isMain 标记时,
|
// 把第一条当作主亲属,其余作为其他亲属,避免主亲属整块空白
|
mainMember = parsedMembers[0];
|
mainIndex = 0;
|
}
|
}
|
this.familyMemberList.push({
|
name: (mainMember && mainMember.name) || "",
|
relation: (mainMember && mainMember.relation) || "",
|
idcard: (mainMember && mainMember.idcard) || "",
|
phone: (mainMember && mainMember.phone) || "",
|
address: this.normalizeAddress(mainMember && mainMember.address),
|
currentResidence: this.normalizeAddress(
|
mainMember && mainMember.currentResidence
|
),
|
isMain: true
|
});
|
|
// 其他亲属(姓名来自 JSON,关系按 kinship 勾选生成)
|
// 历史脏数据的行没有 isMain / seq / 住址字段,这里统一补齐默认值
|
(parsedMembers || []).forEach((member, idx) => {
|
if (!member) return;
|
if (idx === mainIndex) return;
|
if (member.isMain === true) return;
|
this.familyMemberList.push({
|
relation: member.relation != null ? String(member.relation) : "",
|
seq: member.seq != null ? member.seq : 0,
|
name: member.name || "",
|
phone: member.phone || "",
|
idcard: member.idcard || "",
|
address: this.normalizeAddress(member.address),
|
currentResidence: this.normalizeAddress(member.currentResidence),
|
isMain: false
|
});
|
});
|
|
// 字典若已缓存,watch 不会触发,这里主动把勾选值归一化一次
|
this.form.kinship = this.normalizeKinshipValues(this.form.kinship || []);
|
|
// 根据 kinship 勾选自动生成亲属表行(保留已有姓名)
|
this.kinshipHydrated = true;
|
this.syncKinshipRelatives();
|
|
if (detailData.organdecision) {
|
this.organdecision = Array.isArray(detailData.organdecision)
|
? detailData.organdecision
|
: detailData.organdecision.split(",");
|
}
|
this.organdecisionOther = detailData.organdecisionOther || "";
|
|
this.processAssessannexData();
|
|
// 调试用:确认 kinship / 子女数量 / 亲属大字段 是否都回来了
|
console.log("[affirmInfo] 亲属关系回显", {
|
kinship: this.form.kinship,
|
kinshipChildrennum: this.form.kinshipChildrennum,
|
otherFamilyMembers: this.form.otherFamilyMembers
|
});
|
},
|
|
// 加载档案表信息(人体器官获取组织等字段属于档案表,不在亲属确认表)
|
async loadArchiveInfo() {
|
if (!this.caseId) return;
|
try {
|
const caseRes = await getDonatebaseinfo(this.caseId);
|
if (caseRes.code === 200 && caseRes.data) {
|
this.archiveData = caseRes.data;
|
this.form.acquisitiontissuename =
|
caseRes.data.acquisitiontissuename || "";
|
}
|
} catch (e) {
|
console.warn("获取档案表信息失败:", e);
|
}
|
},
|
|
// 用主键精确拉取完整记录,补齐列表接口可能缺失的 kinship 字段
|
async loadFullKinship() {
|
const rid = this.form.id || this.$route.query.id;
|
if (!rid) return;
|
try {
|
const res = await getRelativesconfirmation(rid);
|
const data = res && res.data;
|
if (!data) return;
|
let changed = false;
|
if (data.kinship != null && data.kinship !== "") {
|
const values = this.parseKinship(data.kinship);
|
// 接口返回的真实值优先于从 otherFamilyMembers 反推的兜底值
|
// (反推值只用于字段缺失的情况,属于降级真相源)
|
if (
|
values.length &&
|
values.join(",") !== (this.form.kinship || []).join(",")
|
) {
|
this.form.kinship = values;
|
changed = true;
|
}
|
}
|
if (
|
(this.form.kinshipChildrennum == null ||
|
this.form.kinshipChildrennum === "") &&
|
data.kinshipChildrennum != null
|
) {
|
this.form.kinshipChildrennum = data.kinshipChildrennum;
|
changed = true;
|
}
|
if (changed) {
|
this.kinshipHydrated = true;
|
this.syncKinshipRelatives();
|
}
|
} catch (e) {
|
console.warn("获取亲属确认完整记录失败(getInfo):", e);
|
}
|
},
|
|
// 把已保存的勾选值映射到当前字典的 value(兼容两套字典编码不一致)
|
// 注意:映射不到的历史脏值(如 relation="4"/"6")必须原样保留,不能丢弃;
|
// 字典是异步加载的,若在此处过滤,字典一到位就会把反推出来的勾选清空。
|
normalizeKinshipValues(values) {
|
const options = this.orderedKinshipOptions || [];
|
if (!options.length) return values;
|
const result = [];
|
(values || []).forEach(v => {
|
const mapped = this.toCurrentKinshipValue(v);
|
if (mapped !== "" && !result.includes(mapped)) result.push(mapped);
|
});
|
return result;
|
},
|
|
// 单个关系值 → 当前字典体系下的值
|
// 命中顺序:当前字典 value → 当前字典同名 label → 子女类归一化(子女 ⇄ 成年子女)
|
// 全都命中不了说明是历史脏数据,原样返回,保证表格行不丢、勾选不被抹掉
|
toCurrentKinshipValue(v) {
|
if (v == null || v === "") return "";
|
const options = this.orderedKinshipOptions || [];
|
if (!options.length) return String(v);
|
const raw = String(v);
|
if (options.some(o => String(o.value) === raw)) return raw;
|
|
const label = this.getKinshipLabel(raw);
|
if (!label || label === raw) return raw; // 三个字典都查不到 → 脏值原样保留
|
|
const sameLabel = options.find(o => o.label === label);
|
if (sameLabel) return String(sameLabel.value);
|
|
// 子女类关系在不同字典里叫法不同("子女"/"成年子女"),视为同一关系
|
if (this.isChildLabel(label)) {
|
const exact = options.find(o => o.label === "成年子女");
|
if (exact) return String(exact.value);
|
const any = options.find(o => this.isChildLabel(o.label));
|
if (any) return String(any.value);
|
}
|
return raw;
|
},
|
|
// 解析亲属关系确认(多选,; 或 , 分隔,统一转为字符串数组)
|
parseKinship(val) {
|
if (!val) return [];
|
const arr = Array.isArray(val) ? val : String(val).split(/[;,]/);
|
return arr
|
.map(v => (v == null ? "" : String(v).trim()))
|
.filter(v => v !== "");
|
},
|
|
// 签署日期统一转成 yyyy-MM-dd,兼容后端返回的多种日期格式
|
normalizeSigndate(val) {
|
if (!val) return null;
|
const m = String(val).match(/^(\d{4}-\d{2}-\d{2})/);
|
return m ? m[1] : null;
|
},
|
|
processAssessannexData() {
|
Object.keys(this.confirmAttachmentData).forEach(key => {
|
this.confirmAttachmentData[key] = [];
|
});
|
Object.keys(this.registerAttachmentData).forEach(key => {
|
this.registerAttachmentData[key] = [];
|
});
|
if (!this.form.assessannex) return;
|
try {
|
const annexData =
|
typeof this.form.assessannex === "string"
|
? JSON.parse(this.form.assessannex)
|
: this.form.assessannex;
|
if (Array.isArray(annexData)) {
|
annexData.forEach(attachment => {
|
const type = String(attachment.type || "");
|
// 确认阶段(2/3/4)与登记阶段(1/5/6)均维护在 assessannex
|
if (this.confirmAttachmentData[type]) {
|
this.confirmAttachmentData[type].push(attachment);
|
} else if (this.registerAttachmentData[type]) {
|
this.registerAttachmentData[type].push(attachment);
|
}
|
});
|
}
|
} catch (error) {
|
console.warn("assessannex数据解析失败:", error);
|
}
|
},
|
|
getConfirmAttachments(type) {
|
return this.confirmAttachmentData[type] || [];
|
},
|
|
getRegisterAttachments(type) {
|
return this.registerAttachmentData[type] || [];
|
},
|
|
openUploadDialog(type) {
|
this.currentUploadType = type;
|
this.tempFileList = [];
|
this.uploadDialogVisible = true;
|
this.$nextTick(() => {
|
if (this.$refs.uploadRef) {
|
this.$refs.uploadRef.clearFiles();
|
}
|
});
|
},
|
|
beforeUpload(file) {
|
const allowedTypes = [
|
"application/pdf",
|
"image/jpeg",
|
"image/png",
|
"application/msword",
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
];
|
const maxSize = 10 * 1024 * 1024;
|
const isTypeOk =
|
allowedTypes.includes(file.type) ||
|
file.name.endsWith(".pdf") ||
|
file.name.endsWith(".jpg") ||
|
file.name.endsWith(".jpeg") ||
|
file.name.endsWith(".png") ||
|
file.name.endsWith(".doc") ||
|
file.name.endsWith(".docx");
|
if (!isTypeOk) {
|
this.$message.error(
|
"文件格式不支持,请上传pdf、jpg、png、doc或docx格式文件"
|
);
|
return false;
|
}
|
if (file.size > maxSize) {
|
this.$message.error("文件大小不能超过10MB");
|
return false;
|
}
|
return true;
|
},
|
|
handleFileChange(file, fileList) {
|
this.tempFileList = fileList;
|
},
|
|
handleTempRemove(file, fileList) {
|
this.tempFileList = fileList;
|
},
|
|
handleUploadSuccess(response, file) {
|
if (response.code !== 200) {
|
this.$message.error(response.msg || "上传失败");
|
return;
|
}
|
const newAttachment = {
|
id: Date.now(),
|
fileName: file.name,
|
fileUrl: response.url,
|
fileSize: file.size,
|
fileType: this.getFileExtension(file.name),
|
type: this.currentUploadType,
|
uploadTime: this.getCurrentTime(),
|
uploader: "当前用户"
|
};
|
if (this.confirmAttachmentData[this.currentUploadType]) {
|
this.confirmAttachmentData[this.currentUploadType].push(newAttachment);
|
} else if (this.registerAttachmentData[this.currentUploadType]) {
|
this.registerAttachmentData[this.currentUploadType].push(newAttachment);
|
}
|
|
this.updateAssessannexField();
|
this.$message.success("上传成功");
|
this.uploadLoading = false;
|
this.uploadDialogVisible = false;
|
this.tempFileList = [];
|
},
|
|
submitUpload() {
|
if (this.tempFileList.length === 0) {
|
this.$message.warning("请先选择要上传的文件");
|
return;
|
}
|
this.uploadLoading = true;
|
this.$refs.uploadRef.submit();
|
},
|
|
handleRemoveAttachment(type, index) {
|
this.$confirm("确定要删除这个附件吗?", "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning"
|
})
|
.then(() => {
|
const container =
|
this.confirmAttachmentData[type] || this.registerAttachmentData[type];
|
if (container && container[index]) {
|
container.splice(index, 1);
|
this.$message.success("附件删除成功");
|
this.updateAssessannexField();
|
}
|
})
|
.catch(() => {});
|
},
|
|
// 根据 kinship 勾选自动生成亲属表行,同时保留已有亲属数据(合并,避免丢失)
|
syncKinshipRelatives() {
|
// 勾选值统一映射到当前字典体系,保证与表格行的 relation 处在同一值域。
|
// 必须去重:历史脏数据里多个不同编码(如 "4"、"6")可能映射成同一个 value,
|
// 不去重的话每个重复值都会再生成一遍行,表现就是"行数成倍变多"。
|
const kinship = Array.from(
|
new Set(
|
(this.form.kinship || [])
|
.map(v => this.toCurrentKinshipValue(v))
|
.filter(v => v !== "")
|
)
|
);
|
const adultChildValue = this.findAdultChildValue();
|
|
// 已有亲属(非主要亲属),保留姓名与关系;关系值统一映射到当前字典体系
|
const existing = this.familyMemberList
|
.slice(1)
|
.filter(m => m && !m.isMain)
|
.map(m => ({ ...m, relation: this.toCurrentKinshipValue(m.relation) }));
|
|
// 子女数量:接口可能不返回该字段
|
// 回显时用已有子女行数兜底;用户主动清空("")则不兜底,行照常清掉
|
const existingChildCount = adultChildValue
|
? existing.filter(e => e.relation === adultChildValue).length
|
: 0;
|
const rawCountStr = this.form.kinshipChildrennum;
|
// null / "" / 数字 0 都视为"没填数量"(真要 0 个,取消勾选即可)
|
const rawMissing =
|
rawCountStr == null || rawCountStr === "" || rawCountStr === 0;
|
let childCount;
|
if (rawMissing) {
|
childCount = this.kinshipHydrated ? existingChildCount : 0;
|
// 回显兜底:把推算出的数量写回表单,否则数量框空白、保存时又丢
|
// (程序赋值不会触发 el-input 的 @input,不会形成循环)
|
if (this.kinshipHydrated && existingChildCount > 0) {
|
this.form.kinshipChildrennum = String(existingChildCount);
|
}
|
} else {
|
const n = parseInt(rawCountStr, 10);
|
childCount = isNaN(n) || n < 0 ? 0 : n;
|
}
|
|
// 取一行已有数据复用姓名:先按 关系+序号 精确匹配,再退化为同关系顺序取
|
const used = new Set();
|
const takeExisting = (relation, seq) => {
|
let idx = existing.findIndex(
|
(e, i) =>
|
!used.has(i) && e.relation === relation && (e.seq || 0) === seq
|
);
|
if (idx === -1) {
|
idx = existing.findIndex(
|
(e, i) => !used.has(i) && e.relation === relation
|
);
|
}
|
if (idx === -1) return null;
|
used.add(idx);
|
return existing[idx];
|
};
|
|
// 生成一行:命中已有行时整行复用(含 phone/idcard/住址等),
|
// 历史脏数据里这些字段可能有值,重建时不能只保留 relation/name 把其余抹掉
|
const buildRow = (relation, seq, hit) => ({
|
relation: relation,
|
seq: seq,
|
name: (hit && hit.name) || "",
|
phone: (hit && hit.phone) || "",
|
idcard: (hit && hit.idcard) || "",
|
address: this.normalizeAddress(hit && hit.address),
|
currentResidence: this.normalizeAddress(hit && hit.currentResidence),
|
isMain: false
|
});
|
|
const merged = [];
|
kinship.forEach(rel => {
|
if (adultChildValue && rel === adultChildValue) {
|
// 成年子女:子女数量决定行数
|
for (let i = 0; i < childCount; i++) {
|
const hit = takeExisting(rel, i);
|
merged.push(buildRow(rel, i, hit));
|
}
|
} else {
|
const hit = takeExisting(rel, 0);
|
merged.push(buildRow(rel, 0, hit));
|
}
|
});
|
|
// 未被勾选命中的已有亲属一律保留。
|
// 不能按关系去重:历史数据里"两个兄弟姐妹"这类同关系多行是合法的,
|
// 去重会静默丢人。之前出现"同关系重复行"的根因是 takeExisting 没命中,
|
// 现在 used 保证每个已有行最多被消耗一次,不会重复生成。
|
existing.forEach((e, idx) => {
|
if (used.has(idx)) return;
|
// 子女行数由子女数量决定,多余的子女行不保留
|
if (adultChildValue && e.relation === adultChildValue) return;
|
merged.push(buildRow(e.relation || "", e.seq != null ? e.seq : 0, e));
|
});
|
|
this.familyMemberList = [this.familyMemberList[0], ...merged];
|
this.updateFamilyMemberField();
|
// 兜底只在回显那一次生效
|
this.kinshipHydrated = false;
|
},
|
|
// 删除亲属表某一行,并同步取消上方对应的关系勾选
|
handleDeleteFamilyRow(index) {
|
const rowIndex = index + 1; // otherFamilyMemberList = familyMemberList.slice(1)
|
const row = this.familyMemberList[rowIndex];
|
if (!row) return;
|
if (row.isMain) return;
|
|
const relation = row.relation
|
? this.toCurrentKinshipValue(row.relation)
|
: "";
|
const adultChildValue = this.findAdultChildValue();
|
const isChild = !!adultChildValue && relation === adultChildValue;
|
|
// 勾选里存的可能是历史编码(如 "4")而不是映射后的值(如 "2"),
|
// 取消勾选时两种都要比对,否则会出现"行删了但勾选还在"
|
const removeKinship = target => {
|
const mapped = this.toCurrentKinshipValue(target);
|
this.form.kinship = (this.form.kinship || []).filter(
|
v =>
|
String(v) !== String(target) &&
|
this.toCurrentKinshipValue(v) !== mapped
|
);
|
};
|
|
console.log("[affirmInfo] 删除亲属行", {
|
index,
|
relation,
|
adultChildValue,
|
isChild,
|
before: this.familyMemberList.slice(1).map(m => m.relation),
|
kinship: this.form.kinship
|
});
|
|
if (isChild) {
|
// 成年子女:直接删这一行,数量按剩余子女行数重算,归零才取消勾选
|
this.familyMemberList.splice(rowIndex, 1);
|
const rest = this.familyMemberList.filter(
|
(m, i) =>
|
i > 0 &&
|
!m.isMain &&
|
this.toCurrentKinshipValue(m.relation) === adultChildValue
|
).length;
|
if (rest > 0) {
|
this.form.kinshipChildrennum = String(rest);
|
} else {
|
this.form.kinshipChildrennum = "";
|
removeKinship(adultChildValue);
|
}
|
} else if (relation) {
|
// 其余关系:一行对应一个勾选,取消勾选并整组清掉
|
removeKinship(relation);
|
this.familyMemberList = this.familyMemberList.filter(
|
(m, i) =>
|
i === 0 ||
|
m.isMain ||
|
this.toCurrentKinshipValue(m.relation) !== relation
|
);
|
} else {
|
// 关系为空的历史遗留行,只删这一行
|
this.familyMemberList.splice(rowIndex, 1);
|
}
|
|
// 直接回写大字段。不调用 syncKinshipRelatives,避免行被按勾选重新生成回来
|
this.updateFamilyMemberField();
|
|
if (relation) {
|
this.$message.success(
|
`已移除「${this.getKinshipLabel(relation)}」并同步取消勾选`
|
);
|
}
|
},
|
|
getKinshipLabel(value) {
|
if (value == null || value === "") return "";
|
const dictLists = [
|
this.dict.type.sys_KinshipConfirm,
|
this.dict.type.sys_Kinship,
|
this.dict.type.sys_FamilyRelation
|
];
|
for (const list of dictLists) {
|
const dict = (list || []).find(d => String(d.value) === String(value));
|
if (dict) return dict.label;
|
}
|
// 三个字典都查不到 → 老编码/脏数据,按原值展示,并记录一次便于排查
|
if (!this._warnedKinshipValues) this._warnedKinshipValues = new Set();
|
const key = String(value);
|
if (!this._warnedKinshipValues.has(key)) {
|
this._warnedKinshipValues.add(key);
|
console.warn(
|
`[affirmInfo] 亲属关系编码「${key}」在 sys_KinshipConfirm / sys_Kinship / sys_FamilyRelation 中均不存在,按原值展示`
|
);
|
}
|
return value;
|
},
|
|
isChildLabel(label) {
|
return label === "子女" || label === "成年子女";
|
},
|
|
findAdultChildValue() {
|
const list = this.kinshipDictOptions || [];
|
// 优先精确匹配"成年子女",避免把"子女"当成成年子女处理
|
const exact = list.find(d => d.label === "成年子女");
|
if (exact) return String(exact.value);
|
const any = list.find(d => this.isChildLabel(d.label));
|
return any ? String(any.value) : null;
|
},
|
|
formatAddress(addr) {
|
if (!addr) return "";
|
if (typeof addr === "string") return addr;
|
return [addr.sheng, addr.shi, addr.qu, addr.detail]
|
.filter(p => p)
|
.join("");
|
},
|
|
normalizeAddress(addr) {
|
if (!addr) {
|
return { sheng: "", shi: "", qu: "", detail: "" };
|
}
|
if (typeof addr === "string") {
|
return { sheng: "", shi: "", qu: "", detail: addr };
|
}
|
return {
|
sheng: (addr && addr.sheng) || "",
|
shi: (addr && addr.shi) || "",
|
qu: (addr && addr.qu) || "",
|
detail: (addr && addr.detail) || ""
|
};
|
},
|
|
updateFamilyMemberField() {
|
const mainMember = this.familyMemberList[0];
|
if (mainMember) {
|
this.form.relativeconfirmationsignname = mainMember.name || "";
|
this.form.signfamilyrelations = mainMember.relation || "";
|
this.form.relativeidcardno = mainMember.idcard || "";
|
this.form.phone = mainMember.phone || "";
|
this.form.relativephone = mainMember.phone || "";
|
} else {
|
this.form.relativeconfirmationsignname = "";
|
this.form.signfamilyrelations = "";
|
this.form.relativeidcardno = "";
|
this.form.phone = "";
|
this.form.relativephone = "";
|
}
|
|
// 所有亲属(含主要亲属)统一存入一个 JSON 大字段,住址/现居住地也存这里
|
const allMembers = this.familyMemberList.map((member, index) => ({
|
name: member.name || "",
|
relation: member.relation || "",
|
idcard: member.idcard || "",
|
phone: member.phone || "",
|
address: member.address || "",
|
currentResidence: member.currentResidence || "",
|
seq: member.seq != null ? member.seq : 0,
|
isMain: index === 0
|
}));
|
this.form.otherFamilyMembers = JSON.stringify(allMembers);
|
},
|
|
updateAssessannexField() {
|
const allAttachments = [];
|
[
|
...Object.values(this.confirmAttachmentData),
|
...Object.values(this.registerAttachmentData)
|
].forEach(attachments => {
|
allAttachments.push(...attachments);
|
});
|
this.form.assessannex = JSON.stringify(allAttachments);
|
},
|
|
handlePreview(file) {
|
this.currentPreviewFile = {
|
fileName: file.fileName,
|
fileUrl: file.fileUrl,
|
fileType: this.getFileType(file.fileName)
|
};
|
this.previewVisible = true;
|
},
|
|
handleDownload(file) {
|
const fileUrl = file.fileUrl;
|
const fileName = file.fileName;
|
if (fileUrl) {
|
const link = document.createElement("a");
|
link.href = fileUrl;
|
link.download = fileName;
|
link.style.display = "none";
|
document.body.appendChild(link);
|
link.click();
|
document.body.removeChild(link);
|
this.$message.success("开始下载文件");
|
} else {
|
this.$message.warning("文件路径不存在,无法下载");
|
}
|
},
|
|
getFileType(fileName) {
|
if (!fileName) return "other";
|
const extension = this.getFileExtension(fileName);
|
const imageTypes = ["jpg", "jpeg", "png"];
|
const pdfTypes = ["pdf"];
|
const officeTypes = ["doc", "docx"];
|
if (imageTypes.includes(extension)) return "image";
|
if (pdfTypes.includes(extension)) return "pdf";
|
if (officeTypes.includes(extension)) return "office";
|
return "other";
|
},
|
|
getFileExtension(filename) {
|
return filename
|
.split(".")
|
.pop()
|
.toLowerCase();
|
},
|
|
formatFileSize(size) {
|
if (!size) return "0 B";
|
const k = 1024;
|
const sizes = ["B", "KB", "MB", "GB"];
|
const i = Math.floor(Math.log(size) / Math.log(k));
|
return parseFloat((size / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
},
|
|
formatDateTime(dateTime) {
|
if (!dateTime) return "";
|
try {
|
const date = new Date(dateTime);
|
if (isNaN(date.getTime())) return dateTime;
|
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");
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
} catch (error) {
|
return dateTime;
|
}
|
},
|
|
getCurrentTime() {
|
const now = new Date();
|
return `${now.getFullYear()}-${(now.getMonth() + 1)
|
.toString()
|
.padStart(2, "0")}-${now
|
.getDate()
|
.toString()
|
.padStart(2, "0")} ${now
|
.getHours()
|
.toString()
|
.padStart(2, "0")}:${now
|
.getMinutes()
|
.toString()
|
.padStart(2, "0")}:${now
|
.getSeconds()
|
.toString()
|
.padStart(2, "0")}`;
|
},
|
|
accomplish() {
|
this.$confirm("是否完成该案例捐献确认步骤?", "提醒", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning"
|
})
|
.then(() => {
|
this.form.state = 3;
|
this.handleSave();
|
})
|
.catch(() => {});
|
},
|
|
async handleSave() {
|
if (this.saveLoading) return;
|
try {
|
await this.$refs.form.validate();
|
this.saveLoading = true;
|
|
this.updateFamilyMemberField();
|
this.updateAssessannexField();
|
|
const saveData = {
|
...this.form,
|
infoid: this.infoid,
|
organdecision: this.organdecision.join(","),
|
organdecisionOther: this.organdecisionOther,
|
kinship: (this.form.kinship || []).join(",")
|
};
|
// 人体器官获取组织属于档案表字段,不随亲属确认表保存
|
delete saveData.acquisitiontissuename;
|
// 签署日期为空时不提交,避免后端 Date 反序列化报错
|
if (!saveData.signdate) {
|
delete saveData.signdate;
|
}
|
if (saveData.state == 1 || !saveData.state) {
|
saveData.state = 2;
|
}
|
|
// 新增时校验是否已有相同患者
|
if (!saveData.id) {
|
const exists = await this.checkPatientExists();
|
if (exists) {
|
this.$message.warning("该患者已存在,请勿重复添加");
|
return;
|
}
|
}
|
|
let response = null;
|
if (saveData.id) {
|
response = await relativesEdit(saveData);
|
} else {
|
response = await relativesAdd(saveData);
|
}
|
|
if (response.code === 200) {
|
// 人体器官获取组织单独保存到档案表
|
await this.saveArchiveAcquisitionTissue();
|
this.$message.success("保存成功");
|
this.$router.push("/case/affirm");
|
} else {
|
this.$message.error("保存失败:" + (response.msg || "未知错误"));
|
}
|
} catch (error) {
|
if (error !== "cancel") {
|
console.error("保存失败:", error);
|
this.$message.error("保存失败");
|
}
|
} finally {
|
this.saveLoading = false;
|
}
|
},
|
// 保存档案表的人体器官获取组织字段
|
async saveArchiveAcquisitionTissue() {
|
if (!this.caseId) return;
|
try {
|
const payload = {
|
...(this.archiveData || {}),
|
id: this.caseId,
|
acquisitiontissuename: this.form.acquisitiontissuename
|
};
|
await updateDonatebaseinfo(payload);
|
} catch (e) {
|
console.warn("保存档案表人体器官获取组织失败:", e);
|
}
|
},
|
|
// 新增时校验是否已有相同患者(按住院号)
|
async checkPatientExists() {
|
let inpatientno = this.form.inpatientno;
|
if (!inpatientno && this.caseId) {
|
try {
|
const caseRes = await getDonatebaseinfo(this.caseId);
|
inpatientno = (caseRes.data && caseRes.data.inpatientno) || "";
|
} catch (e) {
|
inpatientno = "";
|
}
|
}
|
if (!inpatientno) return false;
|
try {
|
const res = await relativesList({
|
inpatientno,
|
pageNum: 1,
|
pageSize: 1
|
});
|
const list = res.data || [];
|
return list.some(item => item && item.id != null && item.id !== "");
|
} catch (e) {
|
return false;
|
}
|
}
|
}
|
};
|
</script>
|
|
<style scoped>
|
.confirmation-detail {
|
padding: 20px;
|
}
|
|
.detail-card {
|
margin-bottom: 20px;
|
}
|
|
.main-family {
|
font-weight: bold;
|
color: #e6a23c;
|
}
|
|
.attachment-card {
|
margin-bottom: 20px;
|
}
|
|
.detail-title {
|
font-size: 16px;
|
font-weight: bold;
|
margin-right: 20px;
|
}
|
|
.attachment-section {
|
padding: 15px;
|
}
|
|
.attachment-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 15px;
|
padding-bottom: 10px;
|
border-bottom: 1px solid #ebeef5;
|
}
|
|
.upload-title {
|
font-size: 14px;
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.attachment-list {
|
margin-top: 15px;
|
}
|
|
.empty-attachment {
|
text-align: center;
|
padding: 30px 0;
|
color: #909399;
|
}
|
|
.file-name {
|
font-size: 13px;
|
color: #606266;
|
}
|
|
/* ===== 纸质表格网格样式 ===== */
|
.paper-card >>> .el-card__body {
|
padding: 16px;
|
}
|
|
.paper-form {
|
border-left: 1px solid #d0d3db;
|
background: #fff;
|
}
|
|
.paper-form >>> .form-row {
|
display: flex;
|
width: 100%;
|
border-top: 1px solid #d0d3db;
|
/* 上移1px与上一行的border-bottom重叠,两行之间只显示一条1px细线 */
|
margin-top: -1px;
|
}
|
.paper-form >>> .form-row:first-child {
|
margin-top: 0;
|
}
|
|
/* 行尾空白填充:不满整行的右侧自动补一个空白格子,网格保持闭合(右侧适当留白) */
|
.paper-form >>> .form-row::after {
|
content: "";
|
flex: 1 1 0;
|
min-height: 42px;
|
background: linear-gradient(to left, #d0d3db 1px, transparent 1px),
|
linear-gradient(to top, #d0d3db 1px, transparent 1px);
|
background-color: #fff;
|
}
|
|
.paper-form >>> .el-form-item {
|
flex: 0 0 33.3333%;
|
display: flex;
|
margin: 0;
|
background: #fff;
|
border-right: 1px solid #d0d3db;
|
border-bottom: 1px solid #d0d3db;
|
}
|
.paper-form >>> .el-form-item::after,
|
.paper-form >>> .el-form-item::before {
|
display: none;
|
}
|
|
.paper-form >>> .col-1-3 {
|
flex: 0 0 33.3333%;
|
}
|
.paper-form >>> .col-half {
|
flex: 0 0 50%;
|
}
|
.paper-form >>> .col-2-3 {
|
flex: 0 0 66.6666%;
|
}
|
.paper-form >>> .col-full {
|
flex: 0 0 100%;
|
}
|
|
.paper-form >>> .el-form-item__label {
|
flex: 0 0 150px;
|
width: 150px !important;
|
float: none;
|
padding: 0 8px !important;
|
background: #ecf5ff;
|
border-right: 1px solid #d0d3db;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
text-align: center;
|
font-size: 13px;
|
font-weight: 600;
|
color: #2c3038;
|
line-height: 1.3;
|
white-space: nowrap;
|
}
|
.paper-form
|
>>> .el-form-item.is-required:not(.is-no-asterisk)
|
> .el-form-item__label:before {
|
color: #f56c6c;
|
margin-right: 2px;
|
}
|
|
.paper-form >>> .el-form-item__content {
|
flex: 1;
|
margin-left: 0 !important;
|
display: flex;
|
align-items: center;
|
flex-wrap: wrap;
|
padding: 6px 10px;
|
background: #fff;
|
min-height: 42px;
|
line-height: 1.4;
|
}
|
|
/* 控件宽度适中,不铺满整格(内容短时输入框不再拉得很长);窄屏时自动收缩避免溢出 */
|
.paper-form >>> .el-form-item__content .el-input,
|
.paper-form >>> .el-form-item__content .el-select,
|
.paper-form >>> .el-form-item__content .el-cascader {
|
width: 100%;
|
max-width: 220px;
|
}
|
.paper-form >>> .el-form-item__content .el-date-editor {
|
width: 100%;
|
max-width: 240px;
|
}
|
.paper-form >>> .el-form-item__content .el-range-editor {
|
width: 360px;
|
max-width: 100%;
|
}
|
|
/* 地址选择器(省市区三级联动 + 详细地址,详细地址分行显示) */
|
.paper-form >>> .area-select-box {
|
flex: 0 0 auto;
|
width: 480px;
|
max-width: 100%;
|
}
|
.paper-form >>> .address-detail {
|
flex: 0 0 100%;
|
width: 100% !important;
|
margin: 6px 0 0 0;
|
}
|
|
/* 捐献决定语句排版 */
|
.paper-form >>> .decision-statement {
|
flex: 0 0 100%;
|
width: 100%;
|
line-height: 1.8;
|
font-size: 13px;
|
color: #303133;
|
padding: 2px 0;
|
}
|
.paper-form >>> .decision-other {
|
width: 320px !important;
|
margin-left: 8px;
|
}
|
.paper-form >>> .decision-selected {
|
flex: 0 0 100%;
|
width: 100%;
|
line-height: 1.8;
|
font-size: 13px;
|
color: #303133;
|
padding: 2px 0;
|
}
|
</style>
|