WXL
2026-03-19 5aa165156994b1b8a601063fa0a1107d1893ea57
更新
已删除1个文件
已重命名5个文件
已修改17个文件
9157 ■■■■■ 文件已修改
src/api/businessApi/GetWitness.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/businessApi/OrganUtilization.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/MaintainComponents/BloodRoutinePanel.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/components/MaintainComponents/LiverKidneyPanel.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/components/MaintainComponents/UrineRoutinePanel.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/components/assessInfoComponents/AttachmentPreview.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/components/assessInfoComponents/OrganAssessmentForm.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/GetWitness/GetWitnessInfo.vue 1646 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/GetWitness/index.vue 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/GetWitness/organProcurement.js 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/OrganUtilization/index.vue 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/allocation/allocationInfo.vue 1161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/allocation/index.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/assess/assessInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/course/components/DeathJudgmentStage.vue 418 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/course/components/DonationConfirmStage.vue 908 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/course/components/DonorMaintenanceStage.vue 1343 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/course/components/MedicalAssessmentStage.vue 1751 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/course/donationProcess.js 462 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/course/index.vue 714 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/maintain/maintainInfo.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/components/orgselect/index.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/businessApi/GetWitness.js
@@ -0,0 +1,28 @@
import request from '@/utils/request'
// æ¡ˆä¾‹åˆ—表及详情
export function witnessList(data) {
  return request({
    url: '/project/donationwitness/getDonationwitnessBaseInfo',
    method: 'post',
    data: data
  })
}
// èŽ·å–è§è¯ä¿®æ”¹
export function witnessadd(data) {
  return request({
    url: '/project/donationwitness/add',
    method: 'post',
    data: data
  })
}
// èŽ·å–è§è¯ä¿®æ”¹
export function witnessedit(data) {
  return request({
    url: '/project/donationwitness/edit',
    method: 'post',
    data: data
  })
}
src/api/businessApi/OrganUtilization.js
@@ -0,0 +1,28 @@
import request from '@/utils/request'
// æ¡ˆä¾‹åˆ—表及详情
export function completionList(data) {
  return request({
    url: '/project/donatecompletioninfo/getDonationCompletionBaseInfo',
    method: 'post',
    data: data
  })
}
// èŽ·å–è§è¯ä¿®æ”¹
export function completionadd(data) {
  return request({
    url: '/project/donatecompletioninfo/add',
    method: 'post',
    data: data
  })
}
// èŽ·å–è§è¯ä¿®æ”¹
export function completionedit(data) {
  return request({
    url: '/project/donatecompletioninfo/edit',
    method: 'post',
    data: data
  })
}
src/components/MaintainComponents/BloodRoutinePanel.vue
src/components/MaintainComponents/LiverKidneyPanel.vue
src/components/MaintainComponents/UrineRoutinePanel.vue
src/components/assessInfoComponents/AttachmentPreview.vue
src/components/assessInfoComponents/OrganAssessmentForm.vue
src/views/business/GetWitness/GetWitnessInfo.vue
@@ -11,7 +11,8 @@
          <el-button
            type="success"
            @click="handleProcure"
            :disabled="form.procurementStatus === 'procured'"
            :disabled="form.recordstate === 'completed'"
            :loading="confirmLoading"
          >
            ç¡®è®¤èŽ·å–
          </el-button>
@@ -21,26 +22,26 @@
      <el-form :model="form" ref="form" :rules="rules" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="住院号" prop="hospitalNo">
              <el-input v-model="form.hospitalNo" readonly />
            <el-form-item label="住院号" prop="inpatientno">
              <el-input v-model="form.inpatientno" readonly />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="住院号" prop="caseNo">
              <el-input v-model="form.caseNo" readonly />
            <el-form-item label="案例编号" prop="caseNo">
              <el-input v-model="form.caseNo" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="捐献者姓名" prop="donorName">
              <el-input v-model="form.donorName" />
            <el-form-item label="捐献者姓名" prop="name">
              <el-input v-model="form.name" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="性别" prop="gender">
              <el-select v-model="form.gender" style="width: 100%">
            <el-form-item label="性别" prop="sex">
              <el-select v-model="form.sex" style="width: 100%">
                <el-option label="男" value="0" />
                <el-option label="女" value="1" />
              </el-select>
@@ -52,36 +53,96 @@
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="出生日期" prop="birthDate">
              <el-date-picker
                v-model="form.birthDate"
                type="date"
                value-format="yyyy-MM-dd"
                style="width: 100%"
              />
            <el-form-item label="捐献类别" prop="donationcategory">
              <el-select v-model="form.donationcategory" style="width: 100%">
                <el-option
                  v-for="dict in dict.type.sys_DonationCategory || []"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="血型" prop="bloodtype">
              <el-select v-model="form.bloodtype" style="width: 100%">
                <el-option
                  v-for="dict in dict.type.sys_BloodType || []"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="证件号码" prop="idcardno">
              <el-input v-model="form.idcardno" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="记录状态" prop="recordstate">
              <el-input v-model="form.recordstate" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="疾病诊断" prop="diagnosis">
            <el-form-item label="疾病诊断" prop="diagnosisname">
              <el-input
                type="textarea"
                :rows="2"
                v-model="form.diagnosis"
                v-model="form.diagnosisname"
                placeholder="请输入疾病诊断信息"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="获取时间" prop="procurementTime">
            <el-form-item label="死亡时间" prop="deathtime">
              <el-date-picker
                v-model="form.procurementTime"
                v-model="form.deathtime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.procurementStatus !== 'procured'"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- æ‰‹æœ¯ç›¸å…³ä¿¡æ¯ -->
        <el-divider content-position="left">手术信息</el-divider>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="手术医生" prop="operationdoctor">
              <el-input v-model="form.operationdoctor" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="手术开始时间" prop="operationbegtime">
              <el-date-picker
                v-model="form.operationbegtime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="手术结束时间" prop="operationendtime">
              <el-date-picker
                v-model="form.operationendtime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
@@ -89,27 +150,30 @@
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="手术名称" prop="surgeryName">
              <el-input v-model="form.surgeryName" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="手术开始时间" prop="surgeryStartTime">
            <el-form-item
              label="腹主动脉插管时间"
              prop="abdominalaortacannulatime"
            >
              <el-date-picker
                v-model="form.surgeryStartTime"
                v-model="form.abdominalaortacannulatime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="供体死亡时间" prop="donorDeathTime">
            <el-form-item
              label="腹主动脉灌注时间"
              prop="abdominalaortaperfusiontime"
            >
              <el-date-picker
                v-model="form.donorDeathTime"
                v-model="form.abdominalaortaperfusiontime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
@@ -117,51 +181,152 @@
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="腹主动脉插管时间" prop="abdominalAortaCannulationTime">
            <el-form-item
              label="门静脉插管时间"
              prop="portalveincannulatime"
            >
              <el-date-picker
                v-model="form.abdominalAortaCannulationTime"
                v-model="form.portalveincannulatime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="下腔静脉插管时间" prop="inferiorVenaCavaCannulationTime">
            <el-form-item
              label="门静脉灌注时间"
              prop="portalveinperfusiontime"
            >
              <el-date-picker
                v-model="form.inferiorVenaCavaCannulationTime"
                v-model="form.portalveinperfusiontime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- åè°ƒå‘˜ä¿¡æ¯ -->
        <el-divider content-position="left">协调员信息</el-divider>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="协调员姓名" prop="coordinatorName">
              <el-input v-model="form.coordinatorName" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="进手术室时间" prop="coordinatorInOperating">
              <el-date-picker
                v-model="form.coordinatorInOperating"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="肠系膜上静脉插管时间" prop="superiorMesentericVeinCannulationTime">
            <el-form-item label="出手术室时间" prop="coordinatorOutOperating">
              <el-date-picker
                v-model="form.superiorMesentericVeinCannulationTime"
                v-model="form.coordinatorOutOperating"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="登记人" prop="registrant">
              <el-input v-model="form.registrant" />
          <el-col :span="8">
            <el-form-item label="协调员签字" prop="coordinatorSign">
              <el-input v-model="form.coordinatorSign" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="登记时间" prop="registrationTime">
          <el-col :span="8">
            <el-form-item label="签字时间" prop="coordinatorSignTime">
              <el-date-picker
                v-model="form.registrationTime"
                v-model="form.coordinatorSignTime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                readonly
                :disabled="form.recordstate === 'completed'"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- è´Ÿè´£äººä¿¡æ¯ -->
        <el-divider content-position="left">负责人信息</el-divider>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="负责人姓名" prop="responsibleusername">
              <el-input v-model="form.responsibleusername" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="联络人一" prop="coordinatedusernameo">
              <el-input v-model="form.coordinatedusernameo" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="联络人二" prop="coordinatedusernamet">
              <el-input v-model="form.coordinatedusernamet" />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- èŽ·å–æœºæž„ä¿¡æ¯ -->
        <el-divider content-position="left">获取机构信息</el-divider>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="获取机构名称" prop="gainhospitalname">
              <el-input
                v-model="form.gainhospitalname"
                placeholder="请输入获取机构名称"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="获取机构编码" prop="gainhospitalno">
              <el-input
                v-model="form.gainhospitalno"
                placeholder="请输入获取机构编码"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- è®°å½•信息 -->
        <el-divider content-position="left">记录信息</el-divider>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="住院号" prop="donorno">
              <el-input v-model="form.donorno" readonly />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="所在医疗机构" prop="treatmenthospitalname">
              <el-input
                v-model="form.treatmenthospitalname"
                placeholder="请输入医疗机构名称"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="医疗机构编码" prop="treatmenthospitalno">
              <el-input
                v-model="form.treatmenthospitalno"
                placeholder="请输入医疗机构编码"
              />
            </el-form-item>
          </el-col>
@@ -174,8 +339,12 @@
      <div slot="header" class="clearfix">
        <span class="detail-title">器官获取记录</span>
        <div style="float: right;">
          <el-tag :type="form.procurementStatus === 'procured' ? 'success' : 'warning'">
            {{ form.procurementStatus === 'procured' ? '已获取' : '待获取' }}
          <el-tag
            :type="
              form.recordstate === 'completed' ? 'success' : 'warning'
            "
          >
            {{ form.recordstate === "completed" ? "已完成" : "进行中" }}
          </el-tag>
        </div>
      </div>
@@ -188,13 +357,16 @@
      >
        <el-row>
          <el-col>
            <el-form-item label-width="100px" label="获取器官">
              <el-checkbox-group v-model="selectedOrgans" @change="handleOrganSelectionChange">
            <el-form-item label-width="100px" label="捐献器官">
              <el-checkbox-group
                v-model="selectedOrgans"
                @change="handleOrganSelectionChange"
              >
                <el-checkbox
                  v-for="dict in dict.type.sys_Organ || []"
                  :key="dict.value"
                  :label="dict.value"
                  :disabled="form.procurementStatus === 'procured'"
                  :disabled="form.recordstate === 'completed'"
                >
                  {{ dict.label }}
                </el-checkbox>
@@ -207,7 +379,7 @@
          <el-col>
            <el-form-item>
              <el-table
                :data="procurementData.records"
                :data="procurementData.serviceDonationwitnessorgans"
                v-loading="loading"
                border
                style="width: 100%"
@@ -217,11 +389,11 @@
                  label="器官名称"
                  align="center"
                  width="120"
                  prop="organName"
                  prop="organname"
                >
                  <template slot-scope="scope">
                    <el-input
                      v-model="scope.row.organName"
                      v-model="scope.row.organname"
                      placeholder="器官名称"
                      :disabled="true"
                    />
@@ -243,7 +415,7 @@
                      type="datetime"
                      value-format="yyyy-MM-dd HH:mm:ss"
                      placeholder="选择获取开始时间"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    />
                  </template>
                </el-table-column>
@@ -263,7 +435,7 @@
                      type="datetime"
                      value-format="yyyy-MM-dd HH:mm:ss"
                      placeholder="选择器官离体时间"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    />
                  </template>
                </el-table-column>
@@ -279,7 +451,7 @@
                      v-model="scope.row.gainHospitalNo"
                      placeholder="请选择获取医院"
                      style="width: 100%"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                      @change="handleHospitalChange(scope.row, $event)"
                    >
                      <el-option
@@ -302,7 +474,7 @@
                    <el-input
                      v-model="scope.row.organGetDoctor"
                      placeholder="获取医师"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    />
                  </template>
                </el-table-column>
@@ -317,7 +489,7 @@
                    <el-input
                      v-model="scope.row.assistant"
                      placeholder="助手"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    />
                  </template>
                </el-table-column>
@@ -332,7 +504,7 @@
                    <el-input
                      v-model="scope.row.procurementNurse"
                      placeholder="获取护士"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    />
                  </template>
                </el-table-column>
@@ -347,7 +519,7 @@
                    <el-input
                      v-model="scope.row.operatingRoomNurse"
                      placeholder="手术室护士"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    />
                  </template>
                </el-table-column>
@@ -362,7 +534,7 @@
                    <el-input
                      v-model="scope.row.anesthesiologist"
                      placeholder="麻醉医生"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    />
                  </template>
                </el-table-column>
@@ -378,7 +550,7 @@
                      v-model="scope.row.organState"
                      placeholder="请选择获取状态"
                      style="width: 100%"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    >
                      <el-option
                        v-for="dict in organStateList"
@@ -402,7 +574,7 @@
                      clearable
                      v-model="scope.row.notGetReason"
                      placeholder="请输入未获取说明"
                      :disabled="form.procurementStatus === 'procured'"
                      :disabled="form.recordstate === 'completed'"
                    />
                  </template>
                </el-table-column>
@@ -412,7 +584,7 @@
                  align="center"
                  width="120"
                  class-name="small-padding fixed-width"
                  v-if="form.procurementStatus !== 'procured'"
                  v-if="form.recordstate !== 'completed'"
                >
                  <template slot-scope="scope">
                    <el-button
@@ -431,12 +603,17 @@
        </el-row>
        <!-- èŽ·å–ç»Ÿè®¡ä¿¡æ¯ -->
        <div class="procurement-stats" v-if="procurementData.records.length > 0">
        <div
          class="procurement-stats"
          v-if="procurementData.serviceDonationwitnessorgans && procurementData.serviceDonationwitnessorgans.length > 0"
        >
          <el-row :gutter="20">
            <el-col :span="6">
              <div class="stat-item">
                <span class="stat-label">已获取器官:</span>
                <span class="stat-value">{{ procurementData.records.length }} ä¸ª</span>
                <span class="stat-value"
                  >{{ procurementData.serviceDonationwitnessorgans.length }} ä¸ª</span
                >
              </div>
            </el-col>
            <el-col :span="6">
@@ -455,8 +632,18 @@
              <div class="stat-item">
                <span class="stat-label">获取状态:</span>
                <span class="stat-value">
                  <el-tag :type="form.procurementStatus === 'procured' ? 'success' : 'warning'">
                    {{ form.procurementStatus === 'procured' ? '已完成' : '进行中' }}
                  <el-tag
                    :type="
                      form.recordstate === 'completed'
                        ? 'success'
                        : 'warning'
                    "
                  >
                    {{
                      form.recordstate === "completed"
                        ? "已完成"
                        : "进行中"
                    }}
                  </el-tag>
                </span>
              </div>
@@ -471,12 +658,12 @@
        </div>
      </el-form>
      <div class="dialog-footer" v-if="form.procurementStatus !== 'procured'">
      <div class="dialog-footer" v-if="form.recordstate !== 'completed'">
        <el-button
          type="primary"
          @click="handleSaveProcurement"
          :loading="saveLoading"
          :disabled="procurementData.records.length === 0"
          :disabled="!procurementData.serviceDonationwitnessorgans || procurementData.serviceDonationwitnessorgans.length === 0"
        >
          ä¿å­˜èŽ·å–è®°å½•
        </el-button>
