WXL
3 天以前 c80bc467a41daa6cbae4e5515a300a8ca98cfeaa
pagesSub/case/CaseInfo.vue
@@ -5,7 +5,7 @@
      <view class="case-header">
        <view class="hospital-info">
          <image
            :src="caseDetail.hospitalLogo"
            :src="caseDetail.hospitalLogo || '/static/hospital-default.png'"
            mode="aspectFit"
            class="hospital-logo"
          />
@@ -47,7 +47,7 @@
      <text
        v-for="tab in tabs"
        :key="tab.id"
        :class="{ active: activeTab === tab.id }"
        :class="{ active: activeTab == tab.id }"
        @tap="switchTab(tab.id)"
        class="tab-item"
      >
@@ -55,41 +55,50 @@
      </text>
    </view>
    <!-- 捐献者基本信息 -->
    <view v-if="activeTab === 'basic'" class="info-section fade-in-up">
    <!-- ==================== 基本信息 ==================== -->
    <view v-if="activeTab == 'basic'" class="info-section fade-in-up">
      <view class="section-card">
        <view class="section-header">
          <text class="section-title">捐献者基本信息</text>
          <text class="section-title">📋 捐献者基本信息</text>
        </view>
        <view class="info-grid">
          <!-- 第一行:姓名、性别、年龄 -->
          <view class="info-item">
            <text class="label">姓名</text>
            <text class="value">{{ caseDetail.name || "未填写" }}</text>
          </view>
          <view class="info-item">
            <text class="label">性别</text>
            <text class="value">{{ getGenderText(caseDetail.sex) }}</text>
            <text class="value">{{
              getDictLabel("sys_user_sex", caseDetail.sex)
            }}</text>
          </view>
          <view class="info-item">
            <text class="label">年龄</text>
            <text class="value"
              >{{ caseDetail.age || "0" }}
              {{ getAgeUnitText(caseDetail.ageunit) }}</text
              {{ getDictLabel("sys_AgeUnit", caseDetail.ageunit) }}</text
            >
          </view>
          <!-- 第二行:证件类型、证件号码 -->
          <view class="info-item">
            <text class="label">证件类型</text>
            <text class="value">{{
              getDictLabel("sys_IDType", caseDetail.idcardtype)
            }}</text>
          </view>
          <view class="info-item full-width">
            <text class="label">证件号码</text>
            <text class="value">{{ caseDetail.idcardno || "未填写" }}</text>
          </view>
          <view class="info-item">
            <text class="label">血型</text>
            <text class="value">{{
              getBloodTypeText(caseDetail.bloodType)
            }}</text>
          </view>
          <!-- 第三行:民族、国籍、籍贯 -->
          <view class="info-item">
            <text class="label">民族</text>
            <text class="value">{{ caseDetail.nation || "未填写" }}</text>
            <text class="value">{{
              getDictLabel("sys_Nation", caseDetail.nation)
            }}</text>
          </view>
          <view class="info-item">
            <text class="label">国籍</text>
