| | |
| | | </div> |
| | | |
| | | <el-form :model="form" ref="form" label-width="120px" class="paper-form"> |
| | | <div class="form-row"> |
| | | <el-form-item |
| | | label="协调员1" |
| | | prop="coordinatedusernameo" |
| | | class="col-1-3" |
| | | > |
| | | <el-input v-model="form.coordinatedusernameo" /> |
| | | </el-form-item> |
| | | <el-form-item |
| | | label="协调员2" |
| | | prop="coordinatedusernamet" |
| | | class="col-1-3" |
| | | > |
| | | <el-input v-model="form.coordinatedusernamet" /> |
| | | </el-form-item> |
| | | <!-- <el-col :span="8"> |
| | | <el-form-item label="业务人员" prop="responsibleusername"> |
| | | <el-input v-model="form.responsibleusername" /> |
| | | </el-form-item> |
| | | </el-col> --> |
| | | </div> |
| | | |
| | | <!-- <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="确认时间" prop="signdate"> |
| | |
| | | <!-- 亲属信息 --> |
| | | <el-divider content-position="left">亲属确认信息</el-divider> |
| | | |
| | | <el-alert |
| | | title="第一条数据为主要亲属,需填写完整的姓名、关系、身份证和电话信息" |
| | | type="info" |
| | | show-icon |
| | | :closable="false" |
| | | style="margin-bottom: 15px;" |
| | | <!-- 主要亲属表单 --> |
| | | <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-alert> |
| | | <div style="margin-top: 15px;"> |
| | | <el-button |
| | | style="float: right; margin-bottom: 5px;" |
| | | type="primary" |
| | | size="mini" |
| | | icon="el-icon-plus" |
| | | @click="openFamilyDialog(false)" |
| | | > |
| | | 添加其他家属 |
| | | </el-button> |
| | | <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> |
| | | <el-table :data="familyMemberList" size="small" border> |
| | | <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="姓名" prop="name" min-width="120"> |
| | | <el-table-column label="与捐赠者关系" width="160" align="center"> |
| | | <template slot-scope="scope"> |
| | | <span :class="{ 'main-family': scope.$index === 0 }">{{ |
| | | scope.row.name |
| | | }}</span> |
| | | {{ getKinshipLabel(scope.row.relation) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="与捐赠者关系" prop="relation" min-width="140"> |
| | | <el-table-column label="姓名" min-width="200"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag |
| | | :options="dict.type.sys_FamilyRelation" |
| | | :value="scope.row.relation" |
| | | <el-input |
| | | style="width: 160px" |
| | | v-model="scope.row.name" |
| | | :disabled="isReadOnly" |
| | | placeholder="请输入姓名" |
| | | size="small" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="身份证号" prop="idcard" min-width="180"> |
| | | <template slot-scope="scope"> |
| | | <span v-if="scope.$index === 0">{{ scope.row.idcard }}</span> |
| | | <span v-else style="color: #909399;">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="联系电话" prop="phone" min-width="130"> |
| | | <template slot-scope="scope"> |
| | | <span v-if="scope.$index === 0">{{ scope.row.phone }}</span> |
| | | <span v-else style="color: #909399;">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="类型" width="70" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-tag v-if="scope.$index === 0" type="warning" size="small" |
| | | >主要</el-tag |
| | | <el-table-column |
| | | v-if="!isReadOnly" |
| | | label="操作" |
| | | width="80" |
| | | align="center" |
| | | > |
| | | <el-tag v-else type="info" size="small">其他</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="150" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | @click="editFamilyMember(scope.$index)" |
| | | > |
| | | 编辑 |
| | | </el-button> |
| | | <el-button |
| | | v-if="scope.$index > 0" |
| | | size="mini" |
| | | type="text" |
| | | style="color:red" |
| | | @click="deleteFamilyMember(scope.$index)" |
| | | > |
| | | 删除 |
| | | </el-button> |
| | | <span v-else style="color: #909399; font-size: 12px;" |
| | | >不可删除</span |
| | | size="small" |
| | | style="color: #f56c6c" |
| | | @click="handleDeleteFamilyRow(scope.$index)" |
| | | >删除</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | |
| | | |
| | | <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" |
| | |
| | | v-if="organdecision.includes('其他')" |
| | | v-model="organdecisionOther" |
| | | placeholder="请输入其他捐献决定的具体内容" |
| | | style="margin-top: 10px; width: 300px;" |
| | | 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"> |
| | | <!-- <div class="form-row"> |
| | | <el-form-item |
| | | label="家属意见备注" |
| | | prop="relativeRemark" |
| | |
| | | :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-card class="attachment-card"> |
| | | <div slot="header" class="clearfix"> |
| | | <span class="detail-title">相关附件上传</span> |
| | | <span class="detail-title">捐献确认阶段附件</span> |
| | | </div> |
| | | |
| | | <el-tabs v-model="activeAttachmentType" type="card"> |
| | | <el-tab-pane |
| | | v-for="type in attachmentTypes" |
| | | v-for="type in confirmAttachmentTypes" |
| | | :key="type.value" |
| | | :label="type.label" |
| | | :name="type.value" |
| | |
| | | <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" |
| | |
| | | |
| | | <div class="attachment-list"> |
| | | <el-table |
| | | :data="getAttachmentsByType(type.value)" |
| | | :data="getConfirmAttachments(type.value)" |
| | | size="small" |
| | | v-loading="attachmentLoading" |
| | | style="width: 100%;" |
| | |
| | | 预览 |
| | | </el-button> |
| | | <el-button |
| | | v-if="!isReadOnly" |
| | | size="mini" |
| | | type="danger" |
| | | @click="handleRemoveAttachment(type.value, scope.$index)" |
| | |
| | | </el-table> |
| | | |
| | | <div |
| | | v-if="getAttachmentsByType(type.value).length === 0" |
| | | 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 |
| | |
| | | <!-- 底部操作按钮 --> |
| | | <div style="text-align: center; margin: 30px 0;"> |
| | | <el-button |
| | | v-if="!isReadOnly" |
| | | type="primary" |
| | | @click="handleSave" |
| | | :loading="saveLoading" |
| | |
| | | 保存确认信息 |
| | | </el-button> |
| | | <el-button |
| | | v-if="!isReadOnly" |
| | | type="success" |
| | | @click="accomplish" |
| | | :loading="saveLoading" |
| | |
| | | 确认完成 |
| | | </el-button> |
| | | </div> |
| | | <!-- 家属弹窗 --> |
| | | <el-dialog |
| | | :title="familyDialogTitle" |
| | | :visible.sync="familyDialogVisible" |
| | | width="420px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <el-form :model="currentFamilyMember" label-width="110px"> |
| | | <el-form-item label="姓名" prop="name"> |
| | | <el-input |
| | | v-model="currentFamilyMember.name" |
| | | placeholder="请输入姓名" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="与捐赠者关系" prop="relation"> |
| | | <el-select |
| | | v-model="currentFamilyMember.relation" |
| | | style="width:100%" |
| | | 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> |
| | | |
| | | <template v-if="currentFamilyMember.isMain"> |
| | | <el-form-item label="身份证号" prop="idcard"> |
| | | <el-input |
| | | v-model="currentFamilyMember.idcard" |
| | | placeholder="请输入身份证号" |
| | | maxlength="18" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="联系电话" prop="phone"> |
| | | <el-input |
| | | v-model="currentFamilyMember.phone" |
| | | placeholder="请输入联系电话" |
| | | maxlength="11" |
| | | /> |
| | | </el-form-item> |
| | | </template> |
| | | </el-form> |
| | | |
| | | <span slot="footer"> |
| | | <el-button @click="familyDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="saveFamilyMember">确定</el-button> |
| | | </span> |
| | | </el-dialog> |
| | | |
| | | <!-- 上传对话框 --> |
| | | <el-dialog |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { relativesList, relativesEdit, relativesAdd } from "@/api/businessApi"; |
| | | 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 |
| | | CaseBasicInfo, |
| | | Li_area_select |
| | | }, |
| | | dicts: ["sys_FamilyRelation"], |
| | | dicts: ["sys_FamilyRelation", "sys_KinshipConfirm", "sys_Kinship"], |
| | | data() { |
| | | return { |
| | | caseId: null, |
| | |
| | | coordinatedusernameo: "", |
| | | coordinatedusernamet: "", |
| | | responsibleusername: "", |
| | | acquisitiontissuename: "", |
| | | relativeconfirmationsign: "0", |
| | | signdate: "", |
| | | relativeconfirmationsignname: "", |
| | | signfamilyrelations: "", |
| | | relativeidcardno: "", |
| | | relativephone: "", |
| | | phone: "", |
| | | kinship: [], |
| | | kinshipChildrennum: null, |
| | | relativeRemark: "", |
| | | assessannex: "", |
| | | otherFamilyMembers: "" |
| | | }, |
| | | // 统一亲属列表(第一条为主要亲属,其余为其他亲属) |
| | | familyMemberList: [], |
| | | // 当前编辑的家属(弹窗用) |
| | | currentFamilyMember: { |
| | | familyMemberList: [ |
| | | { |
| | | name: "", |
| | | relation: "", |
| | | phone: "", |
| | | idcard: "", |
| | | isMain: false |
| | | }, |
| | | isEditFamilyMember: false, |
| | | editFamilyMemberIndex: -1, |
| | | familyDialogVisible: false, |
| | | familyDialogTitle: "添加家属", |
| | | 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: { |
| | |
| | | loading: false, |
| | | saveLoading: false, |
| | | infoid: null, |
| | | activeAttachmentType: "1", |
| | | archiveData: null, |
| | | activeAttachmentType: "2", |
| | | activeRegisterType: "1", |
| | | attachmentLoading: false, |
| | | uploadDialogVisible: false, |
| | | uploadLoading: false, |
| | |
| | | currentUploadType: "", |
| | | previewVisible: false, |
| | | currentPreviewFile: null, |
| | | attachmentTypes: [ |
| | | { value: "1", label: "人体器官潜在捐献者登记表" }, |
| | | // 捐献确认阶段附件(可编辑,存储于亲属确认表 assessannex) |
| | | confirmAttachmentTypes: [ |
| | | { value: "2", label: "人体器官捐献亲属确认登记表" }, |
| | | { value: "3", label: "捐献者及直系亲属身份证、户口簿相关证明" }, |
| | | { value: "4", label: "公民身故后人体器官(角膜)遗体捐献告知书" }, |
| | | { value: "5", label: "脑死亡判定知情同意书" }, |
| | | { value: "6", label: "心死亡判定知情同意书" } |
| | | { value: "7", label: "授权委托资料" } |
| | | ], |
| | | attachmentData: { |
| | | "1": [], |
| | | 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.attachmentTypes.find( |
| | | 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() { |
| | |
| | | async getDetail(infoid) { |
| | | this.loading = true; |
| | | try { |
| | | const response = await relativesList({ infoid }); |
| | | 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); |
| | | this.handleDetailData(response, fullRecord); |
| | | await this.loadArchiveInfo(); |
| | | // 再用主键精确拉一次完整记录,确保 kinship 一定能拿到 |
| | | await this.loadFullKinship(); |
| | | } else { |
| | | this.$message.error("获取详情失败:" + (response.msg || "未知错误")); |
| | | } |
| | |
| | | } |
| | | }, |
| | | |
| | | handleDetailData(response) { |
| | | handleDetailData(response, fullRecord) { |
| | | let detailData = null; |
| | | if (response.data) { |
| | | if (Array.isArray(response.data)) { |
| | |
| | | } 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 = { |
| | |
| | | coordinatedusernameo: detailData.coordinatedusernameo || "", |
| | | coordinatedusernamet: detailData.coordinatedusernamet || "", |
| | | responsibleusername: detailData.responsibleusername || "", |
| | | acquisitiontissuename: "", |
| | | relativeconfirmationsign: detailData.relativeconfirmationsign || "0", |
| | | signdate: detailData.signdate, |
| | | 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 || "" |
| | | }; |
| | | |
| | | // 初始化家属列表 |
| | | this.familyMemberList = []; |
| | | if (detailData.relativeconfirmationsignname) { |
| | | this.familyMemberList.push({ |
| | | name: detailData.relativeconfirmationsignname, |
| | | // 主要亲属:优先取 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 || "", |
| | | phone: detailData.relativephone || "", |
| | | idcard: detailData.relativeidcardno || "" |
| | | }); |
| | | idcard: detailData.relativeidcardno || "", |
| | | phone: detailData.phone || detailData.relativephone || "", |
| | | address: "", |
| | | currentResidence: "", |
| | | isMain: true |
| | | }; |
| | | } else if (parsedMembers.length) { |
| | | // 脏数据兜底:主亲属专用字段全空、JSON 里也没有 isMain 标记时, |
| | | // 把第一条当作主亲属,其余作为其他亲属,避免主亲属整块空白 |
| | | mainMember = parsedMembers[0]; |
| | | mainIndex = 0; |
| | | } |
| | | if (detailData.otherFamilyMembers) { |
| | | try { |
| | | const otherMembers = |
| | | typeof detailData.otherFamilyMembers === "string" |
| | | ? JSON.parse(detailData.otherFamilyMembers) |
| | | : detailData.otherFamilyMembers || []; |
| | | otherMembers.forEach(member => { |
| | | } |
| | | this.familyMemberList.push({ |
| | | name: member.name, |
| | | relation: member.relation, |
| | | 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 || "" |
| | | idcard: member.idcard || "", |
| | | address: this.normalizeAddress(member.address), |
| | | currentResidence: this.normalizeAddress(member.currentResidence), |
| | | isMain: false |
| | | }); |
| | | }); |
| | | } catch (e) { |
| | | console.warn("解析其他家属数据失败:", e); |
| | | } |
| | | } |
| | | |
| | | // 字典若已缓存,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() { |
| | | if (this.form.assessannex) { |
| | | 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; |
| | | Object.keys(this.attachmentData).forEach(key => { |
| | | this.attachmentData[key] = []; |
| | | }); |
| | | if (Array.isArray(annexData)) { |
| | | annexData.forEach(attachment => { |
| | | const type = attachment.type || "1"; |
| | | if (this.attachmentData[type]) { |
| | | this.attachmentData[type].push(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); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | getAttachmentsByType(type) { |
| | | return this.attachmentData[type] || []; |
| | | getConfirmAttachments(type) { |
| | | return this.confirmAttachmentData[type] || []; |
| | | }, |
| | | |
| | | getRegisterAttachments(type) { |
| | | return this.registerAttachmentData[type] || []; |
| | | }, |
| | | |
| | | openUploadDialog(type) { |
| | |
| | | uploadTime: this.getCurrentTime(), |
| | | uploader: "当前用户" |
| | | }; |
| | | this.attachmentData[this.currentUploadType].push(newAttachment); |
| | | 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("上传成功"); |
| | |
| | | type: "warning" |
| | | }) |
| | | .then(() => { |
| | | if (this.attachmentData[type] && this.attachmentData[type][index]) { |
| | | this.attachmentData[type].splice(index, 1); |
| | | const container = |
| | | this.confirmAttachmentData[type] || this.registerAttachmentData[type]; |
| | | if (container && container[index]) { |
| | | container.splice(index, 1); |
| | | this.$message.success("附件删除成功"); |
| | | this.updateAssessannexField(); |
| | | } |
| | |
| | | .catch(() => {}); |
| | | }, |
| | | |
| | | // 家属操作方法 |
| | | openFamilyDialog(isMain) { |
| | | this.isEditFamilyMember = false; |
| | | this.editFamilyMemberIndex = -1; |
| | | this.familyDialogTitle = "添加其他家属"; |
| | | |
| | | // 如果还没有任何家属,则添加主要亲属 |
| | | if (this.familyMemberList.length === 0) { |
| | | this.currentFamilyMember = { |
| | | name: "", |
| | | relation: "", |
| | | phone: "", |
| | | idcard: "", |
| | | isMain: true |
| | | }; |
| | | this.familyDialogTitle = "添加主要亲属"; |
| | | } else { |
| | | this.currentFamilyMember = { |
| | | name: "", |
| | | relation: "", |
| | | phone: "", |
| | | idcard: "", |
| | | isMain: false |
| | | }; |
| | | } |
| | | |
| | | this.familyDialogVisible = true; |
| | | }, |
| | | |
| | | editFamilyMember(index) { |
| | | this.isEditFamilyMember = true; |
| | | this.editFamilyMemberIndex = index; |
| | | const member = this.familyMemberList[index]; |
| | | this.currentFamilyMember = { |
| | | name: member.name, |
| | | relation: member.relation, |
| | | phone: member.phone || "", |
| | | idcard: member.idcard || "", |
| | | isMain: index === 0 |
| | | }; |
| | | this.familyDialogTitle = index === 0 ? "编辑主要亲属" : "编辑其他家属"; |
| | | this.familyDialogVisible = true; |
| | | }, |
| | | |
| | | deleteFamilyMember(index) { |
| | | if (index === 0) { |
| | | this.$message.warning("主要亲属不可删除"); |
| | | return; |
| | | } |
| | | this.$confirm("确认删除该家属?", "提示", { type: "warning" }) |
| | | .then(() => { |
| | | this.familyMemberList.splice(index, 1); |
| | | this.updateFamilyMemberField(); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | |
| | | saveFamilyMember() { |
| | | if (!this.currentFamilyMember.name) { |
| | | this.$message.warning("请输入姓名"); |
| | | return; |
| | | } |
| | | if (!this.currentFamilyMember.relation) { |
| | | this.$message.warning("请选择与捐赠者关系"); |
| | | return; |
| | | } |
| | | |
| | | // 如果是主要亲属,验证身份证和电话 |
| | | if (this.currentFamilyMember.isMain) { |
| | | if (!this.currentFamilyMember.idcard) { |
| | | this.$message.warning("请输入主要亲属的身份证号"); |
| | | return; |
| | | } |
| | | if ( |
| | | !/^(\d{15}|\d{18}|\d{17}(\d|X|x))$/.test( |
| | | this.currentFamilyMember.idcard |
| | | // 根据 kinship 勾选自动生成亲属表行,同时保留已有亲属数据(合并,避免丢失) |
| | | syncKinshipRelatives() { |
| | | // 勾选值统一映射到当前字典体系,保证与表格行的 relation 处在同一值域。 |
| | | // 必须去重:历史脏数据里多个不同编码(如 "4"、"6")可能映射成同一个 value, |
| | | // 不去重的话每个重复值都会再生成一遍行,表现就是"行数成倍变多"。 |
| | | const kinship = Array.from( |
| | | new Set( |
| | | (this.form.kinship || []) |
| | | .map(v => this.toCurrentKinshipValue(v)) |
| | | .filter(v => v !== "") |
| | | ) |
| | | ) { |
| | | this.$message.warning("请输入正确的身份证号"); |
| | | return; |
| | | ); |
| | | 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); |
| | | } |
| | | if (!this.currentFamilyMember.phone) { |
| | | this.$message.warning("请输入主要亲属的联系电话"); |
| | | return; |
| | | } |
| | | if (!/^1[3-9]\d{9}$/.test(this.currentFamilyMember.phone)) { |
| | | this.$message.warning("请输入正确的手机号"); |
| | | return; |
| | | } |
| | | } else { |
| | | const n = parseInt(rawCountStr, 10); |
| | | childCount = isNaN(n) || n < 0 ? 0 : n; |
| | | } |
| | | |
| | | const memberData = { |
| | | name: this.currentFamilyMember.name, |
| | | relation: this.currentFamilyMember.relation, |
| | | phone: this.currentFamilyMember.isMain |
| | | ? this.currentFamilyMember.phone |
| | | : "", |
| | | idcard: this.currentFamilyMember.isMain |
| | | ? this.currentFamilyMember.idcard |
| | | : "" |
| | | // 取一行已有数据复用姓名:先按 关系+序号 精确匹配,再退化为同关系顺序取 |
| | | 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]; |
| | | }; |
| | | |
| | | if (this.isEditFamilyMember) { |
| | | this.familyMemberList.splice(this.editFamilyMemberIndex, 1, memberData); |
| | | // 生成一行:命中已有行时整行复用(含 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 { |
| | | this.familyMemberList.push(memberData); |
| | | 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(); |
| | | this.familyDialogVisible = false; |
| | | this.$message.success(this.isEditFamilyMember ? "编辑成功" : "添加成功"); |
| | | |
| | | 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() { |
| | | if (this.familyMemberList.length > 0) { |
| | | const mainMember = this.familyMemberList[0]; |
| | | this.form.relativeconfirmationsignname = mainMember.name; |
| | | this.form.signfamilyrelations = mainMember.relation; |
| | | 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 || ""; |
| | | |
| | | const otherMembers = this.familyMemberList.slice(1).map(member => ({ |
| | | name: member.name, |
| | | relation: member.relation, |
| | | phone: member.phone || "", |
| | | idcard: member.idcard || "" |
| | | })); |
| | | this.form.otherFamilyMembers = JSON.stringify(otherMembers); |
| | | } else { |
| | | this.form.relativeconfirmationsignname = ""; |
| | | this.form.signfamilyrelations = ""; |
| | | this.form.relativeidcardno = ""; |
| | | this.form.phone = ""; |
| | | this.form.relativephone = ""; |
| | | this.form.otherFamilyMembers = "[]"; |
| | | } |
| | | |
| | | // 所有亲属(含主要亲属)统一存入一个 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.attachmentData).forEach(attachments => { |
| | | [ |
| | | ...Object.values(this.confirmAttachmentData), |
| | | ...Object.values(this.registerAttachmentData) |
| | | ].forEach(attachments => { |
| | | allAttachments.push(...attachments); |
| | | }); |
| | | this.form.assessannex = JSON.stringify(allAttachments); |
| | |
| | | }, |
| | | |
| | | async handleSave() { |
| | | if (this.saveLoading) return; |
| | | try { |
| | | await this.$refs.form.validate(); |
| | | this.saveLoading = true; |
| | |
| | | ...this.form, |
| | | infoid: this.infoid, |
| | | organdecision: this.organdecision.join(","), |
| | | organdecisionOther: this.organdecisionOther |
| | | 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 (response.code === 200) { |
| | | // 人体器官获取组织单独保存到档案表 |
| | | await this.saveArchiveAcquisitionTissue(); |
| | | this.$message.success("保存成功"); |
| | | this.$router.push("/case/affirm"); |
| | | } else { |
| | |
| | | } |
| | | } 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; |
| | | } |
| | | } |
| | | } |
| | |
| | | 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: 220px; |
| | | max-width: 100%; |
| | | width: 100%; |
| | | max-width: 220px; |
| | | } |
| | | .paper-form >>> .el-form-item__content .el-date-editor { |
| | | width: 240px; |
| | | max-width: 100%; |
| | | 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> |