@@ -491,70 +678,86 @@
      </div>
    </el-card>
    <!-- é™„件管理部分 -->
    <!-- é™„件管理部分优化 -->
    <el-card class="attachment-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">相关附件</span>
        <el-button
          type="primary"
          size="mini"
          icon="el-icon-upload"
          @click="handleUploadAttachment"
      <div class="attachment-header">
        <i class="el-icon-paperclip"></i>
        <span class="attachment-title">相关附件</span>
        <span class="attachment-tip"
          >支持上传器官获取相关文件 (最多{{ attachmentLimit }}个)</span
        >
          ä¸Šä¼ é™„ä»¶
        </el-button>
      </div>
      <div class="attachment-list">
        <el-table :data="attachments" style="width: 100%">
          <el-table-column label="文件名称" min-width="200">
      <!-- ä½¿ç”¨ UploadAttachment ç»„ä»¶ -->
      <UploadAttachment
        ref="uploadAttachment"
        :file-list="attachmentFileList"
        :limit="attachmentLimit"
        :accept="attachmentAccept"
        :multiple="true"
        @change="handleAttachmentChange"
        @upload-success="handleUploadSuccess"
        @upload-error="handleUploadError"
        @remove="handleAttachmentRemove"
      />
      <!-- é™„件列表展示 -->
      <div
        class="attachment-list"
        v-if="attachments && attachments.length > 0"
      >
        <div class="list-title">已上传附件 ({{ attachments.length }})</div>
        <el-table :data="attachments" style="width: 100%" size="small">
          <el-table-column label="文件名" min-width="200">
            <template slot-scope="scope">
              <div class="file-info">
                <i :class="getFileIcon(scope.row.fileName)" style="margin-right: 8px; color: #409EFF;"></i>
                <span>{{ scope.row.fileName }}</span>
              </div>
              <i
                class="el-icon-document"
                :style="{ color: getFileIconColor(scope.row.fileName) }"
              ></i>
              <span class="file-name">{{ scope.row.fileName }}</span>
            </template>
          </el-table-column>
          <el-table-column label="文件类型" width="100" align="center">
          <el-table-column label="文件类型" width="100">
            <template slot-scope="scope">
              <el-tag size="small">{{ getFileType(scope.row.fileName) }}</el-tag>
              <el-tag :type="getFileTagType(scope.row.fileName)" size="small">
                {{ getFileTypeText(scope.row.fileName) }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="文件大小" width="100" align="center">
          <el-table-column label="上传时间" width="160">
            <template slot-scope="scope">
              <span>{{ formatDateTime(scope.row.uploadTime) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="文件大小" width="100">
            <template slot-scope="scope">
              <span>{{ formatFileSize(scope.row.fileSize) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="上传时间" width="160" align="center">
            <template slot-scope="scope">
              <span>{{ parseTime(scope.row.uploadTime) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="150" align="center">
          <el-table-column label="操作" width="200" fixed="right">
            <template slot-scope="scope">
              <el-button
                size="mini"
                type="text"
                icon="el-icon-view"
                @click="handlePreviewAttachment(scope.row)"
              >预览</el-button>
                type="primary"
                @click="handlePreview(scope.row)"
                :disabled="!isPreviewable(scope.row.fileName)"
              >
                é¢„览
              </el-button>
              <el-button
                size="mini"
                type="text"
                icon="el-icon-download"
                @click="handleDownloadAttachment(scope.row)"
              >下载</el-button>
                type="success"
                @click="handleDownload(scope.row)"
              >
                ä¸‹è½½
              </el-button>
              <el-button
                size="mini"
                type="text"
                icon="el-icon-delete"
                style="color: #F56C6C;"
                @click="handleRemoveAttachment(scope.row)"
              >删除</el-button>
                type="danger"
                @click="handleRemoveAttachment(scope.$index)"
              >
                åˆ é™¤
              </el-button>
            </template>
          </el-table-column>
        </el-table>
@@ -571,7 +774,7 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="器官名称">
              <el-input v-model="currentRecord.organName" readonly />
              <el-input v-model="currentRecord.organname" readonly />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -591,12 +794,18 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="获取医师">
              <el-input v-model="currentRecord.organGetDoctor" placeholder="请输入获取医师" />
              <el-input
                v-model="currentRecord.organGetDoctor"
                placeholder="请输入获取医师"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="助手">
              <el-input v-model="currentRecord.assistant" placeholder="请输入助手姓名" />
              <el-input
                v-model="currentRecord.assistant"
                placeholder="请输入助手姓名"
              />
            </el-form-item>
          </el-col>
        </el-row>
@@ -604,21 +813,33 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="获取护士">
              <el-input v-model="currentRecord.procurementNurse" placeholder="请输入获取护士" />
              <el-input
                v-model="currentRecord.procurementNurse"
                placeholder="请输入获取护士"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="手术室护士">
              <el-input v-model="currentRecord.operatingRoomNurse" placeholder="请输入手术室护士" />
              <el-input
                v-model="currentRecord.operatingRoomNurse"
                placeholder="请输入手术室护士"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="麻醉医生">
          <el-input v-model="currentRecord.anesthesiologist" placeholder="请输入麻醉医生" />
          <el-input
            v-model="currentRecord.anesthesiologist"
            placeholder="请输入麻醉医生"
          />
        </el-form-item>
        <el-form-item label="未获取说明" v-if="currentRecord.organState === '0'">
        <el-form-item
          label="未获取说明"
          v-if="currentRecord.organState === '0'"
        >
          <el-input
            type="textarea"
            :rows="3"
@@ -633,67 +854,108 @@
        <el-button type="primary" @click="handleEditConfirm">确认</el-button>
      </div>
    </el-dialog>
    <!-- æ–‡ä»¶é¢„览弹窗 -->
    <el-dialog
      :title="filePreviewTitle"
      :visible.sync="filePreviewVisible"
      width="800px"
      @close="filePreviewVisible = false"
    >
      <div v-if="currentPreviewFile">
        <div v-if="currentPreviewFile.fileType === 'image'">
          <img :src="currentPreviewFile.fileUrl" style="max-width: 100%; max-height: 500px;" />
        </div>
        <div v-else-if="currentPreviewFile.fileType === 'pdf'">
          <iframe :src="currentPreviewFile.fileUrl" width="100%" height="500px"></iframe>
        </div>
        <div v-else>
          <p>不支持预览此文件类型,请下载查看</p>
        </div>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import {
  getOrganProcurementDetail,
  updateOrganProcurement,
  saveProcurementRecords,
  getHospitalList,
  getCoordinatorList
} from "./organProcurement";
import { witnessList, witnessadd, witnessedit } from "@/api/businessApi";
import UploadAttachment from "@/components/UploadAttachment";
import dayjs from "dayjs";
export default {
  name: "OrganProcurementDetail",
  dicts: ["sys_user_sex", "sys_Organ", "sys_0_1", "sys_DonationCategory"],
  components: {
    UploadAttachment
  },
  dicts: ["sys_BloodType", "sys_DonationCategory", "sys_Organ"],
  data() {
    return {
      // è¡¨å•数据
      form: {
        id: undefined,
        hospitalNo: "",
        infoid: undefined,
        name: "",
        inpatientno: "",
        recordstate: "pending",
        caseNo: "",
        donorName: "",
        gender: "",
        donorno: "",
        treatmenthospitalname: "",
        treatmenthospitalno: "",
        sex: "",
        age: "",
        birthDate: "",
        diagnosis: "",
        procurementStatus: "pending",
        procurementTime: "",
        registrant: "",
        registrationTime: "",
        surgeryName: "",
        surgeryStartTime: "",
        donorDeathTime: "",
        abdominalAortaCannulationTime: "",
        inferiorVenaCavaCannulationTime: "",
        superiorMesentericVeinCannulationTime: "",
        donationCategory: "1",
        deathJudgeDoctor1: "",
        deathJudgeDoctor2: "",
        deathReason: "",
        operationEndTime: "",
        bloodtype: "",
        idcardno: "",
        diagnosisname: "",
        coordinatorName: "",
        gainhospitalno: "",
        gainhospitalname: "",
        deathtime: "",
        deathreason: "",
        deathjudgedocto: "",
        deathjudgedoctt: "",
        deathjudgeannex: "",
        operationbegtime: "",
        operationendtime: "",
        operationdoctor: "",
        isspendremember: 1,
        isrestoreremains: 1,
        rememberannex: "",
        responsibleuserid: "",
        responsibleusername: "",
        coordinateduserido: "",
        coordinatedusernameo: "",
        coordinateduseridt: "",
        coordinatedusernamet: "",
        abdominalaortacannulatime: "",
        abdominalaortaperfusiontime: "",
        portalveincannulatime: "",
        portalveinperfusiontime: "",
        pulmonaryarterycannulatime: "",
        pulmonaryarteryperfusiontime: "",
        aortacannulatime: "",
        aortaperfusiontime: "",
        organdonation: "",
        organdonationOther: "",
        donationcategory: "",
        coordinatorInOperating: "",
        coordinatorOutOperating: "",
        coordinatorSign: "",
        coordinatorSignTime: "",
        responsibleUserName: "",
        coordinatedUserId1: "",
        coordinatedUserId2: "",
        isSpendRemember: 1,
        isRestoreRemains: 1
        attachments: []
      },
      // è¡¨å•验证规则
      rules: {
        donorName: [
        name: [
          { required: true, message: "捐献者姓名不能为空", trigger: "blur" }
        ],
        diagnosis: [
        diagnosisname: [
          { required: true, message: "疾病诊断不能为空", trigger: "blur" }
        ],
        surgeryName: [
          { required: true, message: "手术名称不能为空", trigger: "blur" }
        donationcategory: [
          { required: true, message: "捐献类别不能为空", trigger: "change" }
        ],
        operationdoctor: [
          { required: true, message: "手术医生不能为空", trigger: "blur" }
        ]
      },
      // èŽ·å–è®°å½•éªŒè¯è§„åˆ™
@@ -707,8 +969,6 @@
      selectedOrgans: [],
      // åŒ»é™¢åˆ—表
      hospitalList: [],
      // åè°ƒå‘˜åˆ—表
      coordinatorList: [],
      // å™¨å®˜çŠ¶æ€åˆ—è¡¨
      organStateList: [
        { value: "1", label: "已获取" },
@@ -717,14 +977,22 @@
      ],
      // èŽ·å–è®°å½•æ•°æ®
      procurementData: {
        records: []
        serviceDonationwitnessorgans: []
      },
      // é™„件数据
      attachments: [],
      attachmentFileList: [],
      // é™„件相关配置
      attachmentLimit: 10,
      attachmentAccept: ".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt",
      // ç¼–辑对话框
      editDialogVisible: false,
      currentRecord: {},
      currentEditIndex: -1
      currentEditIndex: -1,
      // æ–‡ä»¶é¢„览相关
      filePreviewVisible: false,
      currentPreviewFile: null,
      filePreviewTitle: ""
    };
  },
  computed: {
@@ -734,7 +1002,8 @@
    },
    // ä¸å®Œæ•´çš„记录数量
    incompleteRecords() {
      return this.procurementData.records.filter(
      if (!this.procurementData.serviceDonationwitnessorgans) return 0;
      return this.procurementData.serviceDonationwitnessorgans.filter(
        record =>
          !record.organStartTime ||
          !record.organGetTime ||
@@ -744,110 +1013,164 @@
    },
    // å”¯ä¸€åŒ»é™¢æ•°é‡
    uniqueHospitals() {
      const hospitals = this.procurementData.records
      if (!this.procurementData.serviceDonationwitnessorgans) return 0;
      const hospitals = this.procurementData.serviceDonationwitnessorgans
        .map(record => record.gainHospitalNo)
        .filter(Boolean);
      return new Set(hospitals).size;
    },
    // èŽ·å–å™¨å®˜å­—å…¸
    organDict() {
      return this.dict.type.sys_Organ || [];
    }
  },
  watch: {
    // ç›‘听附件数据变化
    attachments: {
      handler(newAttachments) {
        this.attachmentFileList = newAttachments.map(item => ({
          uid: item.id || Math.random(),
          name: item.fileName,
          fileSize: item.fileSize,
          url: item.path || item.fileUrl,
          uploadTime: item.uploadTime,
          status: "success"
        }));
      },
      deep: true
    }
  },
  created() {
    const id = this.$route.query.id;
    if (id) {
      this.getDetail(id);
    } else {
      this.generateCaseNo();
      this.form.registrant = this.currentUser.username || "当前用户";
      this.form.registrationTime = new Date()
        .toISOString()
        .replace("T", " ")
        .substring(0, 19);
    }
    this.getHospitalData();
    this.getCoordinatorData();
    this.initData();
  },
  methods: {
    // ç”Ÿæˆä½é™¢å·
    generateCaseNo() {
    // æ ¹æ®å­—å…¸value获取label
    getOrganLabel(organValue) {
      const dictItem = this.organDict.find(item => item.value === organValue);
      return dictItem ? dictItem.label : organValue;
    },
    // åˆå§‹åŒ–数据
    initData() {
      const { id, infoid } = this.$route.query;
      if (!infoid) {
        this.$message.error("缺少必要的路由参数 infoid");
        this.$router.back();
        return;
      }
      this.form.infoid = infoid;
      this.generateDonorNo();
      if (infoid) {
        this.getDetail(infoid);
      }
      this.getHospitalData();
    },
    // ç”ŸæˆæçŒ®è€…编号
    generateDonorNo() {
      const timestamp = Date.now().toString();
      this.form.hospitalNo = "D" + timestamp.slice(-6);
      this.form.caseNo = "C" + timestamp.slice(-6);
      this.form.donorno = "D" + timestamp.slice(-8);
      this.form.caseNo = "CASE" + timestamp.slice(-6);
      this.form.inpatientno = "IP" + timestamp.slice(-6);
    },
    // èŽ·å–è¯¦æƒ…
    getDetail(id) {
    async getDetail(infoid) {
      this.loading = true;
      getOrganProcurementDetail(id)
        .then(response => {
          if (response.code === 200) {
            this.form = response.data;
            if (response.data.procurementRecords) {
              this.procurementData.records = response.data.procurementRecords;
              this.selectedOrgans = response.data.procurementRecords.map(
                item => item.organNo
              );
            }
      try {
        const response = await witnessList({ infoid });
        if (
          response.code === 200 &&
          response.data &&
          response.data.length > 0
        ) {
          const data = response.data[0];
          // å¡«å……表单数据
          Object.assign(this.form, data);
          // åˆå§‹åŒ–附件
          if (this.form.attachments) {
            this.attachments = Array.isArray(this.form.attachments)
              ? [...this.form.attachments]
              : [];
          }
          this.loading = false;
        })
        .catch(error => {
          console.error("获取器官获取详情失败:", error);
          this.loading = false;
          this.$message.error("获取详情失败");
        });
          // å¤„理捐献器官字段
          if (data.organdonation) {
            const organArray = Array.isArray(data.organdonation)
              ? data.organdonation
              : (data.organdonation || "").split(",").filter(item => item);
            this.selectedOrgans = organArray;
            this.form.organdonationOther = data.organdonationOther || "";
          }
          // å¤„理器官获取记录
          if (data.serviceDonationwitnessorgans) {
            this.procurementData.serviceDonationwitnessorgans = Array.isArray(
              data.serviceDonationwitnessorgans
            )
              ? data.serviceDonationwitnessorgans
              : [];
          }
          this.$message.success("数据加载成功");
        } else {
          this.$message.warning("未找到对应的器官获取数据");
        }
      } catch (error) {
        console.error("获取器官获取详情失败:", error);
        this.$message.error("获取详情失败");
      } finally {
        this.loading = false;
      }
    },
    // èŽ·å–åŒ»é™¢æ•°æ®
    getHospitalData() {
      getHospitalList().then(response => {
        if (response.code === 200) {
          this.hospitalList = response.data;
        }
      });
    },
    // èŽ·å–åè°ƒå‘˜æ•°æ®
    getCoordinatorData() {
      getCoordinatorList().then(response => {
        if (response.code === 200) {
          this.coordinatorList = response.data;
        }
      });
    async getHospitalData() {
      try {
        // TODO: æ›¿æ¢ä¸ºå®žé™…的医院列表接口
        // æš‚时使用模拟数据
        this.hospitalList = [
          { hospitalNo: "H001", hospitalName: "北京协和医院" },
          { hospitalNo: "H002", hospitalName: "上海华山医院" },
          { hospitalNo: "H003", hospitalName: "广州中山医院" },
          { hospitalNo: "H004", hospitalName: "武汉同济医院" },
          { hospitalNo: "H005", hospitalName: "成都华西医院" }
        ];
      } catch (error) {
        console.error("获取医院数据失败:", error);
        this.$message.error("获取医院数据失败");
      }
    },
    // å™¨å®˜é€‰æ‹©çŠ¶æ€å˜åŒ–
    handleOrganSelectionChange(selectedValues) {
      const currentOrganNos = this.procurementData.records.map(
        item => item.organNo
      if (!this.procurementData.serviceDonationwitnessorgans) {
        this.procurementData.serviceDonationwitnessorgans = [];
      }
      const currentOrganValues = this.procurementData.serviceDonationwitnessorgans.map(
        item => item.organno
      );
      // å¤„理互斥逻辑
      this.handleExclusiveSelections(selectedValues);
      // æ›´æ–°æçŒ®å™¨å®˜å­—段
      this.form.organdonation = selectedValues.join(",");
      // æ–°å¢žé€‰æ‹©çš„器官
      selectedValues.forEach(organValue => {
        if (!currentOrganNos.includes(organValue)) {
          const organInfo = this.dict.type.sys_Organ.find(
            item => item.value === organValue
          );
          if (organInfo) {
            this.procurementData.records.push({
              organName: organInfo.label,
              organNo: organValue,
              id: null,
              procurementId: this.form.id,
              organStartTime: "",
              organGetTime: "",
              gainHospitalNo: "",
              gainHospitalName: "",
              organGetDoctor: "",
              assistant: "",
              procurementNurse: "",
              operatingRoomNurse: "",
              anesthesiologist: "",
              organState: "1",
              notGetReason: ""
            });
          }
        if (!currentOrganValues.includes(organValue)) {
          this.createOrganRecord(organValue);
        }
      });
      // ç§»é™¤å–消选择的器官
      this.procurementData.records = this.procurementData.records.filter(
      this.procurementData.serviceDonationwitnessorgans = this.procurementData.serviceDonationwitnessorgans.filter(
        record => {
          if (selectedValues.includes(record.organNo)) {
          if (selectedValues.includes(record.organno)) {
            return true;
          } else {
            if (record.id) {
@@ -861,13 +1184,13 @@
                }
              )
                .then(() => {
                  this.procurementData.records = this.procurementData.records.filter(
                    r => r.organNo !== record.organNo
                  this.procurementData.serviceDonationwitnessorgans = this.procurementData.serviceDonationwitnessorgans.filter(
                    r => r.organno !== record.organno
                  );
                  this.$message.success("删除成功");
                })
                .catch(() => {
                  this.selectedOrgans.push(record.organNo);
                  this.selectedOrgans.push(record.organno);
                });
              return true;
            } else {
@@ -877,6 +1200,54 @@
        }
      );
    },
    // å¤„理互斥选择
    handleExclusiveSelections(selectedValues) {
      // å¦‚果选择了"双肾"(假设字典值为C64),自动取消单独的"左肾"(C64L)和"右肾"(C64R)选择
      if (selectedValues.includes("C64")) {
        this.selectedOrgans = selectedValues.filter(
          item => item !== "C64L" && item !== "C64R"
        );
      }
      // å¦‚果选择了"左肾"或"右肾",取消"双肾"选择
      else if (selectedValues.includes("C64L") || selectedValues.includes("C64R")) {
        this.selectedOrgans = selectedValues.filter(item => item !== "C64");
      }
      // å¦‚果选择了"全肺"(假设字典值为C34),自动取消单独的"左肺"(C34L)和"右肺"(C34R)选择
      if (selectedValues.includes("C34")) {
        this.selectedOrgans = selectedValues.filter(
          item => item !== "C34L" && item !== "C34R"
        );
      }
      // å¦‚果选择了"左肺"或"右肺",取消"全肺"选择
      else if (selectedValues.includes("C34L") || selectedValues.includes("C34R")) {
        this.selectedOrgans = selectedValues.filter(item => item !== "C34");
      }
    },
    // åˆ›å»ºå™¨å®˜è®°å½•
    createOrganRecord(organValue) {
      const organName = this.getOrganLabel(organValue);
      this.procurementData.serviceDonationwitnessorgans.push({
        id: null,
        organname: organName,
        organno: organValue,
        organStartTime: "",
        organGetTime: "",
        gainHospitalNo: "",
        gainHospitalName: "",
        organGetDoctor: "",
        assistant: "",
        procurementNurse: "",
        operatingRoomNurse: "",
        anesthesiologist: "",
        organState: "1",
        notGetReason: ""
      });
    },
    // åŒ»é™¢é€‰æ‹©å˜åŒ–
    handleHospitalChange(row, hospitalNo) {
      const hospital = this.hospitalList.find(
@@ -888,8 +1259,8 @@
    },
    // ç¼–辑获取记录
    handleEditProcurement(row) {
      const index = this.procurementData.records.findIndex(
        item => item.organNo === row.organNo
      const index = this.procurementData.serviceDonationwitnessorgans.findIndex(
        item => item.organno === row.organno
      );
      if (index !== -1) {
        this.currentRecord = { ...row };
@@ -900,7 +1271,7 @@
    // ç¡®è®¤ç¼–辑
    handleEditConfirm() {
      if (this.currentEditIndex !== -1) {
        this.procurementData.records[this.currentEditIndex] = {
        this.procurementData.serviceDonationwitnessorgans[this.currentEditIndex] = {
          ...this.currentRecord
        };
        this.$message.success("获取记录更新成功");
@@ -920,60 +1291,77 @@
      return "";
    },
    // ä¿å­˜åŸºæœ¬ä¿¡æ¯
    handleSave() {
      this.$refs.form.validate(valid => {
    async handleSave() {
      this.$refs.form.validate(async valid => {
        if (valid) {
          this.saveLoading = true;
          const apiMethod = this.form.id
            ? updateOrganProcurement
            : addOrganProcurement;
          try {
            const saveData = {
              ...this.form,
              attachments: this.attachments,
              organdonation: this.selectedOrgans.join(","),
              serviceDonationwitnessorgans:
                this.procurementData.serviceDonationwitnessorgans || []
            };
          apiMethod(this.form)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("保存成功");
                if (!this.form.id) {
                  this.form.id = response.data.id;
                  this.$router.replace({
                    query: { ...this.$route.query, id: this.form.id }
                  });
                }
            const apiMethod = this.form.id ? witnessedit : witnessadd;
            const response = await apiMethod(saveData);
            if (response.code === 200) {
              this.$message.success("保存成功");
              if (!this.form.id && response.data && response.data.id) {
                this.form.id = response.data.id;
                this.$router.replace({
                  query: { ...this.$route.query, id: this.form.id }
                });
              }
            })
            .catch(error => {
              console.error("保存失败:", error);
              this.$message.error("保存失败");
            })
            .finally(() => {
              this.saveLoading = false;
            });
            } else {
              this.$message.error("保存失败:" + (response.msg || "未知错误"));
            }
          } catch (error) {
            console.error("保存失败:", error);
            this.$message.error("保存失败");
          } finally {
            this.saveLoading = false;
          }
        }
      });
    },
    // ä¿å­˜èŽ·å–è®°å½•
    handleSaveProcurement() {
    async handleSaveProcurement() {
      if (!this.form.id) {
        this.$message.warning("请先保存基本信息");
        return;
      }
      this.saveLoading = true;
      saveProcurementRecords(this.form.id, this.procurementData.records)
        .then(response => {
          if (response.code === 200) {
            this.$message.success("获取记录保存成功");
          }
        })
        .catch(error => {
          console.error("保存获取记录失败:", error);
          this.$message.error("保存获取记录失败");
        })
        .finally(() => {
          this.saveLoading = false;
        });
      try {
        const saveData = {
          ...this.form,
          attachments: this.attachments,
          organdonation: this.selectedOrgans.join(","),
          serviceDonationwitnessorgans:
            this.procurementData.serviceDonationwitnessorgans || []
        };
        const response = await witnessedit(saveData);
        if (response.code === 200) {
          this.$message.success("获取记录保存成功");
        } else {
          this.$message.error(
            "保存获取记录失败:" + (response.msg || "未知错误")
          );
        }
      } catch (error) {
        console.error("保存获取记录失败:", error);
        this.$message.error("保存获取记录失败");
      } finally {
        this.saveLoading = false;
      }
    },
    // ç¡®è®¤å®ŒæˆèŽ·å–
    handleConfirmProcurement() {
    async handleConfirmProcurement() {
      if (this.incompleteRecords > 0) {
        this.$message.warning("请先完善所有获取记录的信息");
        return;
@@ -984,118 +1372,223 @@
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
        .then(async () => {
          this.confirmLoading = true;
          this.form.procurementStatus = "procured";
          this.form.procurementTime = new Date()
          this.form.recordstate = "completed";
          this.form.operationendtime = this.form.operationendtime || new Date()
            .toISOString()
            .replace("T", " ")
            .substring(0, 19);
          updateOrganProcurement(this.form)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("器官获取已完成");
              }
            })
            .catch(error => {
              console.error("确认获取失败:", error);
              this.$message.error("确认获取失败");
            })
            .finally(() => {
              this.confirmLoading = false;
            });
          try {
            const saveData = {
              ...this.form,
              attachments: this.attachments,
              organdonation: this.selectedOrgans.join(","),
              serviceDonationwitnessorgans:
                this.procurementData.serviceDonationwitnessorgans || []
            };
            const response = await witnessedit(saveData);
            if (response.code === 200) {
              this.$message.success("器官获取已完成");
            } else {
              this.$message.error(
                "确认获取失败:" + (response.msg || "未知错误")
              );
              this.form.recordstate = "pending";
              this.form.operationendtime = "";
            }
          } catch (error) {
            console.error("确认获取失败:", error);
            this.$message.error("确认获取失败");
            this.form.recordstate = "pending";
            this.form.operationendtime = "";
          } finally {
            this.confirmLoading = false;
          }
        })
        .catch(() => {});
    },
    // ä¸Šä¼ é™„ä»¶
    handleUploadAttachment() {
      this.$message.info("附件上传功能");
    // ç¡®è®¤èŽ·å–æŒ‰é’®åˆ«å
    handleProcure() {
      this.handleConfirmProcurement();
    },
    // é¢„览附件
    handlePreviewAttachment(attachment) {
      this.$message.info("附件预览功能");
    // é™„件相关方法
    /** é™„件变化处理 */
    handleAttachmentChange(fileList) {
      this.attachmentFileList = fileList;
    },
    // ä¸‹è½½é™„ä»¶
    handleDownloadAttachment(attachment) {
      this.$message.info("附件下载功能");
    },
    // åˆ é™¤é™„ä»¶
    handleRemoveAttachment(attachment) {
      this.$confirm("确定要删除这个附件吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
    /** é™„件移除处理 */
    handleAttachmentRemove(file) {
      if (file.url) {
        const index = this.attachments.findIndex(
          item => item.path === file.url || item.fileUrl === file.url
        );
        if (index > -1) {
          this.attachments.splice(index, 1);
          this.$message.success("附件删除成功");
        })
        .catch(() => {});
        }
      }
    },
    // èŽ·å–æ–‡ä»¶å›¾æ ‡
    getFileIcon(fileName) {
      const ext = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const iconMap = {
        pdf: "el-icon-document",
        doc: "el-icon-document",
        docx: "el-icon-document",
        xls: "el-icon-document",
        xlsx: "el-icon-document",
        jpg: "el-icon-picture",
        jpeg: "el-icon-picture",
        png: "el-icon-picture"
    /** ä¸Šä¼ æˆåŠŸå¤„ç† */
    handleUploadSuccess({ file, fileList, response }) {
      if (response.code === 200) {
        const attachmentObj = {
          fileName: file.name,
          path: response.fileUrl || file.url,
          fileUrl: response.fileUrl || file.url,
          fileType: this.getFileExtension(file.name),
          fileSize: file.size,
          uploadTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
        };
        if (!Array.isArray(this.attachments)) {
          this.attachments = [];
        }
        this.attachments.push(attachmentObj);
        this.attachmentFileList = fileList;
        this.$message.success("文件上传成功");
      }
    },
    /** ä¸Šä¼ é”™è¯¯å¤„理 */
    handleUploadError({ file, fileList, error }) {
      console.error("附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    /** æ‰‹åŠ¨åˆ é™¤é™„ä»¶ */
    handleRemoveAttachment(index) {
      this.attachments.splice(index, 1);
      this.attachmentFileList.splice(index, 1);
      this.$message.success("附件删除成功");
    },
    /** æ–‡ä»¶é¢„览 */
    handlePreview(file) {
      this.currentPreviewFile = {
        fileName: file.fileName,
        fileUrl: file.path || file.fileUrl,
        fileType: this.getFileType(file.fileName)
      };
      return iconMap[ext] || "el-icon-document";
      this.filePreviewTitle = file.fileName;
      this.filePreviewVisible = true;
    },
    // èŽ·å–æ–‡ä»¶ç±»åž‹
    /** æ–‡ä»¶ä¸‹è½½ */
    handleDownload(file) {
      const fileUrl = file.path || 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) {
      const ext = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const typeMap = {
        pdf: "PDF",
        doc: "DOC",
        docx: "DOCX",
        xls: "XLS",
        xlsx: "XLSX",
        jpg: "JPG",
        jpeg: "JPEG",
        png: "PNG"
      };
      return typeMap[ext] || ext.toUpperCase();
      if (!fileName) return "other";
      const extension = fileName.split(".").pop().toLowerCase();
      const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
      const pdfTypes = ["pdf"];
      const officeTypes = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"];
      if (imageTypes.includes(extension)) return "image";
      if (pdfTypes.includes(extension)) return "pdf";
      if (officeTypes.includes(extension)) return "office";
      return "other";
    },
    // æ–‡ä»¶å¤§å°æ ¼å¼åŒ–
    formatFileSize(size) {
      if (size === 0) return "0 B";
    /** èŽ·å–æ–‡ä»¶å›¾æ ‡é¢œè‰² */
    getFileIconColor(fileName) {
      const type = this.getFileType(fileName);
      const colorMap = {
        image: "#67C23A",
        pdf: "#F56C6C",
        office: "#409EFF",
        other: "#909399"
      };
      return colorMap[type] || "#909399";
    },
    /** èŽ·å–æ–‡ä»¶æ ‡ç­¾ç±»åž‹ */
    getFileTagType(fileName) {
      const type = this.getFileType(fileName);
      const typeMap = {
        image: "success",
        pdf: "danger",
        office: "primary",
        other: "info"
      };
      return typeMap[type] || "info";
    },
    /** èŽ·å–æ–‡ä»¶ç±»åž‹æ–‡æœ¬ */
    getFileTypeText(fileName) {
      const type = this.getFileType(fileName);
      const textMap = {
        image: "图片",
        pdf: "PDF",
        office: "文档",
        other: "其他"
      };
      return textMap[type] || "未知";
    },
    /** æ£€æŸ¥æ˜¯å¦å¯é¢„览 */
    isPreviewable(fileName) {
      const type = this.getFileType(fileName);
      return ["image", "pdf"].includes(type);
    },
    /** èŽ·å–æ–‡ä»¶æ‰©å±•å */
    getFileExtension(filename) {
      return filename.split(".").pop().toLowerCase();
    },
    /** æ ¼å¼åŒ–文件大小 */
    formatFileSize(bytes) {
      if (!bytes || bytes === 0) 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];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
    },
    // æ—¶é—´æ ¼å¼åŒ–
    parseTime(time) {
      if (!time) return "";
      const date = new Date(time);
      return `${date.getFullYear()}-${(date.getMonth() + 1)
        .toString()
        .padStart(2, "0")}-${date
        .getDate()
        .toString()
        .padStart(2, "0")} ${date
        .getHours()
        .toString()
        .padStart(2, "0")}:${date
        .getMinutes()
        .toString()
        .padStart(2, "0")}`;
    /** æ—¥æœŸæ—¶é—´æ ¼å¼åŒ– */
    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;
      }
    }
  }
};
</script>
<style scoped>
.organ-procurement-detail {
  padding: 20px;
@@ -1122,6 +1615,44 @@
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  border: 1px solid #e4e7ed;
  padding: 20px;
  background: #fafafa;
}
.attachment-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid #ebeef5;
}
.attachment-title {
  font-weight: 600;
  color: #303133;
}
.attachment-tip {
  font-size: 12px;
  color: #909399;
  margin-left: auto;
}
.attachment-list {
  margin-top: 16px;
}
.list-title {
  font-weight: bold;
  margin-bottom: 12px;
  color: #303133;
  font-size: 14px;
}
.file-name {
  font-size: 13px;
  margin-left: 8px;
}
.detail-title {
@@ -1193,385 +1724,14 @@
  background-color: #ffecc2 !important;
}
:deep(.el-table .cell) {
  padding: 8px 12px;
  line-height: 1.5;
}
:deep(.el-table th) {
  background-color: #f5f7fa;
  color: #606266;
  font-weight: 600;
}
:deep(.el-table--border) {
  border: 1px solid #e4e7ed;
  border-radius: 4px;
}
:deep(.el-table--border th) {
  border-right: 1px solid #e4e7ed;
}
:deep(.el-table--border td) {
  border-right: 1px solid #e4e7ed;
}
/* è¡¨å•样式优化 */
:deep(.el-form-item__label) {
  font-weight: 500;
  color: #606266;
}
:deep(.el-input__inner) {
  border-radius: 4px;
  border: 1px solid #dcdfe6;
  transition: border-color 0.3s ease;
}
:deep(.el-input__inner:focus) {
  border-color: #409EFF;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
:deep(.el-textarea__inner) {
  border-radius: 4px;
  resize: vertical;
  min-height: 60px;
}
:deep(.el-select) {
  width: 100%;
}
/* æŒ‰é’®æ ·å¼ä¼˜åŒ– */
:deep(.el-button--primary) {
  background: linear-gradient(135deg, #409EFF 0%, #3375e0 100%);
  border: none;
  border-radius: 4px;
  transition: all 0.3s ease;
}
:deep(.el-button--primary:hover) {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}
:deep(.el-button--success) {
  background: linear-gradient(135deg, #67C23A 0%, #529b2f 100%);
  border: none;
  border-radius: 4px;
  transition: all 0.3s ease;
}
:deep(.el-button--success:hover) {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(103, 194, 58, 0.4);
}
:deep(.el-button--warning) {
  background: linear-gradient(135deg, #E6A23C 0%, #d18c2a 100%);
  border: none;
  border-radius: 4px;
  transition: all 0.3s ease;
}
:deep(.el-button--warning:hover) {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(230, 162, 60, 0.4);
}
:deep(.el-button--danger) {
  background: linear-gradient(135deg, #F56C6C 0%, #e05b5b 100%);
  border: none;
  border-radius: 4px;
  transition: all 0.3s ease;
}
:deep(.el-button--danger:hover) {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(245, 108, 108, 0.4);
}
/* æ ‡ç­¾æ ·å¼ */
:deep(.el-tag) {
  border-radius: 12px;
  border: none;
  font-weight: 500;
  padding: 4px 12px;
}
:deep(.el-tag--success) {
  background: linear-gradient(135deg, #67C23A 0%, #529b2f 100%);
  color: white;
}
:deep(.el-tag--warning) {
  background: linear-gradient(135deg, #E6A23C 0%, #d18c2a 100%);
  color: white;
}
/* å¤é€‰æ¡†ç»„样式 */
:deep(.el-checkbox-group) {
  display: flex;
  flex-wrap: wrap;
  gap: 15px;
  margin-top: 10px;
}
:deep(.el-checkbox) {
  margin-right: 0;
}
:deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
  background: linear-gradient(135deg, #409EFF 0%, #3375e0 100%);
  border-color: #409EFF;
}
:deep(.el-checkbox__input.is-checked + .el-checkbox__label) {
  color: #409EFF;
  font-weight: 500;
}
/* æ—¥æœŸé€‰æ‹©å™¨æ ·å¼ */
:deep(.el-date-editor) {
  width: 100%;
}
:deep(.el-picker-panel) {
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
/* åŠ è½½çŠ¶æ€ */
:deep(.el-loading-mask) {
  border-radius: 4px;
}
/* å¡ç‰‡å¤´éƒ¨æ ·å¼ä¼˜åŒ– */
:deep(.el-card__header) {
  background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
  border-bottom: 1px solid #e4e7ed;
  padding: 15px 20px;
}
/* å“åº”式设计 */
@media (max-width: 1200px) {
  .organ-procurement-detail {
    padding: 15px;
  }
  :deep(.el-col) {
    margin-bottom: 10px;
  }
}
@media (max-width: 768px) {
  .organ-procurement-detail {
    padding: 10px;
  }
  .detail-title {
    font-size: 16px;
  }
  .stat-item {
    padding: 5px;
  }
  .stat-label {
    font-size: 10px;
  }
  .stat-value {
    font-size: 14px;
  }
  :deep(.el-table .cell) {
    padding: 4px 8px;
    font-size: 12px;
  }
  :deep(.el-checkbox-group) {
    gap: 8px;
  }
  .dialog-footer .el-button {
    margin: 5px;
    min-width: 100px;
  }
}
@media (max-width: 480px) {
  .organ-procurement-detail {
    padding: 5px;
  }
  :deep(.el-card__header) {
    padding: 10px 15px;
  }
  :deep(.el-form-item__label) {
    font-size: 12px;
  }
  :deep(.el-table) {
    font-size: 11px;
  }
}
/* åŠ¨ç”»æ•ˆæžœ */
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
/* è‡ªå®šä¹‰æ»šåŠ¨æ¡ */
:deep(::-webkit-scrollbar) {
  width: 6px;
  height: 6px;
}
:deep(::-webkit-scrollbar-track) {
  background: #f1f1f1;
  border-radius: 3px;
}
:deep(::-webkit-scrollbar-thumb) {
  background: #c1c1c1;
  border-radius: 3px;
}
:deep(::-webkit-scrollbar-thumb:hover) {
  background: #a8a8a8;
}
/* æ–‡ä»¶ä¿¡æ¯æ ·å¼ */
.file-info {
  display: flex;
  align-items: center;
  padding: 5px 0;
}
.file-info i {
  font-size: 18px;
  margin-right: 10px;
}
/* æ“ä½œæŒ‰é’®ç»„样式 */
:deep(.small-padding .el-button) {
  margin: 0 2px;
  padding: 4px 8px;
}
/* è¡¨å•行间距优化 */
:deep(.el-form-item) {
  margin-bottom: 18px;
}
:deep(.el-row) {
  margin-bottom: 10px;
}
/* å¯¹è¯æ¡†æ ·å¼ä¼˜åŒ– */
:deep(.el-dialog) {
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
:deep(.el-dialog__header) {
  background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
  border-bottom: 1px solid #e4e7ed;
  padding: 15px 20px;
  border-radius: 8px 8px 0 0;
}
:deep(.el-dialog__title) {
  font-weight: 600;
  color: #303133;
}
:deep(.el-dialog__body) {
  padding: 20px;
}
:deep(.el-dialog__footer) {
  padding: 15px 20px;
  border-top: 1px solid #e4e7ed;
}
/* ç‰¹æ®ŠçŠ¶æ€æç¤º */
.procurement-warning {
  background-color: #fff7e6;
  border: 1px solid #ffecc2;
  border-radius: 4px;
  padding: 10px 15px;
  margin: 10px 0;
  color: #e6a23c;
  font-size: 14px;
}
.procurement-success {
  background-color: #f0f9ff;
  border: 1px solid #b3e0ff;
  border-radius: 4px;
  padding: 10px 15px;
  margin: 10px 0;
  color: #409EFF;
  font-size: 14px;
}
/* æ—¶é—´çº¿æ ·å¼ï¼ˆç”¨äºŽæ‰‹æœ¯æ—¶é—´å±•示) */
.procurement-timeline {
  margin: 20px 0;
  padding: 15px;
  background: #f8f9fa;
  border-radius: 4px;
}
.timeline-item {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
  padding: 8px 12px;
  background: white;
  border-radius: 4px;
  border-left: 4px solid #409EFF;
}
.timeline-label {
  font-weight: 500;
  min-width: 120px;
  color: #606266;
}
.timeline-value {
  color: #303133;
  font-weight: 500;
}
/* æ‰“印样式 */
@media print {
  .organ-procurement-detail {
    padding: 0;
    background: white;
  }
  .detail-card,
  .procurement-card,
  .attachment-card {
    box-shadow: none;
    border: 1px solid #ddd;
    margin-bottom: 15px;
    page-break-inside: avoid;
  }
  .dialog-footer,
  .el-button {
    display: none;
  .procurement-stats .el-col {
    margin-bottom: 10px;
  }
}
</style>
src/views/business/GetWitness/index.vue
@@ -8,33 +8,33 @@
        :inline="true"
        label-width="100px"
      >
        <el-form-item label="住院号" prop="hospitalNo">
        <el-form-item label="住院号" prop="inpatientno">
          <el-input
            v-model="queryParams.hospitalNo"
            v-model="queryParams.inpatientno"
            placeholder="请输入住院号"
            clearable
            style="width: 200px"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="捐献者姓名" prop="donorName">
        <el-form-item label="捐献者姓名" prop="name">
          <el-input
            v-model="queryParams.donorName"
            v-model="queryParams.name"
            placeholder="请输入捐献者姓名"
            clearable
            style="width: 200px"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="获取状态" prop="procurementStatus">
        <el-form-item label="获取状态" prop="recordstate">
          <el-select
            v-model="queryParams.procurementStatus"
            v-model="queryParams.recordstate"
            placeholder="请选择获取状态"
            clearable
            style="width: 200px"
          >
            <el-option label="已获取" value="procured" />
            <el-option label="待获取" value="pending" />
            <el-option label="已获取" value="1" />
            <el-option label="待获取" value="0" />
          </el-select>
        </el-form-item>
        <el-form-item>
@@ -60,13 +60,6 @@
            @click="handleUpdate"
            >修改</el-button
          >
          <el-button
            type="danger"
            icon="el-icon-delete"
            :disabled="multiple"
            @click="handleDelete"
            >删除</el-button
          >
        </el-col>
        <el-col :span="8" style="text-align: right">
          <el-tooltip content="刷新" placement="top">
@@ -87,20 +80,20 @@
        <el-table-column
          label="住院号"
          align="center"
          prop="hospitalNo"
          prop="inpatientno"
          width="120"
        />
        <el-table-column
          label="捐献者姓名"
          align="center"
          prop="donorName"
          prop="name"
          width="120"
        />
        <el-table-column label="性别" align="center" prop="gender" width="80">
        <el-table-column label="性别" align="center" prop="sex" width="80">
          <template slot-scope="scope">
            <dict-tag
              :options="dict.type.sys_user_sex"
              :value="parseInt(scope.row.gender)"
              :value="scope.row.sex"
            />
          </template>
        </el-table-column>
@@ -108,56 +101,50 @@
        <el-table-column
          label="疾病诊断"
          align="center"
          prop="diagnosis"
          prop="diagnosisname"
          min-width="180"
          show-overflow-tooltip
        />
        <el-table-column
          label="获取状态"
          align="center"
          prop="procurementStatus"
          prop="recordstate"
          width="100"
        >
          <template slot-scope="scope">
            <el-tag :type="scope.row.procurementStatus === 'procured' ? 'success' : 'warning'">
              {{ scope.row.procurementStatus === 'procured' ? '已获取' : '待获取' }}
            <el-tag
              :type="scope.row.recordstate === '1' ? 'success' : 'warning'"
            >
              {{ scope.row.recordstate === "1" ? "已获取" : "待获取" }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="获取时间"
          label="获取手术时间"
          align="center"
          prop="procurementTime"
          prop="operationendtime"
          width="160"
        >
          <template slot-scope="scope">
            <span>{{
              scope.row.procurementTime
                ? parseTime(scope.row.procurementTime, "{y}-{m}-{d} {h}:{i}")
              scope.row.operationendtime
                ? parseTime(scope.row.operationendtime, "{y}-{m}-{d} {h}:{i}")
                : "-"
            }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="登记人"
          label="手术医生"
          align="center"
          prop="registrant"
          prop="operationdoctor"
          width="100"
        />
        <el-table-column
          label="登记时间"
          label="负责人"
          align="center"
          prop="registrationTime"
          width="160"
        >
          <template slot-scope="scope">
            <span>{{
              scope.row.registrationTime
                ? parseTime(scope.row.registrationTime, "{y}-{m}-{d} {h}:{i}")
                : "-"
            }}</span>
          </template>
        </el-table-column>
          prop="responsibleusername"
          width="100"
        />
        <el-table-column
          label="操作"
          align="center"
@@ -180,14 +167,6 @@
              @click="handleUpdate(scope.row)"
              >修改</el-button
            >
            <el-button
              size="mini"
              type="text"
              icon="el-icon-delete"
              style="color: #F56C6C"
              @click="handleDelete(scope.row)"
              >删除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
@@ -205,7 +184,8 @@
</template>
<script>
import { listOrganProcurement, delOrganProcurement } from "./organProcurement";
// import { witnessList, delWitness } from "@/api/case/witness";
import { witnessList } from "@/api/businessApi";
import Pagination from "@/components/Pagination";
export default {
@@ -230,9 +210,9 @@
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        hospitalNo: undefined,
        donorName: undefined,
        procurementStatus: undefined
        inpatientno: undefined,
        name: undefined,
        recordstate: undefined
      }
    };
  },
@@ -243,13 +223,13 @@
    // æŸ¥è¯¢å™¨å®˜èŽ·å–åˆ—è¡¨
    getList() {
      this.loading = true;
      listOrganProcurement(this.queryParams)
      witnessList(this.queryParams)
        .then(response => {
          if (response.code === 200) {
            this.organProcurementList = response.data.rows;
            this.total = response.data.total;
            this.organProcurementList = response.data;
            this.total = response.total;
          } else {
            this.$message.error("获取数据失败");
            this.$message.error(response.msg || "获取数据失败");
          }
          this.loading = false;
        })
@@ -279,7 +259,7 @@
    handleView(row) {
      this.$router.push({
        path: "/case/GetWitnessInfo",
        query: { id: row.id }
        query: { id: row.id, infoid: row.infoid }
      });
    },
    // æ–°å¢žæŒ‰é’®æ“ä½œ
@@ -288,30 +268,10 @@
    },
    // ä¿®æ”¹æŒ‰é’®æ“ä½œ
    handleUpdate(row) {
      const id = row.id || this.ids[0];
      this.$router.push({
        path: "/case/GetWitnessInfo",
        query: { id: id }
        query: { id: row.id, infoid: row.infoid }
      });
    },
    // åˆ é™¤æŒ‰é’®æ“ä½œ
    handleDelete(row) {
      const ids = row.id ? [row.id] : this.ids;
      this.$confirm("是否确认删除选中的数据项?", "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          return delOrganProcurement(ids);
        })
        .then(response => {
          if (response.code === 200) {
            this.$message.success("删除成功");
            this.getList();
          }
        })
        .catch(() => {});
    },
    // æ—¶é—´æ ¼å¼åŒ–
    parseTime(time, pattern) {
src/views/business/GetWitness/organProcurement.js
ÎļþÒÑɾ³ý
src/views/business/OrganUtilization/index.vue
@@ -8,34 +8,35 @@
        :inline="true"
        label-width="100px"
      >
        <el-form-item label="住院号" prop="hospitalNo">
        <el-form-item label="住院号" prop="inpatientno">
          <el-input
            v-model="queryParams.hospitalNo"
            v-model="queryParams.inpatientno"
            placeholder="请输入住院号"
            clearable
            style="width: 200px"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="捐献者姓名" prop="donorName">
        <el-form-item label="捐献者姓名" prop="name">
          <el-input
            v-model="queryParams.donorName"
            v-model="queryParams.name"
            placeholder="请输入捐献者姓名"
            clearable
            style="width: 200px"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="利用状态" prop="utilizationStatus">
        <el-form-item label="记录状态" prop="recordstate">
          <el-select
            v-model="queryParams.utilizationStatus"
            placeholder="请选择利用状态"
            v-model="queryParams.recordstate"
            placeholder="请选择记录状态"
            clearable
            style="width: 200px"
          >
            <el-option label="已完成" value="completed" />
            <el-option label="进行中" value="in_progress" />
            <el-option label="进行中" value="processing" />
            <el-option label="待处理" value="pending" />
            <el-option label="已关闭" value="closed" />
          </el-select>
        </el-form-item>
        <el-form-item>
@@ -88,61 +89,81 @@
        <el-table-column
          label="住院号"
          align="center"
          prop="hospitalNo"
          prop="inpatientno"
          width="120"
        />
        <el-table-column
          label="案例编号"
          align="center"
          prop="caseNo"
          width="120"
        />
        <el-table-column
          label="捐献者姓名"
          align="center"
          prop="donorName"
          prop="name"
          width="120"
        />
        <el-table-column label="性别" align="center" prop="gender" width="80">
        <el-table-column label="性别" align="center" prop="sex" width="80">
          <template slot-scope="scope">
            <dict-tag
              :options="dict.type.sys_user_sex"
              :value="parseInt(scope.row.gender)"
              :value="scope.row.sex"
            />
          </template>
        </el-table-column>
        <el-table-column label="年龄" align="center" prop="age" width="80" />
        <el-table-column label="血型" align="center" prop="bloodtype" width="80">
          <template slot-scope="scope">
            <dict-tag
              v-if="scope.row.bloodtype"
              :options="dict.type.sys_BloodType"
              :value="scope.row.bloodtype"
            />
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column
          label="疾病诊断"
          align="center"
          prop="diagnosis"
          prop="diagnosisname"
          min-width="180"
          show-overflow-tooltip
        />
        <el-table-column
          label="利用状态"
          label="完成时间"
          align="center"
          prop="utilizationStatus"
          width="100"
        >
          <template slot-scope="scope">
            <el-tag :type="getStatusTagType(scope.row.utilizationStatus)">
              {{ getStatusText(scope.row.utilizationStatus) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="登记人"
          align="center"
          prop="registrant"
          width="100"
        />
        <el-table-column
          label="登记时间"
          align="center"
          prop="registrationTime"
          prop="completetime"
          width="160"
        >
          <template slot-scope="scope">
            <span>{{
              scope.row.registrationTime
                ? parseTime(scope.row.registrationTime, "{y}-{m}-{d} {h}:{i}")
              scope.row.completetime
                ? parseTime(scope.row.completetime, "{y}-{m}-{d} {h}:{i}")
                : "-"
            }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="负责人"
          align="center"
          prop="responsibleusername"
          width="100"
        >
          <template slot-scope="scope">
            <span>{{ scope.row.responsibleusername || "-" }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="记录状态"
          align="center"
          prop="recordstate"
          width="100"
        >
          <template slot-scope="scope">
            <el-tag :type="getStatusTagType(scope.row.recordstate)">
              {{ getStatusText(scope.row.recordstate) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
@@ -192,13 +213,13 @@
</template>
<script>
import { listOrganUtilization, delOrganUtilization } from "./organUtilization";
import { completionList, completionadd, completionedit } from "@/api/businessApi";
import Pagination from "@/components/Pagination";
export default {
  name: "OrganUtilizationList",
  components: { Pagination },
  dicts: ["sys_user_sex"],
  dicts: ["sys_user_sex", "sys_BloodType"],
  data() {
    return {
      // é®ç½©å±‚
@@ -217,9 +238,11 @@
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        hospitalNo: undefined,
        donorName: undefined,
        utilizationStatus: undefined
        inpatientno: undefined,
        name: undefined,
        recordstate: undefined,
        caseNo: undefined,
        donorno: undefined
      }
    };
  },
@@ -228,30 +251,44 @@
  },
  methods: {
    // æŸ¥è¯¢å™¨å®˜åˆ©ç”¨åˆ—表
    getList() {
    async getList() {
      this.loading = true;
      listOrganUtilization(this.queryParams)
        .then(response => {
          if (response.code === 200) {
            this.organUtilizationList = response.data.rows;
            this.total = response.data.total;
      try {
        const response = await completionList(this.queryParams);
        if (response.code === 200) {
          // å¤„理不同格式的响应数据
          let data = response.data;
          if (Array.isArray(data)) {
            this.organUtilizationList = data;
            this.total = data.length;
          } else if (data && data.rows) {
            this.organUtilizationList = data.rows;
            this.total = data.total || data.rows.length;
          } else if (data && data.list) {
            this.organUtilizationList = data.list;
            this.total = data.total || data.list.length;
          } else {
            this.$message.error("获取数据失败");
            this.organUtilizationList = [];
            this.total = 0;
          }
          this.loading = false;
        })
        .catch(error => {
          console.error("获取器官利用列表失败:", error);
          this.loading = false;
          this.$message.error("获取数据失败");
        });
        } else {
          this.$message.error("获取数据失败:" + (response.msg || "未知错误"));
        }
      } catch (error) {
        console.error("获取器官利用列表失败:", error);
        this.$message.error("获取数据失败");
      } finally {
        this.loading = false;
      }
    },
    // èŽ·å–çŠ¶æ€æ ‡ç­¾ç±»åž‹
    getStatusTagType(status) {
      const typeMap = {
        completed: "success",
        in_progress: "warning",
        pending: "info"
        processing: "warning",
        pending: "info",
        closed: "danger"
      };
      return typeMap[status] || "info";
    },
@@ -259,8 +296,9 @@
    getStatusText(status) {
      const textMap = {
        completed: "已完成",
        in_progress: "进行中",
        pending: "待处理"
        processing: "进行中",
        pending: "待处理",
        closed: "已关闭"
      };
      return textMap[status] || "未知";
    },
@@ -284,7 +322,10 @@
    handleView(row) {
      this.$router.push({
        path: "/case/organUtilizationInfo",
        query: { id: row.id }
        query: {
          id: row.id,
          infoid: row.infoid
        }
      });
    },
    // æ–°å¢žæŒ‰é’®æ“ä½œ
@@ -294,9 +335,13 @@
    // ä¿®æ”¹æŒ‰é’®æ“ä½œ
    handleUpdate(row) {
      const id = row.id || this.ids[0];
      const infoid = row.infoid;
      this.$router.push({
        path: "/case/organUtilizationInfo",
        query: { id: id }
        query: {
          id: id,
          infoid: infoid
        }
      });
    },
    // åˆ é™¤æŒ‰é’®æ“ä½œ
@@ -307,14 +352,17 @@
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          return delOrganUtilization(ids);
        })
        .then(response => {
          if (response.code === 200) {
            this.$message.success("删除成功");
            this.getList();
          }
        .then(async () => {
          // TODO: è¿™é‡Œéœ€è¦è°ƒç”¨åˆ é™¤æŽ¥å£ï¼Œä½†ç›®å‰API中没有提供删除接口
          // å¦‚果需要删除功能,需要先确认后端是否有对应的删除接口
          this.$message.warning("删除功能暂未实现,请确认后端接口");
          // ä¸´æ—¶æ¨¡æ‹Ÿåˆ é™¤æˆåŠŸ
          // const response = await completiondelete(ids);
          // if (response.code === 200) {
          //   this.$message.success("删除成功");
          //   this.getList();
          // }
        })
        .catch(() => {});
    },
src/views/business/allocation/allocationInfo.vue
@@ -10,8 +10,9 @@
          </el-button>
          <el-button
            type="success"
            @click="handleAllocate"
            :disabled="form.allocationStatus === 'allocated'"
            @click="handleConfirmAllocation"
            :disabled="form.allocationStatus === '1'"
            :loading="confirmLoading"
          >
            ç¡®è®¤åˆ†é…
          </el-button>
@@ -21,26 +22,26 @@
      <el-form :model="form" ref="form" :rules="rules" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="住院号" prop="hospitalNo">
              <el-input v-model="form.hospitalNo" readonly />
            <el-form-item label="住院号" prop="donorno">
              <el-input v-model="form.donorno" readonly />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="住院号" prop="caseNo">
              <el-input v-model="form.caseNo" readonly />
            <el-form-item label="案例编号" prop="caseNo">
              <el-input v-model="form.caseNo" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="捐献者姓名" prop="donorName">
              <el-input v-model="form.donorName" />
            <el-form-item label="捐献者姓名" prop="name">
              <el-input v-model="form.name" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="性别" prop="gender">
              <el-select v-model="form.gender" style="width: 100%">
            <el-form-item label="性别" prop="sex">
              <el-select v-model="form.sex" style="width: 100%">
                <el-option label="男" value="0" />
                <el-option label="女" value="1" />
              </el-select>
@@ -51,25 +52,52 @@
              <el-input v-model="form.age" />
            </el-form-item>
          </el-col>
          <!-- <el-col :span="8">
            <el-form-item label="捐献类别" prop="donationcategory">
              <el-select v-model="form.donationcategory" style="width: 100%">
                <el-option
                  v-for="dict in dict.type.sys_DonationCategory || []"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                />
              </el-select>
            </el-form-item>
          </el-col> -->
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="出生日期" prop="birthDate">
              <el-date-picker
                v-model="form.birthDate"
                type="date"
                value-format="yyyy-MM-dd"
                style="width: 100%"
              />
            <el-form-item label="血型" prop="bloodtype">
              <el-select v-model="form.bloodtype" style="width: 100%">
                <el-option
                  v-for="dict in dict.type.sys_BloodType || []"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="证件号码" prop="idcardno">
              <el-input v-model="form.idcardno" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="记录状态" prop="recordstate">
              <el-input v-model="form.recordstate" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="疾病诊断" prop="diagnosis">
            <el-form-item label="疾病诊断" prop="diagnosisname">
              <el-input
                type="textarea"
                :rows="2"
                v-model="form.diagnosis"
                v-model="form.diagnosisname"
                placeholder="请输入疾病诊断信息"
              />
            </el-form-item>
@@ -81,7 +109,7 @@
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="form.allocationStatus !== 'allocated'"
                :disabled="form.allocationStatus === '1'"
              />
            </el-form-item>
          </el-col>
@@ -89,8 +117,27 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="登记人" prop="registrant">
              <el-input v-model="form.registrant" />
            <el-form-item label="所在医疗机构" prop="treatmenthospitalname">
              <el-input
                v-model="form.treatmenthospitalname"
                placeholder="请输入医疗机构名称"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="医疗机构编码" prop="treatmenthospitalno">
              <el-input
                v-model="form.treatmenthospitalno"
                placeholder="请输入医疗机构编码"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="登记人" prop="registrationName">
              <el-input v-model="form.registrationName" readonly />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -115,10 +162,20 @@
        <div style="float: right;">
          <el-tag
            :type="
              form.allocationStatus === 'allocated' ? 'success' : 'warning'
              form.allocationStatus === '1'
                ? 'success'
                : form.allocationStatus === '2'
                ? 'danger'
                : 'warning'
            "
          >
            {{ form.allocationStatus === "allocated" ? "已分配" : "待分配" }}
            {{
              form.allocationStatus === "1"
                ? "已分配"
                : form.allocationStatus === "2"
                ? "作废"
                : "未分配"
            }}
          </el-tag>
        </div>
      </div>
@@ -137,14 +194,20 @@
                @change="handleOrganSelectionChange"
              >
                <el-checkbox
                  v-for="organ in organDict"
                  :key="organ.value"
                  :label="organ.value"
                  :disabled="form.allocationStatus === 'allocated'"
                  v-for="dict in dict.type.sys_Organ || []"
                  :key="dict.value"
                  :label="dict.value"
                  :disabled="form.allocationStatus === '1'"
                >
                  {{ organ.label }}
                  {{ dict.label }}
                </el-checkbox>
              </el-checkbox-group>
              <el-input
                v-if="showOtherInput"
                v-model="otherOrganInput"
                placeholder="请输入其他器官名称"
                style="margin-top: 10px; width: 300px;"
              />
            </el-form-item>
          </el-col>
        </el-row>
@@ -153,7 +216,7 @@
          <el-col>
            <el-form-item>
              <el-table
                :data="allocationData.records"
                :data="allocationData.serviceDonateorganList"
                v-loading="loading"
                border
                style="width: 100%"
@@ -163,11 +226,11 @@
                  label="器官名称"
                  align="center"
                  width="120"
                  prop="organName"
                  prop="organname"
                >
                  <template slot-scope="scope">
                    <el-input
                      v-model="scope.row.organName"
                      v-model="scope.row.organname"
                      placeholder="器官名称"
                      :disabled="true"
                    />
@@ -178,13 +241,13 @@
                  label="分配系统编号"
                  align="center"
                  width="150"
                  prop="systemNo"
                  prop="caseno"
                >
                  <template slot-scope="scope">
                    <el-input
                      v-model="scope.row.systemNo"
                      v-model="scope.row.caseno"
                      placeholder="分配系统编号"
                      :disabled="form.allocationStatus === 'allocated'"
                      :disabled="form.allocationStatus === '1'"
                    />
                  </template>
                </el-table-column>
@@ -193,18 +256,18 @@
                  label="分配接收时间"
                  align="center"
                  width="180"
                  prop="applicantTime"
                  prop="applicanttime"
                >
                  <template slot-scope="scope">
                    <el-date-picker
                      clearable
                      size="small"
                      style="width: 100%"
                      v-model="scope.row.applicantTime"
                      v-model="scope.row.applicanttime"
                      type="datetime"
                      value-format="yyyy-MM-dd HH:mm:ss"
                      placeholder="选择分配接收时间"
                      :disabled="form.allocationStatus === 'allocated'"
                      :disabled="form.allocationStatus === '1'"
                    />
                  </template>
                </el-table-column>
@@ -213,13 +276,13 @@
                  label="受体姓氏"
                  align="center"
                  width="120"
                  prop="recipientName"
                  prop="name"
                >
                  <template slot-scope="scope">
                    <el-input
                      v-model="scope.row.recipientName"
                      v-model="scope.row.name"
                      placeholder="受体姓氏"
                      :disabled="form.allocationStatus === 'allocated'"
                      :disabled="form.allocationStatus === '1'"
                    />
                  </template>
                </el-table-column>
@@ -228,39 +291,34 @@
                  label="移植医院"
                  align="center"
                  width="200"
                  prop="transplantHospitalNo"
                  prop="transplanthospitalno"
                >
                  <template slot-scope="scope">
                    <el-select
                      v-model="scope.row.transplantHospitalNo"
                      placeholder="请选择移植医院"
                      style="width: 100%"
                      :disabled="form.allocationStatus === 'allocated'"
                      @change="handleHospitalChange(scope.row, $event)"
                    >
                      <el-option
                        v-for="hospital in hospitalList"
                        :key="hospital.hospitalNo"
                        :label="hospital.hospitalName"
                        :value="hospital.hospitalNo"
                    <div>
                      <org-selecter
                        ref="tranHosSelect"
                        :org-type="'4'"
                        :dataList="dataList"
                        v-model="scope.row.transplanthospitalno"
                        style="width: 100%"
                      />
                    </el-select>
                    </div>
                  </template>
                </el-table-column>
                <el-table-column
                  label="说明"
                  align="center"
                  prop="reallocationReason"
                  prop="reallocationreason"
                  min-width="200"
                >
                  <template slot-scope="scope">
                    <el-input
                      type="textarea"
                      clearable
                      v-model="scope.row.reallocationReason"
                      v-model="scope.row.reallocationreason"
                      placeholder="请输入说明"
                      :disabled="form.allocationStatus === 'allocated'"
                      :disabled="form.allocationStatus === '1'"
                    />
                  </template>
                </el-table-column>
@@ -270,7 +328,7 @@
                  align="center"
                  width="120"
                  class-name="small-padding fixed-width"
                  v-if="form.allocationStatus !== 'allocated'"
                  v-if="form.allocationStatus !== '1'"
                >
                  <template slot-scope="scope">
                    <el-button
@@ -278,7 +336,7 @@
                      type="text"
                      icon="el-icon-copy-document"
                      @click="handleRedistribution(scope.row)"
                      :disabled="!scope.row.systemNo"
                      :disabled="!scope.row.caseno"
                    >
                      é‡åˆ†é…
                    </el-button>
@@ -290,13 +348,19 @@
        </el-row>
        <!-- åˆ†é…ç»Ÿè®¡ä¿¡æ¯ -->
        <div class="allocation-stats" v-if="allocationData.records.length > 0">
        <div
          class="allocation-stats"
          v-if="
            allocationData.serviceDonateorganList &&
              allocationData.serviceDonateorganList.length > 0
          "
        >
          <el-row :gutter="20">
            <el-col :span="6">
              <div class="stat-item">
                <span class="stat-label">已分配器官:</span>
                <span class="stat-value"
                  >{{ allocationData.records.length }} ä¸ª</span
                  >{{ allocationData.serviceDonateorganList.length }} ä¸ª</span
                >
              </div>
            </el-col>
@@ -318,15 +382,19 @@
                <span class="stat-value">
                  <el-tag
                    :type="
                      form.allocationStatus === 'allocated'
                      form.allocationStatus === '1'
                        ? 'success'
                        : form.allocationStatus === '2'
                        ? 'danger'
                        : 'warning'
                    "
                  >
                    {{
                      form.allocationStatus === "allocated"
                        ? "已完成"
                        : "进行中"
                      form.allocationStatus === "1"
                        ? "已分配"
                        : form.allocationStatus === "2"
                        ? "作废"
                        : "未分配"
                    }}
                  </el-tag>
                </span>
@@ -342,12 +410,15 @@
        </div>
      </el-form>
      <div class="dialog-footer" v-if="form.allocationStatus !== 'allocated'">
      <div class="dialog-footer" v-if="form.allocationStatus !== '1'">
        <el-button
          type="primary"
          @click="handleSaveAllocation"
          :loading="saveLoading"
          :disabled="allocationData.records.length === 0"
          :disabled="
            !allocationData.serviceDonateorganList ||
              allocationData.serviceDonateorganList.length === 0
          "
        >
          ä¿å­˜åˆ†é…è®°å½•
        </el-button>
@@ -362,88 +433,89 @@
      </div>
    </el-card>
    <!-- é™„件管理部分 -->
    <!-- é™„件管理部分优化 -->
    <el-card class="attachment-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">相关附件</span>
        <upload-attachment
          :file-list="attachments"
          @change="handleAttachmentChange"
          :limit="10"
          :accept="'.pdf,.jpg,.jpeg,.png,.doc,.docx'"
        />
      <div class="attachment-header">
        <i class="el-icon-paperclip"></i>
        <span class="attachment-title">相关附件</span>
        <span class="attachment-tip"
          >支持上传器官分配相关文件 (最多{{ attachmentLimit }}个)</span
        >
      </div>
      <div class="attachment-list">
        <el-table :data="attachments" style="width: 100%">
          <el-table-column label="文件名称" min-width="200">
      <!-- ä½¿ç”¨ UploadAttachment ç»„ä»¶ -->
      <UploadAttachment
        ref="uploadAttachment"
        :file-list="attachmentFileList"
        :limit="attachmentLimit"
        :accept="attachmentAccept"
        :multiple="true"
        @change="handleAttachmentChange"
        @upload-success="handleUploadSuccess"
        @upload-error="handleUploadError"
        @remove="handleAttachmentRemove"
      />
      <!-- é™„件列表展示 -->
      <div class="attachment-list" v-if="attachments && attachments.length > 0">
        <div class="list-title">已上传附件 ({{ attachments.length }})</div>
        <el-table :data="attachments" style="width: 100%" size="small">
          <el-table-column label="文件名" min-width="200">
            <template slot-scope="scope">
              <div class="file-info">
                <i
                  :class="getFileIcon(scope.row.fileName)"
                  style="margin-right: 8px; color: #409EFF;"
                ></i>
                <span>{{ scope.row.fileName }}</span>
              </div>
              <i
                class="el-icon-document"
                :style="{ color: getFileIconColor(scope.row.fileName) }"
              ></i>
              <span class="file-name">{{ scope.row.fileName }}</span>
            </template>
          </el-table-column>
          <el-table-column label="文件类型" width="100" align="center">
          <el-table-column label="文件类型" width="100">
            <template slot-scope="scope">
              <el-tag size="small">{{
                getFileType(scope.row.fileName)
              }}</el-tag>
              <el-tag :type="getFileTagType(scope.row.fileName)" size="small">
                {{ getFileTypeText(scope.row.fileName) }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="文件大小" width="100" align="center">
          <el-table-column label="上传时间" width="160">
            <template slot-scope="scope">
              <span>{{ formatDateTime(scope.row.uploadTime) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="文件大小" width="100">
            <template slot-scope="scope">
              <span>{{ formatFileSize(scope.row.fileSize) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="上传时间" width="160" align="center">
            <template slot-scope="scope">
              <span>{{ parseTime(scope.row.uploadTime) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="150" align="center">
          <el-table-column label="操作" width="200" fixed="right">
            <template slot-scope="scope">
              <el-button
                size="mini"
                type="text"
                icon="el-icon-view"
                @click="handlePreviewAttachment(scope.row)"
                >预览</el-button
                type="primary"
                @click="handlePreview(scope.row)"
                :disabled="!isPreviewable(scope.row.fileName)"
              >
                é¢„览
              </el-button>
              <el-button
                size="mini"
                type="text"
                icon="el-icon-download"
                type="success"
                @click="handleDownloadAttachment(scope.row)"
                >下载</el-button
              >
                ä¸‹è½½
              </el-button>
              <el-button
                size="mini"
                type="text"
                icon="el-icon-delete"
                style="color: #F56C6C;"
                @click="handleRemoveAttachment(scope.row)"
                >删除</el-button
                type="danger"
                @click="handleRemoveAttachment(scope.$index)"
              >
                åˆ é™¤
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </el-card>
    <!-- é™„件预览对话框 -->
    <attachment-preview
      :visible="attachmentPreviewVisible"
      :file-list="currentAttachmentList"
      :title="attachmentPreviewTitle"
      @close="attachmentPreviewVisible = false"
    />
    <!-- é‡åˆ†é…å¯¹è¯æ¡† -->
    <el-dialog
      title="器官重分配"
@@ -452,7 +524,7 @@
    >
      <el-form :model="redistributionForm" label-width="100px">
        <el-form-item label="原器官信息">
          <el-input v-model="redistributionForm.organName" readonly />
          <el-input v-model="redistributionForm.organname" readonly />
        </el-form-item>
        <el-form-item label="重分配原因" prop="reason">
          <el-input
@@ -470,49 +542,84 @@
        >
      </div>
    </el-dialog>
    <!-- é™„件预览对话框 -->
    <FilePreviewDialog
      :visible="filePreviewVisible"
      :file="currentPreviewFile"
      @close="filePreviewVisible = false"
      @download="handleDownloadAttachment"
    />
  </div>
</template>
<script>
import {
  getOrganAllocationDetail,
  updateOrganAllocation,
  saveAllocationRecords,
  getHospitalList,
  getOrganDict
} from "./organAllocation";
  allocationList,
  allocationadd,
  allocationedit
} from "@/api/businessApi";
import UploadAttachment from "@/components/UploadAttachment";
import AttachmentPreview from "@/components/AttachmentPreview";
import FilePreviewDialog from "@/components/FilePreviewDialog";
import OrgSelecter from "@/views/project/components/orgselect";
import dayjs from "dayjs";
export default {
  name: "OrganAllocationDetail",
  components: {
    UploadAttachment,
    AttachmentPreview,
    OrgSelecter,
    FilePreviewDialog
  },
  dicts: [
    "sys_BloodType",
    "sys_DonationCategory",
    "sys_RecordState",
    "sys_Organ"
  ],
  data() {
    return {
      // è¡¨å•数据
      form: {
        id: undefined,
        hospitalNo: "",
        infoid: undefined,
        donationcategory: "",
        recordstate: "",
        caseNo: "",
        donorName: "",
        gender: "",
        donorno: "",
        treatmenthospitalname: "",
        treatmenthospitalno: "",
        sex: "",
        name: "",
        age: "",
        birthDate: "",
        diagnosis: "",
        allocationStatus: "pending",
        bloodtype: "",
        idcardno: "",
        diagnosisname: "",
        allocationStatus: "0", // 0:未分配;1:已分配;2作废
        allocationTime: "",
        registrant: "",
        registrationTime: ""
        registrationCode: "",
        registrationName: "",
        registrationTime: "",
        attachments: [] // æ·»åŠ é™„ä»¶å­—æ®µ
      },
      // é™„件预览相关
      attachmentPreviewVisible: false,
      currentAttachmentList: [],
      attachmentPreviewTitle: "",
      dataList: [],
      // è¡¨å•验证规则
      rules: {
        donorName: [
        name: [
          { required: true, message: "捐献者姓名不能为空", trigger: "blur" }
        ],
        diagnosis: [
        diagnosisname: [
          { required: true, message: "疾病诊断不能为空", trigger: "blur" }
        ],
        recordstate: [
          { required: true, message: "记录状态不能为空", trigger: "change" }
        ]
      },
      // åˆ†é…è®°å½•验证规则
@@ -522,25 +629,34 @@
      confirmLoading: false,
      // åŠ è½½çŠ¶æ€
      loading: false,
      // é€‰ä¸­çš„器官
      // é€‰ä¸­çš„器官(存储字典value)
      selectedOrgans: [],
      // å™¨å®˜å­—å…¸
      organDict: [],
      // å…¶ä»–器官输入
      otherOrganInput: "",
      // åŒ»é™¢åˆ—表
      hospitalList: [],
      // åˆ†é…è®°å½•数据
      allocationData: {
        records: []
        serviceDonateorganList: []
      },
      // é™„件数据
      attachments: [],
      attachmentFileList: [],
      // é™„件相关配置
      attachmentLimit: 10,
      attachmentAccept:
        ".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt",
      // é‡åˆ†é…å¯¹è¯æ¡†
      redistributionDialogVisible: false,
      redistributionForm: {
        organName: "",
        organname: "",
        reason: ""
      },
      currentRedistributeRecord: null
      currentRedistributeRecord: null,
      // æ–‡ä»¶é¢„览相关
      filePreviewVisible: false,
      currentPreviewFile: null,
      filePreviewTitle: ""
    };
  },
  computed: {
@@ -550,120 +666,218 @@
    },
    // ä¸å®Œæ•´çš„记录数量
    incompleteRecords() {
      return this.allocationData.records.filter(
      if (!this.allocationData.serviceDonateorganList) return 0;
      return this.allocationData.serviceDonateorganList.filter(
        record =>
          !record.systemNo ||
          !record.applicantTime ||
          !record.recipientName ||
          !record.transplantHospitalNo
          !record.caseno ||
          !record.applicanttime ||
          !record.name ||
          !record.transplanthospitalno
      ).length;
    },
    // å”¯ä¸€åŒ»é™¢æ•°é‡
    uniqueHospitals() {
      const hospitals = this.allocationData.records
        .map(record => record.transplantHospitalNo)
      if (!this.allocationData.serviceDonateorganList) return 0;
      const hospitals = this.allocationData.serviceDonateorganList
        .map(record => record.transplanthospitalno)
        .filter(Boolean);
      return new Set(hospitals).size;
    },
    // èŽ·å–å™¨å®˜å­—å…¸
    organDict() {
      return this.dict.type.sys_Organ || [];
    },
    // åˆ¤æ–­æ˜¯å¦éœ€è¦æ˜¾ç¤ºå…¶ä»–输入框
    showOtherInput() {
      return this.selectedOrgans.includes("C01"); // å‡è®¾"其他"的字典值是C01
    }
  },
  watch: {
    // ç›‘听附件数据变化
    attachments: {
      handler(newAttachments) {
        this.attachmentFileList = newAttachments.map(item => ({
          uid: item.id || Math.random(),
          name: item.fileName,
          fileSize: item.fileSize,
          url: item.path || item.fileUrl,
          uploadTime: item.uploadTime,
          status: "success"
        }));
      },
      deep: true
    },
    // ç›‘听其他器官输入变化
    otherOrganInput(newValue) {
      if (newValue && this.selectedOrgans.includes("C01")) {
        // æ›´æ–°å™¨å®˜å­—典中的"其他"标签显示
        const otherRecord = this.allocationData.serviceDonateorganList.find(
          item => item.organname && item.organname.includes("其他")
        );
        if (otherRecord) {
          otherRecord.organname = `其他(${newValue})`;
        }
      }
    }
  },
  created() {
    const id = this.$route.query.id;
    if (id) {
      this.getDetail(id);
    } else {
      this.generateCaseNo();
      this.form.registrant = this.currentUser.username || "当前用户";
    this.initData();
  },
  methods: {
    // æ ¹æ®å­—å…¸value获取label
    getOrganLabel(organValue) {
      const dictItem = this.organDict.find(item => item.value === organValue);
      return dictItem ? dictItem.label : organValue;
    },
    // åˆå§‹åŒ–数据
    initData() {
      const { id, infoid } = this.$route.query;
      if (!infoid) {
        this.$message.error("缺少必要的路由参数 infoid");
        this.$router.back();
        return;
      }
      this.form.infoid = infoid;
      this.form.registrationName =
        this.currentUser.nickName || this.currentUser.username || "当前用户";
      this.form.registrationTime = new Date()
        .toISOString()
        .replace("T", " ")
        .substring(0, 19);
    }
    this.getOrganDictionary();
    this.getHospitalData();
  },
  methods: {
    // ç”Ÿæˆä½é™¢å·
    generateCaseNo() {
      if (infoid) {
        this.getDetail(infoid);
      } else {
        this.generateDonorNo();
      }
      this.getHospitalData();
    },
    // ç”ŸæˆæçŒ®è€…编号
    generateDonorNo() {
      const timestamp = Date.now().toString();
      this.form.hospitalNo = "D" + timestamp.slice(-6);
      this.form.caseNo = "C" + timestamp.slice(-6);
      this.form.donorno = "D" + timestamp.slice(-8);
      this.form.caseNo = "CASE" + timestamp.slice(-6);
    },
    // èŽ·å–è¯¦æƒ…
    getDetail(id) {
    async getDetail(infoid) {
      this.loading = true;
      getOrganAllocationDetail(id)
        .then(response => {
          if (response.code === 200) {
            this.form = response.data;
            if (response.data.allocationRecords) {
              this.allocationData.records = response.data.allocationRecords;
              this.selectedOrgans = response.data.allocationRecords.map(
                item => item.organNo
              );
      try {
        const response = await allocationList({ infoid });
        if (
          response.code === 200 &&
          response.data &&
          response.data.length > 0
        ) {
          const data = response.data[0];
          // å¡«å……表单数据
          Object.assign(this.form, data);
          // åˆå§‹åŒ–附件
          if (this.form.attachments) {
            this.attachments = Array.isArray(this.form.attachments)
              ? [...this.form.attachments]
              : [];
          }
          // å¤„理分配记录
          if (data.serviceDonateorganList) {
            this.allocationData.serviceDonateorganList = Array.isArray(
              data.serviceDonateorganList
            )
              ? data.serviceDonateorganList
              : [];
            // æ›´æ–°é€‰ä¸­çš„器官
            this.selectedOrgans = this.allocationData.serviceDonateorganList
              .map(item => {
                // ä»ŽåŽç«¯æ•°æ®ä¸­èŽ·å–å™¨å®˜çš„value
                if (item.organno) {
                  return item.organno;
                }
                // å¦‚果后端只有器官名称,尝试从字典中匹配
                if (item.organname) {
                  const dictItem = this.organDict.find(
                    org =>
                      org.label === item.organname ||
                      (item.organname && item.organname.includes(org.label))
                  );
                  return dictItem ? dictItem.value : null;
                }
                return null;
              })
              .filter(Boolean);
            // å¤„理"其他"器官
            const otherRecord = this.allocationData.serviceDonateorganList.find(
              item => item.organname && item.organname.includes("其他(")
            );
            if (otherRecord) {
              const match = otherRecord.organname.match(/其他\((.*?)\)/);
              if (match) {
                this.otherOrganInput = match[1];
              }
            }
          }
          this.loading = false;
        })
        .catch(error => {
          console.error("获取器官分配详情失败:", error);
          this.loading = false;
          this.$message.error("获取详情失败");
        });
    },
    // èŽ·å–å™¨å®˜å­—å…¸
    getOrganDictionary() {
      getOrganDict().then(response => {
        if (response.code === 200) {
          this.organDict = response.data;
          this.$message.success("数据加载成功");
        } else {
          this.$message.warning("未找到对应的器官分配数据");
        }
      });
      } catch (error) {
        console.error("获取器官分配详情失败:", error);
        this.$message.error("获取详情失败");
      } finally {
        this.loading = false;
      }
    },
    // èŽ·å–åŒ»é™¢æ•°æ®
    getHospitalData() {
      getHospitalList().then(response => {
        if (response.code === 200) {
          this.hospitalList = response.data;
        }
      });
    },
      handleAttachmentChange(fileList) {
console.log(fileList,'测试');
    async getHospitalData() {
      try {
        // TODO: æ›¿æ¢ä¸ºå®žé™…的医院列表接口
        // æš‚时使用模拟数据
        this.hospitalList = [
          { hospitalNo: "H001", hospitalName: "北京协和医院" },
          { hospitalNo: "H002", hospitalName: "上海华山医院" },
          { hospitalNo: "H003", hospitalName: "广州中山医院" },
          { hospitalNo: "H004", hospitalName: "武汉同济医院" },
          { hospitalNo: "H005", hospitalName: "成都华西医院" }
        ];
      } catch (error) {
        console.error("获取医院数据失败:", error);
        this.$message.error("获取医院数据失败");
      }
    },
    // å™¨å®˜é€‰æ‹©çŠ¶æ€å˜åŒ–
    handleOrganSelectionChange(selectedValues) {
      const currentOrganNos = this.allocationData.records.map(
        item => item.organNo
      if (!this.allocationData.serviceDonateorganList) {
        this.allocationData.serviceDonateorganList = [];
      }
      const currentOrganValues = this.allocationData.serviceDonateorganList.map(
        item => item.organno
      );
      // å¤„理互斥逻辑(参考捐献决定页面)
      this.handleExclusiveSelections(selectedValues);
      // æ–°å¢žé€‰æ‹©çš„器官
      selectedValues.forEach(organValue => {
        if (!currentOrganNos.includes(organValue)) {
          const organInfo = this.organDict.find(
            item => item.value === organValue
          );
          if (organInfo) {
            this.allocationData.records.push({
              organName: organInfo.label,
              organNo: organValue,
              id: null,
              allocationId: this.form.id,
              systemNo: "",
              applicantTime: "",
              recipientName: "",
              transplantHospitalNo: "",
              transplantHospitalName: "",
              reallocationReason: "",
              organState: 1
            });
          }
        if (!currentOrganValues.includes(organValue)) {
          this.createOrganRecord(organValue);
        }
      });
      // ç§»é™¤å–消选择的器官
      this.allocationData.records = this.allocationData.records.filter(
      this.allocationData.serviceDonateorganList = this.allocationData.serviceDonateorganList.filter(
        record => {
          if (selectedValues.includes(record.organNo)) {
          if (selectedValues.includes(record.organno)) {
            return true;
          } else {
            if (record.id) {
@@ -677,14 +891,13 @@
                }
              )
                .then(() => {
                  // å®žé™…项目中这里应该调用删除API
                  this.allocationData.records = this.allocationData.records.filter(
                    r => r.organNo !== record.organNo
                  this.allocationData.serviceDonateorganList = this.allocationData.serviceDonateorganList.filter(
                    r => r.organno !== record.organno
                  );
                  this.$message.success("删除成功");
                })
                .catch(() => {
                  this.selectedOrgans.push(record.organNo);
                  this.selectedOrgans.push(record.organno);
                });
              return true; // ç­‰å¾…用户确认
            } else {
@@ -694,6 +907,61 @@
        }
      );
    },
    // å¤„理互斥选择(参考捐献决定页面)
    handleExclusiveSelections(selectedValues) {
      // å¦‚果选择了"双肾"(假设字典值为C64),自动取消单独的"左肾"(C64L)和"右肾"(C64R)选择
      if (selectedValues.includes("C64")) {
        this.selectedOrgans = selectedValues.filter(
          item => item !== "C64L" && item !== "C64R"
        );
      }
      // å¦‚果选择了"左肾"或"右肾",取消"双肾"选择
      else if (
        selectedValues.includes("C64L") ||
        selectedValues.includes("C64R")
      ) {
        this.selectedOrgans = selectedValues.filter(item => item !== "C64");
      }
      // å¦‚果选择了"全肺"(假设字典值为C34),自动取消单独的"左肺"(C34L)和"右肺"(C34R)选择
      if (selectedValues.includes("C34")) {
        this.selectedOrgans = selectedValues.filter(
          item => item !== "C34L" && item !== "C34R"
        );
      }
      // å¦‚果选择了"左肺"或"右肺",取消"全肺"选择
      else if (
        selectedValues.includes("C34L") ||
        selectedValues.includes("C34R")
      ) {
        this.selectedOrgans = selectedValues.filter(item => item !== "C34");
      }
    },
    // åˆ›å»ºå™¨å®˜è®°å½•
    createOrganRecord(organValue) {
      let organName = this.getOrganLabel(organValue);
      // å¦‚果是"其他"器官且有输入值
      if (organValue === "C01" && this.otherOrganInput) {
        organName = `其他(${this.otherOrganInput})`;
      }
      this.allocationData.serviceDonateorganList.push({
        id: null,
        organname: organName,
        organno: organValue,
        caseno: "",
        applicanttime: "",
        name: "",
        transplanthospitalno: "",
        transplantHospitalName: "",
        reallocationreason: "",
        organState: 1
      });
    },
    // åŒ»é™¢é€‰æ‹©å˜åŒ–
    handleHospitalChange(row, hospitalNo) {
      const hospital = this.hospitalList.find(
@@ -706,8 +974,8 @@
    // é‡åˆ†é…æ“ä½œ
    handleRedistribution(row) {
      this.currentRedistributeRecord = row;
      this.redistributionForm.organName = row.organName;
      this.redistributionForm.reason = row.reallocationReason || "";
      this.redistributionForm.organname = row.organname;
      this.redistributionForm.reason = row.reallocationreason || "";
      this.redistributionDialogVisible = true;
    },
    // ç¡®è®¤é‡åˆ†é…
@@ -718,7 +986,7 @@
      }
      if (this.currentRedistributeRecord) {
        this.currentRedistributeRecord.reallocationReason = this.redistributionForm.reason;
        this.currentRedistributeRecord.reallocationreason = this.redistributionForm.reason;
        this.$message.success("重分配原因已更新");
        this.redistributionDialogVisible = false;
      }
@@ -726,70 +994,89 @@
    // å™¨å®˜è¡Œæ ·å¼
    getOrganRowClassName({ row }) {
      if (
        !row.systemNo ||
        !row.applicantTime ||
        !row.recipientName ||
        !row.transplantHospitalNo
        !row.caseno ||
        !row.applicanttime ||
        !row.name ||
        !row.transplanthospitalno
      ) {
        return "warning-row";
      }
      return "";
    },
    // ä¿å­˜åŸºæœ¬ä¿¡æ¯
    handleSave() {
      this.$refs.form.validate(valid => {
        if (valid) {
          this.saveLoading = true;
          const apiMethod = this.form.id
            ? updateOrganAllocation
            : addOrganAllocation;
    async handleSave() {
      this.$refs.form.validate(async valid => {
        if (!valid) {
          this.$message.warning("请完善表单信息");
          return;
        }
          apiMethod(this.form)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("保存成功");
                if (!this.form.id) {
                  this.form.id = response.data.id;
                  this.$router.replace({
                    query: { ...this.$route.query, id: this.form.id }
                  });
                }
              }
            })
            .catch(error => {
              console.error("保存失败:", error);
              this.$message.error("保存失败");
            })
            .finally(() => {
              this.saveLoading = false;
            });
        this.saveLoading = true;
        try {
          const saveData = {
            ...this.form,
            attachments: this.attachments,
            serviceDonateorganList:
              this.allocationData.serviceDonateorganList || []
          };
          console.log(this.form.recordstate);
          this.form.recordstate = 1;
          const apiMethod = this.form.id ? allocationedit : allocationadd;
          const response = await apiMethod(saveData);
          if (response.code === 200) {
            this.$message.success("保存成功");
            if (!this.form.id && response.data && response.data.id) {
              this.form.id = response.data.id;
              this.$router.replace({
                query: { ...this.$route.query, id: this.form.id }
              });
            }
          } else {
            this.$message.error("保存失败:" + (response.msg || "未知错误"));
          }
        } catch (error) {
          console.error("保存失败:", error);
          this.$message.error("保存失败");
        } finally {
          this.saveLoading = false;
        }
      });
    },
    // ä¿å­˜åˆ†é…è®°å½•
    handleSaveAllocation() {
    async handleSaveAllocation() {
      if (!this.form.id) {
        this.$message.warning("请先保存基本信息");
        return;
      }
      this.saveLoading = true;
      saveAllocationRecords(this.form.id, this.allocationData.records)
        .then(response => {
          if (response.code === 200) {
            this.$message.success("分配记录保存成功");
          }
        })
        .catch(error => {
          console.error("保存分配记录失败:", error);
          this.$message.error("保存分配记录失败");
        })
        .finally(() => {
          this.saveLoading = false;
        });
      try {
        const saveData = {
          ...this.form,
          attachments: this.attachments,
          serviceDonateorganList:
            this.allocationData.serviceDonateorganList || []
        };
        const response = await allocationedit(saveData);
        if (response.code === 200) {
          this.$message.success("分配记录保存成功");
        } else {
          this.$message.error(
            "保存分配记录失败:" + (response.msg || "未知错误")
          );
        }
      } catch (error) {
        console.error("保存分配记录失败:", error);
        this.$message.error("保存分配记录失败");
      } finally {
        this.saveLoading = false;
      }
    },
    // ç¡®è®¤å®Œæˆåˆ†é…
    handleConfirmAllocation() {
    async handleConfirmAllocation() {
      if (this.incompleteRecords > 0) {
        this.$message.warning("请先完善所有分配记录的信息");
        return;
@@ -800,117 +1087,217 @@
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
        .then(async () => {
          this.confirmLoading = true;
          this.form.allocationStatus = "allocated";
          this.form.allocationStatus = "1";
          this.form.allocationTime = new Date()
            .toISOString()
            .replace("T", " ")
            .substring(0, 19);
          updateOrganAllocation(this.form)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("器官分配已完成");
              }
            })
            .catch(error => {
              console.error("确认分配失败:", error);
              this.$message.error("确认分配失败");
            })
            .finally(() => {
              this.confirmLoading = false;
            });
          try {
            const saveData = {
              ...this.form,
              attachments: this.attachments,
              serviceDonateorganList:
                this.allocationData.serviceDonateorganList || []
            };
            const response = await allocationedit(saveData);
            if (response.code === 200) {
              this.$message.success("器官分配已完成");
            } else {
              this.$message.error(
                "确认分配失败:" + (response.msg || "未知错误")
              );
              this.form.allocationStatus = "0";
              this.form.allocationTime = "";
            }
          } catch (error) {
            console.error("确认分配失败:", error);
            this.$message.error("确认分配失败");
            this.form.allocationStatus = "0";
            this.form.allocationTime = "";
          } finally {
            this.confirmLoading = false;
          }
        })
        .catch(() => {});
    },
    // ä¸Šä¼ é™„ä»¶
    handleUploadAttachment() {
      // é™„件上传逻辑
      this.$message.info("附件上传功能");
    // é™„件相关方法
    /** é™„件变化处理 */
    handleAttachmentChange(fileList) {
      this.attachmentFileList = fileList;
    },
    // é¢„览附件
    handlePreviewAttachment(attachment) {
      // é™„件预览逻辑
      this.$message.info("附件预览功能");
    },
    // ä¸‹è½½é™„ä»¶
    handleDownloadAttachment(attachment) {
      // é™„件下载逻辑
      this.$message.info("附件下载功能");
    },
    // åˆ é™¤é™„ä»¶
    handleRemoveAttachment(attachment) {
      this.$confirm("确定要删除这个附件吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
    /** é™„件移除处理 */
    handleAttachmentRemove(file) {
      if (file.url) {
        const index = this.attachments.findIndex(
          item => item.path === file.url || item.fileUrl === file.url
        );
        if (index > -1) {
          this.attachments.splice(index, 1);
          this.$message.success("附件删除成功");
        })
        .catch(() => {});
        }
      }
    },
    // èŽ·å–æ–‡ä»¶å›¾æ ‡
    getFileIcon(fileName) {
      const ext = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const iconMap = {
        pdf: "el-icon-document",
        doc: "el-icon-document",
        docx: "el-icon-document",
        xls: "el-icon-document",
        xlsx: "el-icon-document",
        jpg: "el-icon-picture",
        jpeg: "el-icon-picture",
        png: "el-icon-picture"
    /** ä¸Šä¼ æˆåŠŸå¤„ç† */
    handleUploadSuccess({ file, fileList, response }) {
      if (response.code === 200) {
        const attachmentObj = {
          fileName: file.name,
          path: response.fileUrl || file.url,
          fileUrl: response.fileUrl || file.url,
          fileType: this.getFileExtension(file.name),
          fileSize: file.size,
          uploadTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
        };
        if (!Array.isArray(this.attachments)) {
          this.attachments = [];
        }
        this.attachments.push(attachmentObj);
        this.attachmentFileList = fileList;
        this.$message.success("文件上传成功");
      }
    },
    /** ä¸Šä¼ é”™è¯¯å¤„理 */
    handleUploadError({ file, fileList, error }) {
      console.error("附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    /** æ‰‹åŠ¨åˆ é™¤é™„ä»¶ */
    handleRemoveAttachment(index) {
      this.attachments.splice(index, 1);
      this.attachmentFileList.splice(index, 1);
      this.$message.success("附件删除成功");
    },
    /** æ–‡ä»¶é¢„览 */
    handlePreview(file) {
      this.currentPreviewFile = {
        fileName: file.fileName,
        fileUrl: file.path || file.fileUrl,
        fileType: this.getFileType(file.fileName)
      };
      return iconMap[ext] || "el-icon-document";
      // this.filePreviewTitle = file.fileName;
      this.filePreviewVisible = true;
    },
    // èŽ·å–æ–‡ä»¶ç±»åž‹
    handleDownloadAttachment(file) {
      const fileUrl = file.path || 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) {
      const ext = fileName
      if (!fileName) return "other";
      const extension = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const typeMap = {
        pdf: "PDF",
        doc: "DOC",
        docx: "DOCX",
        xls: "XLS",
        xlsx: "XLSX",
        jpg: "JPG",
        jpeg: "JPEG",
        png: "PNG"
      };
      return typeMap[ext] || ext.toUpperCase();
      const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
      const pdfTypes = ["pdf"];
      const officeTypes = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"];
      if (imageTypes.includes(extension)) return "image";
      if (pdfTypes.includes(extension)) return "pdf";
      if (officeTypes.includes(extension)) return "office";
      return "other";
    },
    // æ–‡ä»¶å¤§å°æ ¼å¼åŒ–
    formatFileSize(size) {
      if (size === 0) return "0 B";
    /** èŽ·å–æ–‡ä»¶å›¾æ ‡é¢œè‰² */
    getFileIconColor(fileName) {
      const type = this.getFileType(fileName);
      const colorMap = {
        image: "#67C23A",
        pdf: "#F56C6C",
        office: "#409EFF",
        other: "#909399"
      };
      return colorMap[type] || "#909399";
    },
    /** èŽ·å–æ–‡ä»¶æ ‡ç­¾ç±»åž‹ */
    getFileTagType(fileName) {
      const type = this.getFileType(fileName);
      const typeMap = {
        image: "success",
        pdf: "danger",
        office: "primary",
        other: "info"
      };
      return typeMap[type] || "info";
    },
    /** èŽ·å–æ–‡ä»¶ç±»åž‹æ–‡æœ¬ */
    getFileTypeText(fileName) {
      const type = this.getFileType(fileName);
      const textMap = {
        image: "图片",
        pdf: "PDF",
        office: "文档",
        other: "其他"
      };
      return textMap[type] || "未知";
    },
    /** æ£€æŸ¥æ˜¯å¦å¯é¢„览 */
    isPreviewable(fileName) {
      const type = this.getFileType(fileName);
      return ["image", "pdf"].includes(type);
    },
    /** èŽ·å–æ–‡ä»¶æ‰©å±•å */
    getFileExtension(filename) {
      return filename
        .split(".")
        .pop()
        .toLowerCase();
    },
    /** æ ¼å¼åŒ–文件大小 */
    formatFileSize(bytes) {
      if (!bytes || bytes === 0) 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];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
    },
    // æ—¶é—´æ ¼å¼åŒ–
    parseTime(time) {
      if (!time) return "";
      const date = new Date(time);
      return `${date.getFullYear()}-${(date.getMonth() + 1)
        .toString()
        .padStart(2, "0")}-${date
        .getDate()
        .toString()
        .padStart(2, "0")} ${date
        .getHours()
        .toString()
        .padStart(2, "0")}:${date
        .getMinutes()
        .toString()
        .padStart(2, "0")}`;
    /** æ—¥æœŸæ—¶é—´æ ¼å¼åŒ– */
    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;
      }
    }
  }
};
@@ -938,6 +1325,44 @@
  margin-bottom: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  padding: 20px;
  background: #fafafa;
}
.attachment-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid #ebeef5;
}
.attachment-title {
  font-weight: 600;
  color: #303133;
}
.attachment-tip {
  font-size: 12px;
  color: #909399;
  margin-left: auto;
}
.attachment-list {
  margin-top: 16px;
}
.list-title {
  font-weight: bold;
  margin-bottom: 12px;
  color: #303133;
  font-size: 14px;
}
.file-name {
  font-size: 13px;
  margin-left: 8px;
}
.detail-title {
@@ -964,17 +1389,13 @@
}
.stat-label {
  /* font-size: 12px; */
  opacity: 0.9;
  /* color: #606266; */
  margin-bottom: 5px;
}
.stat-value {
  font-size: 20px;
  font-weight: bold;
  /* color: #303133; */
}
/* ç©ºçŠ¶æ€æ ·å¼ */
src/views/business/allocation/index.vue
@@ -121,12 +121,7 @@
          prop="donorno"
          width="120"
        />
        <el-table-column
          label="姓名"
          align="center"
          prop="name"
          width="100"
        />
        <el-table-column label="姓名" align="center" prop="name" width="100" />
        <el-table-column
          label="住院号"
          align="center"
@@ -263,7 +258,7 @@
</template>
<script>
import { allocationList,  } from "@/api/businessApi";
import { allocationList } from "@/api/businessApi";
import Pagination from "@/components/Pagination";
export default {
@@ -315,9 +310,14 @@
      delete params.allocationTimeRange;
      // å¦‚果有时间范围参数
      if (this.queryParams.allocationTimeRange && this.queryParams.allocationTimeRange.length === 2) {
        params.allocationTimeStart = this.queryParams.allocationTimeRange[0] + ' 00:00:00';
        params.allocationTimeEnd = this.queryParams.allocationTimeRange[1] + ' 23:59:59';
      if (
        this.queryParams.allocationTimeRange &&
        this.queryParams.allocationTimeRange.length === 2
      ) {
        params.allocationTimeStart =
          this.queryParams.allocationTimeRange[0] + " 00:00:00";
        params.allocationTimeEnd =
          this.queryParams.allocationTimeRange[1] + " 23:59:59";
      }
      allocationList(params)
@@ -357,7 +357,7 @@
    handleView(row) {
      this.$router.push({
        path: "/case/allocationInfo",
        query: { id: row.infoid }
        query: { id: row.id, infoid: row.infoid }
      });
    },
    // æ–°å¢žæŒ‰é’®æ“ä½œ
@@ -366,15 +366,15 @@
    },
    // ä¿®æ”¹æŒ‰é’®æ“ä½œ
    handleUpdate(row) {
      const id = row.infoid || this.ids[0];
      this.$router.push({
        path: "/case/allocationInfo",
        query: { id: id }
        query: { id: row.id, infoid: row.infoid }
      });
    },
    // åˆ é™¤æŒ‰é’®æ“ä½œ
    handleDelete(row) {
      const selectedRow = row || (this.selectedRows.length === 1 ? this.selectedRows[0] : null);
      const selectedRow =
        row || (this.selectedRows.length === 1 ? this.selectedRows[0] : null);
      if (!selectedRow) {
        this.$message.warning("请选择一条记录");
        return;
src/views/business/assess/assessInfo.vue
@@ -428,7 +428,7 @@
  assessAdd
} from "@/api/businessApi/index";
import FilePreviewDialog from "@/components/FilePreviewDialog";
import OrganAssessmentForm from "./components/OrganAssessmentForm.vue";
import OrganAssessmentForm from "@/components/assessInfoComponents/OrganAssessmentForm.vue";
export default {
  name: "AssessmentDetail",
src/views/business/course/components/DeathJudgmentStage.vue
@@ -4,14 +4,70 @@
      <!-- åŸºç¡€ä¿¡æ¯ -->
      <div slot="header" class="clearfix">
        <span class="detail-title">死亡判定基本信息</span>
        <el-button
          v-if="isEdit"
          type="success"
          style="float: right"
          @click="handleSave"
          :loading="saveLoading"
        >
          ä¿å­˜ä¿¡æ¯
        </el-button>
        <el-button
          v-else
          type="primary"
          style="float: right"
          @click="handleEdit"
        >
          ç¼–辑信息
        </el-button>
      </div>
      <el-form :model="form" ref="form" :rules="rules" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="死亡原因" prop="deathReason">
            <el-form-item label="捐献者编号" prop="donorno">
              <el-input
                v-model="form.donorno"
                :readonly="!isEdit"
                placeholder="自动生成捐献者编号"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="捐献者姓名" prop="name">
              <el-input v-model="form.name" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="性别" prop="sex">
              <el-select
                v-model="form.deathReason"
                v-model="form.sex"
                :disabled="!isEdit"
                style="width: 100%"
              >
                <el-option label="男" value="1" />
                <el-option label="女" value="2" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="年龄" prop="age">
              <el-input v-model="form.age" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="疾病诊断" prop="diagnosisname">
              <el-input v-model="form.diagnosisname" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="死亡原因" prop="deathreason">
              <el-select
                v-model="form.deathreason"
                :disabled="!isEdit"
                style="width: 100%"
              >
@@ -25,9 +81,9 @@
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="死亡时间" prop="deathTime">
            <el-form-item label="死亡时间" prop="deathtime">
              <el-date-picker
                v-model="form.deathTime"
                v-model="form.deathtime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
@@ -36,20 +92,63 @@
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="判定医生一" prop="judgmentDoctor">
              <el-input v-model="form.judgmentDoctorone" :readonly="!isEdit" />
            <el-form-item label="判定医生一" prop="deathjudgedocto">
              <el-input v-model="form.deathjudgedocto" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="判定医生二" prop="judgmentDoctor">
              <el-input v-model="form.judgmentDoctortwo" :readonly="!isEdit" />
            <el-form-item label="判定医生二" prop="deathjudgedoctt">
              <el-input v-model="form.deathjudgedoctt" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="登记人" prop="registrant">
              <el-input v-model="form.registrant" :readonly="!isEdit" />
            <el-form-item label="器官获取机构" prop="gainhospitalname">
              <el-input v-model="form.gainhospitalname" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="是否默哀缅怀" prop="isspendremember">
              <el-select
                v-model="form.isspendremember"
                :disabled="!isEdit"
                style="width: 100%"
              >
                <el-option label="是" :value="1" />
                <el-option label="否" :value="0" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="是否恢复遗体仪容" prop="isrestoreremains">
              <el-select
                v-model="form.isrestoreremains"
                :disabled="!isEdit"
                style="width: 100%"
              >
                <el-option label="是" :value="1" />
                <el-option label="否" :value="0" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="负责人" prop="responsibleusername">
              <el-input
                v-model="form.responsibleusername"
                :readonly="!isEdit"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="记录状态" prop="recordstate">
              <el-tag :type="getStatusTag(form.recordstate)">
                {{ getStatusText(form.recordstate) }}
              </el-tag>
            </el-form-item>
          </el-col>
        </el-row>
@@ -70,7 +169,7 @@
    <el-card class="attachment-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">死亡判定评估表附件</span>
        <el-button
        <!-- <el-button
          v-if="isEdit"
          type="primary"
          size="mini"
@@ -78,7 +177,7 @@
          :loading="uploadLoading"
        >
          ä¸Šä¼ é™„ä»¶
        </el-button>
        </el-button> -->
      </div>
      <!-- é™„件类型选项卡 -->
@@ -203,12 +302,14 @@
        ref="uploadRef"
        class="upload-demo"
        drag
        action="#"
        :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>
@@ -236,13 +337,17 @@
</template>
<script>
import {
  getDeathJudgmentDetail,
  updateDeathJudgment
} from "./api/mockDeathJudgmentApi";
import { deathinfoedit, queryDathInfoBaseInfo } from "@/api/businessApi";
import { getToken } from "@/utils/auth";
export default {
  name: "DeathJudgmentDetail",
  props: {
    infoid: {
      type: String,
      default: true
    }
  },
  data() {
    return {
      // æ˜¯å¦ç¼–辑模式
@@ -252,31 +357,39 @@
      // è¡¨å•数据
      form: {
        id: undefined,
        hospitalNo: "",
        donorName: "",
        gender: "",
        infoid: undefined,
        donorno: "",
        name: "",
        sex: "",
        age: "",
        diagnosis: "",
        deathReason: "",
        deathTime: "",
        judgmentDoctor: "",
        judgmentDescription: "",
        registrant: "",
        registrationTime: ""
        diagnosisname: "",
        deathreason: "",
        deathtime: "",
        deathjudgedocto: "",
        deathjudgedoctt: "",
        gainhospitalno: "",
        gainhospitalname: "",
        isspendremember: 0,
        isrestoreremains: 0,
        rememberannex: "",
        responsibleuserid: "",
        responsibleusername: "",
        recordstate: "0",
        judgmentDescription: ""
      },
      // è¡¨å•验证规则
      rules: {
        donorName: [
        name: [
          { required: true, message: "捐献者姓名不能为空", trigger: "blur" }
        ],
        deathReason: [
        deathreason: [
          { required: true, message: "死亡原因不能为空", trigger: "change" }
        ],
        deathTime: [
        deathtime: [
          { required: true, message: "死亡时间不能为空", trigger: "change" }
        ],
        judgmentDoctor: [
          { required: true, message: "判定医生不能为空", trigger: "blur" }
        deathjudgedocto: [
          { required: true, message: "判定医生一不能为空", trigger: "blur" }
        ]
      },
      // é™„件相关数据
@@ -286,7 +399,10 @@
      uploadLoading: false,
      tempFileList: [],
      currentUploadType: "",
      uploadAction: process.env.VUE_APP_BASE_API + "/common/upload",
      headers: {
        Authorization: "Bearer " + getToken()
      },
      // è¯„估表类型定义
      attachmentTypes: [
        { value: "1", label: "脑死亡判定表" },
@@ -311,58 +427,89 @@
    }
  },
  created() {
    const id = this.$route.query.id;
    this.isEdit =
      this.$route.path.includes("/edit") || this.$route.path.includes("/add");
    if (id && !this.$route.path.includes("/add")) {
      this.getDetail(id);
    } else if (this.$route.path.includes("/add")) {
      this.generateHospitalNo();
    }
    this.getDetail(this.infoid);
    this.getAttachmentList();
  },
  methods: {
    // ç”Ÿæˆä½é™¢å·
    generateHospitalNo() {
      // æ¨¡æ‹Ÿç”Ÿæˆä½é™¢å·ï¼šD + æ—¶é—´æˆ³åŽ6位
    // ç”ŸæˆæçŒ®è€…编号
    generateDonorNo() {
      const timestamp = Date.now().toString();
      this.form.hospitalNo = "D" + timestamp.slice(-6);
      this.form.donorno = "DONOR" + timestamp.slice(-8);
    },
    // èŽ·å–è¯¦æƒ…
    getDetail(id) {
      getDeathJudgmentDetail(id).then(response => {
        if (response.code === 200) {
          this.form = response.data;
    async getDetail(id) {
      try {
        const response = await queryDathInfoBaseInfo({ infoid: id });
        let realData = {};
        if (response && response.data) {
          realData = response.data[0];
        } else if (response) {
          realData = response;
        }
      });
        // æ˜ å°„字段到表单
        this.form = {
          ...this.form,
          ...realData,
          // ç¡®ä¿æ•°å€¼ç±»åž‹å­—段正确转换
          isspendremember: realData.isspendremember
            ? parseInt(realData.isspendremember)
            : 0,
          isrestoreremains: realData.isrestoreremains
            ? parseInt(realData.isrestoreremains)
            : 0
        };
        // è§£æžé™„件信息
        if (realData.rememberannex) {
          this.parseAttachmentData(realData.rememberannex);
        }
      } catch (error) {
        console.error("获取死亡判定详情失败:", error);
        this.$message.error("数据加载失败");
      }
    },
    // è§£æžé™„件数据
    parseAttachmentData(attachmentJson) {
      try {
        if (attachmentJson) {
          const attachments = JSON.parse(attachmentJson);
          if (Array.isArray(attachments)) {
            this.attachmentList = attachments;
          }
        }
      } catch (error) {
        console.error("解析附件数据失败:", error);
      }
    },
    // æž„建附件JSON数据
    buildAttachmentJson() {
      return JSON.stringify(this.attachmentList);
    },
    // èŽ·å–çŠ¶æ€æ ‡ç­¾æ ·å¼
    getStatusTag(status) {
      const statusMap = {
        "0": "warning", // ç»´æŠ¤ä¸­
        "1": "success", // å·²å®Œæˆ
        "99": "danger" // å·²ç»ˆæ­¢
      };
      return statusMap[status] || "info";
    },
    // èŽ·å–çŠ¶æ€æ–‡æœ¬
    getStatusText(status) {
      const textMap = {
        "0": "维护中",
        "1": "已完成",
        "99": "已终止"
      };
      return textMap[status] || "未知状态";
    },
    // èŽ·å–é™„ä»¶åˆ—è¡¨
    getAttachmentList() {
      this.attachmentLoading = true;
      // æ¨¡æ‹Ÿé™„件数据 - å®žé™…项目中从接口获取
      // å®žé™…项目中从接口获取附件数据
      setTimeout(() => {
        this.attachmentList = [
          {
            id: 1,
            type: "1",
            typeName: "脑死亡判定表",
            fileName: "脑死亡判定表_202512001.pdf",
            fileSize: 2548321,
            uploadTime: "2025-12-01 10:30:00",
            uploader: "张医生",
            fileUrl: "/attachments/brain_death_1.pdf"
          },
          {
            id: 2,
            type: "2",
            typeName: "脑电图评估表",
            fileName: "脑电图评估表_202512001.docx",
            fileSize: 512345,
            uploadTime: "2025-12-01 14:20:00",
            uploader: "李医生",
            fileUrl: "/attachments/eeg_1.docx"
          }
        ];
        this.attachmentLoading = false;
      }, 500);
    },
@@ -413,7 +560,6 @@
      const maxSize = 10 * 1024 * 1024; // 10MB
      // æ ¡éªŒæ–‡ä»¶ç±»åž‹
      const isTypeOk =
        allowedTypes.includes(file.type) ||
        file.name.endsWith(".pdf") ||
@@ -432,7 +578,6 @@
        return false;
      }
      // æ ¡éªŒæ–‡ä»¶å¤§å°
      if (file.size > maxSize) {
        this.$message.error("文件大小不能超过10MB");
        return false;
@@ -448,17 +593,11 @@
    handleTempRemove(file, fileList) {
      this.tempFileList = fileList;
    },
    // æäº¤ä¸Šä¼ 
    async submitUpload() {
      if (this.tempFileList.length === 0) {
        this.$message.warning("请先选择要上传的文件");
        return;
      }
      this.uploadLoading = true;
      try {
        // æ¨¡æ‹Ÿä¸Šä¼ è¿‡ç¨‹ - å®žé™…项目中调用上传接口
    /** ä¸Šä¼ æˆåŠŸå¤„ç† */
    handleUploadSuccess(response, file, fileList) {
      if (response.code === 200) {
        file.url = response.data || response.url;
        this.$message.success("文件上传成功");
        for (const file of this.tempFileList) {
          const newAttachment = {
            id: Date.now() + Math.random(),
@@ -468,21 +607,60 @@
            fileSize: file.size,
            uploadTime: new Date().toISOString(),
            uploader: "当前用户",
            fileUrl: URL.createObjectURL(file.raw)
            fileUrl: file.url
          };
          this.attachmentList.push(newAttachment);
        }
        // æ›´æ–°é™„ä»¶JSON数据到表单
        this.form.rememberannex = this.buildAttachmentJson();
        this.$message.success("文件上传成功");
        this.uploadDialogVisible = false;
        this.tempFileList = [];
      } catch (error) {
        this.$message.error("文件上传失败");
        console.error("上传失败:", error);
      } finally {
        this.uploadLoading = false;
        this.tempFileList = [];
      } else {
        this.$message.error(response.msg || "文件上传失败");
      }
    },
    // æäº¤ä¸Šä¼ 
    async submitUpload() {
      if (this.tempFileList.length === 0) {
        this.$message.warning("请先选择要上传的文件");
        return;
      }
      this.$refs.uploadRef.submit();
      this.uploadLoading = true;
      // try {
      //   for (const file of this.tempFileList) {
      //     const newAttachment = {
      //       id: Date.now() + Math.random(),
      //       type: this.currentUploadType,
      //       typeName: this.getCurrentTypeLabel,
      //       fileName: file.name,
      //       fileSize: file.size,
      //       uploadTime: new Date().toISOString(),
      //       uploader: "当前用户",
      //       fileUrl: URL.createObjectURL(file.raw)
      //     };
      //     this.attachmentList.push(newAttachment);
      //   }
      //   // æ›´æ–°é™„ä»¶JSON数据到表单
      //   this.form.rememberannex = this.buildAttachmentJson();
      //   this.$message.success("文件上传成功");
      //   this.uploadDialogVisible = false;
      //   this.tempFileList = [];
      // } catch (error) {
      //   this.$message.error("文件上传失败");
      //   console.error("上传失败:", error);
      // } finally {
      //   this.uploadLoading = false;
      // }
    },
    // åˆ é™¤é™„ä»¶
    handleRemoveAttachment(attachment) {
@@ -497,6 +675,8 @@
          );
          if (index !== -1) {
            this.attachmentList.splice(index, 1);
            // æ›´æ–°é™„ä»¶JSON数据到表单
            this.form.rememberannex = this.buildAttachmentJson();
            this.$message.success("评估表删除成功");
          }
        })
@@ -521,38 +701,48 @@
    },
    // ä¸‹è½½é™„ä»¶
    handleDownload(attachment) {
      // å®žé™…项目中调用下载接口
      const link = document.createElement("a");
      link.href = attachment.fileUrl;
      link.download = attachment.fileName;
      link.click();
      this.$message.success(`开始下载: ${attachment.fileName}`);
    },
    // ç¼–辑信息
    handleEdit() {
      this.isEdit = true;
    },
    // ä¿å­˜ä¿¡æ¯
    handleSave() {
      this.$refs.form.validate(valid => {
    async handleSave() {
      this.$refs.form.validate(async valid => {
        if (valid) {
          this.saveLoading = true;
          // æ¨¡æ‹Ÿä¿å­˜è¿‡ç¨‹
          updateDeathJudgment(this.form)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("保存成功");
                if (this.$route.path.includes("/add")) {
                  this.$router.push("/case/deathJudgment");
                } else {
                  this.isEdit = false;
                }
          try {
            // æž„建提交数据
            const submitData = {
              ...this.form,
              // ç¡®ä¿é™„件数据最新
              rememberannex: this.buildAttachmentJson()
            };
            const response = await deathinfoedit(submitData);
            if (response.code === 200) {
              this.$message.success("保存成功");
              this.isEdit = false;
              if (this.$route.path.includes("/add")) {
                this.$router.push("/case/deathJudgment");
              }
            })
            .catch(error => {
              console.error("保存失败:", error);
              this.$message.error("保存失败");
            })
            .finally(() => {
              this.saveLoading = false;
            });
            } else {
              this.$message.error(response.message || "保存失败");
            }
          } catch (error) {
            console.error("保存失败:", error);
            this.$message.error("保存失败");
          } finally {
            this.saveLoading = false;
          }
        }
      });
    },
src/views/business/course/components/DonationConfirmStage.vue
@@ -5,87 +5,192 @@
      <div slot="header" class="clearfix">
        <span class="detail-title">捐献确认基本信息</span>
        <el-button
          v-if="$route.query.confirm"
          type="primary"
          style="float: right; padding: 3px 0"
          type="success"
          style="float: right;"
          @click="handleSave"
          :loading="saveLoading"
        >
          ä¿å­˜ç¡®è®¤ä¿¡æ¯
        </el-button>
      </div>
      <el-form :model="form" ref="form" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="协调员1" prop="coordinator1">
              <el-input v-model="form.coordinator1" :readonly="!isEdit" />
            <el-form-item label="住院号" prop="caseNo">
              <el-input v-model="form.caseNo" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="协调员2" prop="coordinator2">
              <el-input v-model="form.coordinator2" :readonly="!isEdit" />
            <el-form-item label="捐献者姓名" prop="name">
              <el-input v-model="form.name" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="业务人员" prop="assignee">
              <el-input v-model="form.assignee" :readonly="!isEdit" />
            <el-form-item label="性别" prop="sex">
              <el-select v-model="form.sex" style="width: 100%">
                <el-option label="男" value="1" />
                <el-option label="女" value="2" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="确认状态" prop="confirmationStatus">
            <el-form-item label="年龄" prop="age">
              <el-input v-model="form.age" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="疾病诊断" prop="diagnosisname">
              <el-input v-model="form.diagnosisname" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="所在医疗机构" prop="treatmenthospitalname">
              <el-input v-model="form.treatmenthospitalname" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="协调员1" prop="coordinatedusernameo">
              <el-input v-model="form.coordinatedusernameo" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="协调员2" prop="coordinatedusernamet">
              <el-input v-model="form.coordinatedusernamet" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="业务人员" prop="responsibleusername">
              <el-input v-model="form.responsibleusername" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <!-- <el-col :span="8">
            <el-form-item label="确认状态" prop="relativeconfirmationsign">
              <el-select
                v-model="form.confirmationStatus"
                :disabled="!isEdit"
                v-model="form.relativeconfirmationsign"
                style="width: 100%"
              >
                <el-option label="未确认" value="0" />
                <el-option label="家属确认" value="1" />
                <el-option label="不同意捐献" value="2" />
              </el-select>
            </el-form-item>
          </el-col>
          </el-col> -->
          <el-col :span="8">
            <el-form-item label="确认时间" prop="confirmationTime">
            <el-form-item label="签字时间" prop="signdate">
              <el-date-picker
                v-model="form.confirmationTime"
                v-model="form.signdate"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                value-format="yyyy-MM-dd"
                style="width: 100%"
                :disabled="!isEdit"
              />
            </el-form-item>
          </el-col>
          <!-- <el-col :span="8">
            <el-form-item label="血型" prop="bloodtype">
              <el-input v-model="form.bloodtype"  />
            </el-form-item>
          </el-col> -->
        </el-row>
        <!-- <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="证件号码" prop="idcardno">
              <el-input v-model="form.idcardno"  />
            </el-form-item>
          </el-col>
        </el-row> -->
        <!-- äº²å±žä¿¡æ¯ -->
        <!-- äº²å±žä¿¡æ¯ -->
        <el-divider content-position="left">亲属确认信息</el-divider>
        <el-row :gutter="20">
          <el-col :span="6">
            <el-form-item label="亲属姓名" prop="relativeconfirmationsignname">
              <el-input
                v-model="form.relativeconfirmationsignname"
                placeholder="请输入亲属姓名"
              />
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="与捐赠者关系" prop="signfamilyrelations">
              <el-select
                v-model="form.signfamilyrelations"
                placeholder="请选择与捐赠者关系"
              >
                <el-option
                  v-for="dict in dict.type.sys_FamilyRelation || []"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="亲属身份证" prop="relativeidcardno">
              <el-input
                v-model="form.relativeidcardno"
                placeholder="请输入亲属证件号码"
              />
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="亲属电话" prop="relativephone">
              <el-input
                v-model="form.relativephone"
                placeholder="请输入亲属联系电话"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="家属意见备注" prop="familyRemark">
        <el-row>
          <el-form-item label-width="100px" label="捐献决定">
            <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="请输入其他捐献决定的具体内容"
              style="margin-top: 10px; width: 300px;"
            ></el-input>
          </el-form-item>
        </el-row>
        <el-form-item label="家属意见备注" prop="relativeRemark">
          <el-input
            type="textarea"
            :rows="3"
            v-model="form.familyRemark"
            :readonly="!isEdit"
            v-model="form.relativeRemark"
            placeholder="记录家属的意见和沟通情况"
          />
        </el-form-item>
      </el-form>
    </el-card>
    <!-- é™„件列表 -->
    <!-- é™„件信息 - æŒ‰ç±»åž‹åˆ†ç±» -->
    <el-card class="attachment-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">相关附件上传</span>
        <el-button
          v-if="isEdit"
          type="primary"
          size="mini"
          @click="handleSaveAll"
          :loading="saveLoading"
        >
          ä¿å­˜æ‰€æœ‰é™„ä»¶
        </el-button>
      </div>
      <!-- é™„件类型选项卡 -->
@@ -96,8 +201,8 @@
          :label="type.label"
          :name="type.value"
        >
          <div class="attachment-upload-section">
            <div class="upload-header">
          <div class="attachment-section">
            <div class="attachment-header">
              <span class="upload-title">{{ type.label }}</span>
              <el-tooltip content="点击上传该类型附件" placement="top">
                <el-button
@@ -105,97 +210,87 @@
                  type="primary"
                  icon="el-icon-plus"
                  @click="openUploadDialog(type.value)"
                  :disabled="!isEdit"
                >
                  æ·»åР附件
                </el-button>
              </el-tooltip>
            </div>
            <!-- é™„件列表 -->
            <el-table
              :data="getAttachmentsByType(type.value)"
              v-loading="attachmentLoading"
              style="width: 100%; margin-top: 15px;"
            >
              <el-table-column label="文件名称" min-width="200">
                <template slot-scope="scope">
                  <div class="file-info">
            <!-- å½“前类型的附件列表 -->
            <div class="attachment-list">
              <el-table
                :data="getAttachmentsByType(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="margin-right: 8px; color: #409EFF;"
                      style="color: #409EFF; margin-right: 8px;"
                    ></i>
                    <span>{{ scope.row.fileName }}</span>
                  </div>
                </template>
              </el-table-column>
                    <span class="file-name">{{ scope.row.fileName }}</span>
                  </template>
                </el-table-column>
              <el-table-column label="文件大小" width="100" align="center">
                <template slot-scope="scope">
                  <span>{{ formatFileSize(scope.row.fileSize) }}</span>
                </template>
              </el-table-column>
                <el-table-column label="文件类型" width="100" 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="160" align="center">
                <template slot-scope="scope">
                  <span>{{ parseTime(scope.row.uploadTime) }}</span>
                </template>
              </el-table-column>
                <el-table-column label="文件大小" width="100" align="center">
                  <template slot-scope="scope">
                    <span>{{ formatFileSize(scope.row.fileSize) }}</span>
                  </template>
                </el-table-column>
              <el-table-column label="上传人" width="100" align="center">
                <template slot-scope="scope">
                  <span>{{ scope.row.uploader }}</span>
                </template>
              </el-table-column>
                <el-table-column label="上传时间" width="160" align="center">
                  <template slot-scope="scope">
                    <span>{{ formatDateTime(scope.row.uploadTime) }}</span>
                  </template>
                </el-table-column>
              <el-table-column
                label="操作"
                width="120"
                align="center"
                v-if="isEdit"
                <el-table-column label="操作" width="150" align="center">
                  <template slot-scope="scope">
                    <el-button
                      size="mini"
                      type="primary"
                      @click="handlePreview(scope.row)"
                    >
                      é¢„览
                    </el-button>
                    <el-button
                      size="mini"
                      type="danger"
                      @click="handleRemoveAttachment(type.value, scope.$index)"
                    >
                      åˆ é™¤
                    </el-button>
                  </template>
                </el-table-column>
              </el-table>
              <!-- ç©ºçŠ¶æ€æç¤º -->
              <div
                v-if="getAttachmentsByType(type.value).length === 0"
                class="empty-attachment"
              >
                <template slot-scope="scope">
                  <el-button
                    size="mini"
                    type="text"
                    icon="el-icon-view"
                    @click="handlePreview(scope.row)"
                    >预览</el-button
                  >
                  <el-button
                    size="mini"
                    type="text"
                    icon="el-icon-delete"
                    style="color: #F56C6C;"
                    @click="handleRemoveAttachment(scope.row, type.value)"
                    >删除</el-button
                  >
                </template>
              </el-table-column>
              <el-table-column label="操作" width="80" align="center" v-else>
                <template slot-scope="scope">
                  <el-button
                    size="mini"
                    type="text"
                    icon="el-icon-view"
                    @click="handlePreview(scope.row)"
                    >预览</el-button
                  >
                </template>
              </el-table-column>
            </el-table>
            <div
              v-if="getAttachmentsByType(type.value).length === 0"
              class="empty-attachment"
            >
              <el-empty description="暂无附件" :image-size="80"></el-empty>
                <el-empty
                  :description="`暂无${type.label}附件`"
                  :image-size="80"
                ></el-empty>
              </div>
            </div>
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-card>
    <!-- ä¸Šä¼ å¯¹è¯æ¡† -->
    <el-dialog
      :title="`上传${getCurrentTypeLabel}附件`"
      :visible.sync="uploadDialogVisible"
@@ -234,17 +329,33 @@
        </el-button>
      </span>
    </el-dialog>
    <!-- æ–‡ä»¶é¢„览弹窗 -->
    <FilePreviewDialog
      :visible="previewVisible"
      :file="currentPreviewFile"
      @close="previewVisible = false"
      @download="handleDownload"
    />
  </div>
</template>
<script>
import {
  getConfirmationDetail,
  updateConfirmation
} from "./api/mockConfirmationApi";
import { relativesList, relativesEdit } from "@/api/businessApi";
import FilePreviewDialog from "@/components/FilePreviewDialog";
export default {
  name: "ConfirmationDetail",
  components: {
    FilePreviewDialog
  },
  props: {
    infoid: {
      type: String,
      default: true
    }
  },
  dicts: ["sys_FamilyRelation"],
  data() {
    return {
      // æ˜¯å¦ç¼–辑模式
@@ -252,28 +363,53 @@
      // è¡¨å•数据
      form: {
        id: undefined,
        infoid: undefined,
        caseNo: "",
        donorName: "",
        gender: "",
        name: "",
        sex: "",
        age: "",
        diagnosis: "",
        hospitalName: "",
        coordinator1: "",
        coordinator2: "",
        assignee: "",
        confirmationStatus: "0",
        confirmationTime: "",
        familyRemark: ""
        diagnosisname: "",
        treatmenthospitalname: "",
        coordinatedusernameo: "",
        coordinatedusernamet: "",
        responsibleusername: "",
        relativeconfirmationsign: "0",
        signdate: "",
        relativeconfirmationsignname: "",
        signfamilyrelations: "",
        relativeidcardno: "",
        relativephone: "",
        relativeRemark: "",
        assessannex: "" // JSON字符串存储所有附件
      },
      // é™„件列表
      organdecision: [],
      organdecisionOther: "",
      organselection: [
        "肝脏",
        "双肾",
        "左肾",
        "右肾",
        "心脏",
        "肺脏",
        "胰腺",
        "小肠",
        "双眼组织",
        "遗体",
        "其他"
      ],
      // åŠ è½½çŠ¶æ€
      loading: false,
      saveLoading: false,
      // é™„件相关数据
      activeAttachmentType: "1",
      attachmentLoading: false,
      uploadDialogVisible: false,
      uploadLoading: false,
      saveLoading: false,
      tempFileList: [],
      currentUploadType: "",
      // é¢„览相关
      previewVisible: false,
      currentPreviewFile: null,
      // é™„件类型定义
      attachmentTypes: [
@@ -285,36 +421,18 @@
        { value: "6", label: "心死亡判定知情同意书" }
      ],
      // é™„件列表数据
      attachmentList: [
        // æ¨¡æ‹Ÿæ•°æ® - å®žé™…项目中从接口获取
        {
          id: 1,
          type: "1",
          typeName: "人体器官潜在捐献者登记表",
          fileName: "潜在捐献者登记表_202512001.pdf",
          fileSize: 2548321,
          uploadTime: "2025-12-01 10:30:00",
          uploader: "张三",
          fileUrl: "/attachments/1.pdf"
        },
        {
          id: 2,
          type: "1",
          typeName: "人体器官潜在捐献者登记表",
          fileName: "补充说明.docx",
          fileSize: 512345,
          uploadTime: "2025-12-01 14:20:00",
          uploader: "李四",
          fileUrl: "/attachments/2.docx"
        }
      ]
      // é™„件数据结构 - æŒ‰ç±»åž‹åˆ†å¼€å­˜å‚¨
      attachmentData: {
        "1": [], // ç±»åž‹1的附件列表
        "2": [],
        "3": [],
        "4": [],
        "5": [],
        "6": []
      }
    };
  },
  computed: {
    isEdit() {
      return this.$route.query.confirm === "true";
    },
    getCurrentTypeLabel() {
      const type = this.attachmentTypes.find(
        t => t.value === this.currentUploadType
@@ -323,113 +441,125 @@
    }
  },
  created() {
    const id = this.$route.query.id;
    this.isEdit = this.$route.query.confirm === "true";
    if (id) {
      this.getDetail(id);
    console.log(this.infoid, "this.infoid");
    if (this.infoid) {
      console.log(1);
      this.getDetail(this.infoid);
    }
    this.getAttachmentList();
  },
  methods: {
    // èŽ·å–è¯¦æƒ…
    getDetail(id) {
      getConfirmationDetail(id).then(response => {
    async getDetail(infoid) {
      this.loading = true;
      try {
        const response = await relativesList({ infoid });
        console.log(2);
        if (response.code === 200) {
          this.form = response.data;
          console.log(3);
          this.handleDetailData(response);
        } else {
          this.$message.error("获取详情失败:" + (response.msg || "未知错误"));
        }
      });
      } catch (error) {
        console.error("获取捐献确认详情失败:", error);
        this.$message.error("获取详情失败");
      } finally {
        this.loading = false;
      }
    },
    // èŽ·å–é™„ä»¶åˆ—è¡¨
    getAttachmentList() {
      this.attachmentLoading = true;
      // æ¨¡æ‹Ÿé™„件数据
      this.attachmentList = [
        {
          id: 1,
          type: "1",
          typeName: "人体器官潜在捐献者登记表",
          fileName: "潜在捐献者登记表_202512001.pdf",
          uploadTime: "2025-12-01 10:30:00",
          uploader: "张三",
          fileSize: "2.5MB",
          fileUrl: "/attachments/1.pdf"
        },
        {
          id: 2,
          type: "2",
          typeName: "人体器官捐献亲属确认登记表",
          fileName: "亲属确认登记表_202512001.pdf",
          uploadTime: "2025-12-01 14:20:00",
          uploader: "李四",
          fileSize: "1.8MB",
          fileUrl: "/attachments/2.pdf"
        },
        {
          id: 3,
          type: "3",
          typeName: "捐献者及直系亲属身份证、户口簿相关证明",
          fileName: "身份证明_202512001.zip",
          uploadTime: "2025-12-01 16:45:00",
          uploader: "王五",
          fileSize: "5.2MB",
          fileUrl: "/attachments/3.zip"
        },
        {
          id: 4,
          type: "4",
          typeName: "公民身故后人体器官(角膜)遗体捐献告知书",
          fileName: "捐献告知书_202512001.pdf",
          uploadTime: "2025-12-02 09:15:00",
          uploader: "张三",
          fileSize: "1.2MB",
          fileUrl: "/attachments/4.pdf"
        },
        {
          id: 5,
          type: "5",
          typeName: "脑死亡判定知情同意书",
          fileName: "脑死亡判定同意书_202512001.pdf",
          uploadTime: "2025-12-02 11:30:00",
          uploader: "李四",
          fileSize: "0.8MB",
          fileUrl: "/attachments/5.pdf"
        },
        {
          id: 6,
          type: "6",
          typeName: "心死亡判定知情同意书",
          fileName: "心死亡判定同意书_202512001.pdf",
          uploadTime: "2025-12-02 13:20:00",
          uploader: "王五",
          fileSize: "0.9MB",
          fileUrl: "/attachments/6.pdf"
    // å¤„理详情数据
    handleDetailData(response) {
      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;
        }
      ];
      this.attachmentLoading = false;
      } else {
        detailData = response;
      }
      // æ˜ å°„字段到表单
      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 || "",
        relativeconfirmationsign: detailData.relativeconfirmationsign || "0",
        signdate: detailData.signdate,
        relativeconfirmationsignname:
          detailData.relativeconfirmationsignname || "",
        signfamilyrelations: detailData.signfamilyrelations || "",
        relativeidcardno: detailData.relativeidcardno || "",
        relativephone: detailData.relativephone || "",
        relativeRemark: detailData.relativeRemark || "",
        assessannex: detailData.assessannex || ""
      };
      // å¤„理捐献决定数据
      if (detailData.organdecision) {
        this.organdecision = Array.isArray(detailData.organdecision)
          ? detailData.organdecision
          : detailData.organdecision.split(",");
      }
      console.log(this.organdecision, "this.organdecision");
      // å¤„理附件数据 - ä»Žassessannex字段解析JSON并按类型分类
      this.processAssessannexData();
    },
    // ä¸‹è½½é™„ä»¶
    handleDownload(row) {
      // å®žé™…项目中这里调用文件下载接口
      this.$message.success(`下载文件: ${row.fileName}`);
      console.log("下载文件:", row.fileUrl);
    },
    // ä¿å­˜ç¡®è®¤ä¿¡æ¯
    handleSave() {
      this.$refs.form.validate(valid => {
        if (valid) {
          updateConfirmation(this.form).then(response => {
            if (response.code === 200) {
              this.$message.success("保存成功");
              this.isEdit = false;
              this.$router.push("/case/confirmation");
            }
    // å¤„理assessannex字段数据并按类型分类
    processAssessannexData() {
      if (this.form.assessannex) {
        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);
              }
            });
          }
        } catch (error) {
          console.warn("assessannex数据解析失败:", error);
        }
      });
      }
    },
 // æ ¹æ®ç±»åž‹èŽ·å–é™„ä»¶
    // æ ¹æ®ç±»åž‹èŽ·å–é™„ä»¶åˆ—è¡¨
    getAttachmentsByType(type) {
      return this.attachmentList.filter(item => item.type === type);
      return this.attachmentData[type] || [];
    },
    // æ‰“开上传对话框
@@ -444,35 +574,38 @@
      });
    },
    // ä¸Šä¼ å‰æ ¡éªŒ
    // ä¸Šä¼ å‰æ ¡éªŒ[7](@ref)
    beforeUpload(file) {
      const allowedTypes = [
        'application/pdf',
        'image/jpeg',
        'image/png',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        "application/pdf",
        "image/jpeg",
        "image/png",
        "application/msword",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
      ];
      const maxSize = 10 * 1024 * 1024; // 10MB
      // æ ¡éªŒæ–‡ä»¶ç±»åž‹
      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');
      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格式文件');
        this.$message.error(
          "文件格式不支持,请上传pdf、jpg、png、doc或docx格式文件"
        );
        return false;
      }
      // æ ¡éªŒæ–‡ä»¶å¤§å°
      if (file.size > maxSize) {
        this.$message.error('文件大小不能超过10MB');
        this.$message.error("文件大小不能超过10MB");
        return false;
      }
@@ -491,25 +624,29 @@
    // è‡ªå®šä¹‰ä¸Šä¼ è¯·æ±‚
    handleHttpRequest(options) {
      // æ¨¡æ‹Ÿä¸Šä¼ è¿‡ç¨‹
      return new Promise((resolve, reject) => {
        this.uploadLoading = true;
        // æ¨¡æ‹Ÿä¸Šä¼ å»¶è¿Ÿ
        // æ¨¡æ‹Ÿä¸Šä¼ è¿‡ç¨‹
        setTimeout(() => {
          const newAttachment = {
            id: Date.now(),
            type: this.currentUploadType,
            typeName: this.getCurrentTypeLabel,
            fileName: options.file.name,
            fileUrl: URL.createObjectURL(options.file),
            fileSize: options.file.size,
            fileType: this.getFileExtension(options.file.name),
            type: this.currentUploadType, // è®°å½•附件类型
            uploadTime: new Date().toISOString(),
            uploader: '当前用户', // å®žé™…项目中从用户信息获取
            fileUrl: URL.createObjectURL(options.file) // ä¸´æ—¶URL,实际项目中为服务器返回的URL
            uploader: "当前用户"
          };
          this.attachmentList.push(newAttachment);
          // æ·»åŠ åˆ°å¯¹åº”ç±»åž‹çš„é™„ä»¶åˆ—è¡¨
          if (this.attachmentData[this.currentUploadType]) {
            this.attachmentData[this.currentUploadType].push(newAttachment);
          }
          this.uploadLoading = false;
          this.updateAssessannexField(); // æ›´æ–°å­˜å‚¨å­—段
          resolve({ code: 200, data: newAttachment });
        }, 1500);
      });
@@ -518,90 +655,184 @@
    // æäº¤ä¸Šä¼ 
    async submitUpload() {
      if (this.tempFileList.length === 0) {
        this.$message.warning('请先选择要上传的文件');
        this.$message.warning("请先选择要上传的文件");
        return;
      }
      try {
        // ä¾æ¬¡ä¸Šä¼ æ‰€æœ‰æ–‡ä»¶
        for (const file of this.tempFileList) {
          await this.$refs.uploadRef.submit();
        }
        this.$message.success('文件上传成功');
        this.$message.success("文件上传成功");
        this.uploadDialogVisible = false;
        this.tempFileList = [];
      } catch (error) {
        this.$message.error('文件上传失败');
        console.error('上传失败:', error);
        this.$message.error("文件上传失败");
        console.error("上传失败:", error);
      }
    },
    // åˆ é™¤é™„ä»¶
    handleRemoveAttachment(attachment, type) {
      this.$confirm('确定要删除这个附件吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        const index = this.attachmentList.findIndex(item => item.id === attachment.id);
        if (index !== -1) {
          this.attachmentList.splice(index, 1);
          this.$message.success('附件删除成功');
          // å®žé™…项目中调用删除接口
          // this.deleteAttachment(attachment.id);
        }
      }).catch(() => {});
    handleRemoveAttachment(type, index) {
      this.$confirm("确定要删除这个附件吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          if (this.attachmentData[type] && this.attachmentData[type][index]) {
            this.attachmentData[type].splice(index, 1);
            this.$message.success("附件删除成功");
            this.updateAssessannexField(); // æ›´æ–°å­˜å‚¨å­—段
          }
        })
        .catch(() => {});
    },
    // é¢„览附件
    handlePreview(attachment) {
      // å®žé™…项目中根据文件类型调用不同的预览方式
      if (attachment.fileName.endsWith('.pdf')) {
        // PDF预览
        window.open(attachment.fileUrl, '_blank');
      } else if (attachment.fileName.match(/\.(jpg|jpeg|png)$/i)) {
        // å›¾ç‰‡é¢„览
        this.$alert(`<img src="${attachment.fileUrl}" style="max-width: 100%;" alt="${attachment.fileName}">`,
          '图片预览', {
            dangerouslyUseHTMLString: true,
            customClass: 'image-preview-dialog'
          });
    // æ›´æ–°assessannex存储字段
    updateAssessannexField() {
      // å°†æ‰€æœ‰ç±»åž‹çš„附件合并为一个数组
      const allAttachments = [];
      Object.values(this.attachmentData).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.info('该文件类型暂不支持在线预览,请下载后查看');
        this.$message.warning("文件路径不存在,无法下载");
      }
    },
    // ä¿å­˜æ‰€æœ‰é™„件信息
    handleSaveAll() {
      this.saveLoading = true;
    // èŽ·å–æ–‡ä»¶ç±»åž‹
    getFileType(fileName) {
      if (!fileName) return "other";
      const extension = this.getFileExtension(fileName);
      const imageTypes = ["jpg", "jpeg", "png"];
      const pdfTypes = ["pdf"];
      const officeTypes = ["doc", "docx"];
      // æ¨¡æ‹Ÿä¿å­˜è¿‡ç¨‹
      setTimeout(() => {
        this.$message.success('附件信息保存成功');
        this.saveLoading = false;
      if (imageTypes.includes(extension)) return "image";
      if (pdfTypes.includes(extension)) return "pdf";
      if (officeTypes.includes(extension)) return "office";
      return "other";
    },
        // å®žé™…项目中调用保存接口
        // this.saveAttachments();
      }, 1000);
    // èŽ·å–æ–‡ä»¶æ‰©å±•å
    getFileExtension(filename) {
      return filename
        .split(".")
        .pop()
        .toLowerCase();
    },
    // æ–‡ä»¶å¤§å°æ ¼å¼åŒ–
    formatFileSize(size) {
      if (size === 0) return '0 B';
      if (!size) return "0 B";
      const k = 1024;
      const sizes = ['B', 'KB', 'MB', 'GB'];
      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];
      return parseFloat((size / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
    },
    // æ—¶é—´æ ¼å¼åŒ–
    parseTime(time) {
      if (!time) return '';
      const date = new Date(time);
      return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
    // æ—¥æœŸæ—¶é—´æ ¼å¼åŒ–
    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")}`;
    },
    // ä¿å­˜ç¡®è®¤ä¿¡æ¯
    async handleSave() {
      try {
        await this.$refs.form.validate();
        this.saveLoading = true;
        // ç¡®ä¿é™„件数据是最新的
        this.updateAssessannexField();
        const saveData = {
          ...this.form,
          organdecision: this.organdecision.join(","),
          organdecisionOther: this.organdecisionOther
          // assessannex字段已在updateAssessannexField中更新
        };
        const response = await relativesEdit(saveData);
        if (response.code === 200) {
          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;
      }
    }
  }
};
@@ -623,37 +854,20 @@
.detail-title {
  font-size: 16px;
  font-weight: bold;
  margin-right: 20px;
}
.fixed-width .el-button {
  margin: 0 5px;
}
.confirmation-detail {
  padding: 20px;
.attachment-section {
  padding: 15px;
}
.detail-card {
  margin-bottom: 20px;
}
.attachment-card {
  margin-bottom: 20px;
}
.detail-title {
  font-size: 16px;
  font-weight: bold;
}
.attachment-upload-section {
  padding: 10px;
}
.upload-header {
.attachment-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  padding-bottom: 10px;
  border-bottom: 1px solid #ebeef5;
}
.upload-title {
@@ -662,9 +876,8 @@
  color: #303133;
}
.file-info {
  display: flex;
  align-items: center;
.attachment-list {
  margin-top: 15px;
}
.empty-attachment {
@@ -673,13 +886,8 @@
  color: #909399;
}
/* å›¾ç‰‡é¢„览对话框样式 */
:deep(.image-preview-dialog) {
  width: auto;
  max-width: 90vw;
}
:deep(.image-preview-dialog .el-message-box__content) {
  text-align: center;
.file-name {
  font-size: 13px;
  color: #606266;
}
</style>
src/views/business/course/components/DonorMaintenanceStage.vue
@@ -1,8 +1,156 @@
<template>
  <div class="maintenance-detail">
    <!-- åŸºç¡€ä¿¡æ¯ -->
    <el-card class="detail-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">供者基本信息</span>
      </div>
      <el-form :model="form" ref="form" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="住院号" prop="caseNo">
              <el-input v-model="form.caseNo" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="捐献者姓名" prop="name">
              <el-input v-model="form.name" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="性别" prop="gender">
              <el-select v-model="form.sex" style="width: 100%">
                <el-option label="男" value="0" />
                <el-option label="女" value="1" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="年龄" prop="age">
              <el-input v-model="form.age" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="疾病诊断" prop="diagnosisname">
              <el-input v-model="form.diagnosisname" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="首诊医疗机构" prop="treatmenthospitalname">
              <el-input v-model="form.treatmenthospitalname" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="患者状态" prop="recordstate">
              <el-select v-model="form.recordstate" style="width: 100%">
                <el-option
                  v-for="dict in dict.type.sys_DonationCategory || []"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item
              label="未完成原因"
              prop="incompleteReason"
              v-if="form.recordstate === '5'"
            >
              <el-input
                v-model="form.incompleteReason"
                placeholder="请输入未完成捐献的原因"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="上报时间" prop="reporttime">
              <el-date-picker
                v-model="form.reporttime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="死亡时间" prop="deathTime">
              <el-date-picker
                v-model="form.deathTime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="协调员" prop="coordinatorName">
              <el-input v-model="form.coordinatorName" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="血型" prop="bloodtype">
              <el-select v-model="form.bloodtype" style="width: 100%">
                <el-option
                  v-for="dict in dict.type.sys_BloodType"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="Rh(D)" prop="rhYin">
              <el-radio-group v-model="form.rhYin">
                <el-radio
                  v-for="dict in dict.type.sys_bloodtype_rhd || []"
                  :key="dict.value"
                  :label="dict.value"
                  >{{ dict.label }}</el-radio
                >
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="特殊病史" prop="specialMedicalHistory">
          <el-input
            type="textarea"
            :rows="3"
            v-model="form.specialMedicalHistory"
            placeholder="记录特殊病史信息"
          />
        </el-form-item>
      </el-form>
    </el-card>
    <el-card class="assessment-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">供者评估各项记录</span>
        <el-button
          type="primary"
          size="mini"
          @click="toggleEditMode"
          style="float: right;"
        >
          {{ isEdit ? "完成编辑" : "开始编辑" }}
        </el-button>
      </div>
      <el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick">
        <!-- åŸ¹å…»ç»“果记录 -->
@@ -10,6 +158,14 @@
          <el-card class="culture-card">
            <div slot="header" class="clearfix">
              <span class="detail-title">培养结果记录</span>
              <el-button
                type="primary"
                size="mini"
                icon="el-icon-plus"
                @click="handleAddCulture"
              >
                æ–°å¢žåŸ¹å…»è®°å½•
              </el-button>
            </div>
            <el-table :data="cultureList" v-loading="cultureLoading">
@@ -17,14 +173,7 @@
                label="培养类型"
                align="center"
                prop="cultureType"
              >
                <!-- <template slot-scope="scope">
            <dict-tag
              :options="cultureTypeOptions"
              :value="scope.row.cultureType"
            />
          </template> -->
              </el-table-column>
              />
              <el-table-column
                label="采样时间"
                align="center"
@@ -40,7 +189,6 @@
                  </el-tag>
                </template>
              </el-table-column>
              <!-- é™„件列 -->
              <el-table-column label="附件" align="center">
                <template slot-scope="scope">
                  <el-button
@@ -56,16 +204,31 @@
                  <span v-else>无附件</span>
                </template>
              </el-table-column>
              <el-table-column
                label="操作"
                align="center"
                width="200"
                class-name="small-padding fixed-width"
              >
                <template slot-scope="scope">
                  <el-button
                    size="mini"
                    type="text"
                    icon="el-icon-edit"
                    @click="handleEditCulture(scope.row)"
                    >编辑</el-button
                  >
                  <el-button
                    size="mini"
                    type="text"
                    icon="el-icon-delete"
                    style="color: #F56C6C;"
                    @click="handleDeleteCulture(scope.row)"
                    >删除</el-button
                  >
                </template>
              </el-table-column>
            </el-table>
            <pagination
              v-show="cultureTotal > 0"
              :total="cultureTotal"
              :page.sync="cultureQueryParams.pageNum"
              :limit.sync="cultureQueryParams.pageSize"
              @pagination="getCultureList"
            />
          </el-card>
        </el-tab-pane>
@@ -73,6 +236,7 @@
        <el-tab-pane label="肝功能肾功能" name="liverKidney">
          <liver-kidney-panel
            ref="liverKidney"
            :initial-data="assessmentData.liverKidney"
            :is-editing="isEdit && activeTab === 'liverKidney'"
            @data-change="handleLiverKidneyDataChange"
          />
@@ -82,6 +246,7 @@
        <el-tab-pane label="血常规" name="bloodRoutine">
          <blood-routine-panel
            ref="bloodRoutine"
            :initial-data="assessmentData.bloodRoutine"
            :is-editing="isEdit && activeTab === 'bloodRoutine'"
            @data-change="handleBloodRoutineDataChange"
          />
@@ -91,18 +256,26 @@
        <el-tab-pane label="尿常规" name="urineRoutine">
          <urine-routine-panel
            ref="urineRoutine"
            :initial-data="assessmentData.urineRoutine"
            :is-editing="isEdit && activeTab === 'urineRoutine'"
            @data-change="handleUrineRoutineDataChange"
          />
        </el-tab-pane>
      </el-tabs>
    </el-card>
    <!-- åŸ¹å…»ç»“果记录 -->
    <!-- ç®€åŒ–后的护理核查记录 -->
    <!-- æŠ¤ç†æ ¸æŸ¥è®°å½• -->
    <el-card class="record-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">护理核查记录</span>
        <el-button
          type="primary"
          size="mini"
          icon="el-icon-plus"
          @click="handleAddRecord"
        >
          æ–°å¢žæ ¸æŸ¥è®°å½•
        </el-button>
      </div>
      <el-table :data="recordList" v-loading="recordLoading">
@@ -125,7 +298,6 @@
          min-width="200"
          show-overflow-tooltip
        />
        <!-- é™„件列 -->
        <el-table-column label="附件" align="center" width="120">
          <template slot-scope="scope">
            <el-button
@@ -139,16 +311,33 @@
            <span v-else>无附件</span>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          align="center"
          width="180"
          class-name="small-padding fixed-width"
        >
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="text"
              icon="el-icon-edit"
              @click="handleEditRecord(scope.row)"
              >编辑</el-button
            >
            <el-button
              size="mini"
              type="text"
              icon="el-icon-delete"
              style="color: #F56C6C;"
              @click="handleDeleteRecord(scope.row)"
              >删除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="recordTotal > 0"
        :total="recordTotal"
        :page.sync="recordQueryParams.pageNum"
        :limit.sync="recordQueryParams.pageSize"
        @pagination="getRecordList"
      />
    </el-card>
    <!-- åŸ¹å…»è®°å½•编辑对话框 -->
    <el-dialog
      :title="cultureDialogTitle"
@@ -207,11 +396,17 @@
          </el-col>
        </el-row>
        <!-- é™„件上传 -->
        <el-form-item label="附件">
          <upload-attachment
            :file-list="cultureForm.attachments"
          <UploadAttachment
            ref="cultureUploadAttachment"
            :file-list="cultureFileList"
            :limit="10"
            :accept="attachmentAccept"
            :multiple="true"
            @change="handleCultureAttachmentChange"
            @upload-success="handleCultureUploadSuccess"
            @upload-error="handleCultureUploadError"
            @remove="handleCultureAttachmentRemove"
          />
        </el-form-item>
      </el-form>
@@ -271,181 +466,154 @@
          />
        </el-form-item>
        <!-- é™„件上传 -->
        <el-form-item label="附件">
          <upload-attachment
            :file-list="recordForm.attachments"
          <UploadAttachment
            ref="recordUploadAttachment"
            :file-list="recordFileList"
            :limit="10"
            :accept="attachmentAccept"
            :multiple="true"
            @change="handleRecordAttachmentChange"
            @upload-success="handleRecordUploadSuccess"
            @upload-error="handleRecordUploadError"
            @remove="handleRecordAttachmentRemove"
          />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="recordDialogVisible = false">取消</el-button>
        <el-button
          type="primary"
          @click="handleSaveRecord"
          :loading="recordSaveLoading"
          >保存</el-button
        >
      </span>
    </el-dialog>
    <!-- é™„件预览对话框 -->
    <attachment-preview
      :visible="attachmentPreviewVisible"
      :file-list="currentAttachmentList"
    <el-dialog
      :title="attachmentPreviewTitle"
      @close="attachmentPreviewVisible = false"
      :visible.sync="attachmentPreviewVisible"
      width="900px"
      @close="handleAttachmentPreviewClose"
    >
      <el-table :data="currentAttachmentList" style="width: 100%" size="small">
        <el-table-column label="文件名" min-width="200">
          <template slot-scope="scope">
            <i
              class="el-icon-document"
              :style="{ color: getFileIconColor(scope.row.fileName) }"
            ></i>
            <span class="file-name">{{ scope.row.fileName }}</span>
          </template>
        </el-table-column>
        <el-table-column label="文件类型" width="100">
          <template slot-scope="scope">
            <el-tag :type="getFileTagType(scope.row.fileName)" size="small">
              {{ getFileTypeText(scope.row.fileName) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="上传时间" width="160">
          <template slot-scope="scope">
            <span>{{ formatDateTime(scope.row.uploadTime) }}</span>
          </template>
        </el-table-column>
        <el-table-column label="文件大小" width="100">
          <template slot-scope="scope">
            <span>{{ formatFileSize(scope.row.fileSize) }}</span>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="150" fixed="right">
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="primary"
              @click="handlePreviewAttachment(scope.row)"
              :disabled="!isPreviewable(scope.row.fileName)"
            >
              é¢„览
            </el-button>
            <el-button
              size="mini"
              type="success"
              @click="handleDownloadAttachment(scope.row)"
            >
              ä¸‹è½½
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-dialog>
    <!-- æ–‡ä»¶é¢„览弹窗 -->
    <FilePreviewDialog
      :visible="filePreviewVisible"
      :file="currentPreviewFile"
      @close="filePreviewVisible = false"
      @download="handleDownloadAttachment"
    />
  </div>
</template>
<script>
import { getMaintenanceDetail, updateMaintenance } from "./api//mockMaintenanceApi";
import {
  listCultureResults,
  addCultureResult,
  updateCultureResult,
  deleteCultureResult
} from "./api/mockMaintenanceApi";
import {
  listNursingRecords,
  addNursingRecord,
  updateNursingRecord,
  deleteNursingRecord
} from "./api//mockMaintenanceApi";
import { maintainList, maintainedit, maintainAdd } from "@/api/businessApi";
import Pagination from "@/components/Pagination";
import UploadAttachment from "@/components/UploadAttachment";
import AttachmentPreview from "@/components/AttachmentPreview";
import LiverKidneyPanel from "./components/LiverKidneyPanel.vue";
import BloodRoutinePanel from "./components/BloodRoutinePanel.vue";
import UrineRoutinePanel from "./components/UrineRoutinePanel.vue";
import FilePreviewDialog from "@/components/FilePreviewDialog";
import LiverKidneyPanel from "@/components/MaintainComponents/LiverKidneyPanel.vue";
import BloodRoutinePanel from "@/components/MaintainComponents/BloodRoutinePanel.vue";
import UrineRoutinePanel from "@/components/MaintainComponents/UrineRoutinePanel.vue";
import dayjs from "dayjs";
export default {
  name: "MaintenanceDetail",
  components: {
    Pagination,
    UploadAttachment,
    AttachmentPreview,
    FilePreviewDialog,
    LiverKidneyPanel,
    BloodRoutinePanel,
    UrineRoutinePanel
  },
  dicts: [
    "sys_donornode",
    "sys_BloodType",
    "sys_EthicalReview",
    "sys_BaseAssessConclusion",
    "sys_bloodtype_rhd",
    "sys_DonationCategory"
  ],
  props: {
    infoid: {
      type: String,
      default: true
    }
  },
  data() {
    return {
      isEdit: true,
      isEdit: false,
      currentMaintenanceId: null,
      isEditMode: false,
      form: {
        id: undefined,
        caseNo: "",
        donorName: "",
        name: "",
        gender: "",
        age: "",
        diagnosis: "",
        hospitalName: "",
        patientStatus: "1",
        admissionTime: "",
        dischargeTime: "",
        coordinator: "",
        bloodType: "",
        diagnosisname: "",
        treatmenthospitalname: "",
        recordstate: "1",
        reporttime: "",
        deathTime: "",
        coordinatorName: "",
        bloodtype: "",
        rhFactor: "",
        specialMedicalHistory: "",
        incompleteReason: ""
      },
      activeTab: "culture",
      // åŸ¹å…»ç»“果相关数据
      // åŸ¹å…»ç»“果相关数据
      cultureList: [
        {
          id: 1,
          cultureType: "血培养",
          sampleTime: "2024-12-19 10:30:00",
          result: "阴性",
          bacteria: "",
          drugSensitivity: "",
          testingInstitution: "青岛大学附属医院",
          specimenType: "血液",
          remarks: "常规检测",
          attachments: [
            {
              id: 1,
              fileName: "血培养报告.pdf",
              fileUrl:
                "http://localhost:8080/profile/upload/2025/12/27/1.COPO供体评估表.pdf",
              fileSize: 1024000,
              uploadTime: "2024-12-19 11:00:00"
            }
          ]
        },
        {
          id: 3,
          cultureType: "血培养",
          sampleTime: "2024-12-20 09:15:00",
          result: "阴性",
          bacteria: "",
          drugSensitivity: "",
          testingInstitution: "广州检测中心",
          specimenType: "å°¿æ¶²",
          remarks: "清洁中段尿标本",
          attachments: []
        },
        {
          id: 2,
          cultureType: "痰培养结果",
          sampleTime: "2024-12-19 14:20:00",
          result: "阳性",
          bacteria: "金黄色葡萄球菌",
          drugSensitivity: "对青霉素敏感,对头孢类中介",
          testingInstitution: "上海医学检验所",
          specimenType: "痰液",
          remarks: "药敏试验完成",
          attachments: [
            {
              id: 2,
              fileName: "痰培养结果.jpg",
              fileUrl:
                "https://img95.699pic.com/photo/40142/8262.jpg_wh860.jpg",
              fileSize: 512000,
              uploadTime: "2024-12-19 15:30:00"
            },
            {
              id: 3,
              fileName: "药敏报告.pdf",
              fileUrl:
                "http://localhost:8080/profile/upload/2025/12/27/(吴龙8.7)每日工作总结.pdf",
              fileSize: 768000,
              uploadTime: "2024-12-19 16:00:00"
            }
          ]
        },
      extracontentinfo: {},
        {
          id: 4,
          cultureType: "真菌培养",
          sampleTime: "2024-12-20 11:45:00",
          result: "阳性",
          bacteria: "大肠杆菌",
          drugSensitivity: "对左氧氟沙星敏感",
          testingInstitution: "深圳人民医院",
          specimenType: "伤口分泌物",
          remarks: "术后伤口感染监测",
          attachments: [
            {
              id: 4,
              fileName: "真菌培养.pdf",
              fileUrl: "/reports/culture4.pdf",
              fileSize: 890000,
              uploadTime: "2024-12-20 13:20:00"
            }
          ]
        }
      ],
      // åŸ¹å…»ç»“果相关数据
      cultureList: [],
      cultureLoading: false,
      cultureTotal: 5,
      cultureQueryParams: {
        pageNum: 1,
        pageSize: 10
      },
      cultureDialogVisible: false,
      cultureDialogTitle: "",
      cultureSaveLoading: false,
@@ -454,8 +622,9 @@
        cultureType: "",
        sampleTime: "",
        result: "阴性",
        attachments: [] // æ–°å¢žé™„件字段
        attachments: []
      },
      cultureFileList: [],
      cultureRules: {
        cultureType: [
          { required: true, message: "请选择培养类型", trigger: "change" }
@@ -477,120 +646,8 @@
      ],
      // æŠ¤ç†æ ¸æŸ¥è®°å½•相关数据
      recordList: [
        {
          id: 1,
          recordTime: "2024-12-19 08:30:00",
          recorder: "张护士",
          temperature: 36.8,
          heartRate: 78,
          bloodPressure: "120/80",
          respirationRate: 18,
          oxygenSaturation: 98,
          urineOutput: 60,
          cvp: 8,
          checkRecord: "患者生命体征平稳,意识清楚,配合治疗",
          remarks: "夜间睡眠良好",
          attachments: [
            {
              id: 1,
              fileName: "早班护理记录.jpg",
              fileUrl: "/records/nursing1.jpg",
              fileSize: 1024000,
              uploadTime: "2024-12-19 09:00:00"
            }
          ]
        },
        {
          id: 2,
          recordTime: "2024-12-19 14:30:00",
          recorder: "李护士",
          temperature: 37.2,
          heartRate: 82,
          bloodPressure: "118/76",
          respirationRate: 16,
          oxygenSaturation: 97,
          urineOutput: 45,
          cvp: 7.5,
          checkRecord: "患者午后体温略有升高,观察中",
          remarks: "建议增加水分摄入",
          attachments: []
        },
        {
          id: 3,
          recordTime: "2024-12-19 20:30:00",
          recorder: "王护士",
          temperature: 36.9,
          heartRate: 75,
          bloodPressure: "122/78",
          respirationRate: 17,
          oxygenSaturation: 98,
          urineOutput: 55,
          cvp: 8.2,
          checkRecord: "晚间生命体征稳定,患者休息良好",
          remarks: "夜间监测无异常",
          attachments: [
            {
              id: 2,
              fileName: "晚班护理记录.pdf",
              fileUrl: "/records/nursing3.pdf",
              fileSize: 890000,
              uploadTime: "2024-12-19 21:00:00"
            },
            {
              id: 3,
              fileName: "体征监测表.xlsx",
              fileUrl: "/records/monitoring3.xlsx",
              fileSize: 256000,
              uploadTime: "2024-12-19 21:15:00"
            }
          ]
        },
        {
          id: 4,
          recordTime: "2024-12-20 08:30:00",
          recorder: "赵护士",
          temperature: 36.7,
          heartRate: 80,
          bloodPressure: "119/77",
          respirationRate: 18,
          oxygenSaturation: 99,
          urineOutput: 65,
          cvp: 7.8,
          checkRecord: "晨间生命体征正常,患者精神状态良好",
          remarks: "准备今日检查",
          attachments: []
        },
        {
          id: 5,
          recordTime: "2024-12-20 12:30:00",
          recorder: "刘护士",
          temperature: 37.1,
          heartRate: 85,
          bloodPressure: "121/79",
          respirationRate: 19,
          oxygenSaturation: 96,
          urineOutput: 40,
          cvp: 8.5,
          checkRecord: "午间体温略有波动,继续观察",
          remarks: "已通知医生",
          attachments: [
            {
              id: 4,
              fileName: "午间护理记录.jpg",
              fileUrl: "/records/nursing5.jpg",
              fileSize: 765000,
              uploadTime: "2024-12-20 13:00:00"
            }
          ]
        }
      ],
      recordList: [],
      recordLoading: false,
      recordTotal: 4,
      recordQueryParams: {
        pageNum: 1,
        pageSize: 10
      },
      recordDialogVisible: false,
      recordDialogTitle: "",
      recordSaveLoading: false,
@@ -598,9 +655,10 @@
        id: undefined,
        recordTime: "",
        recorder: "",
        checkRecord: "", // æ”¹ä¸ºå•字段记录
        attachments: [] // æ–°å¢žé™„件字段
        checkRecord: "",
        attachments: []
      },
      recordFileList: [],
      recordRules: {
        recordTime: [
          { required: true, message: "请选择核查时间", trigger: "change" }
@@ -616,87 +674,112 @@
      // é™„件预览相关
      attachmentPreviewVisible: false,
      currentAttachmentList: [],
      attachmentPreviewTitle: ""
      attachmentPreviewTitle: "",
      // æ–‡ä»¶é¢„览相关
      filePreviewVisible: false,
      currentPreviewFile: null,
      // é™„件相关配置
      attachmentLimit: 10,
      attachmentAccept:
        ".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt",
      // è¯„估数据存储
      assessmentData: {
        liverKidney: {},
        bloodRoutine: {},
        urineRoutine: {},
        cultureResults: [],
        nursingRecords: []
      }
    };
  },
  created() {
    const id = this.$route.query.id;
    // this.isEdit = this.$route.query.edit === "true";
    if (id) {
      this.getDetail(id);
      this.getCultureList();
      this.getRecordList();
    this.loadMaintenanceData();
  },
  watch: {
    $route(to, from) {
      this.loadMaintenanceData();
    }
  },
  methods: {
    // èŽ·å–è¯¦æƒ…
    getDetail(id) {
      getMaintenanceDetail(id).then(response => {
    // åŠ è½½ç»´æŠ¤æ•°æ®
    async loadMaintenanceData() {
      try {
        this.cultureLoading = true;
        this.recordLoading = true;
        const queryParams = {};
        console.log(this.infoid, "this.infoid");
        if (this.infoid) {
          queryParams.infoid = this.infoid;
        } else {
          this.$message.error("缺少必要的路由参数");
          return;
        }
        const response = await maintainList(queryParams);
        if (response.code === 200) {
          this.form = response.data;
        }
      });
    },
    // åŸ¹å…»è®°å½•附件变更
    handleCultureAttachmentChange(fileList) {
      this.cultureForm.attachments = fileList;
    },
          let maintenanceData = response.data[0];
    // æŠ¤ç†è®°å½•附件变更
    handleRecordAttachmentChange(fileList) {
      this.recordForm.attachments = fileList;
    },
    // æŸ¥çœ‹åŸ¹å…»è®°å½•附件
    handleViewCultureAttachments(row) {
      console.log(22, row.attachments);
      this.currentAttachmentList = row.attachments || [];
      this.attachmentPreviewTitle = `培养记录附件 - ${row.cultureType}`;
      this.attachmentPreviewVisible = true;
    },
    handleTabClick(tab) {
      this.$nextTick(() => {
        console.log(tab.name, 88);
        const tableRef=null;
        if (tab.name == "liverKidney") {
           tableRef = this.$refs.liverKidney; // è¯·æ›¿æ¢ä¸ºæ‚¨çš„表格 ref
        } else if (tab.name == "bloodRoutine") {
           tableRef = this.$refs.bloodRoutine; // è¯·æ›¿æ¢ä¸ºæ‚¨çš„表格 ref
        } else if (tab.name == "bloodRoutine") {
           tableRef = this.$refs.bloodRoutine; // è¯·æ›¿æ¢ä¸ºæ‚¨çš„表格 ref
        }
        // å¦‚果是 el-table,尝试调用其 doLayout æ–¹æ³•
        if (tableRef && tableRef.doLayout) {
          tableRef.doLayout();
        }
        // æˆ–者,更通用的强制重新渲染方式
        this.$forceUpdate(); // æ…Žç”¨ï¼Œå¯èƒ½å¼•发其他问题[1](@ref)
      });
    },
    // æŸ¥çœ‹æŠ¤ç†è®°å½•附件
    handleViewRecordAttachments(row) {
      this.currentAttachmentList = row.attachments || [];
      this.attachmentPreviewTitle = `护理核查记录附件 - ${row.recorder}`;
      this.attachmentPreviewVisible = true;
    },
    // åŸ¹å…»è®°å½•相关方法
    getCultureList() {
      this.cultureLoading = true;
      listCultureResults(this.form.id, this.cultureQueryParams)
        .then(response => {
          if (response.code === 200) {
            // this.cultureList = response.data.rows;
            // this.cultureTotal = response.data.total;
          if (Array.isArray(maintenanceData)) {
            maintenanceData = maintenanceData[0] || {};
          }
          this.cultureLoading = false;
        })
        .catch(() => {
          this.cultureLoading = false;
        });
          if (maintenanceData.extracontent) {
            this.extracontentinfo = JSON.parse(maintenanceData.extracontent);
            if (this.extracontentinfo.specialMedicalHistory) {
              this.form.specialMedicalHistory = this.extracontentinfo.specialMedicalHistory;
            }
          }
          if (maintenanceData.itemDesc) {
            try {
              const itemDescData = maintenanceData.itemDesc;
              this.assessmentData = { ...this.assessmentData, ...itemDescData };
              if (itemDescData.cultureResults) {
                this.cultureList = itemDescData.cultureResults;
              }
              if (itemDescData.nursingRecords) {
                this.recordList = itemDescData.nursingRecords;
              }
              if (itemDescData.liverKidney) {
                this.assessmentData.liverKidney = itemDescData.liverKidney;
              }
              if (itemDescData.bloodRoutine) {
                this.assessmentData.bloodRoutine = itemDescData.bloodRoutine;
              }
              if (itemDescData.urineRoutine) {
                this.assessmentData.urineRoutine = itemDescData.urineRoutine;
              }
            } catch (error) {
              console.error("解析itemDesc JSON失败:", error);
            }
          }
          this.form = { ...this.form, ...maintenanceData };
          this.$message.success("数据加载成功");
        } else {
          this.$message.error("数据加载失败:" + (response.msg || "未知错误"));
        }
      } catch (error) {
        console.error("加载维护数据失败:", error);
        this.$message.error("数据加载失败");
      } finally {
        this.cultureLoading = false;
        this.recordLoading = false;
      }
    },
    // åˆ‡æ¢ç¼–辑模式
    toggleEditMode() {
      this.isEdit = !this.isEdit;
      if (!this.isEdit) {
      }
    },
    // åŸ¹å…»è®°å½•相关方法
    handleAddCulture() {
      this.cultureDialogTitle = "新增培养记录";
      this.cultureForm = {
@@ -704,12 +787,9 @@
        cultureType: "",
        sampleTime: "",
        result: "阴性",
        bacteria: "",
        drugSensitivity: "",
        testingInstitution: "",
        specimenType: "",
        remarks: ""
        attachments: []
      };
      this.cultureFileList = [];
      this.cultureDialogVisible = true;
      this.$nextTick(() => {
        this.$refs.cultureForm && this.$refs.cultureForm.clearValidate();
@@ -719,76 +799,42 @@
    handleEditCulture(row) {
      this.cultureDialogTitle = "编辑培养记录";
      this.cultureForm = { ...row };
      this.cultureFileList = row.attachments
        ? row.attachments.map(item => ({
            uid: item.id || Math.random(),
            name: item.fileName,
            fileSize: item.fileSize,
            url: item.path || item.fileUrl,
            uploadTime: item.uploadTime,
            status: "success"
          }))
        : [];
      this.cultureDialogVisible = true;
      this.$nextTick(() => {
        this.$refs.cultureForm && this.$refs.cultureForm.clearValidate();
      });
    },
    handleViewCulture(row) {
      this.$alert(
        `
        <div>
          <p><strong>培养类型:</strong>${this.getCultureTypeLabel(
            row.cultureType
          )}</p>
          <p><strong>采样时间:</strong>${row.sampleTime}</p>
          <p><strong>培养结果:</strong>${row.result}</p>
          <p><strong>检测机构:</strong>${row.testingInstitution}</p>
        </div>
      `,
        "培养记录详情",
        {
          dangerouslyUseHTMLString: true,
          customClass: "detail-dialog"
        }
      );
    },
    toggleEditMode() {
      this.isEdit = !this.isEdit;
      // if (!this.isEdit) {
      //   this.saveAllData();
      // }
    },
    handleLiverKidneyDataChange(data) {
      console.log("肝功能肾功能数据变更:", data);
      // å¤„理数据保存或临时存储
    },
    handleBloodRoutineDataChange(data) {
      console.log("血常规功能数据变更:", data);
      // å¤„理数据保存或临时存储
    },
    handleUrineRoutineDataChange(data) {
      console.log("尿常规功能数据变更:", data);
      // å¤„理数据保存或临时存储
    },
    // ä¿å­˜åŸ¹å…»è®°å½•
    handleSaveCulture() {
      this.$refs.cultureForm.validate(valid => {
        if (valid) {
          this.cultureSaveLoading = true;
          const api = this.cultureForm.id
            ? updateCultureResult
            : addCultureResult;
          const requestData = {
            ...this.cultureForm,
            maintenanceId: this.form.id
          };
          api(requestData)
            .then(response => {
              if (response.code === 200) {
                this.$message.success(
                  this.cultureForm.id ? "修改成功" : "新增成功"
                );
                this.cultureDialogVisible = false;
                this.getCultureList();
              }
              this.cultureSaveLoading = false;
            })
            .catch(() => {
              this.cultureSaveLoading = false;
            });
          if (this.cultureForm.id) {
            const index = this.cultureList.findIndex(
              item => item.id === this.cultureForm.id
            );
            if (index !== -1) {
              this.cultureList.splice(index, 1, { ...this.cultureForm });
            }
          } else {
            this.cultureForm.id = Date.now();
            this.cultureList.push({ ...this.cultureForm });
          }
          this.$message.success(this.cultureForm.id ? "修改成功" : "新增成功");
          this.cultureDialogVisible = false;
          this.cultureSaveLoading = false;
        }
      });
    },
@@ -800,37 +846,15 @@
        type: "warning"
      })
        .then(() => {
          deleteCultureResult(row.id).then(response => {
            if (response.code === 200) {
              this.$message.success("删除成功");
              this.getCultureList();
            }
          });
          this.cultureList = this.cultureList.filter(
            item => item.id !== row.id
          );
          this.$message.success("删除成功");
        })
        .catch(() => {});
    },
    getCultureTypeLabel(value) {
      const type = this.cultureTypeOptions.find(item => item.value === value);
      return type ? type.label : "未知";
    },
    // æŠ¤ç†æ ¸æŸ¥è®°å½•相关方法
    getRecordList() {
      this.recordLoading = true;
      listNursingRecords(this.form.id, this.recordQueryParams)
        .then(response => {
          if (response.code === 200) {
            // this.recordList = response.data.rows;
            // this.recordTotal = response.data.total;
          }
          this.recordLoading = false;
        })
        .catch(() => {
          this.recordLoading = false;
        });
    },
    // æŠ¤ç†è®°å½•相关方法
    handleAddRecord() {
      this.recordDialogTitle = "新增护理核查记录";
      this.recordForm = {
@@ -839,16 +863,11 @@
          .toISOString()
          .replace("T", " ")
          .substring(0, 19),
        recorder: "当前用户", // å®žé™…项目中从用户信息获取
        temperature: 36.5,
        heartRate: 80,
        bloodPressure: "120/80",
        respirationRate: 18,
        oxygenSaturation: 98,
        urineOutput: 50,
        cvp: 8,
        remarks: ""
        recorder: "当前用户",
        checkRecord: "",
        attachments: []
      };
      this.recordFileList = [];
      this.recordDialogVisible = true;
      this.$nextTick(() => {
        this.$refs.recordForm && this.$refs.recordForm.clearValidate();
@@ -858,64 +877,42 @@
    handleEditRecord(row) {
      this.recordDialogTitle = "编辑护理核查记录";
      this.recordForm = { ...row };
      this.recordFileList = row.attachments
        ? row.attachments.map(item => ({
            uid: item.id || Math.random(),
            name: item.fileName,
            fileSize: item.fileSize,
            url: item.path || item.fileUrl,
            uploadTime: item.uploadTime,
            status: "success"
          }))
        : [];
      this.recordDialogVisible = true;
      this.$nextTick(() => {
        this.$refs.recordForm && this.$refs.recordForm.clearValidate();
      });
    },
    handleViewRecord(row) {
      this.$alert(
        `
        <div>
          <p><strong>核查时间:</strong>${row.recordTime}</p>
          <p><strong>核查人:</strong>${row.recorder}</p>
          <p><strong>生命体征:</strong></p>
          <ul>
            <li>体温:${row.temperature}℃</li>
            <li>心率:${row.heartRate}次/分</li>
            <li>血压:${row.bloodPressure}mmHg</li>
            <li>呼吸:${row.respirationRate}次/分</li>
            <li>血氧饱和度:${row.oxygenSaturation}%</li>
            <li>尿量:${row.urineOutput}ml/h</li>
          </ul>
          <p><strong>备注:</strong>${row.remarks || "无"}</p>
        </div>
      `,
        "护理核查记录详情",
        {
          dangerouslyUseHTMLString: true,
          customClass: "detail-dialog"
        }
      );
    },
    handleSaveRecord() {
      this.$refs.recordForm.validate(valid => {
        if (valid) {
          this.recordSaveLoading = true;
          const api = this.recordForm.id
            ? updateNursingRecord
            : addNursingRecord;
          const requestData = {
            ...this.recordForm,
            maintenanceId: this.form.id
          };
          api(requestData)
            .then(response => {
              if (response.code === 200) {
                this.$message.success(
                  this.recordForm.id ? "修改成功" : "新增成功"
                );
                this.recordDialogVisible = false;
                this.getRecordList();
              }
              this.recordSaveLoading = false;
            })
            .catch(() => {
              this.recordSaveLoading = false;
            });
          if (this.recordForm.id) {
            const index = this.recordList.findIndex(
              item => item.id === this.recordForm.id
            );
            if (index !== -1) {
              this.recordList.splice(index, 1, { ...this.recordForm });
            }
          } else {
            this.recordForm.id = Date.now();
            this.recordList.push({ ...this.recordForm });
          }
          this.$message.success(this.recordForm.id ? "修改成功" : "新增成功");
          this.recordDialogVisible = false;
          this.recordSaveLoading = false;
        }
      });
    },
@@ -927,26 +924,260 @@
        type: "warning"
      })
        .then(() => {
          deleteNursingRecord(row.id).then(response => {
            if (response.code === 200) {
              this.$message.success("删除成功");
              this.getRecordList();
            }
          });
          this.recordList = this.recordList.filter(item => item.id !== row.id);
          this.$message.success("删除成功");
        })
        .catch(() => {});
    },
    // ä¿å­˜åŸºæœ¬ä¿¡æ¯
    handleSave() {
      this.$refs.form.validate(valid => {
        if (valid) {
          updateMaintenance(this.form).then(response => {
            if (response.code === 200) {
              this.$message.success("保存成功");
              this.isEdit = false;
            }
          });
    // åŸ¹å…»è®°å½•附件相关方法
    handleCultureAttachmentChange(fileList) {
      this.cultureFileList = fileList;
    },
    handleCultureUploadSuccess({ file, fileList, response }) {
      if (response.code === 200) {
        const attachmentObj = {
          fileName: file.name,
          path: response.fileUrl || file.url,
          fileUrl: response.fileUrl || file.url,
          fileType: this.getFileExtension(file.name),
          fileSize: file.size,
          uploadTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
        };
        if (!Array.isArray(this.cultureForm.attachments)) {
          this.cultureForm.attachments = [];
        }
        this.cultureForm.attachments.push(attachmentObj);
        this.cultureFileList = fileList;
        this.$message.success("文件上传成功");
      }
    },
    handleCultureUploadError({ file, fileList, error }) {
      console.error("培养记录附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    handleCultureAttachmentRemove(file) {
      if (file.url) {
        const index = this.cultureForm.attachments.findIndex(
          item => item.path === file.url || item.fileUrl === file.url
        );
        if (index > -1) {
          this.cultureForm.attachments.splice(index, 1);
          this.$message.success("附件删除成功");
        }
      }
    },
    // æŠ¤ç†è®°å½•附件相关方法
    handleRecordAttachmentChange(fileList) {
      this.recordFileList = fileList;
    },
    handleRecordUploadSuccess({ file, fileList, response }) {
      if (response.code === 200) {
        const attachmentObj = {
          fileName: file.name,
          path: response.fileUrl || file.url,
          fileUrl: response.fileUrl || file.url,
          fileType: this.getFileExtension(file.name),
          fileSize: file.size,
          uploadTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
        };
        if (!Array.isArray(this.recordForm.attachments)) {
          this.recordForm.attachments = [];
        }
        this.recordForm.attachments.push(attachmentObj);
        this.recordFileList = fileList;
        this.$message.success("文件上传成功");
      }
    },
    handleRecordUploadError({ file, fileList, error }) {
      console.error("护理记录附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    handleRecordAttachmentRemove(file) {
      if (file.url) {
        const index = this.recordForm.attachments.findIndex(
          item => item.path === file.url || item.fileUrl === file.url
        );
        if (index > -1) {
          this.recordForm.attachments.splice(index, 1);
          this.$message.success("附件删除成功");
        }
      }
    },
    handleViewCultureAttachments(row) {
      this.currentAttachmentList = row.attachments || [];
      this.attachmentPreviewTitle = `培养记录附件 - ${row.cultureType}`;
      this.attachmentPreviewVisible = true;
    },
    handleViewRecordAttachments(row) {
      this.currentAttachmentList = row.attachments || [];
      this.attachmentPreviewTitle = `护理核查记录附件 - ${row.recorder}`;
      this.attachmentPreviewVisible = true;
    },
    handleAttachmentPreviewClose() {
      this.currentAttachmentList = [];
      this.attachmentPreviewTitle = "";
    },
    handlePreviewAttachment(file) {
      this.currentPreviewFile = {
        fileName: file.fileName,
        fileUrl: file.path || file.fileUrl,
        fileType: this.getFileType(file.fileName)
      };
      this.filePreviewVisible = true;
    },
    handleDownloadAttachment(file) {
      const fileUrl = file.path || 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 = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
      const pdfTypes = ["pdf"];
      const officeTypes = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"];
      if (imageTypes.includes(extension)) return "image";
      if (pdfTypes.includes(extension)) return "pdf";
      if (officeTypes.includes(extension)) return "office";
      return "other";
    },
    /** èŽ·å–æ–‡ä»¶å›¾æ ‡é¢œè‰² */
    getFileIconColor(fileName) {
      const type = this.getFileType(fileName);
      const colorMap = {
        image: "#67C23A",
        pdf: "#F56C6C",
        office: "#409EFF",
        other: "#909399"
      };
      return colorMap[type] || "#909399";
    },
    /** èŽ·å–æ–‡ä»¶æ ‡ç­¾ç±»åž‹ */
    getFileTagType(fileName) {
      const type = this.getFileType(fileName);
      const typeMap = {
        image: "success",
        pdf: "danger",
        office: "primary",
        other: "info"
      };
      return typeMap[type] || "info";
    },
    /** èŽ·å–æ–‡ä»¶ç±»åž‹æ–‡æœ¬ */
    getFileTypeText(fileName) {
      const type = this.getFileType(fileName);
      const textMap = {
        image: "图片",
        pdf: "PDF",
        office: "文档",
        other: "其他"
      };
      return textMap[type] || "未知";
    },
    /** æ£€æŸ¥æ˜¯å¦å¯é¢„览 */
    isPreviewable(fileName) {
      const type = this.getFileType(fileName);
      return ["image", "pdf"].includes(type);
    },
    /** èŽ·å–æ–‡ä»¶æ‰©å±•å */
    getFileExtension(filename) {
      return filename
        .split(".")
        .pop()
        .toLowerCase();
    },
    /** æ ¼å¼åŒ–文件大小 */
    formatFileSize(bytes) {
      if (!bytes || bytes === 0) return "0 B";
      const k = 1024;
      const sizes = ["B", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / 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;
      }
    },
    // è¯„估数据变更处理
    handleLiverKidneyDataChange(data) {
      this.assessmentData.liverKidney = data;
    },
    handleBloodRoutineDataChange(data) {
      this.assessmentData.bloodRoutine = data;
    },
    handleUrineRoutineDataChange(data) {
      this.assessmentData.urineRoutine = data;
    },
    handleTabClick(tab) {
      this.$nextTick(() => {
        let tableRef = null;
        if (tab.name === "liverKidney") {
          tableRef = this.$refs.liverKidney;
        } else if (tab.name === "bloodRoutine") {
          tableRef = this.$refs.bloodRoutine;
        } else if (tab.name === "urineRoutine") {
          tableRef = this.$refs.urineRoutine;
        }
        if (tableRef && tableRef.doLayout) {
          tableRef.doLayout();
        }
      });
    }
@@ -963,92 +1194,30 @@
  margin-bottom: 20px;
}
.culture-card {
  margin-bottom: 20px;
}
.record-card {
  margin-bottom: 20px;
}
.detail-title {
  font-size: 16px;
  font-weight: bold;
}
.maintenance-detail {
  padding: 20px;
}
.detail-card {
  margin-bottom: 20px;
}
.culture-card {
  margin-bottom: 20px;
}
.record-card {
  margin-bottom: 20px;
}
.detail-title {
  font-size: 16px;
  font-weight: bold;
}
.medical-panel {
  padding: 20px;
}
.attachment-section {
  margin-top: 20px;
  padding: 15px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
}
.attachment-title {
  font-weight: bold;
  margin-bottom: 10px;
  color: #409eff;
}
.required-item::before {
  content: "*";
  color: #f56c6c;
  margin-right: 4px;
}
.assessment-card {
  margin-bottom: 20px;
}
.medical-table {
  width: 100%;
.record-card {
  margin-bottom: 20px;
}
.dynamic-column {
  min-width: 120px;
.detail-title {
  font-size: 16px;
  font-weight: bold;
  margin-right: 20px;
}
.culture-card {
  margin-bottom: 20px;
}
.fixed-width .el-button {
  margin: 0 2px;
}
/* è¯¦æƒ…对话框样式 */
:deep(.detail-dialog) {
  width: 500px;
}
:deep(.detail-dialog .el-message-box__content) {
  line-height: 1.8;
}
:deep(.detail-dialog ul) {
  margin: 10px 0;
  padding-left: 20px;
}
:deep(.detail-dialog li) {
  margin-bottom: 5px;
.file-name {
  font-size: 13px;
  margin-left: 8px;
}
</style>
src/views/business/course/components/MedicalAssessmentStage.vue
@@ -1,87 +1,374 @@
<template>
  <div class="assessment-detail">
    <el-card class="basic-info-card">
      <div slot="header" class="clearfix">
        <span>案例基本信息</span>
        <el-button
          style="float: right; padding: 3px 0"
          type="text"
          @click="handleAttachmentPreview"
        >
          <i class="el-icon-folder-opened"></i> æŸ¥çœ‹é™„ä»¶
        </el-button>
      </div>
      <el-descriptions :column="2" border>
        <el-descriptions-item label="住院号">{{
          assessmentData.caseNo
        }}</el-descriptions-item>
        <el-descriptions-item label="潜在捐献者姓名">{{
          assessmentData.name || assessmentData.donorName
        }}</el-descriptions-item>
        <el-descriptions-item label="性别">
          <dict-tag
            :options="dict.type.sys_user_sex"
            :value="assessmentData.sex"
          />
        </el-descriptions-item>
        <el-descriptions-item label="年龄"
          >{{ assessmentData.age }}岁</el-descriptions-item
        >
        <el-descriptions-item label="血型">
          {{ assessmentData.bloodtype }}
        </el-descriptions-item>
        <el-descriptions-item label="证件号码">{{
          assessmentData.idcardno
        }}</el-descriptions-item>
        <el-descriptions-item label="疾病诊断">{{
          assessmentData.diagnosisname
        }}</el-descriptions-item>
        <el-descriptions-item label="所在医疗机构">{{
          assessmentData.treatmenthospitalname
        }}</el-descriptions-item>
        <el-descriptions-item label="协调员">{{
          assessmentData.coordinatorName
        }}</el-descriptions-item>
        <el-descriptions-item label="评估时间">{{
          assessmentData.assessTime
        }}</el-descriptions-item>
        <el-descriptions-item label="评估状态">
          <el-tag :type="statusFilter(assessmentData.assessState)">
            {{ statusTextFilter(assessmentData.assessState) }}
          </el-tag>
        </el-descriptions-item>
      </el-descriptions>
    </el-card>
    <el-card class="organ-assessment-card">
      <div slot="header" class="clearfix">
        <span>器官评估表</span>
        <!-- <el-button
        <el-button
          v-if="isCoordinator && allOrgansAssessed"
          style="float: right; margin-left: 10px"
          type="primary"
          size="mini"
          @click="handleCompleteAssessment"
          :loading="saveLoading"
        >
          ç¡®è®¤å®Œæˆè¯„ä¼°
        </el-button> -->
        <!-- <span v-if="!isCoordinator" class="jstitle">
        </el-button>
        <span v-if="!isCoordinator" class="jstitle">
          å½“前角色:{{ currentDepartment }}评估人员
        </span> -->
        </span>
      </div>
      <!-- æçŒ®å†³å®šè¡¨å• -->
      <el-form>
        <el-form-item label-width="100px" label="捐献决定">
          <el-checkbox-group
            v-model="organdecisionValues"
            @change="handleOrganDecisionChange"
            :disabled="!isEdit"
          >
            <el-checkbox
              v-for="dict in dict.type.sys_Organ"
              :key="dict.value"
              :label="dict.value"
              >{{ dict.label }}
            </el-checkbox>
          </el-checkbox-group>
          <el-input
            v-if="showOtherInput"
            v-model="organdecisionOther"
            placeholder="请输入其他捐献决定的具体内容"
            style="margin-top: 10px; width: 300px;"
            :readonly="!isEdit"
            @input="handleOtherDecisionInput"
          ></el-input>
        </el-form-item>
      </el-form>
      <!-- å™¨å®˜è¯„估表格 -->
      <el-table
        :data="organAssessmentList"
        :data="filteredOrganAssessmentList"
        v-loading="assessmentLoading"
        style="width: 100%"
        :row-class-name="getRowClassName"
        :expand-row-keys="expandedRowKeys"
        @expand-change="handleExpandChange"
        row-key="organno"
        empty-text="当前捐献决定下暂无需要评估的器官"
      >
        <el-table-column type="expand">
          <template slot-scope="scope">
            <div class="organ-expand-content" v-if="scope.row.expanded">
              <el-tabs
                v-model="scope.row.activeTab"
                type="card"
                class="organ-detail-tabs"
                @tab-click="handleTabClick(scope.row, $event)"
              >
                <!-- åŠ¨æ€ç”Ÿæˆè¯„ä¼°Tab -->
                <el-tab-pane
                  v-for="(assessment, index) in getOrganAssessments(scope.row)"
                  :key="`${scope.row.organno}_${index}`"
                  :label="`第${index + 1}次评估`"
                  :name="index"
                >
                  <organ-assessment-form
                    :organ-data="scope.row"
                    :assessment-index="index"
                    :readonly="!canAssessOrgan(scope.row)"
                    :current-user="currentUser"
                    @update-assessment="handleUpdateAssessment"
                    @delete-assessment="handleDeleteAssessment"
                    @save-assessment="handleSaveAssessment"
                  />
                </el-tab-pane>
                <!-- æ·»åŠ è¯„ä¼°æŒ‰é’® -->
                <el-tab-pane
                  name="add"
                  v-if="canAssessOrgan(scope.row) && isEdit"
                >
                  <template slot="label">
                    <el-button
                      type="text"
                      icon="el-icon-plus"
                      @click.stop="handleAddNewAssessment(scope.row)"
                      size="mini"
                    >
                      æ–°å¢žè¯„ä¼°
                    </el-button>
                  </template>
                  <div class="add-assessment-prompt">
                    <el-empty description="点击上方按钮添加新的评估"></el-empty>
                  </div>
                </el-tab-pane>
                <!-- è¯„估汇总Tab -->
              <el-tab-pane label="评估汇总" name="summary">
  <div class="assessment-summary">
    <!-- åŸºæœ¬ä¿¡æ¯æ±‡æ€» -->
    <el-descriptions title="基本信息汇总" :column="2" border>
      <el-descriptions-item label="器官类型">
        <span class="summary-item">{{ getOrganLabel(scope.row.organno) }}</span>
      </el-descriptions-item>
      <el-descriptions-item label="获取机构">
        <span class="summary-item">{{ scope.row.gainhospitalname }}</span>
      </el-descriptions-item>
      <el-descriptions-item label="评估次数" :span="2">
        <el-tag type="info" size="medium">
          {{ getAssessmentCount(scope.row) }}次
        </el-tag>
      </el-descriptions-item>
      <el-descriptions-item label="最新评估时间" :span="2">
        <span class="highlight-text">{{ getLatestAssessmentTime(scope.row) || "-" }}</span>
      </el-descriptions-item>
    </el-descriptions>
    <!-- è¯„估详情汇总 -->
    <el-card header="评估详情列表" style="margin-top: 20px;" class="assessment-detail-card">
      <div v-if="getOrganAssessments(scope.row).length === 0" class="no-assessment">
        <el-empty description="暂无评估记录"></el-empty>
      </div>
      <div v-else>
        <!-- æ¯æ¬¡è¯„估详情 -->
        <div v-for="(assessment, index) in getOrganAssessments(scope.row)"
             :key="index"
             class="assessment-item">
          <el-card shadow="hover" class="assessment-card">
            <div slot="header" class="clearfix">
              <span class="assessment-title">第{{ index + 1 }}次评估</span>
              <el-tag
                :type="getAssessmentTagType(assessment.status)"
                size="small"
                class="status-tag"
              >
                {{ getAssessmentStatusText(assessment.status) }}
              </el-tag>
            </div>
            <el-descriptions :column="2" border class="detail-descriptions">
              <el-descriptions-item label="评估时间" :span="2">
                <span class="time-text">{{ assessment.assessmentTime || "-" }}</span>
              </el-descriptions-item>
              <el-descriptions-item label="评估人">
                <el-tag type="info" size="small">
                  {{ assessment.assessor || "未填写" }}
                </el-tag>
              </el-descriptions-item>
              <el-descriptions-item label="功能状态">
                <el-tag
                  :type="getFunctionStatusTagType(assessment.functionStatus)"
                  size="small"
                >
                  {{ getFunctionStatusText(assessment.functionStatus) }}
                </el-tag>
              </el-descriptions-item>
              <el-descriptions-item label="评估意见" :span="2">
                <div class="opinion-content">
                  {{ assessment.assessmentOpinion || "暂无评估意见" }}
                </div>
              </el-descriptions-item>
              <el-descriptions-item label="附件数量" :span="2" v-if="assessment.attachments && assessment.attachments.length > 0">
                <el-tag type="success" size="small">
                  {{ assessment.attachments.length }}个
                </el-tag>
                <el-button
                  type="text"
                  size="mini"
                  @click="showAttachmentList(assessment.attachments, index + 1)"
                  style="margin-left: 10px;"
                >
                  æŸ¥çœ‹é™„件列表
                </el-button>
              </el-descriptions-item>
            </el-descriptions>
          </el-card>
        </div>
      </div>
    </el-card>
    <!-- å™¨å®˜åŸºæœ¬ä¿¡æ¯å¡ç‰‡ -->
    <el-card header="器官信息" style="margin-top: 20px;" class="organ-info-card">
      <el-descriptions :column="2" border>
        <el-descriptions-item label="获取前活检">
          <el-tag
            :type="
              scope.row.isbiopsybefore === '1'
                ? 'success'
                : 'info'
            "
            size="small"
          >
            {{ scope.row.isbiopsybefore === "1" ? "是" : "否" }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="获取后活检">
          <el-tag
            :type="
              scope.row.isbiopsyafter === '1'
                ? 'success'
                : 'info'
            "
            size="small"
          >
            {{ scope.row.isbiopsyafter === "1" ? "是" : "否" }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="边缘器官">
          <el-tag
            :type="
              scope.row.ismarginalorgan === '1'
                ? 'warning'
                : 'info'
            "
            size="small"
          >
            {{
              scope.row.ismarginalorgan === "1" ? "是" : "否"
            }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="病原菌阳性">
          <el-tag
            :type="
              scope.row.ispathogenpositive === '1'
                ? 'danger'
                : 'info'
            "
            size="small"
          >
            {{
              scope.row.ispathogenpositive === "1" ? "是" : "否"
            }}
          </el-tag>
        </el-descriptions-item>
      </el-descriptions>
    </el-card>
  </div>
</el-tab-pane>
              </el-tabs>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="器官类型" align="center" prop="organname">
          <template slot-scope="scope">
            {{ getOrganLabel(scope.row.organno) }}
          </template>
        </el-table-column>
        <el-table-column label="器官编号" align="center" prop="organnumber" />
        <el-table-column
          label="器官类型"
          label="获取机构"
          align="center"
          prop="organType"
          prop="gainhospitalname"
          show-overflow-tooltip
        />
        <!-- åŠ¨æ€æ˜¾ç¤ºè¯„ä¼°çŠ¶æ€åˆ— -->
        <el-table-column
          v-for="index in getMaxAssessmentCount()"
          :key="index"
          :label="`第${index}次评估`"
          align="center"
          width="120"
        >
          <template slot-scope="scope">
            <dict-tag
              :options="organTypeOptions"
              :value="scope.row.organType"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="评估科室"
          align="center"
          prop="department"
          width="120"
        />
        <el-table-column
          label="评估人员"
          align="center"
          prop="assessor"
          width="100"
        />
        <el-table-column label="评估状态" align="center" width="100">
          <template slot-scope="scope">
            <el-tag
              :type="scope.row.assessmentStatus === '1' ? 'success' : 'warning'"
              v-if="getOrganAssessments(scope.row)[index - 1]"
              :type="
                getAssessmentTagType(
                  getOrganAssessments(scope.row)[index - 1].status
                )
              "
              size="small"
            >
              {{ scope.row.assessmentStatus === "1" ? "已评估" : "待评估" }}
              {{
                getAssessmentStatusText(
                  getOrganAssessments(scope.row)[index - 1].status
                )
              }}
            </el-tag>
            <el-tag v-else type="info" size="small">未评估</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="整体状态" align="center" width="100">
          <template slot-scope="scope">
            <el-tag
              :type="
                getOrganOverallStatus(scope.row) === 'completed'
                  ? 'success'
                  : getOrganOverallStatus(scope.row) === 'assessing'
                  ? 'primary'
                  : 'warning'
              "
              size="small"
            >
              {{ getOrganOverallStatusText(scope.row) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="功能状态" align="center" width="100">
          <template slot-scope="scope">
            <el-tag
              :type="getFunctionStatusType(scope.row.functionStatus)"
              size="small"
            >
              {{ getFunctionStatusText(scope.row.functionStatus) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="评估意见"
          align="center"
          prop="assessmentOpinion"
          min-width="150"
          show-overflow-tooltip
        />
        <el-table-column label="评估时间" align="center" width="120">
          <template slot-scope="scope">
            <span>{{ scope.row.assessmentTime || "-" }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          align="center"
@@ -90,264 +377,995 @@
        >
          <template slot-scope="scope">
            <el-button
              v-if="canAssessOrgan(scope.row)"
              v-if="canAssessOrgan(scope.row) && isEdit"
              size="mini"
              type="text"
              @click="handleOrganAssess(scope.row)"
              >{{
                scope.row.assessmentStatus === "1" ? "查看/修改" : "评估"
              }}</el-button
              @click="handleToggleExpand(scope.row)"
            >
              {{
                expandedRowKeys.includes(scope.row.organno) ? "收起" : "详情"
              }}
            </el-button>
            <el-button
              v-else-if="!isEdit"
              size="mini"
              type="text"
              @click="handleToggleExpand(scope.row)"
            >
              {{
                expandedRowKeys.includes(scope.row.organno) ? "收起" : "查看"
              }}
            </el-button>
            <el-button v-else size="mini" type="text" disabled
              >无权限</el-button
            >
            <el-button
              v-if="isCoordinator"
              size="mini"
              type="text"
              @click="handleViewOrganDetail(scope.row)"
              >详情</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <!-- å™¨å®˜è¯„估详情 -->
      <el-collapse
        v-model="activeOrgans"
        accordion
        class="organ-detail-collapse"
      >
        <el-collapse-item
          v-for="organ in organAssessmentList"
          :key="organ.organType"
          :name="organ.organType"
          :title="getOrganTitle(organ)"
        >
          <organ-assessment-form
            :organ-data="organ"
            :readonly="!canAssessOrgan(organ)"
            @save="handleSaveOrganAssessment"
          />
        </el-collapse-item>
      </el-collapse>
    </el-card>
    <!-- é™„件预览弹窗 -->
    <attachment-preview
    <!-- æ•´ä½“保存按钮 -->
    <div class="footer-actions" v-if="isEdit">
      <el-button type="primary" @click="handleSaveAll" :loading="saveLoading"
        >保存评估表</el-button
      >
      <el-button @click="handleCancel">取消</el-button>
    </div>
    <!-- æ–‡ä»¶é¢„览弹窗 -->
    <FilePreviewDialog
      :visible="attachmentVisible"
      :attachment-list="attachmentList"
      :file="currentPreviewFile"
      @close="attachmentVisible = false"
    />
  </div>
</template>
<script>
// import { getAssessment, updateOrganAssessment, completeAssessment } from "@/api/case/assessment";
import {
  getAssessment,
  updateOrganAssessment,
  completeAssessment
} from "./api/mockAssessmentApi";
import OrganAssessmentForm from "./components/OrganAssessmentForm.vue";
import AttachmentPreview from "./components/AttachmentPreview.vue";
  evaluateBaseInfolist,
  assessedit,
  assessAdd
} from "@/api/businessApi/index";
import FilePreviewDialog from "@/components/FilePreviewDialog";
import OrganAssessmentForm from "@/components/assessInfoComponents/OrganAssessmentForm.vue";
export default {
  name: "AssessmentDetail",
  components: { OrganAssessmentForm, AttachmentPreview },
  components: { OrganAssessmentForm, FilePreviewDialog },
  dicts: ["sys_user_sex", "sys_Organ", "sys_0_1"],
    props: {
    infoid: {
      type: String,
      default: true
    }
  },
  data() {
    return {
      // è¯„ä¼°ID
      assessmentId: undefined,
      // è¯„估数据
      assessmentData: {},
      // å™¨å®˜è¯„估列表
      organAssessmentList: [],
      // é™„件列表
      attachmentList: [],
      // æ˜¯å¦ç¼–辑模式
      isEdit: false,
      // åŠ è½½çŠ¶æ€
      assessmentLoading: false,
      // å½“前用户信息
      saveLoading: false,
      // æ•°æ®ID
      assessmentId: undefined,
      // ä¸»è¦æ•°æ®
      assessmentData: {},
      organAssessmentList: [], // æ‰€æœ‰å™¨å®˜æ•°æ®
      // å±•开行相关
      expandedRowKeys: [],
      // é™„件相关
      currentPreviewFile: null,
      attachmentVisible: false,
      // æçŒ®å†³å®šç›¸å…³
      organdecisionValues: [], // å­˜å‚¨å­—å…¸value
      organdecisionOther: "",
      // è®°å½•捐献决定变化前的值
      prevOrgandecisionValues: [],
      // å™¨å®˜ç±»åž‹æ˜ å°„关系
      organDecisionMapping: {
        C38: ["C38"], // å¿ƒè„ -> å¿ƒè„
        C22: ["C22"], // å…¨è‚ -> å…¨è‚
        C22L: ["C22L"], // å·¦åŠè‚ -> å·¦åŠè‚
        C22L0: ["C22L0"], // å·¦å¤–叶 -> å·¦å¤–叶
        C22R: ["C22R"], // å³åŠè‚ -> å³åŠè‚
        C22R0: ["C22R0"], // å³ä¸‰å¶ -> å³ä¸‰å¶
        C64: ["C64L", "C64R"], // åŒè‚¾ -> å·¦è‚¾, å³è‚¾
        C64L: ["C64L"], // å·¦è‚¾ -> å·¦è‚¾
        C64R: ["C64R"], // å³è‚¾ -> å³è‚¾
        C34: ["C34L", "C34R"], // å…¨è‚º -> å·¦è‚º, å³è‚º
        C34L: ["C34L"], // å·¦è‚º -> å·¦è‚º
        C34R: ["C34R"], // å³è‚º -> å³è‚º
        C17: ["C17"], // å°è‚  -> å°è‚ 
        C25: ["C25"], // èƒ°è…º -> èƒ°è…º
        C00: [], // é—体 -> ä¸æ˜ å°„具体器官(遗体是整体)
        C69L: ["C69L"], // å·¦çœ¼è§’膜 -> å·¦çœ¼è§’膜
        C69R: ["C69R"], // å³çœ¼è§’膜 -> å³çœ¼è§’膜
        C01: [] // å…¶ä»– -> ä¸æ˜ å°„具体器官
      },
      // ç”¨æˆ·ä¿¡æ¯
      currentUser: {
        id: "001",
        name: "张医生",
        department: "心脏外科",
        role: "department" // coordinator: åè°ƒå‘˜, department: ç§‘室人员
        department: "心脏科",
        role: "coordinator"
      },
      // å±•开的器官
      activeOrgans: [],
      // é™„件预览可见性
      attachmentVisible: false,
      // å­—典选项
      genderOptions: [
        { value: "0", label: "男" },
        { value: "1", label: "女" }
      ],
      bloodTypeOptions: [
        { value: "A", label: "A型" },
        { value: "B", label: "B型" },
        { value: "O", label: "O型" },
        { value: "AB", label: "AB型" }
      ],
      assessmentTypeOptions: [
        { value: "1", label: "初次评估" },
        { value: "2", label: "最终评估" }
      ],
      organTypeOptions: [
        { value: "heart", label: "心脏" },
        { value: "liver", label: "肝脏" },
        { value: "kidney", label: "肾脏" },
        { value: "lung", label: "肺脏" },
        { value: "pancreas", label: "胰腺" },
        { value: "intestine", label: "肠道" },
        { value: "cornea", label: "角膜" },
        { value: "skin", label: "皮肤" }
      ]
      // è®°å½•被删除的器官评估
      deletedOrganAssessments: [],
      // ç”¨äºŽè¿½è¸ªtab激活状态
      activeTabMap: new Map(),
      // åˆ·æ–°key
      refreshKey: 0
    };
  },
  computed: {
    // æ˜¯å¦æ˜¯åè°ƒå‘˜
    isCoordinator() {
      return this.currentUser.role === "coordinator";
    },
    // å½“前科室
    currentDepartment() {
      return this.currentUser.department;
    },
    // æ‰€æœ‰å™¨å®˜æ˜¯å¦éƒ½å·²è¯„ä¼°
    allOrgansAssessed() {
      return this.organAssessmentList.every(
        organ => organ.assessmentStatus === "1"
    // æ ¹æ®æçŒ®å†³å®šè¿‡æ»¤åŽçš„器官列表
    filteredOrganAssessmentList() {
      if (!this.organdecisionValues || this.organdecisionValues.length === 0) {
        return [];
      }
      const selectedOrgans = new Set();
      this.organdecisionValues.forEach(decision => {
        const organs = this.organDecisionMapping[decision] || [];
        organs.forEach(organ => selectedOrgans.add(organ));
      });
      return this.organAssessmentList.filter(
        organ =>
          selectedOrgans.has(organ.organno) &&
          organ.delFlag !== "1" &&
          !this.deletedOrganAssessments.includes(organ.organno)
      );
    },
    // æ£€æŸ¥æ‰€æœ‰è¿‡æ»¤åŽçš„器官是否都已评估
    allOrgansAssessed() {
      return this.filteredOrganAssessmentList.every(organ => {
        const assessments = this.getOrganAssessments(organ);
        return (
          assessments &&
          assessments.length > 0 &&
          assessments.every(assessment => assessment.status === "assessed")
        );
      });
    },
    // èŽ·å–å™¨å®˜å­—å…¸
    organDict() {
      return this.dict.type.sys_Organ || [];
    },
    // åˆ¤æ–­æ˜¯å¦éœ€è¦æ˜¾ç¤ºå…¶ä»–输入框
    showOtherInput() {
      return this.organdecisionValues.includes("C01");
    }
  },
  watch: {
    // ç›‘听捐献决定变化,用于复杂逻辑处理
    organdecisionValues: {
      handler(newVal, oldVal) {
        this.handleComplexDecisionChange(newVal, oldVal);
      },
      deep: true
    }
  },
  created() {
    this.assessmentId = this.$route.query.id;
    this.isEdit = this.$route.query.assess === "true";
    this.getAssessmentDetail();
  },
  methods: {
    // èŽ·å–è¯„ä¼°è¯¦æƒ… - ä½¿ç”¨Mock数据
    getAssessmentDetail() {
      this.assessmentLoading = true;
      getAssessment(this.assessmentId)
        .then(response => {
          if (response.code === 200) {
            this.assessmentData = response.data.caseInfo;
            this.organAssessmentList = response.data.organAssessments || [];
            this.attachmentList = response.data.attachments || [];
          } else {
            this.$message.error("获取评估详情失败");
    // æ ¹æ®å­—å…¸value获取label
    getOrganLabel(organValue) {
      const dictItem = this.organDict.find(item => item.value === organValue);
      return dictItem ? dictItem.label : organValue;
    },
    // Tab点击事件
    handleTabClick(organ, tab) {
      if (tab.name !== "add") {
        this.activeTabMap.set(organ.organno, tab.name);
      }
    },
// èŽ·å–åŠŸèƒ½çŠ¶æ€æ ‡ç­¾ç±»åž‹
  getFunctionStatusTagType(status) {
    const typeMap = {
      "1": "success",  // æ­£å¸¸
      "2": "warning",  // è½»åº¦å¼‚常
      "3": "danger",   // é‡åº¦å¼‚常
      "4": "info"      // æ— æ³•评估
    };
    return typeMap[status] || "info";
  },
  // èŽ·å–åŠŸèƒ½çŠ¶æ€æ–‡æœ¬
  getFunctionStatusText(status) {
    const textMap = {
      "1": "正常",
      "2": "轻度异常",
      "3": "重度异常",
      "4": "无法评估"
    };
    return textMap[status] || "未评估";
  },
  // æ˜¾ç¤ºé™„件列表
  showAttachmentList(attachments, assessmentNumber) {
    if (!attachments || attachments.length === 0) {
      this.$message.info(`第${assessmentNumber}次评估暂无附件`);
      return;
    }
    this.$alert(
      `<div>
        <h4>第${assessmentNumber}次评估附件列表</h4>
        <ul style="list-style: none; padding-left: 0;">
          ${attachments.map((item, index) => `
            <li style="margin: 5px 0; padding: 5px; background: #f5f7fa; border-radius: 4px;">
              <i class="el-icon-document"></i>
              <span style="margin-left: 8px;">${item.fileName}</span>
              <el-button
                type="text"
                size="mini"
                onclick="window.open('${item.path || item.fileUrl}')"
                style="margin-left: 10px;"
              >
                ä¸‹è½½
              </el-button>
            </li>
          `).join('')}
        </ul>
      </div>`,
      '附件列表',
      {
        dangerouslyUseHTMLString: true,
        showConfirmButton: false,
        showCancelButton: true,
        cancelButtonText: '关闭'
      }
    );
  },
    // èŽ·å–å™¨å®˜çš„è¯„ä¼°åˆ—è¡¨
    getOrganAssessments(organ) {
      if (!organ.assesscontent) return [];
      try {
        const assessData =
          typeof organ.assesscontent === "string"
            ? JSON.parse(organ.assesscontent)
            : organ.assesscontent;
        if (Array.isArray(assessData)) {
          return assessData.filter(item => item.delFlag !== "1");
        }
        return [];
      } catch (error) {
        console.warn("解析评估内容失败:", error);
        return [];
      }
    },
    // èŽ·å–è¯„ä¼°æ•°é‡
    getAssessmentCount(organ) {
      return this.getOrganAssessments(organ).length;
    },
    // å¤„理评估更新
    handleUpdateAssessment(data) {
      const { organData, assessmentData, assessmentIndex } = data;
      const assessments = this.getOrganAssessments(organData);
      if (assessments[assessmentIndex]) {
        // åˆ›å»ºæ–°çš„评估数组
        const newAssessments = [...assessments];
        newAssessments[assessmentIndex] = {
          ...assessments[assessmentIndex],
          ...assessmentData
        };
        // æ›´æ–° assesscontent
        this.$set(organData, "assesscontent", JSON.stringify(newAssessments));
        this.$forceUpdate();
      }
    },
    // æ•´ä½“保存方法
    async handleSaveAll() {
      this.saveLoading = true;
      try {
        const saveData = {
          id: this.assessmentData.id || undefined,
          infoid: this.infoid,
          caseNo: this.assessmentData.caseNo,
          donorno: this.assessmentData.donorno,
          treatmenthospitalname: this.assessmentData.treatmenthospitalname,
          treatmenthospitalno: this.assessmentData.treatmenthospitalno,
          sex: this.assessmentData.sex,
          age: this.assessmentData.age,
          bloodtype: this.assessmentData.bloodtype,
          idcardno: this.assessmentData.idcardno,
          diagnosisname: this.assessmentData.diagnosisname,
          coordinatorName: this.assessmentData.coordinatorName,
          assessTime:
            this.assessmentData.assessTime || new Date().toISOString(),
          assessState: this.assessmentData.assessState,
          assessannex: this.assessmentData.assessannex,
          organdecision: this.organdecisionValues.join(","),
          organdecisionOther: this.organdecisionOther,
          serviceMedicalevaluationorganList: this.organAssessmentList.map(
            organ => ({
              id: organ.id,
              infoid: organ.infoid,
              donorno: organ.donorno,
              organno: organ.organno,
              organname: this.getOrganLabel(organ.organno),
              organnumber: organ.organnumber,
              gainhospitalno: organ.gainhospitalno,
              gainhospitalname: organ.gainhospitalname,
              isbiopsybefore: organ.isbiopsybefore,
              isbiopsyafter: organ.isbiopsyafter,
              ismarginalorgan: organ.ismarginalorgan,
              ispathogenpositive: organ.ispathogenpositive,
              ispnf: organ.ispnf,
              isdgf: organ.isdgf,
              delFlag: organ.delFlag || "0",
              assesscontent: organ.assesscontent || "[]"
            })
          )
        };
        const saveMethod = this.assessmentData.id ? assessedit : assessAdd;
        const response = await saveMethod(saveData);
        if (response.code === 200) {
          this.$message.success("评估表保存成功!");
          if (!this.assessmentData.id && response.data && response.data.id) {
            this.assessmentData.id = response.data.id;
          }
          this.assessmentLoading = false;
        })
        .catch(error => {
          console.error("获取评估详情失败:", error);
          this.assessmentLoading = false;
          this.$message.error("获取评估详情失败");
          this.refreshKey += 1; // è§¦å‘重新渲染
        } else {
          this.$message.error("保存失败:" + (response.msg || "未知错误"));
        }
      } catch (error) {
        console.error("保存评估表失败:", error);
        this.$message.error("保存失败,请重试");
      } finally {
        this.saveLoading = false;
      }
    },
    // å–消按钮事件
    handleCancel() {
      this.$router.go(-1);
    },
    // æçŒ®å†³å®šå˜æ›´å¤„理
    async handleOrganDecisionChange(newDecision) {
      const oldDecision = [...this.prevOrgandecisionValues];
      this.autoHandleDecisionChange(newDecision);
      const removedDecisions = oldDecision.filter(
        item => !newDecision.includes(item)
      );
      for (const decision of removedDecisions) {
        await this.handleRemovedDecision(decision);
      }
      this.prevOrgandecisionValues = [...newDecision];
      this.$forceUpdate();
      if (newDecision.length === 0) {
        this.expandedRowKeys = [];
      }
    },
    // å…¶ä»–捐献决定输入处理
    handleOtherDecisionInput(value) {
      // è¾“入时不做单独保存,等待整体保存
    },
    // è‡ªåŠ¨å¤„ç†å†³å®šå˜æ›´é€»è¾‘
    autoHandleDecisionChange(newDecision) {
      // å¦‚果选择了"双肾"(value: C64),自动取消单独的"左肾"(value: C64L)和"右肾"(value: C64R)选择
      if (newDecision.includes("C64")) {
        this.organdecisionValues = newDecision.filter(
          item => item !== "C64L" && item !== "C64R"
        );
      }
      // å¦‚果选择了"左肾"或"右肾",取消"双肾"选择
      else if (newDecision.includes("C64L") || newDecision.includes("C64R")) {
        this.organdecisionValues = newDecision.filter(item => item !== "C64");
      }
      // å¦‚果选择了"全肺"(value: C34),自动取消单独的"左肺"(value: C34L)和"右肺"(value: C34R)选择
      if (newDecision.includes("C34")) {
        this.organdecisionValues = newDecision.filter(
          item => item !== "C34L" && item !== "C34R"
        );
      }
      // å¦‚果选择了"左肺"或"右肺",取消"全肺"选择
      else if (newDecision.includes("C34L") || newDecision.includes("C34R")) {
        this.organdecisionValues = newDecision.filter(item => item !== "C34");
      }
      // å¤„理其他互斥逻辑
      this.handleExclusiveDecisions();
    },
    // å¤„理互斥的捐献决定
    handleExclusiveDecisions() {
      // é—体捐献(value: 10)与其他器官捐献互斥(根据业务需求调整)
      if (this.organdecisionValues.includes("10")) {
        // å¯ä»¥è®¾ç½®åªä¿ç•™é—体捐献,或者根据业务需求处理
      }
    },
    // å¤æ‚决策变化处理
    handleComplexDecisionChange(newVal, oldVal) {
      // å¤„理新增的选择
      const addedDecisions = newVal.filter(item => !oldVal.includes(item));
      const removedDecisions = oldVal.filter(item => !newVal.includes(item));
      // å¯¹æ–°å¢žåŠ çš„é€‰æ‹©è¿›è¡Œç‰¹æ®Šå¤„ç†
      addedDecisions.forEach(decision => {
        this.handleNewDecision(decision);
      });
      // å¯¹ç§»é™¤çš„选择进行清理
      removedDecisions.forEach(decision => {
        this.handleRemovedDecision(decision);
      });
    },
    // å¤„理新增的捐献决定
    handleNewDecision(decision) {
      // æ ¹æ®æ˜ å°„关系自动创建对应的器官评估项
      this.autoCreateOrganAssessments(decision);
    },
    // è‡ªåŠ¨åˆ›å»ºå™¨å®˜è¯„ä¼°é¡¹
    autoCreateOrganAssessments(decision) {
      const organsToCreate = this.organDecisionMapping[decision];
      if (organsToCreate && organsToCreate.length > 0) {
        organsToCreate.forEach(organValue => {
          this.ensureOrganExists(organValue);
        });
        // æ˜¾ç¤ºåˆ›å»ºæç¤º
        if (organsToCreate.length > 0) {
          this.$message.success(
            `已为【${this.getOrganLabel(decision)}】创建${
              organsToCreate.length
            }个评估项`
          );
        }
      } else {
        console.warn(
          `捐献决定【${this.getOrganLabel(decision)}】没有配置器官映射关系`
        );
      }
    },
    // çŠ¶æ€è¿‡æ»¤å™¨
    statusFilter(status) {
      const statusMap = {
        "0": "warning",
        "1": "primary",
        "2": "success",
        "3": "danger"
    // å¤„理移除的捐献决定
    async handleRemovedDecision(decision) {
      const relatedOrgans = this.organDecisionMapping[decision] || [];
      for (const organValue of relatedOrgans) {
        const organIndex = this.organAssessmentList.findIndex(
          organ => organ.organno === organValue && organ.delFlag !== "1"
        );
        if (organIndex !== -1) {
          const organ = this.organAssessmentList[organIndex];
          const organLabel = this.getOrganLabel(organValue);
          try {
            await this.$confirm(
              `捐献决定"${this.getOrganLabel(
                decision
              )}"已取消,是否删除对应的${organLabel}评估记录?`,
              "确认删除",
              {
                confirmButtonText: "确定",
                cancelButtonText: "取消",
                type: "warning"
              }
            );
            this.$set(this.organAssessmentList[organIndex], "delFlag", "1");
            this.deletedOrganAssessments.push(organValue);
            const keyIndex = this.expandedRowKeys.indexOf(organValue);
            if (keyIndex > -1) {
              this.expandedRowKeys.splice(keyIndex, 1);
            }
            this.$message.success(`${organLabel}评估记录已标记删除`);
          } catch (cancelError) {
            if (!this.organdecisionValues.includes(decision)) {
              this.organdecisionValues.push(decision);
            }
            this.$message.info(`已取消删除${organLabel}评估记录`);
          }
        }
      }
    },
    // ç¡®ä¿å™¨å®˜å­˜åœ¨
    ensureOrganExists(organValue) {
      const exists = this.organAssessmentList.some(
        organ => organ.organno === organValue && organ.delFlag !== "1"
      );
      if (!exists) {
        const newOrgan = {
          id: null,
          organno: organValue,
          organname: this.getOrganLabel(organValue),
          gainhospitalname: "待分配机构",
          isbiopsybefore: "0",
          isbiopsyafter: "0",
          ismarginalorgan: "0",
          ispathogenpositive: "0",
          assesscontent: JSON.stringify([this.getDefaultAssessment(0)]),
          activeTab: 0,
          expanded: false,
          createTime: new Date().toISOString(),
          delFlag: "0"
        };
        this.organAssessmentList = [...this.organAssessmentList, newOrgan];
        return true;
      }
      return false;
    },
    // èŽ·å–è¯„ä¼°è¯¦æƒ…
    async getAssessmentDetail() {
      this.assessmentLoading = true;
      try {
        const response = await evaluateBaseInfolist({ infoid: this.infoid });
        if (response.code === 200) {
          this.handleResponseData(response);
        } else {
          this.$message.error("获取详情失败:" + (response.msg || "未知错误"));
        }
      } catch (error) {
        console.error("获取评估详情失败:", error);
        this.$message.error("获取详情失败");
      } finally {
        this.assessmentLoading = false;
      }
    },
    // å¤„理接口响应数据
    handleResponseData(response) {
      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 {
        detailData = response;
      }
      this.assessmentData = {
        id: detailData.id || this.assessmentId,
        infoid: detailData.infoid || this.infoid,
        caseNo: detailData.caseNo || "",
        name: detailData.name || detailData.donorName || "",
        sex: detailData.sex || "",
        age: detailData.age || "",
        bloodtype: detailData.bloodtype || "",
        idcardno: detailData.idcardno || "",
        diagnosisname: detailData.diagnosisname || "",
        treatmenthospitalname: detailData.treatmenthospitalname || "",
        coordinatorName: detailData.coordinatorName || "",
        assessTime: detailData.assessTime || "",
        assessState: detailData.assessState || "1",
        assessannex: detailData.assessannex || ""
      };
      return statusMap[status] || "info";
      if (detailData.organdecision) {
        const decisionArray = Array.isArray(detailData.organdecision)
          ? detailData.organdecision
          : (detailData.organdecision || "").split(",").filter(item => item);
        this.organdecisionValues = decisionArray;
        this.prevOrgandecisionValues = [...decisionArray];
        this.organdecisionOther = detailData.organdecisionOther || "";
      }
      this.processOrganAssessmentData(detailData);
    },
    statusTextFilter(status) {
    // å¤„理器官评估数据
    processOrganAssessmentData(detailData) {
      let organList = [];
      if (detailData.serviceMedicalevaluationorganList) {
        if (Array.isArray(detailData.serviceMedicalevaluationorganList)) {
          organList = detailData.serviceMedicalevaluationorganList;
        }
      }
      this.organAssessmentList = organList.map(organ => {
        const organno = organ.organno;
        // å¦‚æžœ assesscontent æ˜¯å­—符串,确保它是有效的 JSON
        if (organ.assesscontent && typeof organ.assesscontent === "string") {
          try {
            JSON.parse(organ.assesscontent);
          } catch (error) {
            console.warn(
              "无效的 assesscontent,重置为空数组:",
              organ.assesscontent
            );
            organ.assesscontent = "[]";
          }
        } else if (!organ.assesscontent) {
          organ.assesscontent = "[]";
        }
        const assessments = this.getOrganAssessments(organ);
        const activeTab = this.activeTabMap.has(organno)
          ? this.activeTabMap.get(organno)
          : assessments.length > 0
          ? 0
          : "summary";
        return {
          ...organ,
          organname: this.getOrganLabel(organ.organno) || organ.organname,
          activeTab: activeTab,
          expanded: false,
          delFlag: organ.delFlag || "0"
        };
      });
    },
    // èŽ·å–é»˜è®¤è¯„ä¼°æ•°æ®ç»“æž„
    getDefaultAssessment(index) {
      return {
        index: index,
        status: "pending",
        assessmentTime: "",
        assessor: "",
        functionStatus: "",
        assessmentOpinion: "",
        clinicalData: {},
        labResults: {},
        createTime: new Date().toISOString(),
        delFlag: "0"
      };
    },
    // è®¡ç®—最大评估次数
    getMaxAssessmentCount() {
      const maxCount = Math.max(
        ...this.organAssessmentList
          .filter(organ => organ.delFlag !== "1")
          .map(organ => this.getAssessmentCount(organ))
      );
      return Math.max(maxCount, 1);
    },
    // èŽ·å–å™¨å®˜æ•´ä½“çŠ¶æ€
    getOrganOverallStatus(organ) {
      const assessments = this.getOrganAssessments(organ);
      if (assessments.length === 0) {
        return "pending";
      }
      const validAssessments = assessments.filter(a => a.delFlag !== "1");
      if (validAssessments.length === 0) {
        return "pending";
      }
      const allAssessed = validAssessments.every(
        assessment => assessment.status === "assessed"
      );
      const someAssessed = validAssessments.some(
        assessment => assessment.status === "assessed"
      );
      if (allAssessed) return "completed";
      if (someAssessed) return "assessing";
      return "pending";
    },
    getOrganOverallStatusText(organ) {
      const status = this.getOrganOverallStatus(organ);
      const statusMap = {
        "0": "待评估",
        "1": "评估中",
        "2": "已完成",
        "3": "已关闭"
        pending: "待评估",
        assessing: "评估中",
        completed: "已完成"
      };
      return statusMap[status] || "未知";
    },
    // èŽ·å–åŠŸèƒ½çŠ¶æ€ç±»åž‹
    getFunctionStatusType(status) {
    // åˆ‡æ¢å±•开行
    handleToggleExpand(row) {
      const key = row.organno;
      const index = this.expandedRowKeys.indexOf(key);
      if (index > -1) {
        this.expandedRowKeys.splice(index, 1);
        row.expanded = false;
      } else {
        this.expandedRowKeys = [key];
        this.organAssessmentList.forEach(item => {
          item.expanded = item.organno === key;
        });
      }
    },
    // å±•开行变化
    handleExpandChange(row, expandedRows) {
      this.expandedRowKeys = expandedRows.map(item => item.organno);
      this.organAssessmentList.forEach(item => {
        item.expanded = this.expandedRowKeys.includes(item.organno);
      });
    },
    // æ·»åŠ æ–°è¯„ä¼°
    handleAddNewAssessment(organ) {
      const assessments = this.getOrganAssessments(organ);
      const newAssessment = {
        ...this.getDefaultAssessment(assessments.length),
        assessor: this.currentUser.name
      };
      const newAssessments = [...assessments, newAssessment];
      this.$set(organ, "assesscontent", JSON.stringify(newAssessments));
      organ.activeTab = assessments.length;
      this.activeTabMap.set(organ.organno, assessments.length);
      this.$message.success("已添加新的评估");
    },
    // å¤„理删除评估事件
    handleDeleteAssessment(data) {
      const { organData, assessmentIndex } = data;
      this.deleteOrganAssessment(organData, assessmentIndex);
    },
    // å¤„理评估保存
    handleSaveAssessment(data) {
      const { organData, assessmentData, assessmentIndex } = data;
      const assessments = this.getOrganAssessments(organData);
      console.log("保存评估数据:", data);
      if (assessments[assessmentIndex]) {
        // åˆ›å»ºæ–°çš„评估数组
        const newAssessments = [...assessments];
        newAssessments[assessmentIndex] = {
          ...assessments[assessmentIndex],
          ...assessmentData,
          status: "assessed", // æ ‡è®°ä¸ºå·²è¯„ä¼°
          assessmentTime: new Date().toISOString(), // è®¾ç½®è¯„ä¼°æ—¶é—´
          delFlag: "0" // ç¡®ä¿åˆ é™¤æ ‡å¿—
        };
        // æ›´æ–° assesscontent
        this.$set(organData, "assesscontent", JSON.stringify(newAssessments));
        // å¼ºåˆ¶é‡æ–°æ¸²æŸ“
        this.$forceUpdate();
        this.$message.success("评估保存成功");
        // æ£€æŸ¥æ˜¯å¦éœ€è¦è‡ªåŠ¨åˆ‡æ¢åˆ°ä¸‹ä¸€ä¸ªè¯„ä¼°
        this.checkAndSwitchTab(organData, assessmentIndex);
      }
    },
    // æ£€æŸ¥å¹¶è‡ªåŠ¨åˆ‡æ¢tab
    checkAndSwitchTab(organData, currentIndex) {
      const assessments = this.getOrganAssessments(organData);
      if (currentIndex < assessments.length - 1) {
        // å¦‚果还有下一个评估,自动切换到下一个
        organData.activeTab = currentIndex + 1;
        this.activeTabMap.set(organData.organno, currentIndex + 1);
      } else {
        // å¦‚果没有下一个评估,切换到汇总页
        organData.activeTab = "summary";
        this.activeTabMap.set(organData.organno, "summary");
      }
    },
    // åˆ é™¤å™¨å®˜è¯„ä¼°
    async deleteOrganAssessment(organ, assessmentIndex) {
      try {
        await this.$confirm(
          `确认删除第${assessmentIndex + 1}次评估记录吗?`,
          "确认删除",
          {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning"
          }
        );
        const assessments = this.getOrganAssessments(organ);
        if (assessments[assessmentIndex]) {
          // æ ‡è®°ä¸ºåˆ é™¤
          assessments[assessmentIndex].delFlag = "1";
          // è¿‡æ»¤æŽ‰å·²åˆ é™¤çš„评估
          const newAssessments = assessments.filter(
            item => item.delFlag !== "1"
          );
          // é‡æ–°è®¡ç®—评估索引
          newAssessments.forEach((assessment, index) => {
            assessment.index = index;
          });
          // æ›´æ–° assesscontent
          this.$set(organ, "assesscontent", JSON.stringify(newAssessments));
          // å¤„理tab切换
          if (newAssessments.length === 0) {
            organ.activeTab = "summary";
            this.activeTabMap.set(organ.organno, "summary");
          } else if (organ.activeTab === assessmentIndex) {
            const newIndex = Math.max(0, assessmentIndex - 1);
            organ.activeTab = newIndex;
            this.activeTabMap.set(organ.organno, newIndex);
          } else if (organ.activeTab === "summary") {
            this.activeTabMap.set(organ.organno, "summary");
          }
          this.$message.success("评估记录已删除");
          this.$forceUpdate();
        }
      } catch (cancelError) {
        this.$message.info("已取消删除");
      }
    },
    // èŽ·å–æœ€æ–°è¯„ä¼°æ—¶é—´
    getLatestAssessmentTime(organ) {
      const assessments = this.getOrganAssessments(organ);
      if (assessments.length === 0) return null;
      const assessed = assessments.filter(
        a => a.assessmentTime && a.delFlag !== "1"
      );
      if (assessed.length === 0) return null;
      return assessed.sort(
        (a, b) => new Date(b.assessmentTime) - new Date(a.assessmentTime)
      )[0].assessmentTime;
    },
    // èŽ·å–è¯„ä¼°çŠ¶æ€æ ‡ç­¾ç±»åž‹
    getAssessmentTagType(status) {
      const typeMap = {
        "1": "success", // æ­£å¸¸
        "2": "warning", // è½»åº¦å¼‚常
        "3": "danger", // é‡åº¦å¼‚常
        "4": "info" // æ— æ³•评估
        pending: "warning",
        assessing: "primary",
        assessed: "success"
      };
      return typeMap[status] || "info";
    },
    // èŽ·å–åŠŸèƒ½çŠ¶æ€æ–‡æœ¬
    getFunctionStatusText(status) {
    // èŽ·å–è¯„ä¼°çŠ¶æ€æ–‡æœ¬
    getAssessmentStatusText(status) {
      const textMap = {
        "1": "正常",
        "2": "轻度异常",
        "3": "重度异常",
        "4": "无法评估"
        pending: "待评估",
        assessing: "评估中",
        assessed: "已评估"
      };
      return textMap[status] || "未知";
    },
    // æ£€æŸ¥æ˜¯å¦æœ‰æƒé™è¯„估该器官
    // æ£€æŸ¥è¯„估权限
    canAssessOrgan(organ) {
      if (this.isCoordinator) return true;
      return organ.department === this.currentDepartment;
      if (!this.isEdit) return false;
      return (
        organ.gainhospitalname &&
        organ.gainhospitalname.includes(this.currentDepartment)
      );
    },
    // èŽ·å–è¡Œç±»å
    getRowClassName({ row }) {
      return this.canAssessOrgan(row) ? "assessable-row" : "non-assessable-row";
    },
    // èŽ·å–å™¨å®˜æ ‡é¢˜
    getOrganTitle(organ) {
      const organName =
        this.organTypeOptions.find(opt => opt.value === organ.organType)
          ?.label || organ.organType;
      return `${organName}评估详情(${organ.department})`;
    // çŠ¶æ€è¿‡æ»¤å™¨
    statusFilter(status) {
      const statusMap = {
        "1": "warning",
        "2": "primary",
        "3": "success"
      };
      return statusMap[status] || "info";
    },
    // å™¨å®˜è¯„ä¼°
    handleOrganAssess(organ) {
      this.activeOrgans = [organ.organType];
    statusTextFilter(status) {
      const statusMap = {
        "1": "待评估",
        "2": "评估中",
        "3": "已完成"
      };
      return statusMap[status] || "未知";
    },
    // æŸ¥çœ‹å™¨å®˜è¯¦æƒ…
    handleViewOrganDetail(organ) {
      this.activeOrgans = [organ.organType];
    },
    // ä¿å­˜å™¨å®˜è¯„ä¼° - ä½¿ç”¨Mock API
    handleSaveOrganAssessment(organData) {
      updateOrganAssessment(organData)
        .then(response => {
          if (response.code === 200) {
            this.$message.success("评估保存成功");
            // åˆ·æ–°æ•°æ®
            this.getAssessmentDetail();
          }
        })
        .catch(error => {
          console.error("保存评估失败:", error);
          this.$message.error("保存失败");
        });
    },
    // æŸ¥çœ‹é™„件预览
    // æŸ¥çœ‹é™„ä»¶
    handleAttachmentPreview() {
      this.attachmentVisible = true;
      if (this.assessmentData.assessannex) {
        try {
          const annexData =
            typeof this.assessmentData.assessannex === "string"
              ? JSON.parse(this.assessmentData.assessannex)
              : this.assessmentData.assessannex;
          if (Array.isArray(annexData) && annexData.length > 0) {
            this.currentPreviewFile = annexData[0];
            this.attachmentVisible = true;
          } else {
            this.$message.info("暂无附件可预览");
          }
        } catch (error) {
          this.$message.info("附件数据格式错误");
        }
      } else {
        this.$message.info("暂无附件");
      }
    },
    // å®Œæˆè¯„ä¼° - ä½¿ç”¨Mock API
    handleCompleteAssessment() {
      this.$confirm("确认完成所有器官评估吗?完成后将无法修改", "确认操作", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        completeAssessment(this.assessmentId)
          .then(response => {
            if (response.code === 200) {
              this.$message.success("评估完成确认成功");
              this.getAssessmentDetail();
            }
          })
          .catch(error => {
            console.error("完成评估失败:", error);
            this.$message.error("操作失败");
          });
      });
    // å®Œæˆè¯„ä¼°
    async handleCompleteAssessment() {
      try {
        await this.$confirm(
          "确认完成所有器官评估吗?完成后将无法修改",
          "确认操作",
          {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning"
          }
        );
        const updateData = {
          ...this.assessmentData,
          assessState: "3",
          assessTime: new Date().toISOString()
        };
        const response = await assessedit(updateData);
        if (response.code === 200) {
          this.$message.success("评估完成确认成功");
          this.assessmentData.assessState = "3";
          this.isEdit = false;
        } else {
          this.$message.error("操作失败:" + (response.msg || "未知错误"));
        }
      } catch (error) {
        if (error !== "cancel") {
          console.error("完成评估失败:", error);
          this.$message.error("操作失败");
        }
      }
    }
  }
};
@@ -366,8 +1384,46 @@
  margin-bottom: 20px;
}
.organ-detail-collapse {
/* å±•开行样式 */
.organ-expand-content {
  padding: 20px;
  background: #fafafa;
  border-radius: 4px;
  margin: 10px 0;
}
.organ-detail-tabs {
  background: white;
  border-radius: 4px;
  padding: 20px;
}
.add-assessment-prompt {
  text-align: center;
  padding: 40px 0;
}
.assessment-summary {
  padding: 10px;
}
/* åº•部保存按钮样式 */
.footer-actions {
  margin-top: 20px;
  text-align: center;
  padding: 20px;
  border-top: 1px solid #eaeaea;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .assessment-detail {
    padding: 10px;
  }
  .organ-expand-content {
    padding: 10px;
  }
}
::v-deep .assessable-row {
@@ -378,6 +1434,24 @@
  background-color: #fafafa;
}
.jstitle {
  float: right;
  font-size: 18px !important;
  font-weight: 600;
  color: #2645f7;
  font-size: 12px;
}
/* è¡¨æ ¼å±•开图标样式 */
::v-deep .el-table__expand-icon {
  cursor: pointer;
}
::v-deep .el-table__expanded-cell {
  padding: 0 !important;
}
/* ç¡®ä¿æè¿°åˆ—表标签宽度一致 */
::v-deep .el-descriptions__label {
  width: 120px;
  background-color: #f5f7fa;
@@ -387,11 +1461,210 @@
.fixed-width .el-button {
  margin: 0 2px;
}
.jstitle {
  float: right;
  font-size: 18px !important;
/* æ±‡æ€»é¡µé¢æ ·å¼ */
.assessment-summary {
  padding: 10px;
}
.summary-item {
  font-weight: 500;
  color: #303133;
}
.highlight-text {
  color: #409EFF;
  font-weight: 500;
}
.assessment-detail-card {
  border: 1px solid #e6ebf5;
}
.organ-info-card {
  border: 1px solid #e6ebf5;
  background-color: #fafafa;
}
.no-assessment {
  text-align: center;
  padding: 40px 0;
  color: #909399;
}
.assessment-item {
  margin-bottom: 20px;
}
.assessment-item:last-child {
  margin-bottom: 0;
}
.assessment-card {
  border: 1px solid #dcdfe6;
  border-radius: 8px;
  transition: all 0.3s;
}
.assessment-card:hover {
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  border-color: #409EFF;
}
.assessment-title {
  font-size: 16px;
  font-weight: 600;
  color: #2645f7;
  color: #303133;
}
.status-tag {
  float: right;
  margin-right: 0;
}
.time-text {
  color: #67C23A;
  font-weight: 500;
}
.opinion-content {
  padding: 8px 12px;
  background: #f5f7fa;
  border-radius: 4px;
  line-height: 1.5;
  color: #606266;
  white-space: pre-wrap;
  word-break: break-word;
}
.detail-descriptions {
  background: #fff;
}
/* ä¼˜åŒ–表格样式 */
::v-deep .el-table {
  color: #303133;
  font-size: 14px;
}
::v-deep .el-table th {
  background-color: #f5f7fa;
  color: #303133;
  font-weight: 600;
  padding: 12px 0;
}
::v-deep .el-table tr {
  background-color: #fff;
}
::v-deep .el-table tr:hover {
  background-color: #f5f7fa;
}
::v-deep .el-table .cell {
  padding: 12px 10px;
  line-height: 1.5;
}
::v-deep .el-table--striped .el-table__body tr.el-table__row--striped td {
  background-color: #fafafa;
}
::v-deep .el-table--enable-row-hover .el-table__body tr:hover>td {
  background-color: #ecf5ff;
}
/* å¯è¯„估行样式 */
::v-deep .assessable-row {
  background-color: #f0f9ff !important;
}
::v-deep .assessable-row:hover {
  background-color: #d9ecff !important;
}
::v-deep .non-assessable-row {
  background-color: #fafafa !important;
}
/* æè¿°åˆ—表样式优化 */
::v-deep .el-descriptions__label {
  width: 120px;
  background-color: #f5f7fa;
  font-weight: 600;
  color: #303133;
}
::v-deep .el-descriptions__content {
  background-color: #fff;
  color: #606266;
}
::v-deep .el-descriptions__body {
  background-color: #fff;
}
/* å¡ç‰‡æ ·å¼ä¼˜åŒ– */
::v-deep .el-card__header {
  background-color: #f5f7fa;
  border-bottom: 1px solid #e6ebf5;
  padding: 12px 20px;
  font-weight: 600;
  color: #303133;
}
::v-deep .el-card__body {
  padding: 20px;
}
/* æ ‡ç­¾æ ·å¼ä¼˜åŒ– */
::v-deep .el-tag {
  font-weight: 500;
  border-radius: 12px;
  padding: 0 10px;
  height: 24px;
  line-height: 22px;
}
/* æŒ‰é’®æ ·å¼ä¼˜åŒ– */
::v-deep .el-button--mini {
  padding: 7px 12px;
  font-size: 12px;
  border-radius: 4px;
}
/* Tab样式优化 */
::v-deep .el-tabs__item {
  font-weight: 500;
  color: #606266;
}
::v-deep .el-tabs__item.is-active {
  color: #409EFF;
  font-weight: 600;
}
::v-deep .el-tabs__nav-wrap::after {
  background-color: #e4e7ed;
}
::v-deep .el-tabs--card>.el-tabs__header .el-tabs__item.is-active {
  background-color: #fff;
  border-bottom-color: #fff;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .assessment-summary {
    padding: 5px;
  }
  ::v-deep .el-table .cell {
    padding: 8px 5px;
  }
  .assessment-card {
    margin-bottom: 10px;
  }
}
</style>
src/views/business/course/donationProcess.js
@@ -1,48 +1,48 @@
// æ¨¡æ‹ŸæçŒ®è¿›ç¨‹æ•°æ®
const mockDonationProcessData = {
  caseInfo: {
    id: '202512001',
    caseNo: 'C202512001',
    hospitalNo: 'D202512001',
    donorName: '张三',
    gender: '0',
    id: "202512001",
    caseNo: "C202512001",
    hospitalNo: "D202512001",
    donorName: "张三",
    gender: "0",
    age: 45,
    bloodType: 'A',
    diagnosis: '脑外伤',
    status: 'in_progress',
    createTime: '2025-12-01 08:00:00',
    registrant: '李协调员',
    currentStage: 'organ_allocation',
    bloodType: "A",
    diagnosis: "脑外伤",
    status: "in_progress",
    createTime: "2025-12-01 08:00:00",
    registrant: "李协调员",
    currentStage: "organ_allocation",
    // æ–°å¢žåŸºæœ¬ä¿¡æ¯
    height: 175,
    weight: 70,
    bloodPressure: '120/80',
    contactPerson: '张父',
    contactPhone: '13800138000',
    hospital: '青岛大学附属医院',
    department: '神经外科',
    attendingDoctor: '王主任'
    bloodPressure: "120/80",
    contactPerson: "张父",
    contactPhone: "13800138000",
    hospital: "青岛大学附属医院",
    department: "神经外科",
    attendingDoctor: "王主任"
  },
  processStages: [
    {
      key: 'donor_maintenance',
      name: '供者维护',
      status: 'completed',
      completeTime: '2025-12-01 10:00:00',
      updateTime: '2025-12-01 10:00:00',
      operator: '张医生',
      key: "donor_maintenance",
      name: "供者维护",
      status: "completed",
      completeTime: "2025-12-01 10:00:00",
      updateTime: "2025-12-01 10:00:00",
      operator: "张医生",
      details: {
        maintenanceRecords: 5,
        lastCheckup: '2025-12-01 09:30:00',
        lastCheckup: "2025-12-01 09:30:00",
        vitalSigns: {
          heartRate: 75,
          bloodPressure: '118/76',
          bloodPressure: "118/76",
          temperature: 36.5,
          oxygenSaturation: 98
        },
        medications: [
          { name: '多巴胺', dosage: '5μg/kg/min', time: '2025-12-01 08:00:00' },
          { name: '甘露醇', dosage: '125ml', time: '2025-12-01 09:00:00' }
          { name: "多巴胺", dosage: "5μg/kg/min", time: "2025-12-01 08:00:00" },
          { name: "甘露醇", dosage: "125ml", time: "2025-12-01 09:00:00" }
        ],
        labResults: {
          wbc: 8.5,
@@ -54,218 +54,227 @@
      }
    },
    {
      key: 'medical_assessment',
      name: '医学评估',
      status: 'completed',
      completeTime: '2025-12-02 14:30:00',
      updateTime: '2025-12-02 14:30:00',
      operator: '李主任',
      details: {
        assessmentItems: [
          { name: '神经系统评估', result: '脑死亡确认', status: 'completed' },
          { name: '心血管系统评估', result: '功能正常', status: 'completed' },
          { name: '呼吸系统评估', result: '呼吸机维持', status: 'completed' },
          { name: '肝肾功能评估', result: '功能良好', status: 'completed' },
          { name: '感染性疾病筛查', result: '阴性', status: 'completed' }
        ],
        imagingResults: {
          ctBrain: '脑水肿,脑干反射消失',
          chestXRay: '双肺清晰',
          abdominalUS: '肝胆胰脾未见异常'
        },
        conclusion: '符合器官捐献医学标准',
        contraindications: '无绝对禁忌症'
      }
    },
    {
      key: 'death_judgment',
      name: '死亡判定',
      status: 'completed',
      completeTime: '2025-12-03 09:15:00',
      updateTime: '2025-12-03 09:15:00',
      operator: '王医生',
      details: {
        judgmentType: '脑死亡判定',
        judgmentTime: '2025-12-03 09:00:00',
        doctors: ['张主任', '王医生'],
        testResults: [
          { test: '自主呼吸测试', result: '无自主呼吸', time: '2025-12-03 08:30:00' },
          { test: '瞳孔对光反射', result: '反射消失', time: '2025-12-03 08:45:00' },
          { test: '脑干听觉诱发电位', result: '脑干功能丧失', time: '2025-12-03 09:00:00' }
        ],
        certificateNo: 'SW20251203001',
        legalDocuments: ['死亡证明书', '脑死亡判定书']
      }
    },
    {
      key: 'donation_confirm',
      name: '捐献确认',
      status: 'completed',
      completeTime: '2025-12-03 11:00:00',
      updateTime: '2025-12-03 11:00:00',
      operator: '赵协调员',
      key: "donation_confirm",
      name: "捐献意愿",
      status: "completed",
      completeTime: "2025-12-03 11:00:00",
      updateTime: "2025-12-03 11:00:00",
      operator: "赵协调员",
      details: {
        familyConsent: {
          mainRelative: '张父',
          relationship: '父子',
          consentTime: '2025-12-03 10:45:00',
          consentForm: '已签署',
          witness: '李护士'
          mainRelative: "张父",
          relationship: "父子",
          consentTime: "2025-12-03 10:45:00",
          consentForm: "已签署",
          witness: "李护士"
        },
        donationType: '多器官捐献',
        organs: ['肝脏', '肾脏', '心脏', '角膜'],
        legalDocuments: [
          '器官捐献同意书',
          '家属关系证明',
          '医疗免责声明'
        ],
        coordinator: '赵协调员',
        confirmationTime: '2025-12-03 11:00:00'
        donationType: "多器官捐献",
        organs: ["肝脏", "肾脏", "心脏", "角膜"],
        legalDocuments: ["器官捐献同意书", "家属关系证明", "医疗免责声明"],
        coordinator: "赵协调员",
        confirmationTime: "2025-12-03 11:00:00"
      }
    },
    {
      key: 'ethical_review',
      name: '伦理审查',
      status: 'in_progress',
      completeTime: '2025-12-03 15:20:00',
      updateTime: '2025-12-03 15:20:00',
      operator: '伦理委员会',
      key: "medical_assessment",
      name: "医学评估",
      status: "completed",
      completeTime: "2025-12-02 14:30:00",
      updateTime: "2025-12-02 14:30:00",
      operator: "李主任",
      details: {
        committee: '医院伦理审查委员会',
        meetingTime: '2025-12-03 14:00:00',
        members: ['张教授', '李主任', '王医生', '赵委员', '钱专家'],
        assessmentItems: [
          { name: "神经系统评估", result: "脑死亡确认", status: "completed" },
          { name: "心血管系统评估", result: "功能正常", status: "completed" },
          { name: "呼吸系统评估", result: "呼吸机维持", status: "completed" },
          { name: "肝肾功能评估", result: "功能良好", status: "completed" },
          { name: "感染性疾病筛查", result: "阴性", status: "completed" }
        ],
        imagingResults: {
          ctBrain: "脑水肿,脑干反射消失",
          chestXRay: "双肺清晰",
          abdominalUS: "肝胆胰脾未见异常"
        },
        conclusion: "符合器官捐献医学标准",
        contraindications: "无绝对禁忌症"
      }
    },
    {
      key: "death_judgment",
      name: "死亡判定",
      status: "completed",
      completeTime: "2025-12-03 09:15:00",
      updateTime: "2025-12-03 09:15:00",
      operator: "王医生",
      details: {
        judgmentType: "脑死亡判定",
        judgmentTime: "2025-12-03 09:00:00",
        doctors: ["张主任", "王医生"],
        testResults: [
          {
            test: "自主呼吸测试",
            result: "无自主呼吸",
            time: "2025-12-03 08:30:00"
          },
          {
            test: "瞳孔对光反射",
            result: "反射消失",
            time: "2025-12-03 08:45:00"
          },
          {
            test: "脑干听觉诱发电位",
            result: "脑干功能丧失",
            time: "2025-12-03 09:00:00"
          }
        ],
        certificateNo: "SW20251203001",
        legalDocuments: ["死亡证明书", "脑死亡判定书"]
      }
    },
    {
      key: "ethical_review",
      name: "伦理审查",
      status: "in_progress",
      completeTime: "2025-12-03 15:20:00",
      updateTime: "2025-12-03 15:20:00",
      operator: "伦理委员会",
      details: {
        committee: "医院伦理审查委员会",
        meetingTime: "2025-12-03 14:00:00",
        members: ["张教授", "李主任", "王医生", "赵委员", "钱专家"],
        reviewItems: [
          { item: '捐献意愿真实性', result: '确认真实', vote: '全票通过' },
          { item: '医学评估准确性', result: '确认准确', vote: '全票通过' },
          { item: '法律文件完整性', result: '确认完整', vote: '全票通过' }
          { item: "捐献意愿真实性", result: "确认真实", vote: "全票通过" },
          { item: "医学评估准确性", result: "确认准确", vote: "全票通过" },
          { item: "法律文件完整性", result: "确认完整", vote: "全票通过" }
        ],
        conclusion: '符合伦理要求,同意进行器官捐献',
        resolutionNo: 'LL20251203001'
        conclusion: "符合伦理要求,同意进行器官捐献",
        resolutionNo: "LL20251203001"
      }
    },
    {
      key: 'organ_allocation',
      name: '器官分配',
      status: 'pending',
      updateTime: '2025-12-04 10:00:00',
      operator: '分配系统',
      key: "organ_allocation",
      name: "器官分配",
      status: "pending",
      updateTime: "2025-12-04 10:00:00",
      operator: "分配系统",
      details: {
        allocationStartTime: '2025-12-04 09:00:00',
        allocationSystem: '中国人体器官分配与共享计算机系统',
        allocationStartTime: "2025-12-04 09:00:00",
        allocationSystem: "中国人体器官分配与共享计算机系统",
        organs: [
          {
            organ: '肝脏',
            status: '分配中',
            organ: "肝脏",
            status: "分配中",
            matchScore: 95,
            recommendedRecipient: '王先生',
            recommendedRecipient: "王先生",
            recipientAge: 45,
            recipientBloodType: 'A',
            hospital: '青岛大学附属医院',
            urgency: '紧急'
            recipientBloodType: "A",
            hospital: "青岛大学附属医院",
            urgency: "紧急"
          },
          {
            organ: '肾脏',
            status: '匹配完成',
            organ: "肾脏",
            status: "匹配完成",
            matchScore: 92,
            recommendedRecipient: '李女士',
            recommendedRecipient: "李女士",
            recipientAge: 38,
            recipientBloodType: 'A',
            hospital: '青岛市市立医院',
            urgency: '高'
            recipientBloodType: "A",
            hospital: "青岛市市立医院",
            urgency: "高"
          },
          {
            organ: '心脏',
            status: '待分配',
            organ: "心脏",
            status: "待分配",
            matchScore: 88,
            recommendedRecipient: '陈先生',
            recommendedRecipient: "陈先生",
            recipientAge: 52,
            recipientBloodType: 'O',
            hospital: '山东大学齐鲁医院',
            urgency: '紧急'
            recipientBloodType: "O",
            hospital: "山东大学齐鲁医院",
            urgency: "紧急"
          }
        ],
        allocationFactors: [
          { factor: '病情危重程度', weight: 35 },
          { factor: '组织配型匹配', weight: 25 },
          { factor: '等待时间', weight: 15 },
          { factor: '地理因素', weight: 10 },
          { factor: '年龄因素', weight: 15 }
          { factor: "病情危重程度", weight: 35 },
          { factor: "组织配型匹配", weight: 25 },
          { factor: "等待时间", weight: 15 },
          { factor: "地理因素", weight: 10 },
          { factor: "年龄因素", weight: 15 }
        ]
      }
    },
    {
      key: 'organ_procurement',
      name: '器官获取',
      status: 'pending',
      updateTime: '2025-12-03 16:00:00',
      operator: '待分配',
      key: "organ_procurement",
      name: "器官获取",
      status: "pending",
      updateTime: "2025-12-03 16:00:00",
      operator: "待分配",
      details: {
        scheduledTime: '2025-12-04 14:00:00',
        operationRoom: '手术室一号',
        scheduledTime: "2025-12-04 14:00:00",
        operationRoom: "手术室一号",
        surgicalTeam: {
          surgeon: '待分配',
          assistant: '待分配',
          anesthesiologist: '待分配',
          nurse: '待分配'
          surgeon: "待分配",
          assistant: "待分配",
          anesthesiologist: "待分配",
          nurse: "待分配"
        },
        preservationPlan: {
          method: '低温机械灌注',
          solution: 'UW保存液',
          temperature: '4°C'
          method: "低温机械灌注",
          solution: "UW保存液",
          temperature: "4°C"
        },
        organs: [
          {
            organ: '肝脏',
            organ: "肝脏",
            planned: true,
            preservation: '待准备',
            estimatedTime: '4小时'
            preservation: "待准备",
            estimatedTime: "4小时"
          },
          {
            organ: '肾脏',
            organ: "肾脏",
            planned: true,
            preservation: '待准备',
            estimatedTime: '3小时'
            preservation: "待准备",
            estimatedTime: "3小时"
          },
          {
            organ: '心脏',
            organ: "心脏",
            planned: true,
            preservation: '待准备',
            estimatedTime: '5小时'
            preservation: "待准备",
            estimatedTime: "5小时"
          }
        ]
      }
    },
    {
      key: 'organ_utilization',
      name: '器官利用',
      status: 'pending',
      updateTime: '2025-12-03 16:00:00',
      operator: '待分配',
      key: "organ_utilization",
      name: "器官利用",
      status: "pending",
      updateTime: "2025-12-03 16:00:00",
      operator: "待分配",
      details: {
        transplantCenters: [
          {
            hospital: '青岛大学附属医院',
            organ: '肝脏',
            recipient: '王先生',
            scheduledTime: '2025-12-04 18:00:00',
            surgicalTeam: '待确认'
            hospital: "青岛大学附属医院",
            organ: "肝脏",
            recipient: "王先生",
            scheduledTime: "2025-12-04 18:00:00",
            surgicalTeam: "待确认"
          },
          {
            hospital: '青岛市市立医院',
            organ: '肾脏',
            recipient: '李女士',
            scheduledTime: '2025-12-04 19:00:00',
            surgicalTeam: '待确认'
            hospital: "青岛市市立医院",
            organ: "肾脏",
            recipient: "李女士",
            scheduledTime: "2025-12-04 19:00:00",
            surgicalTeam: "待确认"
          }
        ],
        followupPlan: {
          frequency: '术后1个月、3个月、6个月、1å¹´',
          items: ['肝功能检查', '免疫抑制剂浓度', '影像学检查'],
          coordinator: '待分配'
          frequency: "术后1个月、3个月、6个月、1å¹´",
          items: ["肝功能检查", "免疫抑制剂浓度", "影像学检查"],
          coordinator: "待分配"
        },
        qualityMetrics: {
          expectedSurvivalRate: 92,
          complicationRisk: '中等',
          complicationRisk: "中等",
          successRate: 95
        }
      }
@@ -274,40 +283,40 @@
  // æ–°å¢žæ—¶é—´çº¿äº‹ä»¶
  timelineEvents: [
    {
      time: '2025-12-01 08:00:00',
      event: '案例登记',
      operator: '李协调员',
      description: '捐献案例正式登记启动'
      time: "2025-12-01 08:00:00",
      event: "案例登记",
      operator: "李协调员",
      description: "捐献案例正式登记启动"
    },
    {
      time: '2025-12-01 10:00:00',
      event: '供者维护完成',
      operator: '张医生',
      description: '完成供者生命体征维护和医疗管理'
      time: "2025-12-01 10:00:00",
      event: "供者维护完成",
      operator: "张医生",
      description: "完成供者生命体征维护和医疗管理"
    },
    {
      time: '2025-12-02 14:30:00',
      event: '医学评估完成',
      operator: '李主任',
      description: '全面医学评估确认符合捐献标准'
      time: "2025-12-02 14:30:00",
      event: "医学评估完成",
      operator: "李主任",
      description: "全面医学评估确认符合捐献标准"
    },
    {
      time: '2025-12-03 09:15:00',
      event: '死亡判定完成',
      operator: '王医生',
      description: '脑死亡判定程序完成'
      time: "2025-12-03 09:15:00",
      event: "死亡判定完成",
      operator: "王医生",
      description: "脑死亡判定程序完成"
    },
    {
      time: '2025-12-03 11:00:00',
      event: '捐献确认完成',
      operator: '赵协调员',
      description: '家属签署捐献同意书'
      time: "2025-12-03 11:00:00",
      event: "捐献确认完成",
      operator: "赵协调员",
      description: "家属签署捐献同意书"
    },
    {
      time: '2025-12-03 15:20:00',
      event: '伦理审查通过',
      operator: '伦理委员会',
      description: '伦理审查委员会全票通过'
      time: "2025-12-03 15:20:00",
      event: "伦理审查通过",
      operator: "伦理委员会",
      description: "伦理审查委员会全票通过"
    }
  ],
  // æ–°å¢žç»Ÿè®¡ä¿¡æ¯
@@ -315,15 +324,15 @@
    totalStages: 8,
    completedStages: 5,
    completionRate: 62.5,
    timeElapsed: '2天6小时',
    estimatedCompletion: '2025-12-04 20:00:00',
    timeElapsed: "2天6小时",
    estimatedCompletion: "2025-12-04 20:00:00",
    organsToDonate: 4,
    potentialRecipients: 3
  }
};
// èŽ·å–æçŒ®è¿›ç¨‹è¯¦æƒ…
export const getDonationProcessDetail = async (caseId) => {
export const getDonationProcessDetail = async caseId => {
  await new Promise(resolve => setTimeout(resolve, 500));
  // æ¨¡æ‹Ÿæ ¹æ®caseId返回不同数据
@@ -332,7 +341,7 @@
  return {
    code: 200,
    message: 'success',
    message: "success",
    data: data
  };
};
@@ -342,19 +351,24 @@
  await new Promise(resolve => setTimeout(resolve, 300));
  // æ¨¡æ‹Ÿæ›´æ–°é€»è¾‘
  const stage = mockDonationProcessData.processStages.find(s => s.key === stageKey);
  const stage = mockDonationProcessData.processStages.find(
    s => s.key === stageKey
  );
  if (stage) {
    stage.status = status;
    stage.updateTime = new Date().toISOString().replace('T', ' ').substring(0, 19);
    stage.updateTime = new Date()
      .toISOString()
      .replace("T", " ")
      .substring(0, 19);
    if (status === 'completed') {
    if (status === "completed") {
      stage.completeTime = stage.updateTime;
    }
  }
  return {
    code: 200,
    message: '阶段状态更新成功',
    message: "阶段状态更新成功",
    data: {
      caseId,
      stageKey,
@@ -368,40 +382,42 @@
export const getStageDetail = async (caseId, stageKey) => {
  await new Promise(resolve => setTimeout(resolve, 200));
  const stage = mockDonationProcessData.processStages.find(s => s.key === stageKey);
  const stage = mockDonationProcessData.processStages.find(
    s => s.key === stageKey
  );
  if (!stage) {
    return {
      code: 404,
      message: '阶段不存在',
      message: "阶段不存在",
      data: null
    };
  }
  return {
    code: 200,
    message: 'success',
    message: "success",
    data: stage
  };
};
// èŽ·å–æ—¶é—´çº¿äº‹ä»¶
export const getTimelineEvents = async (caseId) => {
export const getTimelineEvents = async caseId => {
  await new Promise(resolve => setTimeout(resolve, 150));
  return {
    code: 200,
    message: 'success',
    message: "success",
    data: mockDonationProcessData.timelineEvents
  };
};
// èŽ·å–æ¡ˆä¾‹ç»Ÿè®¡ä¿¡æ¯
export const getCaseStatistics = async (caseId) => {
export const getCaseStatistics = async caseId => {
  await new Promise(resolve => setTimeout(resolve, 100));
  return {
    code: 200,
    message: 'success',
    message: "success",
    data: mockDonationProcessData.statistics
  };
};
@@ -412,12 +428,15 @@
  return {
    code: 200,
    message: '审核提交成功',
    message: "审核提交成功",
    data: {
      caseId,
      stageKey,
      reviewId: `REV${Date.now()}`,
      submitTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
      submitTime: new Date()
        .toISOString()
        .replace("T", " ")
        .substring(0, 19),
      ...reviewData
    }
  };
@@ -429,14 +448,17 @@
  return {
    code: 200,
    message: '文件上传成功',
    message: "文件上传成功",
    data: {
      caseId,
      stageKey,
      fileId: `FILE${Date.now()}`,
      fileName: fileInfo.name,
      fileSize: fileInfo.size,
      uploadTime: new Date().toISOString().replace('T', ' ').substring(0, 19),
      uploadTime: new Date()
        .toISOString()
        .replace("T", " ")
        .substring(0, 19),
      url: `/files/${caseId}/${stageKey}/${fileInfo.name}`
    }
  };
src/views/business/course/index.vue
@@ -2,7 +2,7 @@
  <div class="donation-process-detail">
    <el-card class="process-card">
      <div class="process-container">
        <!-- å·¦ä¾§æ—¶é—´çº¿ -->
        <!-- å·¦ä¾§æ—¶é—´çº¿ - ç‹¬ç«‹å›ºå®šï¼Œå†…部可滚动 -->
        <div class="timeline-section">
          <div class="section-header">
            <h3>捐献进程时间线</h3>
@@ -11,45 +11,55 @@
            </el-tag>
          </div>
          <div class="timeline-container">
            <div
              v-for="stage in processStages"
              :key="stage.key"
              class="timeline-item"
              :class="{
                'active': activeStage === stage.key,
                'completed': stage.status === 'completed',
                'in-progress': stage.status === 'in_progress',
                'pending': stage.status === 'pending'
              }"
              @click="handleStageClick(stage)"
            >
              <div class="timeline-marker">
                <i v-if="stage.status === 'completed'" class="el-icon-check"></i>
                <i v-else-if="stage.status === 'in_progress'" class="el-icon-loading"></i>
                <i v-else class="el-icon-time"></i>
              </div>
              <div class="timeline-content">
                <div class="stage-header">
                  <span class="stage-name">{{ stage.name }}</span>
                  <el-tag
                    size="small"
                    :type="getStageStatusTag(stage.status)"
                  >
                    {{ getStageStatusText(stage.status) }}
                  </el-tag>
          <div class="timeline-scroll-container">
            <div class="timeline-container">
              <div
                v-for="stage in processStages"
                :key="stage.key"
                class="timeline-item"
                :class="{
                  active: activeStage === stage.key,
                  completed: stage.status === 'completed',
                  'in-progress': stage.status === 'in_progress',
                  pending: stage.status === 'pending'
                }"
                @click="handleStageClick(stage)"
              >
                <div class="timeline-marker">
                  <i
                    v-if="stage.status === 'completed'"
                    class="el-icon-check"
                  ></i>
                  <i
                    v-else-if="stage.status === 'in_progress'"
                    class="el-icon-loading"
                  ></i>
                  <i v-else class="el-icon-time"></i>
                </div>
                <div class="stage-info">
                  <div v-if="stage.completeTime" class="time-info">
                    <span>完成时间: {{ formatTime(stage.completeTime) }}</span>
                <div class="timeline-content">
                  <div class="stage-header">
                    <span class="stage-name">{{ stage.name }}</span>
                    <el-tag
                      size="small"
                      :type="getStageStatusTag(stage.status)"
                    >
                      {{ getStageStatusText(stage.status) }}
                    </el-tag>
                  </div>
                  <div v-if="stage.updateTime" class="time-info">
                    <span>最近更新: {{ formatTime(stage.updateTime) }}</span>
                  </div>
                  <div v-if="stage.operator" class="operator-info">
                    <span>负责人: {{ stage.operator }}</span>
                  <div class="stage-info">
                    <div v-if="stage.completeTime" class="time-info">
                      <span
                        >完成时间: {{ formatTime(stage.completeTime) }}</span
                      >
                    </div>
                    <div v-if="stage.updateTime" class="time-info">
                      <span>最近更新: {{ formatTime(stage.updateTime) }}</span>
                    </div>
                    <div v-if="stage.operator" class="operator-info">
                      <span>负责人: {{ stage.operator }}</span>
                    </div>
                  </div>
                </div>
              </div>
@@ -57,9 +67,9 @@
          </div>
        </div>
        <!-- å³ä¾§å†…容区域 -->
        <!-- å³ä¾§å†…容区域 - ç‹¬ç«‹æ»šåЍ -->
        <div class="content-section">
          <!-- æ¡ˆä¾‹åŸºæœ¬ä¿¡æ¯ -->
          <!-- æ¡ˆä¾‹åŸºæœ¬ä¿¡æ¯ - å›ºå®šé«˜åº¦ï¼Œå¯æ»šåЍ -->
          <div class="basic-info-section">
            <div class="section-header">
              <h3>案例基本信息</h3>
@@ -72,88 +82,67 @@
              </el-button>
            </div>
            <el-descriptions :column="2" border>
              <el-descriptions-item label="住院号">
                {{ caseInfo.caseNo }}
              </el-descriptions-item>
              <el-descriptions-item label="住院号">
                {{ caseInfo.hospitalNo }}
              </el-descriptions-item>
              <el-descriptions-item label="捐献者姓名">
                {{ caseInfo.donorName }}
              </el-descriptions-item>
              <el-descriptions-item label="性别">
                <dict-tag
                  :options="dict.type.sys_user_sex"
                  :value="parseInt(caseInfo.gender)"
                />
              </el-descriptions-item>
              <el-descriptions-item label="年龄">
                {{ caseInfo.age }} å²
              </el-descriptions-item>
              <el-descriptions-item label="血型">
                <dict-tag
                  :options="dict.type.sys_BloodType"
                  :value="caseInfo.bloodType"
                />
              </el-descriptions-item>
              <el-descriptions-item label="疾病诊断">
                {{ caseInfo.diagnosis }}
              </el-descriptions-item>
              <el-descriptions-item label="案例状态">
                <el-tag :type="getOverallStatusTag(caseInfo.status)">
                  {{ getStatusText(caseInfo.status) }}
                </el-tag>
              </el-descriptions-item>
              <el-descriptions-item label="创建时间">
                {{ formatTime(caseInfo.createTime) }}
              </el-descriptions-item>
              <el-descriptions-item label="登记人">
                {{ caseInfo.registrant }}
              </el-descriptions-item>
              <el-descriptions-item label="当前阶段">
                {{ getCurrentStageName() }}
              </el-descriptions-item>
            </el-descriptions>
            <div class="basic-info-content">
              <el-descriptions :column="2" border>
                <el-descriptions-item label="住院号">
                  {{ caseInfo.caseNo }}
                </el-descriptions-item>
                <el-descriptions-item label="住院号">
                  {{ caseInfo.hospitalNo }}
                </el-descriptions-item>
                <el-descriptions-item label="捐献者姓名">
                  {{ caseInfo.donorName }}
                </el-descriptions-item>
                <el-descriptions-item label="性别">
                  <dict-tag
                    :options="dict.type.sys_user_sex"
                    :value="parseInt(caseInfo.gender)"
                  />
                </el-descriptions-item>
                <el-descriptions-item label="年龄">
                  {{ caseInfo.age }} å²
                </el-descriptions-item>
                <el-descriptions-item label="血型">
                  <dict-tag
                    :options="dict.type.sys_BloodType"
                    :value="caseInfo.bloodType"
                  />
                </el-descriptions-item>
                <el-descriptions-item label="疾病诊断">
                  {{ caseInfo.diagnosis }}
                </el-descriptions-item>
                <el-descriptions-item label="案例状态">
                  <el-tag :type="getOverallStatusTag(caseInfo.status)">
                    {{ getStatusText(caseInfo.status) }}
                  </el-tag>
                </el-descriptions-item>
                <el-descriptions-item label="创建时间">
                  {{ formatTime(caseInfo.createTime) }}
                </el-descriptions-item>
                <el-descriptions-item label="登记人">
                  {{ caseInfo.registrant }}
                </el-descriptions-item>
                <el-descriptions-item label="当前阶段">
                  {{ getCurrentStageName() }}
                </el-descriptions-item>
              </el-descriptions>
            </div>
          </div>
          <!-- é˜¶æ®µè¯¦æƒ…内容 -->
          <!-- é˜¶æ®µè¯¦æƒ…内容 - è‡ªé€‚应高度,可滚动 -->
          <div class="stage-detail-section">
            <div class="section-header">
              <h3>{{ activeStageName }} - é˜¶æ®µè¯¦æƒ…</h3>
              <div class="stage-actions">
                <!-- <el-button
                  v-if="activeStageData.status !== 'completed'"
                  type="success"
                  size="small"
                  @click="handleCompleteStage"
                >
                  å®Œæˆé˜¶æ®µ
                </el-button>
                <el-button
                  type="primary"
                  size="small"
                  @click="handleViewDetail"
                >
                  æŸ¥çœ‹è¯¦æƒ…
                </el-button>
                <el-button
                  v-if="activeStageData.status === 'completed'"
                  type="warning"
                  size="small"
                  @click="handleModifyStage"
                >
                  ä¿®æ”¹ä¿¡æ¯
                </el-button> -->
              </div>
              <div class="stage-actions"></div>
            </div>
            <!-- åŠ¨æ€é˜¶æ®µå†…å®¹ -->
            <div class="stage-content">
            <div class="stage-content-wrapper">
              <component
                :is="getStageComponent()"
                :stageData="activeStageData"
                :caseInfo="caseInfo"
                :infoid="caseId"
              />
            </div>
          </div>
@@ -164,19 +153,19 @@
</template>
<script>
import { getDonationProcessDetail } from './donationProcess';
import DonorMaintenanceStage from './components/DonorMaintenanceStage';
import MedicalAssessmentStage from './components/MedicalAssessmentStage';
import DeathJudgmentStage from './components/DeathJudgmentStage';
import DonationConfirmStage from './components/DonationConfirmStage';
import EthicalReviewStage from './components/EthicalReviewStage';
import OrganAllocationStage from './components/OrganAllocationStage';
import OrganProcurementStage from './components/OrganProcurementStage';
import OrganUtilizationStage from './components/OrganUtilizationStage';
import { getDonationProcessDetail } from "./donationProcess";
import DonorMaintenanceStage from "./components/DonorMaintenanceStage";
import MedicalAssessmentStage from "./components/MedicalAssessmentStage";
import DeathJudgmentStage from "./components/DeathJudgmentStage";
import DonationConfirmStage from "./components/DonationConfirmStage";
import EthicalReviewStage from "./components/EthicalReviewStage";
import OrganAllocationStage from "./components/OrganAllocationStage";
import OrganProcurementStage from "./components/OrganProcurementStage";
import OrganUtilizationStage from "./components/OrganUtilizationStage";
import dayjs from "dayjs";
export default {
  name: 'DonationProcessDetail',
  name: "DonationProcessDetail",
  components: {
    DonorMaintenanceStage,
    MedicalAssessmentStage,
@@ -187,96 +176,97 @@
    OrganProcurementStage,
    OrganUtilizationStage
  },
  dicts: ['sys_user_sex', 'sys_BloodType', 'sys_0_1'],
  dicts: ["sys_user_sex", "sys_BloodType", "sys_0_1"],
  data() {
    return {
      caseId: null,
      caseInfo: {
        id: '',
        caseNo: '',
        hospitalNo: '',
        donorName: '',
        gender: '',
        age: '',
        bloodType: '',
        diagnosis: '',
        status: 'in_progress',
        createTime: '',
        registrant: '',
        currentStage: 'donor_maintenance'
        id: "",
        caseNo: "",
        hospitalNo: "",
        donorName: "",
        gender: "",
        age: "",
        bloodType: "",
        diagnosis: "",
        status: "in_progress",
        createTime: "",
        registrant: "",
        currentStage: "donor_maintenance"
      },
      processStages: [
        {
          key: 'donor_maintenance',
          name: '供者维护',
          status: 'completed',
          completeTime: '2025-12-01 10:00:00',
          updateTime: '2025-12-01 10:00:00',
          operator: '张医生'
          key: "donor_maintenance",
          name: "供者维护",
          status: "completed",
          completeTime: "2025-12-01 10:00:00",
          updateTime: "2025-12-01 10:00:00",
          operator: "张医生"
        },
        {
          key: 'medical_assessment',
          name: '医学评估',
          status: 'completed',
          completeTime: '2025-12-02 14:30:00',
          updateTime: '2025-12-02 14:30:00',
          operator: '李主任'
          key: "death_judgment",
          name: "死亡判定",
          status: "completed",
          completeTime: "2025-12-02 14:30:00",
          updateTime: "2025-12-02 14:30:00",
          operator: "王医生"
        },
        {
          key: 'death_judgment',
          name: '死亡判定',
          status: 'completed',
          completeTime: '2025-12-03 09:15:00',
          updateTime: '2025-12-03 09:15:00',
          operator: '王医生'
          key: "medical_assessment",
          name: "医学评估",
          status: "completed",
          completeTime: "2025-12-03 09:15:00",
          updateTime: "2025-12-03 09:15:00",
          operator: "李主任"
        },
        {
          key: 'donation_confirm',
          name: '捐献确认',
          status: 'completed',
          completeTime: '2025-12-03 11:00:00',
          updateTime: '2025-12-03 11:00:00',
          operator: '赵协调员'
          key: "donation_confirm",
          name: "捐献确认",
          status: "completed",
          completeTime: "2025-12-03 11:00:00",
          updateTime: "2025-12-03 11:00:00",
          operator: "赵协调员"
        },
        {
          key: 'ethical_review',
          name: '伦理审查',
          status: 'completed',
          completeTime: '2025-12-03 15:20:00',
          updateTime: '2025-12-03 15:20:00',
          operator: '伦理委员会'
          key: "ethical_review",
          name: "伦理审查",
          status: "completed",
          completeTime: "2025-12-03 15:20:00",
          updateTime: "2025-12-03 15:20:00",
          operator: "伦理委员会"
        },
        {
          key: 'organ_allocation',
          name: '器官分配',
          status: 'in_progress',
          updateTime: '2025-12-04 10:00:00',
          operator: '分配系统'
          key: "organ_allocation",
          name: "器官分配",
          status: "in_progress",
          updateTime: "2025-12-04 10:00:00",
          operator: "分配系统"
        },
        {
          key: 'organ_procurement',
          name: '器官获取',
          status: 'pending',
          operator: '待分配'
          key: "organ_procurement",
          name: "器官获取",
          status: "pending",
          operator: "待分配"
        },
        {
          key: 'organ_utilization',
          name: '器官利用',
          status: 'pending',
          operator: '待分配'
          key: "organ_utilization",
          name: "器官利用",
          status: "pending",
          operator: "待分配"
        }
      ],
      activeStage: 'organ_allocation',
      activeStageName: '器官分配',
      activeStage: "organ_allocation",
      activeStageName: "器官分配",
      activeStageData: {},
      loading: false
    };
  },
  computed: {
  },
  computed: {},
  created() {
    this.caseId = this.$route.query.id;
    console.log(this.caseId, "this.caseId");
    if (this.caseId) {
      this.getDetail();
    } else {
@@ -285,16 +275,16 @@
    this.setActiveStage(this.activeStage);
  },
  methods: {
       getStageComponent() {
    getStageComponent() {
      const componentMap = {
        'donor_maintenance': 'DonorMaintenanceStage',
        'medical_assessment': 'MedicalAssessmentStage',
        'death_judgment': 'DeathJudgmentStage',
        'donation_confirm': 'DonationConfirmStage',
        'ethical_review': 'EthicalReviewStage',
        'organ_allocation': 'OrganAllocationStage',
        'organ_procurement': 'OrganProcurementStage',
        'organ_utilization': 'OrganUtilizationStage'
        donor_maintenance: "DonorMaintenanceStage",
        death_judgment: "DeathJudgmentStage",
        medical_assessment: "MedicalAssessmentStage",
        donation_confirm: "DonationConfirmStage",
        ethical_review: "EthicalReviewStage",
        organ_allocation: "OrganAllocationStage",
        organ_procurement: "OrganProcurementStage",
        organ_utilization: "OrganUtilizationStage"
      };
      return componentMap[this.activeStage];
    },
@@ -309,8 +299,8 @@
          this.setActiveStage(response.data.currentStage);
        }
      } catch (error) {
        console.error('获取捐献进程详情失败:', error);
        this.$message.error('获取详情失败');
        console.error("获取捐献进程详情失败:", error);
        this.$message.error("获取详情失败");
      } finally {
        this.loading = false;
      }
@@ -318,18 +308,18 @@
    // ç”Ÿæˆæ¨¡æ‹Ÿæ•°æ®
    generateMockData() {
      this.caseInfo = {
        id: '202512001',
        caseNo: 'C202512001',
        hospitalNo: 'D202512001',
        donorName: '张三',
        gender: '0',
        id: "202512001",
        caseNo: "C202512001",
        hospitalNo: "D202512001",
        donorName: "张三",
        gender: "0",
        age: 45,
        bloodType: 'A',
        diagnosis: '脑外伤',
        status: 'in_progress',
        createTime: '2025-12-01 08:00:00',
        registrant: '李协调员',
        currentStage: 'organ_allocation'
        bloodType: "A",
        diagnosis: "脑外伤",
        status: "in_progress",
        createTime: "2025-12-01 08:00:00",
        registrant: "李协调员",
        currentStage: "organ_allocation"
      };
    },
    // è®¾ç½®å½“前激活阶段
@@ -339,77 +329,78 @@
      if (stage) {
        this.activeStageName = stage.name;
        this.activeStageData = stage;
        console.log(this.activeStageData, "this.activeStageData");
      }
    },
    // å¤„理阶段点击
    handleStageClick(stage) {
      if (stage.status !== 'pending') {
      if (stage.status !== "pending") {
        this.setActiveStage(stage.key);
      } else {
        this.$message.warning('该阶段尚未开始,无法查看详情');
        this.$message.warning("该阶段尚未开始,无法查看详情");
      }
    },
    // èŽ·å–é˜¶æ®µçŠ¶æ€æ ‡ç­¾ç±»åž‹
    getStageStatusTag(status) {
      const map = {
        'completed': 'success',
        'in_progress': 'warning',
        'pending': 'info'
        completed: "success",
        in_progress: "warning",
        pending: "info"
      };
      return map[status] || 'info';
      return map[status] || "info";
    },
    // èŽ·å–é˜¶æ®µçŠ¶æ€æ–‡æœ¬
    getStageStatusText(status) {
      const map = {
        'completed': '已完成',
        'in_progress': '进行中',
        'pending': '未开始'
        completed: "已完成",
        in_progress: "进行中",
        pending: "未开始"
      };
      return map[status] || '未知';
      return map[status] || "未知";
    },
    // èŽ·å–æ•´ä½“çŠ¶æ€æ ‡ç­¾ç±»åž‹
    getOverallStatusTag(status) {
      const map = {
        'completed': 'success',
        'in_progress': 'warning',
        'pending': 'info',
        'terminated': 'danger'
        completed: "success",
        in_progress: "warning",
        pending: "info",
        terminated: "danger"
      };
      return map[status] || 'info';
      return map[status] || "info";
    },
    // èŽ·å–æ•´ä½“çŠ¶æ€æ–‡æœ¬
    getStatusText(status) {
      const map = {
        'completed': '已完成',
        'in_progress': '进行中',
        'pending': '未开始',
        'terminated': '已终止'
        completed: "已完成",
        in_progress: "进行中",
        pending: "未开始",
        terminated: "已终止"
      };
      return map[status] || '未知';
      return map[status] || "未知";
    },
    // æ—¶é—´æ ¼å¼åŒ–
    formatTime(time) {
      if (!time) return '-';
      return dayjs(time).format('YYYY-MM-DD HH:mm');
      if (!time) return "-";
      return dayjs(time).format("YYYY-MM-DD HH:mm");
    },
    // èŽ·å–å½“å‰é˜¶æ®µåç§°
    getCurrentStageName() {
      const currentStage = this.processStages.find(
        stage => stage.status === 'in_progress'
        stage => stage.status === "in_progress"
      );
      return currentStage ? currentStage.name : '已完成';
      return currentStage ? currentStage.name : "已完成";
    },
    // ç¼–辑基本信息
    handleEditBasicInfo() {
      this.$message.info('编辑基本信息功能');
      this.$message.info("编辑基本信息功能");
    },
    // å®Œæˆé˜¶æ®µ
    handleCompleteStage() {
      this.$confirm(`确定要完成【${this.activeStageName}】阶段吗?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      this.$confirm(`确定要完成【${this.activeStageName}】阶段吗?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        // æ›´æ–°å½“前阶段状态
        const currentIndex = this.processStages.findIndex(
@@ -417,32 +408,34 @@
        );
        if (currentIndex !== -1) {
          this.processStages[currentIndex].status = 'completed';
          this.processStages[currentIndex].completeTime = new Date().toISOString();
          this.processStages[currentIndex].status = "completed";
          this.processStages[
            currentIndex
          ].completeTime = new Date().toISOString();
          // æ¿€æ´»ä¸‹ä¸€ä¸ªé˜¶æ®µ
          if (currentIndex < this.processStages.length - 1) {
            this.processStages[currentIndex + 1].status = 'in_progress';
            this.processStages[currentIndex + 1].status = "in_progress";
            this.setActiveStage(this.processStages[currentIndex + 1].key);
          } else {
            this.caseInfo.status = 'completed';
            this.caseInfo.status = "completed";
          }
          this.$message.success('阶段已完成');
          this.$message.success("阶段已完成");
        }
      });
    },
    // æŸ¥çœ‹è¯¦æƒ…
    handleViewDetail() {
      const routeMap = {
        'donor_maintenance': '/case/donorMaintenance/detail',
        'medical_assessment': '/case/medicalAssessment/detail',
        'death_judgment': '/case/deathJudgment/detail',
        'donation_confirm': '/case/donationConfirm/detail',
        'ethical_review': '/case/ethicalReview/detail',
        'organ_allocation': '/case/organAllocation/detail',
        'organ_procurement': '/case/organProcurement/detail',
        'organ_utilization': '/case/organUtilization/detail'
        donor_maintenance: "/case/donorMaintenance/detail",
        death_judgment: "/case/deathJudgment/detail",
        medical_assessment: "/case/medicalAssessment/detail",
        donation_confirm: "/case/donationConfirm/detail",
        ethical_review: "/case/ethicalReview/detail",
        organ_allocation: "/case/organAllocation/detail",
        organ_procurement: "/case/organProcurement/detail",
        organ_utilization: "/case/organUtilization/detail"
      };
      const route = routeMap[this.activeStage];
@@ -466,47 +459,170 @@
  padding: 20px;
  background-color: #f5f7fa;
  min-height: 100vh;
  box-sizing: border-box;
}
.process-card {
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  margin-bottom: 20px;
}
.process-container {
  display: flex;
  min-height: 800px;
  min-height: 600px; /* è®¾ç½®ä¸€ä¸ªæœ€å°é«˜åº¦ */
  gap: 20px;
  align-items: flex-start; /* é¡¶éƒ¨å¯¹é½ */
}
/* å·¦ä¾§æ—¶é—´çº¿æ ·å¼ */
/* å·¦ä¾§æ—¶é—´çº¿æ ·å¼ - å›ºå®šé«˜åº¦ï¼Œå†…部滚动 */
.timeline-section {
  flex: 0 0 300px;
  flex: 0 0 320px; /* å›ºå®šå®½åº¦ */
  display: flex;
  flex-direction: column;
  background: white;
  border-radius: 6px;
  padding: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  height: calc(120vh - 120px); /* æ ¹æ®è§†å£é«˜åº¦è‡ªé€‚应 */
  max-height: 1200px; /* è®¾ç½®æœ€å¤§é«˜åº¦ */
  position: sticky; /* ä½¿ç”¨ sticky å®šä½ */
  top: 20px; /* è·ç¦»é¡¶éƒ¨ 20px */
}
.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding-bottom: 15px;
  border-bottom: 1px solid #e4e7ed;
.timeline-scroll-container {
  flex: 1;
  overflow-y: auto; /* å†…部可滚动 */
  margin-top: 20px;
  padding-right: 8px; /* ä¸ºæ»šåŠ¨æ¡ç•™å‡ºç©ºé—´ */
}
.section-header h3 {
  margin: 0;
  color: #303133;
  font-size: 16px;
.timeline-scroll-container::-webkit-scrollbar {
  width: 6px;
}
.timeline-scroll-container::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 3px;
}
.timeline-scroll-container::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 3px;
}
.timeline-scroll-container::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}
.timeline-container {
  display: flex;
  flex-direction: column;
  gap: 15px;
  padding-bottom: 10px;
}
/* å³ä¾§å†…容区域样式 - è‡ªé€‚应高度 */
.content-section {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-height: 0; /* é‡è¦ï¼šå…è®¸flex子项压缩 */
}
.basic-info-section {
  background: white;
  border-radius: 6px;
  padding: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  min-height: 0; /* é‡è¦ */
}
.basic-info-content {
  flex: 1;
  max-height: 300px; /* åŸºæœ¬ä¿¡æ¯åŒºåŸŸæœ€å¤§é«˜åº¦ */
  overflow-y: auto; /* åŸºæœ¬ä¿¡æ¯å†…部可滚动 */
  margin-top: 20px;
  padding-right: 8px;
}
.basic-info-content::-webkit-scrollbar {
  width: 6px;
}
.basic-info-content::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 3px;
}
.basic-info-content::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 3px;
}
.basic-info-content::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}
.basic-info-content .el-descriptions {
  width: 100%;
}
.stage-detail-section {
  flex: 1; /* å æ®å‰©ä½™ç©ºé—´ */
  background: white;
  border-radius: 6px;
  padding: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  min-height: 400px; /* æœ€å°é«˜åº¦ */
  max-height: 800px; /* æœ€å¤§é«˜åº¦ï¼Œå¯æ ¹æ®éœ€è¦è°ƒæ•´ */
  overflow: hidden; /* éšè—å¤–层溢出 */
}
.stage-content-wrapper {
  flex: 1;
  overflow-y: auto; /* é˜¶æ®µè¯¦æƒ…内部可滚动 */
  margin-top: 20px;
  padding-right: 8px;
  min-height: 0; /* é‡è¦ */
}
.stage-content-wrapper::-webkit-scrollbar {
  width: 6px;
}
.stage-content-wrapper::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 3px;
}
.stage-content-wrapper::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 3px;
}
.stage-content-wrapper::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}
/* åŽŸæœ‰æ ·å¼ä¿æŒä¸å˜ */
.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-shrink: 0; /* é˜²æ­¢è¢«åŽ‹ç¼© */
}
.section-header h3 {
  margin: 0;
  color: #303133;
  font-size: 16px;
  white-space: nowrap;
}
.timeline-item {
@@ -517,6 +633,7 @@
  cursor: pointer;
  transition: all 0.3s ease;
  border: 1px solid #e4e7ed;
  flex-shrink: 0; /* é˜²æ­¢è¢«åŽ‹ç¼© */
}
.timeline-item:hover {
@@ -525,17 +642,17 @@
}
.timeline-item.active {
  border-color: #409EFF;
  border-color: #409eff;
  background-color: #f0f9ff;
}
.timeline-item.completed {
  border-color: #67C23A;
  border-color: #67c23a;
  background-color: #f0f9e8;
}
.timeline-item.in-progress {
  border-color: #E6A23C;
  border-color: #e6a23c;
  background-color: #fdf6ec;
}
@@ -557,11 +674,11 @@
}
.timeline-item.completed .timeline-marker {
  background-color: #67C23A;
  background-color: #67c23a;
}
.timeline-item.in-progress .timeline-marker {
  background-color: #E6A23C;
  background-color: #e6a23c;
}
.timeline-item.pending .timeline-marker {
@@ -583,6 +700,10 @@
  font-weight: 600;
  color: #303133;
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 150px;
}
.stage-info {
@@ -590,33 +711,18 @@
  color: #606266;
}
.time-info, .operator-info {
.time-info,
.operator-info {
  margin-bottom: 4px;
}
/* å³ä¾§å†…容区域样式 */
.content-section {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.basic-info-section,
.stage-detail-section {
  background: white;
  border-radius: 6px;
  padding: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stage-actions {
  display: flex;
  gap: 10px;
}
.stage-content {
  margin-top: 20px;
  flex-wrap: wrap;
}
/* å“åº”式设计 */
@@ -627,7 +733,18 @@
  .timeline-section {
    flex: none;
    margin-bottom: 20px;
    width: 100%;
    height: auto;
    max-height: 300px;
    position: static; /* å°å±å¹•取消 sticky */
  }
  .timeline-scroll-container {
    max-height: 250px;
  }
  .stage-detail-section {
    max-height: 500px;
  }
}
@@ -652,26 +769,17 @@
    gap: 10px;
  }
  .stage-actions {
    flex-wrap: wrap;
  .stage-name {
    max-width: 120px;
  }
}
/* åŠ¨ç”»æ•ˆæžœ */
.timeline-item {
  transition: all 0.3s ease;
}
  .basic-info-content {
    max-height: 250px;
  }
.timeline-item:hover {
  transform: translateY(-2px);
}
/* è¿›åº¦æ¡æ ·å¼ä¼˜åŒ– */
:deep(.el-progress-bar) {
  padding-right: 0;
}
:deep(.el-progress__text) {
  font-size: 12px;
  .stage-detail-section {
    min-height: 300px;
    max-height: 400px;
  }
}
</style>
src/views/business/maintain/maintainInfo.vue
@@ -566,9 +566,9 @@
import Pagination from "@/components/Pagination";
import UploadAttachment from "@/components/UploadAttachment";
import FilePreviewDialog from "@/components/FilePreviewDialog";
import LiverKidneyPanel from "./components/LiverKidneyPanel.vue";
import BloodRoutinePanel from "./components/BloodRoutinePanel.vue";
import UrineRoutinePanel from "./components/UrineRoutinePanel.vue";
import LiverKidneyPanel from "@/components/MaintainComponents/LiverKidneyPanel.vue";
import BloodRoutinePanel from "@/components/MaintainComponents/BloodRoutinePanel.vue";
import UrineRoutinePanel from "@/components/MaintainComponents/UrineRoutinePanel.vue";
import dayjs from "dayjs";
export default {
src/views/project/components/orgselect/index.vue
@@ -115,6 +115,7 @@
      console.error("'lazy-load'必须和'filterable'同时使用!");
      return false;
    }
    console.log(456789);
    if (this.lazyLoad) {
      this.focusEvents.func = () => {
@@ -153,6 +154,8 @@
          };
          this.dataList.unshift(all);
        }
        console.log(this.dataList,'this.dataList');
        this.tempList = this.dataList.map(item => item);
        this.focusEvents.loaded = true;
        this.isLoading = false;
vue.config.js
@@ -33,8 +33,8 @@
    proxy: {
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      [process.env.VUE_APP_BASE_API]: {
        // target:`http://localhost:8080`,
        target:`http://192.168.100.10:8080`,
        target:`http://localhost:8080`,
        // target:`http://192.168.100.10:8080`,
        // target:`http://192.168.100.137:8080`,
        // target: `https://slb.hospitalstar.com:9093`,
        changeOrigin: true,