@@ -99,21 +108,35 @@
            <text class="label">籍贯</text>
            <text class="value">{{ caseDetail.nativeplace || "未填写" }}</text>
          </view>
          <!-- 第四行:学历、职业 -->
          <view class="info-item">
            <text class="label">学历</text>
            <text class="value">{{ caseDetail.education || "未填写" }}</text>
            <text class="value">{{
              getDictLabel("sys_education", caseDetail.education)
            }}</text>
          </view>
          <view class="info-item">
            <text class="label">职业</text>
            <text class="value">{{ caseDetail.occupation || "未填写" }}</text>
            <text class="value">{{
              getDictLabel("sys_occupation", caseDetail.occupation)
            }}</text>
          </view>
          <view class="info-item full-width">
          <!-- 第五行:联系电话 -->
          <view class="info-item">
            <text class="label">联系电话</text>
            <text class="value">{{ caseDetail.phone || "未填写" }}</text>
          </view>
          <!-- 地址信息:单独一行,占据整行 -->
          <view class="info-item full-width address-item">
            <text class="label">户籍地址</text>
            <text class="value">{{
              getFullRegisterAddress() || "未填写"
            }}</text>
          </view>
          <view class="info-item full-width">
          <view class="info-item full-width address-item">
            <text class="label">现住地址</text>
            <text class="value">{{
              getFullResidenceAddress() || "未填写"
@@ -123,71 +146,76 @@
      </view>
    </view>
    <!-- 医疗信息 -->
    <view v-if="activeTab === 'medical'" class="info-section fade-in-up">
    <!-- ==================== 医疗信息 ==================== -->
    <view v-if="activeTab == 'medical'" class="info-section fade-in-up">
      <view class="section-card">
        <view class="section-header">
          <text class="section-title">医疗信息</text>
          <text class="section-title">🏥 医疗信息</text>
        </view>
        <view class="info-content">
          <view class="info-group">
            <text class="group-title">疾病诊断</text>
            <text class="group-content">{{
        <view class="info-grid">
          <!-- 住院号、GCS评分 -->
          <view class="info-item">
            <text class="label">住院号</text>
            <text class="value">{{ caseDetail.inpatientno || "未填写" }}</text>
          </view>
          <view class="info-item">
            <text class="label">GCS评分</text>
            <text class="value">{{ caseDetail.gcsScore || "未评估" }}</text>
          </view>
          <!-- 血型、Rh阴性 -->
          <view class="info-item">
            <text class="label">血型</text>
            <text class="value">{{
              getDictLabel("sys_BloodType", caseDetail.bloodType)
            }}</text>
          </view>
          <view class="info-item">
            <text class="label">Rh(D)阴性</text>
            <text class="value">{{
              caseDetail.rhYin == "1" ? "是" : "否"
            }}</text>
          </view>
          <!-- 传染病:占据整行 -->
          <view class="info-item full-width">
            <text class="label">传染病</text>
            <text class="value">{{ getInfectiousText() || "无" }}</text>
          </view>
          <!-- 疾病诊断:占据整行 -->
          <view class="info-item full-width">
            <text class="label">疾病诊断</text>
            <text class="value diagnosis-text">{{
              caseDetail.diagnosisname || "未填写"
            }}</text>
          </view>
          <view class="info-group">
            <text class="group-title">病情概况</text>
            <text class="group-content">{{
          <!-- 病情概况:占据整行 -->
          <view class="info-item full-width">
            <text class="label">病情概况</text>
            <text class="value">{{
              caseDetail.illnessoverview || "未填写"
            }}</text>
          </view>
          <view class="info-group">
            <text class="group-title">病人状况</text>
            <text class="group-content">{{
              caseDetail.patientstate || "未填写"
            }}</text>
          </view>
          <view class="info-group">
            <text class="group-title">住院号</text>
            <text class="group-content">{{
              caseDetail.inpatientno || "未填写"
            }}</text>
          </view>
          <view class="info-group">
            <text class="group-title">GCS评分</text>
            <text class="group-content">{{
              caseDetail.gcsScore || "未评估"
            }}</text>
          </view>
          <view class="info-group">
            <text class="group-title">传染病情况</text>
            <text class="group-content"
              >{{ caseDetail.infectious || "无"
              }}{{
                caseDetail.infectiousOther
                  ? `(${caseDetail.infectiousOther})`
                  : ""
              }}</text
            >
          </view>
          <view class="info-group">
            <text class="group-title">Rh阴性</text>
            <text class="group-content">{{
              caseDetail.rhYin === "1" ? "是" : "否"
            }}</text>
          <!-- 病人状况:占据整行 -->
          <view class="info-item full-width">
            <text class="label">病人状况</text>
            <text class="value">{{ caseDetail.patientstate || "未填写" }}</text>
          </view>
        </view>
      </view>
    </view>
    <!-- 医院与联系信息 -->
    <view v-if="activeTab === 'contact'" class="info-section fade-in-up">
    <!-- ==================== 联系信息 ==================== -->
    <view v-if="activeTab == 'contact'" class="info-section fade-in-up">
      <view class="section-card">
        <view class="section-header">
          <text class="section-title">医院与联系信息</text>
          <text class="section-title">📞 医院与联系信息</text>
        </view>
        <view class="info-grid">
          <!-- 医院信息 -->
          <view class="info-item full-width">
            <text class="label">治疗医院</text>
            <text class="value">{{
@@ -204,19 +232,18 @@
            <text class="label">上报医院</text>
            <text class="value">{{ caseDetail.toHospital || "未填写" }}</text>
          </view>
          <view class="info-item full-width">
            <text class="label">联系电话</text>
            <text class="value">{{ caseDetail.phone || "未填写" }}</text>
          </view>
          <!-- 联系人信息 -->
          <view class="info-item">
            <text class="label">信息员</text>
            <text class="label">上报信息员</text>
            <text class="value">{{ caseDetail.infoName || "未填写" }}</text>
          </view>
          <view class="info-item">
            <text class="label">信息员编号</text>
            <text class="value">{{ caseDetail.infoNo || "未填写" }}</text>
          </view>
          <view class="info-item">
          <!-- <view class="info-item">
            <text class="label">ICU评估医生</text>
            <text class="value">{{ caseDetail.icuDoctor || "未填写" }}</text>
          </view>
@@ -225,7 +252,8 @@
            <text class="value">{{
              caseDetail.icuDoctorPhone || "未填写"
            }}</text>
          </view>
          </view> -->
          <view class="info-item">
            <text class="label">协调员</text>
            <text class="value">{{
@@ -242,8 +270,8 @@
      </view>
    </view>
    <!-- 转运信息 -->
    <view v-if="activeTab === 'transport'" class="info-section fade-in-up">
    <!-- ==================== 转运信息 ==================== -->
    <view v-if="activeTab == 'transport'" class="info-section fade-in-up">
      <view class="section-card">
        <view class="section-header">
          <view
@@ -251,16 +279,16 @@
              display: flex;
              justify-content: space-between;
              align-items: center;
              width: 100%;
            "
          >
            <text class="section-title">转运信息</text>
            <!-- 转运操作按钮 -->
            <text class="section-title">🚑 转运信息</text>
            <view
              v-if="caseDetail.reportStatus === '3'"
              style="display: flex; gap: 20rpx"
              v-if="caseDetail.reportStatus == '3'"
              style="display: flex; gap: 16rpx"
            >
              <button
                v-if="caseDetail.isTransport === '2' && !hasTransport"
                v-if="caseDetail.isTransport == '2' && !hasTransport"
                class="small-btn primary"
                @tap.stop="createTransport"
              >
@@ -277,121 +305,93 @@
          </view>
        </view>
        <view class="info-content">
          <view class="info-group">
            <text class="group-title">是否需要转运</text>
            <text class="group-content">{{
              caseDetail.isTransport === "2" ? "需要" : "不需要"
        <view class="info-grid">
          <view class="info-item">
            <text class="label">是否需要转运</text>
            <text class="value">{{
              caseDetail.isTransport == "2" ? "需要" : "不需要"
            }}</text>
          </view>
        </view>
          <!-- 有转运信息时显示 -->
        <!-- 有转运信息时显示 -->
        <view
          v-if="hasTransport && caseDetail.serviceTransport"
          class="transport-list"
        >
          <view
            v-if="hasTransport && caseDetail.serviceTransport"
            class="transport-info"
            class="transport-item"
            v-for="transport in caseDetail.serviceTransport"
            :key="transport.id"
          >
            <view
              class="info-group"
              v-for="transport in caseDetail.serviceTransport"
              :key="transport.id"
            >
              <view class="sub-group">
                <text class="sub-label">转运单号:</text>
                <text class="sub-value">{{
                  transport.reportId || transport.id
            <view class="transport-header">
              <text class="transport-no"
                >转运单号:{{ transport.reportId || transport.id }}</text
              >
              <text
                class="transport-status"
                :class="getTransportStatusClass(transport.transitStatus)"
              >
                {{ getTransportStatusText(transport.transitStatus) }}
              </text>
            </view>
            <view class="transport-body">
              <view class="transport-row">
                <text class="t-label">出发地点</text>
                <text class="t-value">{{
                  transport.transportStartPlace || "未填写"
                }}</text>
              </view>
              <view class="sub-group">
                <text class="sub-label">出发地点:</text>
                <text class="sub-value">{{
                  transport.transportStartPlace
                }}</text>
              </view>
              <view class="sub-group">
                <text class="sub-label">出发时间:</text>
                <text class="sub-value">{{
              <view class="transport-row">
                <text class="t-label">出发时间</text>
                <text class="t-value">{{
                  formatDateTime(transport.transportStartTime)
                }}</text>
              </view>
              <view class="sub-group">
                <text class="sub-label">负责协调员:</text>
                <text class="sub-value">{{ transport.contactPerson }}</text>
              <view class="transport-row">
                <text class="t-label">负责协调员</text>
                <text class="t-value">{{
                  transport.contactPerson || "未填写"
                }}</text>
              </view>
              <view class="sub-group">
                <text class="sub-label">转运状态:</text>
                <text
                  class="sub-value status"
                  :class="getTransportStatusClass(transport.transitStatus)"
                >
                  {{ getTransportStatusText(transport.transitStatus) }}
                </text>
              <view v-if="transport.doctor" class="transport-row">
                <text class="t-label">出诊医生</text>
                <text class="t-value">{{ transport.doctor }}</text>
              </view>
              <view class="sub-group" v-if="transport.doctor">
                <text class="sub-label">急诊科医生:</text>
                <text class="sub-value">{{ transport.doctor }}</text>
              <view v-if="transport.nurse" class="transport-row">
                <text class="t-label">出诊护士</text>
                <text class="t-value">{{ transport.nurse }}</text>
              </view>
              <view class="sub-group" v-if="transport.nurse">
                <text class="sub-label">护士:</text>
                <text class="sub-value">{{ transport.nurse }}</text>
              <view v-if="transport.driver" class="transport-row">
                <text class="t-label">驾驶员</text>
                <text class="t-value">{{ transport.driver }}</text>
              </view>
              <view class="sub-group" v-if="transport.driver">
                <text class="sub-label">驾驶员:</text>
                <text class="sub-value">{{ transport.driver }}</text>
              </view>
              <view class="sub-group" v-if="transport.icuDoctor">
                <text class="sub-label">ICU评估医生:</text>
                <text class="sub-value">{{ transport.icuDoctor }}</text>
              <view v-if="transport.icuDoctor" class="transport-row">
                <text class="t-label">ICU评估医生</text>
                <text class="t-value">{{ transport.icuDoctor }}</text>
              </view>
            </view>
          </view>
        </view>
          <!-- 无转运信息但需要转运 -->
          <view
            v-else-if="caseDetail.isTransport === '2'"
            class="transport-info"
        <!-- 无转运信息但需要转运 -->
        <view v-else-if="caseDetail.isTransport == '2'" class="empty-transport">
          <text>该案例需要转运,但尚未创建转运单</text>
          <text
            v-if="caseDetail.reportStatus !== '3'"
            style="color: #f0ad4e; font-size: 24rpx; margin-top: 10rpx"
          >
            <view class="empty-transport">
              <text>该案例需要转运,但尚未创建转运单</text>
              <text
                v-if="caseDetail.reportStatus !== '3'"
                style="color: #f0ad4e; font-size: 24rpx; margin-top: 10rpx"
              >
                需先审批同意才能创建转运单
              </text>
            </view>
          </view>
            需先审批同意才能创建转运单
          </text>
        </view>
          <!-- 无需转运 -->
          <view v-else class="transport-info">
            <view class="empty-transport">
              <text>该案例无需转运</text>
            </view>
          </view>
        <!-- 无需转运 -->
        <view v-else class="empty-transport">
          <text>该案例无需转运</text>
        </view>
      </view>
    </view>
    <!-- 操作按钮 -->
    <view class="action-bar">
      <button class="action-btn secondary" @tap="goBack">返回</button>
      <!-- 根据状态显示不同操作 -->
      <button
        v-if="
          caseDetail.reportStatus === '1' || caseDetail.reportStatus === '2'
        "
        class="action-btn primary"
        @tap="handleEdit"
      >
        编辑案例
      </button>
      <!-- <button v-if="canDelete"
              class="action-btn error"
              @tap="handleDelete">
        删除案例
      </button> -->
    </view>
    <!-- 附件 -->
    <attachment-upload
      ref="attachment"
@@ -400,7 +400,194 @@
      :maxCount="5"
      @preview="handlePreview"
    />
    <!-- <view v-if="showHomeButton" class="home-btn" @click="goHome">首页</view> -->
    <!-- 操作按钮 -->
    <view class="action-bar">
      <button class="action-btn secondary" @tap="goBack">返回</button>
      <!-- 确认按钮:仅admin角色可见,且状态为"已阅读"(2)时显示 -->
      <button
        v-if="canApprove && caseDetail.delFlag == 0"
        class="action-btn approve"
        @tap="openApprovePanel"
      >
        确认案例
      </button>
      <button
        v-if="caseDetail.reportStatus == '1' || caseDetail.reportStatus == '2'"
        class="action-btn primary"
        @tap="handleEdit"
      >
        编辑案例
      </button>
    </view>
    <!-- ==================== 确认底部弹出面板 ==================== -->
    <view
      class="approve-mask"
      v-if="showApprovePanel"
      @tap="closeApprovePanel"
    ></view>
    <view class="approve-panel" :class="{ 'panel-show': showApprovePanel }">
      <view class="panel-header">
        <text class="panel-title">案例确认</text>
        <text class="panel-close" @tap="closeApprovePanel">✕</text>
      </view>
      <scroll-view scroll-y class="panel-body">
        <!-- 案例摘要 -->
        <view class="summary-card">
          <view class="summary-row">
            <text class="s-label">案例编号</text>
            <text class="s-value">{{ caseDetail.caseNo }}</text>
          </view>
          <view class="summary-row">
            <text class="s-label">患者姓名</text>
            <text class="s-value">{{ caseDetail.name }}</text>
          </view>
          <view class="summary-row">
            <text class="s-label">疾病诊断</text>
            <text class="s-value">{{ caseDetail.diagnosisname }}</text>
          </view>
          <view class="summary-row">
            <text class="s-label">上报医院</text>
            <text class="s-value">{{ caseDetail.treatmenthospitalname }}</text>
          </view>
        </view>
        <!-- 确认结果 -->
        <view class="form-group">
          <text class="form-label"
            >确认结果 <text class="required">*</text></text
          >
          <view class="radio-group">
            <view
              class="radio-item"
              :class="{ active: approveForm.approveResult == '3' }"
              @tap="
                approveForm.approveResult = '3';
                handleApproveResultChange('3');
              "
            >
              <text
                class="radio-icon"
                :class="{ checked: approveForm.approveResult == '3' }"
                >✓</text
              >
              <text class="radio-text">同意</text>
            </view>
            <view
              class="radio-item"
              :class="{ active: approveForm.approveResult == '4' }"
              @tap="
                approveForm.approveResult = '4';
                handleApproveResultChange('4');
              "
            >
              <text
                class="radio-icon"
                :class="{ checked: approveForm.approveResult == '4' }"
                >✓</text
              >
              <text class="radio-text">驳回</text>
            </view>
          </view>
        </view>
        <!-- 同意时的转运选项 -->
        <view class="form-group" v-if="approveForm.approveResult == '3'">
          <text class="form-label"
            >是否需要转运 <text class="required">*</text></text
          >
          <view class="radio-group">
            <view
              class="radio-item"
              :class="{ active: approveForm.isTransport == '1' }"
              @tap="approveForm.isTransport = '1'"
            >
              <text
                class="radio-icon"
                :class="{ checked: approveForm.isTransport == '1' }"
                >✓</text
              >
              <text class="radio-text">不需要</text>
            </view>
            <view
              class="radio-item"
              :class="{ active: approveForm.isTransport == '2' }"
              @tap="approveForm.isTransport = '2'"
            >
              <text
                class="radio-icon"
                :class="{ checked: approveForm.isTransport == '2' }"
                >✓</text
              >
              <text class="radio-text">需要</text>
            </view>
          </view>
        </view>
        <!-- 驳回原因 -->
        <view class="form-group" v-if="approveForm.approveResult == '4'">
          <text class="form-label"
            >驳回原因 <text class="required">*</text></text
          >
          <view class="radio-group vertical">
            <view
              v-for="reason in rejectReasons"
              :key="reason.value"
              class="radio-item"
              :class="{ active: approveForm.rejectType == reason.value }"
              @tap="approveForm.rejectType = reason.value"
            >
              <text
                class="radio-icon"
                :class="{ checked: approveForm.rejectType == reason.value }"
                >✓</text
              >
              <text class="radio-text">{{ reason.label }}</text>
            </view>
          </view>
        </view>
        <!-- 确认/驳回意见 -->
        <view class="form-group">
          <text class="form-label">
            {{ approveForm.approveResult == "4" ? "驳回意见" : "确认意见" }}
            <text class="required" v-if="approveForm.approveResult == '4'"
              >*</text
            >
          </text>
          <textarea
            v-model="approveForm.confirmResult"
            class="form-textarea"
            :placeholder="
              approveForm.approveResult == '4'
                ? '请输入详细的驳回意见'
                : '请输入确认意见'
            "
            maxlength="500"
          />
          <text class="word-count"
            >{{ approveForm.confirmResult.length }}/500</text
          >
        </view>
      </scroll-view>
      <!-- 底部按钮 -->
      <view class="panel-footer">
        <button class="panel-btn cancel" @tap="closeApprovePanel">取消</button>
        <button
          class="panel-btn submit"
          @tap="submitApprove"
          :loading="approveLoading"
        >
          确定
        </button>
      </view>
    </view>
    <!-- 加载状态 -->
    <u-loading-icon v-if="loading" :show="loading" text="加载中..." />
  </view>
@@ -408,19 +595,89 @@
<script setup>
import { ref, computed, onMounted } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { useDict } from "@/utils/dict";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { useHomeButton } from "@/stores/useHomeButton";
import attachmentUpload from "@/components/attachment";
const { showHomeButton, checkAutoLogin, goHome } = useHomeButton();
// 字典数据
const dict = ref({});
import { useUserStore } from "@/stores/user";
import { useDictMapper } from "@/utils/useDictMapper";
import dayjs from "dayjs";
// 数据
const { showHomeButton, checkAutoLogin, goHome } = useHomeButton();
// ==================== 字典映射 ====================
const requiredDictTypes = [
  "sys_user_sex",
  "sys_BloodType",
  "sys_AgeUnit",
  "sys_IDType",
  "sys_Nation",
  "sys_education",
  "sys_occupation",
  "sys_Infectious",
];
const { dictData, loading: dictLoading } = useDictMapper(requiredDictTypes);
// 通用字典查询方法
const getDictList = (dictType) => {
  return dictData.value[dictType] || [];
};
const getDictLabel = (dictType, value) => {
  if (!value) return "未填写";
  const dictList = getDictList(dictType);
  const found = dictList.find(
    (item) => item.value == value || item.value === value,
  );
  return found ? found.label : value;
};
// 传染病特殊处理(多个值用逗号分隔)
const getInfectiousText = () => {
  const infectious = caseDetail.value.infectious;
  if (!infectious) return "无";
  const values = infectious.split(",").filter((v) => v.trim());
  if (values.length === 0) return "无";
  const labels = values.map((v) => getDictLabel("sys_Infectious", v.trim()));
  const other = caseDetail.value.infectiousOther;
  return labels.join("、") + (other ? `(${other})` : "");
};
// ==================== 数据 ====================
const caseDetail = ref({});
const userStore = useUserStore();
const loading = ref(false);
const approveLoading = ref(false);
const activeTab = ref("basic");
const caseId = ref(null);
const attachments = ref([]);
const showApprovePanel = ref(false);
const canApprove = computed(() => {
  console.log(userStore.userInfo.roles);
  const roles = userStore.userInfo?.roles || [];
  return roles.some((role) => role.roleKey === "admin" || "opoadmin"|| "leader"|| "business");
});
// 驳回原因选项
const rejectReasons = [
  { label: "好转", value: "1" },
  { label: "死亡", value: "2" },
  { label: "不符合捐献标准", value: "3" },
  { label: "家属放弃捐献", value: "4" },
  { label: "其他", value: "5" },
];
// 确认表单
const approveForm = ref({
  id: null,
  approveResult: "3",
  confirmResult: "",
  rejectType: "",
  isTransport: "2",
  isDonate: "0",
});
// 选项卡
const tabs = ref([
@@ -429,223 +686,101 @@
  { id: "contact", label: "联系信息" },
  { id: "transport", label: "转运信息" },
]);
const attachments = ref([]);
// 计算属性
const hasTransport = computed(() => {
  return caseDetail.value.serviceTransport;
});
const hasTransport = computed(
  () =>
    caseDetail.value.serviceTransport &&
    caseDetail.value.serviceTransport.length > 0,
);
const canDelete = computed(() => {
  // 允许删除未审批或已驳回的案例
  return (
    caseDetail.value.reportStatus === "1" ||
    caseDetail.value.reportStatus === "2" ||
    caseDetail.value.reportStatus === "4"
  );
// ==================== 生命周期 ====================
onShow(() => {
  if (caseId.value) {
    loadCaseDetail(caseId.value);
  }
});
onLoad(async (options) => {
  checkAutoLogin(options);
  if (options.id) {
    caseId.value = options.id;
    // 获取字典数据
    dict.value = await useDict(
      "sys_user_sex",
      "sys_BloodType",
      "sys_IDType",
      "sys_AgeUnit",
    );
    // 加载案例详情
    await loadCaseDetail(options.id);
  }
});
// 加载案例详情
// ==================== 加载数据 ====================
const loadCaseDetail = async (id) => {
  loading.value = true;
  try {
    const res = await uni.$uapi.get(
      `/project/donatebaseinforeport/getInfo/${id}`,
    );
    if (res.data) {
      caseDetail.value = res.data;
      if (res.data.annexfilesList) {
        attachments.value = res.data.annexfilesList;
        attachments.value.forEach((item) => {
          item.url = item.path;
          item.name = item.fileName;
        });
        attachments.value = res.data.annexfilesList.map((item) => ({
          ...item,
          url: item.path,
          name: item.fileName,
        }));
      }
      // 如果状态是"已上报"(1),自动更新为"已阅读"(2)
      // if (caseDetail.value.reportStatus === '1') {
      //   await updateCaseStatus('2')
      // }
    } else {
      throw new Error(res.msg || "数据加载失败");
    }
  } catch (error) {
    console.error("加载案例详情失败:", error);
    uni.showToast({
      title: "数据加载失败,请重试",
      icon: "none",
    });
    uni.showToast({ title: "数据加载失败,请重试", icon: "none" });
  } finally {
    loading.value = false;
  }
};
// 预览文件 - 修改为使用完整URL
const handlePreview = (file) => {
  const fullUrl = file.url.startsWith("http")
    ? file.url
    : baseUrlHt + (file.url.startsWith("/") ? "" : "/") + file.url;
  if (file.type.includes("image")) {
    uni.previewImage({
      urls: attachments.value
        .filter((f) => f.type.includes("image"))
        .map((f) =>
          f.url.startsWith("http")
            ? f.url
            : baseUrlHt + (f.url.startsWith("/") ? "" : "/") + f.url,
        ),
      current: fullUrl,
    });
  } else if (file.type.includes("pdf")) {
    uni.downloadFile({
      url: fullUrl,
      success: (res) => {
        uni.openDocument({
          filePath: res.tempFilePath,
          fileType: "pdf",
          showMenu: true,
        });
      },
      fail: (err) => {
        console.error("打开文档失败:", err);
        uni.showToast({ title: "打开文件失败", icon: "none" });
      },
    });
  } else {
    uni.showToast({ title: "暂不支持此文件类型预览", icon: "none" });
  }
};
// 更新案例状态
const updateCaseStatus = async (newStatus) => {
  try {
    const updateData = {
      ...caseDetail.value,
      reportStatus: newStatus,
      updateTime: new Date().toISOString().replace("T", " ").substring(0, 19),
      updateBy: "移动端用户",
    };
    const res = await uni.$uapi.post(
      "/project/donatebaseinforeport/edit",
      updateData,
    );
    if (res.code === 200) {
      caseDetail.value.reportStatus = newStatus;
    }
  } catch (error) {
    console.error("更新状态失败:", error);
  }
};
// 获取状态样式
const getStatusClass = (status) => {
  const map = {
    1: "reported",
    2: "read",
    3: "agreed",
    4: "rejected",
  };
  return map[status] || "reported";
};
// 获取状态文本
const getStatusText = (status) => {
  const map = {
    1: "已上报",
    2: "已阅读",
    3: "已同意",
    4: "已驳回",
  };
  return map[status] || "已上报";
};
// 获取性别文本
const getGenderText = (gender) => {
  if (!dict.value.sys_user_sex) return gender;
  const genderItem = dict.value.sys_user_sex.find(
    (item) => item.dictValue === gender,
  );
  return genderItem ? genderItem.dictLabel : gender;
};
// 获取血型文本
const getBloodTypeText = (bloodType) => {
  if (!dict.value.sys_BloodType) return bloodType;
  const bloodTypeItem = dict.value.sys_BloodType.find(
    (item) => item.dictValue === bloodType,
  );
  return bloodTypeItem ? bloodTypeItem.dictLabel : bloodType;
};
// 获取年龄单位文本
const getAgeUnitText = (ageunit) => {
  if (!ageunit) return "";
  const unitMap = {
    year: "岁",
    month: "个月",
    day: "天",
  };
  return unitMap[ageunit] || ageunit;
};
// 获取完整户籍地址
// ==================== 地址拼接 ====================
const getFullRegisterAddress = () => {
  const {
    registerprovincename,
    registercityname,
    registertownname,
    registercommunityname,
    registeraddress,
  } = caseDetail.value;
  const parts = [
    registerprovincename,
    registercityname,
    registertownname,
    registercommunityname,
    registeraddress,
  ];
  return parts.filter((part) => part).join("");
  return parts.filter((p) => p && p.trim()).join("");
};
// 获取完整现住地址
const getFullResidenceAddress = () => {
  const {
    residenceprovincename,
    residencecountyname,
    residencecityname,
    residencetownname,
    residencecommunityname,
    residenceaddress,
  } = caseDetail.value;
  const parts = [
    residenceprovincename,
    residencecountyname,
    residencecityname,
    residencetownname,
    residencecommunityname,
    residenceaddress,
  ];
  return parts.filter((part) => part).join("");
  return parts.filter((p) => p && p.trim()).join("");
};
// 获取转运状态文本
// ==================== 状态相关 ====================
const getStatusClass = (status) => {
  const map = { 1: "reported", 2: "read", 3: "agreed", 4: "rejected" };
  return map[status] || "reported";
};
const getStatusText = (status) => {
  const map = { 1: "已上报", 2: "已阅读", 3: "已同意", 4: "已驳回" };
  return map[status] || "已上报";
};
// ==================== 转运相关 ====================
const getTransportStatusText = (status) => {
  const map = {
    1: "待转运",
@@ -657,7 +792,6 @@
  return map[status] || "未知";
};
// 获取转运状态样式
const getTransportStatusClass = (status) => {
  const map = {
    1: "pending",
@@ -669,18 +803,146 @@
  return map[status] || "pending";
};
// 格式化日期时间
// ==================== 工具方法 ====================
const formatDateTime = (dateTime) => {
  if (!dateTime) return "";
  return dateTime.replace("T", " ").substring(0, 16);
};
// 选项卡切换
// ==================== 确认功能 ====================
/** 打开确认面板 */
const openApprovePanel = () => {
  // 初始化表单
  approveForm.value = {
    id: caseDetail.value.id,
    approveResult: "3",
    confirmResult: "",
    rejectType: "",
    isTransport: caseDetail.value.isTransport || "2",
    isDonate: "0",
  };
  showApprovePanel.value = true;
};
/** 关闭确认面板 */
const closeApprovePanel = () => {
  showApprovePanel.value = false;
};
/** 确认结果变化处理 */
const handleApproveResultChange = (value) => {
  if (value == "3") {
    // 选择同意时,重置驳回原因
    approveForm.value.rejectType = "";
    approveForm.value.isDonate = "0";
  } else if (value == "4") {
    // 选择驳回时,重置转运选项
    approveForm.value.isTransport = "1";
  }
};
/** 表单校验 */
const validateApproveForm = () => {
  if (!approveForm.value.approveResult) {
    uni.showToast({ title: "请选择确认结果", icon: "none" });
    return false;
  }
  if (
    approveForm.value.approveResult == "3" &&
    !approveForm.value.isTransport
  ) {
    uni.showToast({ title: "请选择是否需要转运", icon: "none" });
    return false;
  }
  if (approveForm.value.approveResult == "4") {
    if (!approveForm.value.rejectType) {
      uni.showToast({ title: "请选择驳回原因", icon: "none" });
      return false;
    }
    if (!approveForm.value.confirmResult) {
      uni.showToast({ title: "驳回时必须填写驳回意见", icon: "none" });
      return false;
    }
  }
  return true;
};
/** 提交确认 */
const submitApprove = async () => {
  if (!validateApproveForm()) return;
  approveLoading.value = true;
  try {
    // 准备确认数据
    const approveData = {
      ...caseDetail.value,
      reportStatus: approveForm.value.approveResult,
      confirmResult: approveForm.value.confirmResult,
      approvername: userStore.user?.name || "当前用户",
      approvetime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
      updateTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
      updateBy: userStore.user?.name || "当前用户",
    };
    // 处理确认结果
    if (approveForm.value.approveResult == "3") {
      // 同意
      approveData.isTransport = approveForm.value.isTransport;
      // 如果不需要转运,设置isDonate为1
      if (approveForm.value.isTransport == "1") {
        approveData.isDonate = "1";
      } else {
        approveData.isDonate = "0";
      }
    } else if (approveForm.value.approveResult == "4") {
      // 驳回
      approveData.rejectType = approveForm.value.rejectType;
      approveData.isTransport = "1"; // 驳回的案例不需要转运
      approveData.isDonate = "0"; // 驳回的案例标记为已捐献
    }
    // 调用编辑接口更新状态
    const res = await uni.$uapi.post(
      "/project/donatebaseinforeport/edit",
      approveData,
    );
    if (res.code == 200) {
      uni.showToast({ title: "确认成功", icon: "success" });
      showApprovePanel.value = false;
      // 重新加载详情
      await loadCaseDetail(caseId.value);
      // 如果同意且需要转运,跳转到转运单创建页面
      if (
        approveForm.value.approveResult == "3" &&
        approveForm.value.isTransport == "2"
      ) {
        console.log(caseId.value);
        setTimeout(() => {
          uni.navigateTo({
            url: `/pagesSub/case/transferinfo?caseId=${caseId.value}&caseNo=${caseDetail.value.caseNo}&autoCreate=true`,
          });
        }, 800);
      }
    } else {
      uni.showToast({ title: res.msg || "确认失败", icon: "none" });
    }
  } catch (error) {
    console.error("确认失败:", error);
    uni.showToast({ title: "确认失败,请重试", icon: "none" });
  } finally {
    approveLoading.value = false;
  }
};
// ==================== 事件处理 ====================
const switchTab = (tabId) => {
  activeTab.value = tabId;
};
// 返回
const goBack = () => {
  if (showHomeButton) {
    uni.switchTab({ url: "/pages/index/index" });
@@ -689,60 +951,59 @@
  }
};
// 编辑案例
const handleEdit = () => {
  uni.navigateTo({
    url: `/pagesSub/case/CaseDetails?id=${caseId.value}&edit=true`,
  });
};
// 删除案例
const handleDelete = () => {
  uni.showModal({
    title: "确认删除",
    content: `确定要删除案例 ${caseDetail.value.caseNo} 吗?`,
    success: async (res) => {
      if (res.confirm) {
        try {
          const result = await uni.$uapi.delete(
            `/project/donatebaseinforeport/${caseId.value}`,
          );
          if (result.code === 200) {
            uni.showToast({ title: "删除成功", icon: "success" });
            setTimeout(() => {
              uni.navigateBack();
            }, 1500);
          } else {
            uni.showToast({ title: result.msg || "删除失败", icon: "none" });
          }
        } catch (error) {
          console.error("删除失败:", error);
          uni.showToast({ title: "删除失败", icon: "none" });
        }
      }
    },
  });
};
// 创建转运单
const createTransport = () => {
  if (caseDetail.value.reportStatus !== "3") {
    uni.showToast({ title: "案例需先审批同意", icon: "none" });
    return;
  }
  uni.navigateTo({
    url: `/pages/transport/create?caseId=${caseId.value}&caseNo=${caseDetail.value.caseNo}`,
  });
};
// 查看转运单详情
const viewTransportDetail = () => {
  if (hasTransport.value && caseDetail.value.serviceTransport[0]) {
  if (hasTransport.value) {
    const transport = caseDetail.value.serviceTransport[0];
    uni.navigateTo({
      url: `/pages/transport/detail?id=${transport.id}`,
    uni.navigateTo({ url: `/pages/transport/detail?id=${transport.id}` });
  }
};
// 附件预览
const handlePreview = (file) => {
  const fullUrl = file.url.startsWith("http")
    ? file.url
    : (file.url.startsWith("/") ? "" : "/") + file.url;
  if (file.type && file.type.includes("image")) {
    uni.previewImage({
      urls: attachments.value
        .filter((f) => f.type && f.type.includes("image"))
        .map((f) =>
          f.url.startsWith("http")
            ? f.url
            : (f.url.startsWith("/") ? "" : "/") + f.url,
        ),
      current: fullUrl,
    });
  } else if (file.type && file.type.includes("pdf")) {
    uni.downloadFile({
      url: fullUrl,
      success: (res) => {
        uni.openDocument({
          filePath: res.tempFilePath,
          fileType: "pdf",
          showMenu: true,
        });
      },
      fail: () => uni.showToast({ title: "打开文件失败", icon: "none" }),
    });
  } else {
    uni.showToast({ title: "暂不支持此文件类型预览", icon: "none" });
  }
};
</script>
@@ -750,84 +1011,84 @@
<style lang="scss" scoped>
.case-detail {
  min-height: 100vh;
  background: linear-gradient(135deg, #fafdff 0%, #e3f0ff 100%);
  background: #f5f7fa;
  padding: 20rpx;
  padding-bottom: 120rpx; /* 为操作栏留出空间 */
  padding-bottom: 180rpx; /* 增加底部间距给操作栏 */
}
/* ========== 头部卡片 ========== */
.header-card {
  background: linear-gradient(135deg, #fff 60%, #f5f6fa 100%);
  border-radius: 32rpx;
  padding: 40rpx 30rpx;
  margin-bottom: 30rpx;
  box-shadow: 0 12px 48px 0 rgba(0, 113, 227, 0.1);
  border: 1.5px solid #e5eaf0;
  background: #fff;
  border-radius: 24rpx;
  padding: 30rpx 28rpx;
  margin-bottom: 24rpx;
  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
}
.case-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 30rpx;
  margin-bottom: 24rpx;
}
.hospital-info {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
}
.hospital-logo {
  width: 80rpx;
  height: 80rpx;
  width: 72rpx;
  height: 72rpx;
  border-radius: 16rpx;
  margin-right: 20rpx;
  background: #f5f5f7;
  margin-right: 16rpx;
  background: #f0f2f5;
  flex-shrink: 0;
}
.hospital-details {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.hospital-name {
  font-size: 32rpx;
  font-size: 30rpx;
  font-weight: 600;
  color: #1d1d1f;
  margin-bottom: 8rpx;
  max-width: 400rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.case-type {
  font-size: 24rpx;
  color: #86868b;
  font-size: 22rpx;
  color: #8e8e93;
  margin-top: 4rpx;
}
.case-status {
  padding: 8rpx 20rpx;
  padding: 6rpx 18rpx;
  border-radius: 20rpx;
  font-size: 24rpx;
  font-size: 22rpx;
  font-weight: 500;
  flex-shrink: 0;
  &.reported {
    background: rgba(255, 149, 0, 0.1);
    background: rgba(255, 149, 0, 0.12);
    color: #ff9500;
  }
  &.read {
    background: rgba(0, 122, 255, 0.1);
    background: rgba(0, 122, 255, 0.12);
    color: #007aff;
  }
  &.agreed {
    background: rgba(52, 199, 89, 0.1);
    background: rgba(52, 199, 89, 0.12);
    color: #34c759;
  }
  &.rejected {
    background: rgba(255, 59, 48, 0.1);
    background: rgba(255, 59, 48, 0.12);
    color: #ff3b30;
  }
}
@@ -836,36 +1097,34 @@
  .info-row {
    display: flex;
    justify-content: space-between;
    gap: 20rpx;
  }
  .info-item {
    display: flex;
    flex-direction: column;
    flex: 1;
  }
  .label {
    font-size: 24rpx;
    color: #86868b;
    margin-bottom: 8rpx;
    font-size: 22rpx;
    color: #8e8e93;
    margin-bottom: 4rpx;
  }
  .value {
    font-size: 28rpx;
    font-size: 26rpx;
    color: #1d1d1f;
    font-weight: 500;
  }
}
/* ========== 选项卡 ========== */
.tab-navigation {
  display: flex;
  background: #fff;
  border-radius: 16rpx;
  padding: 8rpx;
  margin-bottom: 30rpx;
  box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.05);
  padding: 6rpx;
  margin-bottom: 24rpx;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
  overflow-x: auto;
  white-space: nowrap;
  &::-webkit-scrollbar {
    display: none;
  }
@@ -873,264 +1132,507 @@
.tab-item {
  flex: 1;
  min-width: 140rpx;
  min-width: 120rpx;
  text-align: center;
  padding: 20rpx;
  font-size: 28rpx;
  color: #86868b;
  padding: 16rpx 10rpx;
  font-size: 26rpx;
  color: #8e8e93;
  border-radius: 12rpx;
  transition: all 0.3s ease;
  &.active {
    color: #007aff;
    background: #f0f7ff;
    border-radius: 12rpx;
    font-weight: 600;
  }
}
/* ========== 内容卡片 ========== */
.section-card {
  background: linear-gradient(135deg, #fff 60%, #f5f6fa 100%);
  border-radius: 32rpx;
  padding: 40rpx 30rpx;
  margin-bottom: 30rpx;
  box-shadow: 0 12px 48px 0 rgba(0, 113, 227, 0.1);
  border: 1.5px solid #e5eaf0;
  background: #fff;
  border-radius: 24rpx;
  padding: 28rpx 24rpx;
  margin-bottom: 24rpx;
  box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
}
.section-header {
  margin-bottom: 30rpx;
  margin-bottom: 24rpx;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.section-title {
  font-size: 32rpx;
  font-size: 30rpx;
  font-weight: 600;
  background: linear-gradient(90deg, #0071e3 0%, #2997ff 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #0071e3;
  color: #1d1d1f;
}
/* ========== 信息网格 ========== */
.info-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24rpx;
  gap: 16rpx 24rpx;
}
.info-item {
  display: flex;
  flex-direction: column;
  padding: 12rpx 0;
  border-bottom: 1rpx solid #f5f5f7;
  &.full-width {
    grid-column: 1 / -1;
  }
  &.address-item {
    padding-bottom: 8rpx;
  }
  .label {
    font-size: 24rpx;
    color: #86868b;
    margin-bottom: 8rpx;
    font-size: 22rpx;
    color: #8e8e93;
    margin-bottom: 4rpx;
    font-weight: 400;
  }
  .value {
    font-size: 28rpx;
    font-size: 27rpx;
    color: #1d1d1f;
    font-weight: 500;
    word-break: break-all;
    line-height: 1.5;
  }
  .diagnosis-text {
    color: #007aff;
    font-weight: 600;
  }
}
/* 地址项特殊处理:去掉边框更清爽 */
.address-item {
  border-bottom: none !important;
  padding-bottom: 4rpx !important;
}
/* ========== 转运信息 ========== */
.transport-list {
  margin-top: 16rpx;
}
.transport-item {
  background: #f8f9fc;
  border-radius: 16rpx;
  padding: 20rpx;
  margin-bottom: 16rpx;
  border: 1rpx solid #e8ecf0;
  &:last-child {
    margin-bottom: 0;
  }
}
.transport-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 14rpx;
  border-bottom: 1rpx solid #e8ecf0;
  margin-bottom: 14rpx;
}
.transport-no {
  font-size: 26rpx;
  font-weight: 600;
  color: #1d1d1f;
}
.transport-status {
  font-size: 22rpx;
  padding: 4rpx 16rpx;
  border-radius: 12rpx;
  font-weight: 500;
  &.pending {
    background: rgba(240, 173, 78, 0.15);
    color: #f0ad4e;
  }
  &.transporting {
    background: rgba(0, 122, 255, 0.12);
    color: #007aff;
  }
  &.completed {
    background: rgba(52, 199, 89, 0.12);
    color: #34c759;
  }
  &.cancelled {
    background: rgba(142, 142, 147, 0.12);
    color: #8e8e93;
  }
  &.draft {
    background: rgba(142, 142, 147, 0.08);
    color: #8e8e93;
  }
}
.transport-body {
  display: flex;
  flex-direction: column;
  gap: 8rpx;
}
.transport-row {
  display: flex;
  align-items: baseline;
  .t-label {
    font-size: 24rpx;
    color: #8e8e93;
    width: 140rpx;
    flex-shrink: 0;
  }
  .t-value {
    font-size: 26rpx;
    color: #1d1d1f;
    word-break: break-all;
  }
}
.empty-transport {
  text-align: center;
  padding: 40rpx 0;
  color: #8e8e93;
  font-size: 26rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* ========== 小型按钮 ========== */
.small-btn {
  padding: 8rpx 20rpx;
  font-size: 22rpx;
  border-radius: 8rpx;
  border: none;
  &.primary {
    background: #007aff;
    color: #fff;
  }
  &.secondary {
    background: #ecf5ff;
    color: #007aff;
    border: 1rpx solid #007aff;
  }
  &:active {
    opacity: 0.8;
  }
}
/* ========== 底部操作栏 ========== */
.action-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: #fff;
  padding: 18rpx 30rpx calc(18rpx + env(safe-area-inset-bottom));
  box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
  z-index: 2;
  display: flex;
  gap: 16rpx;
}
.action-btn {
  flex: 1;
  height: 88rpx;
  border-radius: 44rpx;
  font-size: 32rpx;
  font-weight: 700;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  letter-spacing: 1rpx;
  &.secondary {
    background: #f5f5f7;
    color: #1d1d1f;
    &:active {
      background: #e5e5e7;
    }
  }
  &.primary {
    background: linear-gradient(90deg, #0071e3, #2997ff);
    color: #fff;
    &:active {
      transform: scale(0.96);
    }
  }
  &.approve {
    background: linear-gradient(135deg, #34c759, #30b350);
    color: #fff;
    box-shadow: 0 4rpx 16rpx rgba(52, 199, 89, 0.35);
    &:active {
      transform: scale(0.96);
      box-shadow: 0 2rpx 8rpx rgba(52, 199, 89, 0.25);
    }
  }
}
/* ========== 确认底部弹出面板 ========== */
.approve-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 999;
  animation: fadeIn 0.25s ease;
}
.approve-panel {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  background: #ffffff;
  border-radius: 32rpx 32rpx 0 0;
  z-index: 1000;
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  transform: translateY(100%);
  transition: transform 0.35s cubic-bezier(0.32, 0.94, 0.6, 1);
  box-shadow: 0 -8rpx 40rpx rgba(0, 0, 0, 0.12);
  &.panel-show {
    transform: translateY(0);
  }
}
.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 36rpx 32rpx 20rpx;
  border-bottom: 1rpx solid #f0f0f0;
  flex-shrink: 0;
}
.panel-title {
  font-size: 34rpx;
  font-weight: 700;
  color: #1d1d1f;
}
.panel-close {
  width: 50rpx;
  height: 50rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32rpx;
  color: #8e8e93;
  background: #f5f5f7;
  border-radius: 50%;
  &:active {
    background: #e5e5e7;
  }
}
.panel-body {
  flex: 1;
  padding: 24rpx 32rpx 20rpx;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
/* 案例摘要卡片 */
.summary-card {
  background: #f8f9fc;
  border-radius: 16rpx;
  padding: 20rpx 24rpx;
  margin-bottom: 28rpx;
  border: 1rpx solid #e8ecf0;
}
.summary-row {
  display: flex;
  align-items: baseline;
  padding: 8rpx 0;
  .s-label {
    font-size: 24rpx;
    color: #8e8e93;
    width: 130rpx;
    flex-shrink: 0;
  }
  .s-value {
    font-size: 26rpx;
    color: #1d1d1f;
    font-weight: 500;
    word-break: break-all;
  }
}
.info-content {
  .info-group {
    margin-bottom: 32rpx;
    padding-bottom: 20rpx;
    border-bottom: 1rpx solid #f0f0f0;
/* 表单组 */
.form-group {
  margin-bottom: 28rpx;
}
    &:last-child {
      margin-bottom: 0;
      padding-bottom: 0;
      border-bottom: none;
    }
  }
.form-label {
  display: block;
  font-size: 28rpx;
  font-weight: 600;
  color: #1d1d1f;
  margin-bottom: 16rpx;
  .group-title {
    font-size: 26rpx;
    color: #86868b;
    margin-bottom: 12rpx;
    display: block;
    font-weight: 500;
  }
  .group-content {
    font-size: 28rpx;
    color: #1d1d1f;
    line-height: 1.6;
  .required {
    color: #ff3b30;
    margin-left: 4rpx;
  }
}
/* 转运信息样式 */
.transport-info {
  .info-group {
    border: 2rpx solid #f0f0f0;
    border-radius: 12rpx;
    padding: 20rpx;
    margin-bottom: 20rpx;
    background: #fafafa;
/* 单选组 - 水平排列 */
.radio-group {
  display: flex;
  gap: 16rpx;
  flex-wrap: wrap;
    &:last-child {
      margin-bottom: 0;
    }
  }
  .sub-group {
    display: flex;
    margin-bottom: 12rpx;
    align-items: center;
    &:last-child {
      margin-bottom: 0;
    }
  }
  .sub-label {
    font-size: 26rpx;
    color: #606266;
    min-width: 160rpx;
    margin-right: 10rpx;
  }
  .sub-value {
    font-size: 26rpx;
    color: #303133;
    flex: 1;
    &.status {
      padding: 4rpx 12rpx;
      border-radius: 6rpx;
      font-size: 24rpx;
      &.pending {
        background: rgba(240, 173, 78, 0.1);
        color: #f0ad4e;
      }
      &.transporting {
        background: rgba(0, 122, 255, 0.1);
        color: #007aff;
      }
      &.completed {
        background: rgba(76, 217, 100, 0.1);
        color: #4cd964;
      }
      &.cancelled {
        background: rgba(220, 223, 230, 0.1);
        color: #dcdfe6;
      }
      &.draft {
        background: rgba(144, 147, 153, 0.1);
        color: #909399;
      }
    }
  }
  .empty-transport {
    text-align: center;
    padding: 40rpx 0;
    color: #909399;
    font-size: 28rpx;
  &.vertical {
    flex-direction: column;
    gap: 12rpx;
  }
}
/* 小型按钮 */
.small-btn {
  padding: 8rpx 20rpx;
  font-size: 24rpx;
  border-radius: 6rpx;
  border: none;
.radio-item {
  display: flex;
  align-items: center;
  padding: 16rpx 24rpx;
  border-radius: 12rpx;
  border: 2rpx solid #e0e0e0;
  background: #fafafa;
  transition: all 0.2s ease;
  &.primary {
    background: #007aff;
    color: #fff;
  }
  &.active {
    border-color: #007aff;
    background: #f0f7ff;
  &.secondary {
    background: #ecf5ff;
    color: #007aff;
    border: 1rpx solid #007aff;
    .radio-text {
      color: #007aff;
      font-weight: 600;
    }
  }
  &:active {
    opacity: 0.8;
    transform: scale(0.97);
  }
}
.action-bar {
.radio-icon {
  width: 38rpx;
  height: 38rpx;
  border-radius: 19rpx;
  border: 2rpx solid #c8c8cc;
  display: flex;
  gap: 20rpx;
  padding: 20rpx 0;
  margin-top: 40rpx;
  background: #fff;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 20rpx 30rpx;
  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
  box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.08);
  z-index: 9;
  align-items: center;
  justify-content: center;
  font-size: 22rpx;
  color: transparent;
  margin-right: 12rpx;
  flex-shrink: 0;
  transition: all 0.2s ease;
  &.checked {
    background: #007aff;
    border-color: #007aff;
    color: #ffffff;
  }
}
.action-btn {
  flex: 1;
  height: 80rpx;
  border-radius: 20rpx;
  font-size: 28rpx;
  font-weight: 600;
  border: none;
  transition: all 0.3s ease;
.radio-text {
  font-size: 26rpx;
  color: #3a3a3c;
  white-space: nowrap;
}
  &.secondary {
/* 文本域 */
.form-textarea {
  width: 92%;
  min-height: 170rpx;
  padding: 20rpx 24rpx;
  border-radius: 14rpx;
  border: 2rpx solid #e0e0e0;
  background: #fafafa;
  font-size: 26rpx;
  color: #1d1d1f;
  line-height: 1.6;
  resize: none;
  &:focus {
    border-color: #007aff;
    background: #ffffff;
    outline: none;
  }
  &::placeholder {
    color: #aeaeb2;
  }
}
.word-count {
  display: block;
  text-align: right;
  font-size: 22rpx;
  color: #aeaeb2;
  margin-top: 8rpx;
  padding-right: 8rpx;
}
/* 底部按钮 */
.panel-footer {
  display: flex;
  gap: 20rpx;
  padding: 20rpx 32rpx calc(20rpx + env(safe-area-inset-bottom));
  border-top: 1rpx solid #f0f0f0;
  flex-shrink: 0;
}
.panel-btn {
  flex: 1;
  height: 84rpx;
  border-radius: 42rpx;
  font-size: 30rpx;
  font-weight: 700;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  letter-spacing: 1rpx;
  &.cancel {
    background: #f5f5f7;
    color: #1d1d1f;
    &:active {
      background: #e5e5e7;
    }
  }
  &.primary {
    background: linear-gradient(90deg, #0071e3 0%, #2997ff 100%);
  &.submit {
    background: linear-gradient(90deg, #0071e3, #2997ff);
    color: #fff;
    box-shadow: 0 4rpx 16rpx rgba(0, 113, 227, 0.3);
    &:active {
      transform: scale(0.98);
    }
  }
  &.success {
    background: linear-gradient(90deg, #34c759 0%, #4cd964 100%);
    color: #fff;
    &:active {
      transform: scale(0.98);
    }
  }
  &.error {
    background: linear-gradient(90deg, #ff3b30 0%, #ff5a5a 100%);
    color: #fff;
    &:active {
      transform: scale(0.98);
      transform: scale(0.97);
      box-shadow: 0 2rpx 8rpx rgba(0, 113, 227, 0.2);
    }
  }
}
/* ========== 动画 ========== */
.fade-in-up {
  opacity: 0;
  transform: translateY(40rpx);
  animation: fadeInUp 0.6s ease forwards;
  transform: translateY(30rpx);
  animation: fadeInUp 0.4s ease forwards;
}
@keyframes fadeInUp {
@@ -1139,4 +1641,13 @@
    transform: translateY(0);
  }
}
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
</style>