WXL
2 天以前 9bce51f651aad297ef9eb6df832bfdaf1de05d84
pages/case/CaseInfo.vue
@@ -6,24 +6,24 @@
        <view class="hospital-info">
          <image :src="caseDetail.hospitalLogo" mode="aspectFit" class="hospital-logo" />
          <view class="hospital-details">
            <text class="hospital-name">{{ caseDetail.hospitalName }}</text>
            <text class="case-type">{{ caseDetail.caseType }}</text>
            <text class="hospital-name">{{ caseDetail.treatmenthospitalname || '未填写治疗医院' }}</text>
            <text class="case-type">{{ caseDetail.donorNo ? '器官捐献案例' : '案例详情' }}</text>
          </view>
        </view>
        <view class="case-status" :class="caseDetail.status">
          {{ caseDetail.statusText }}
        <view class="case-status" :class="getStatusClass(caseDetail.reportStatus)">
          {{ getStatusText(caseDetail.reportStatus) }}
        </view>
      </view>
      
      <view class="case-basic-info">
        <view class="info-row">
          <view class="info-item">
            <text class="label">捐献编号</text>
            <text class="value">{{ caseDetail.donorNo }}</text>
            <text class="label">案例编号</text>
            <text class="value">{{ caseDetail.caseNo || '未生成' }}</text>
          </view>
          <view class="info-item">
            <text class="label">上报时间</text>
            <text class="value">{{ caseDetail.reportTime }}</text>
            <text class="value">{{ formatDateTime(caseDetail.reporttime) }}</text>
          </view>
        </view>
      </view>
@@ -51,31 +51,51 @@
        <view class="info-grid">
          <view class="info-item">
            <text class="label">姓名</text>
            <text class="value">{{ caseDetail.donorName }}</text>
            <text class="value">{{ caseDetail.name || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">性别</text>
            <text class="value">{{ caseDetail.gender }}</text>
            <text class="value">{{ getGenderText(caseDetail.sex) }}</text>
          </view>
          <view class="info-item">
            <text class="label">年龄</text>
            <text class="value">{{ caseDetail.age }}岁</text>
            <text class="value">{{ caseDetail.age || '0' }} {{ getAgeUnitText(caseDetail.ageunit) }}</text>
          </view>
          <view class="info-item">
            <text class="label">证件号码</text>
            <text class="value">{{ caseDetail.idCardNo }}</text>
            <text class="value">{{ caseDetail.idcardno || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">血型</text>
            <text class="value">{{ caseDetail.bloodType }}</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">{{ caseDetail.nation || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">国籍</text>
            <text class="value">{{ caseDetail.nationality || '未填写' }}</text>
          </view>
          <view class="info-item">
            <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>
          </view>
          <view class="info-item">
            <text class="label">职业</text>
            <text class="value">{{ caseDetail.occupation || '未填写' }}</text>
          </view>
          <view class="info-item full-width">
            <text class="label">住址</text>
            <text class="value">{{ caseDetail.address }}</text>
            <text class="label">户籍地址</text>
            <text class="value">{{ getFullRegisterAddress() || '未填写' }}</text>
          </view>
          <view class="info-item full-width">
            <text class="label">现住地址</text>
            <text class="value">{{ getFullResidenceAddress() || '未填写' }}</text>
          </view>
        </view>
      </view>
@@ -90,135 +110,484 @@
        <view class="info-content">
          <view class="info-group">
            <text class="group-title">疾病诊断</text>
            <text class="group-content">{{ caseDetail.diagnosis }}</text>
            <text class="group-content">{{ caseDetail.diagnosisname || '未填写' }}</text>
          </view>
          <view class="info-group">
            <text class="group-title">病情概况</text>
            <text class="group-content">{{ 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>
            <text class="group-content">{{ caseDetail.inpatientno || '未填写' }}</text>
          </view>
          <view class="info-group">
            <text class="group-title">所在科室</text>
            <text class="group-content">{{ caseDetail.departmentName }}</text>
            <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.infectiousDisease || '无' }}</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>
        </view>
      </view>
    </view>
    <!-- 捐献流程信息 -->
    <view v-if="activeTab === 'process'" 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="process-timeline">
          <view class="timeline-item" v-for="step in processSteps" :key="step.id">
            <view class="timeline-marker" :class="{ active: step.completed }"></view>
            <view class="timeline-content">
              <text class="step-title">{{ step.title }}</text>
              <text class="step-time" v-if="step.time">{{ step.time }}</text>
              <text class="step-person" v-if="step.person">经办人:{{ step.person }}</text>
        <view class="info-grid">
          <view class="info-item full-width">
            <text class="label">治疗医院</text>
            <text class="value">{{ caseDetail.treatmenthospitalname || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">治疗科室</text>
            <text class="value">{{ caseDetail.treatmentdeptname || '未填写' }}</text>
          </view>
          <view class="info-item">
            <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="value">{{ caseDetail.infoName || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">信息员编号</text>
            <text class="value">{{ caseDetail.infoNo || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">报告者</text>
            <text class="value">{{ caseDetail.reportername || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">报告者电话</text>
            <text class="value">{{ caseDetail.reporterphone || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">协调员</text>
            <text class="value">{{ caseDetail.coordinatorName || '未填写' }}</text>
          </view>
          <view class="info-item">
            <text class="label">协调员编号</text>
            <text class="value">{{ caseDetail.coordinatorNo || '未填写' }}</text>
          </view>
        </view>
      </view>
    </view>
    <!-- 转运信息 -->
    <view v-if="activeTab === 'transport'" class="info-section fade-in-up">
      <view class="section-card">
        <view class="section-header">
          <view style="display: flex; justify-content: space-between; align-items: center;">
            <text class="section-title">转运信息</text>
            <!-- 转运操作按钮 -->
            <view v-if="caseDetail.reportStatus === '3'" style="display: flex; gap: 20rpx;">
              <button
                v-if="caseDetail.isTransport === '2' && !hasTransport"
                class="small-btn primary"
                @tap.stop="createTransport"
              >
                创建转运单
              </button>
              <button
                v-if="hasTransport"
                class="small-btn secondary"
                @tap.stop="viewTransportDetail"
              >
                查看转运单
              </button>
            </view>
          </view>
        </view>  +
        </view>
        <view class="info-content">
          <view class="info-group">
            <text class="group-title">是否需要转运</text>
            <text class="group-content">{{ caseDetail.isTransport === '2' ? '需要' : '不需要' }}</text>
          </view>
          <!-- 有转运信息时显示 -->
          <view v-if="hasTransport && caseDetail.serviceTransport" class="transport-info">
            <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 }}</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">{{ formatDateTime(transport.transportStartTime) }}</text>
              </view>
              <view class="sub-group">
                <text class="sub-label">负责协调员:</text>
                <text class="sub-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>
              <view class="sub-group" v-if="transport.doctor">
                <text class="sub-label">急诊科医生:</text>
                <text class="sub-value">{{ transport.doctor }}</text>
              </view>
              <view class="sub-group" v-if="transport.nurse">
                <text class="sub-label">护士:</text>
                <text class="sub-value">{{ transport.nurse }}</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>
            </view>
          </view>
          <!-- 无转运信息但需要转运 -->
          <view v-else-if="caseDetail.isTransport === '2'" class="transport-info">
            <view class="empty-transport">
              <text>该案例需要转运,但尚未创建转运单</text>
              <text v-if="caseDetail.reportStatus !== '3'" style="color: #f0ad4e; font-size: 24rpx; margin-top: 10rpx;">
                需先审批同意才能创建转运单
              </text>
            </view>
          </view>
          <!-- 无需转运 -->
          <view v-else class="transport-info">
            <view class="empty-transport">
              <text>该案例无需转运</text>
            </view>
          </view>
        </view>
      </view>
    </view>
    <!-- 操作按钮 -->
    <view class="action-bar">
      <button class="action-btn secondary" @tap="goBack">返回</button>
      <button class="action-btn primary" v-if="caseDetail.status === 'reported'"
              @tap="withdrawCase">撤回案例</button>
      <button class="action-btn primary" v-else @tap="contactCoordinator">联系协调员</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>
    <!-- 加载状态 -->
    <u-loading-icon v-if="loading" :show="loading" text="加载中..." />
  </view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useDict } from "@/utils/dict"
import {  onLoad } from '@dcloudio/uni-app'
// 字典数据
const dict = ref({})
const caseDetail = ref({
  id: 1,
  donorNo: 'DON20240325001',
  hospitalName: '青岛镜湖医院',
  hospitalLogo: '/static/hospital/kiang-wu.jpg',
  caseType: '器官捐献案例',
  donorName: '张三',
  idCardNo: '370203198510123456',
  gender: '男',
  age: 38,
  bloodType: 'A型',
  nation: '汉族',
  address: '山东省青岛市市南区香港中路100号',
  diagnosis: '脑外伤导致脑死亡',
  inpatientNo: 'ZY20240325001',
  departmentName: '神经外科',
  infectiousDisease: '无',
  reportTime: '2024-03-25 09:30:00',
  status: 'reported',
  statusText: '已上报'
})
// 数据
const caseDetail = ref({})
const loading = ref(false)
const activeTab = ref('basic')
const caseId = ref(null)
// 选项卡
const tabs = ref([
  { id: 'basic', label: '基本信息' },
  { id: 'medical', label: '医疗信息' },
  { id: 'process', label: '流程信息' }
  { id: 'contact', label: '联系信息' },
  { id: 'transport', label: '转运信息' }
])
const activeTab = ref('basic')
// 计算属性
const hasTransport = computed(() => {
  return caseDetail.value.serviceTransport
})
const processSteps = ref([
  { id: 1, title: '案例上报', completed: true, time: '2024-03-25 09:30', person: '李医生' },
  { id: 2, title: '信息审核', completed: false, time: null, person: null },
  { id: 3, title: '家属沟通', completed: false, time: null, person: null },
  { id: 4, title: '捐献确认', completed: false, time: null, person: null },
  { id: 5, title: '器官获取', completed: false, time: null, person: null }
])
const canDelete = computed(() => {
  // 允许删除未审批或已驳回的案例
  return caseDetail.value.reportStatus === '1' ||
         caseDetail.value.reportStatus === '2' ||
         caseDetail.value.reportStatus === '4'
})
onLoad((options) => {
onLoad(async (options) => {
  if (options.id) {
    // 根据ID加载案例详情
    loadCaseDetail(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) {
      caseDetail.value = res
      // 如果状态是"已上报"(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'
    })
  } finally {
    loading.value = false
  }
}
// 更新案例状态
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('')
}
// 获取完整现住地址
const getFullResidenceAddress = () => {
  const {
    residenceprovincename,
    residencecountyname,
    residencetownname,
    residencecommunityname,
    residenceaddress
  } = caseDetail.value
  const parts = [
    residenceprovincename,
    residencecountyname,
    residencetownname,
    residencecommunityname,
    residenceaddress
  ]
  return parts.filter(part => part).join('')
}
// 获取转运状态文本
const getTransportStatusText = (status) => {
  const map = {
    1: '待转运',
    2: '转运中',
    3: '转运完成',
    4: '转运取消',
    5: '暂存'
  }
  return map[status] || '未知'
}
// 获取转运状态样式
const getTransportStatusClass = (status) => {
  const map = {
    1: 'pending',
    2: 'transporting',
    3: 'completed',
    4: 'cancelled',
    5: 'draft'
  }
  return map[status] || 'pending'
}
// 格式化日期时间
const formatDateTime = (dateTime) => {
  if (!dateTime) return ''
  return dateTime.replace('T', ' ').substring(0, 16)
}
// 选项卡切换
const switchTab = (tabId) => {
  activeTab.value = tabId
}
// 返回
const goBack = () => {
  uni.navigateBack()
}
const withdrawCase = () => {
// 编辑案例
const handleEdit = () => {
  uni.navigateTo({
    url: `/pages/case/CaseDetails?id=${caseId.value}&edit=true`
  })
}
// 删除案例
const handleDelete = () => {
  uni.showModal({
    title: '确认撤回',
    content: '确定要撤回这个捐献案例吗?',
    success: (res) => {
    title: '确认删除',
    content: `确定要删除案例 ${caseDetail.value.caseNo} 吗?`,
    success: async (res) => {
      if (res.confirm) {
        uni.showToast({ title: '撤回成功', icon: 'success' })
        setTimeout(() => {
          uni.navigateBack()
        }, 1500)
        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 contactCoordinator = () => {
  uni.makePhoneCall({
    phoneNumber: '13800138000'
// 创建转运单
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 loadCaseDetail = (id) => {
  // 模拟API调用
  console.log('加载案例详情:', id)
// 查看转运单详情
const viewTransportDetail = () => {
  if (hasTransport.value && caseDetail.value.serviceTransport[0]) {
    const transport = caseDetail.value.serviceTransport[0]
    uni.navigateTo({
      url: `/pages/transport/detail?id=${transport.id}`
    })
  }
}
</script>
@@ -227,6 +596,7 @@
  min-height: 100vh;
  background: linear-gradient(135deg, #fafdff 0%, #e3f0ff 100%);
  padding: 20rpx;
  padding-bottom: 120rpx; /* 为操作栏留出空间 */
}
.header-card {
@@ -255,6 +625,7 @@
  height: 80rpx;
  border-radius: 16rpx;
  margin-right: 20rpx;
  background: #f5f5f7;
}
.hospital-details {
@@ -267,6 +638,10 @@
  font-weight: 600;
  color: #1d1d1f;
  margin-bottom: 8rpx;
  max-width: 400rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.case-type {
@@ -293,6 +668,11 @@
  &.agreed {
    background: rgba(52, 199, 89, 0.1);
    color: #34c759;
  }
  &.rejected {
    background: rgba(255, 59, 48, 0.1);
    color: #ff3b30;
  }
}
@@ -327,10 +707,17 @@
  padding: 8rpx;
  margin-bottom: 30rpx;
  box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.05);
  overflow-x: auto;
  white-space: nowrap;
  &::-webkit-scrollbar {
    display: none;
  }
}
.tab-item {
  flex: 1;
  min-width: 140rpx;
  text-align: center;
  padding: 20rpx;
  font-size: 28rpx;
@@ -391,15 +778,20 @@
    font-size: 28rpx;
    color: #1d1d1f;
    font-weight: 500;
    word-break: break-all;
  }
}
.info-content {
  .info-group {
    margin-bottom: 32rpx;
    padding-bottom: 20rpx;
    border-bottom: 1rpx solid #f0f0f0;
    
    &:last-child {
      margin-bottom: 0;
      padding-bottom: 0;
      border-bottom: none;
    }
  }
  
@@ -408,6 +800,7 @@
    color: #86868b;
    margin-bottom: 12rpx;
    display: block;
    font-weight: 500;
  }
  
  .group-content {
@@ -417,70 +810,118 @@
  }
}
.process-timeline {
  position: relative;
  padding-left: 40rpx;
/* 转运信息样式 */
.transport-info {
  .info-group {
    border: 2rpx solid #f0f0f0;
    border-radius: 12rpx;
    padding: 20rpx;
    margin-bottom: 20rpx;
    background: #fafafa;
    &:last-child {
      margin-bottom: 0;
    }
  }
  
  &::before {
    content: '';
    position: absolute;
    left: 15rpx;
    top: 0;
    bottom: 0;
    width: 2rpx;
    background: #e5e5e7;
  .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;
  }
}
.timeline-item {
  position: relative;
  margin-bottom: 40rpx;
  &:last-child {
    margin-bottom: 0;
  }
}
.timeline-marker {
  position: absolute;
  left: -40rpx;
  top: 8rpx;
  width: 32rpx;
  height: 32rpx;
  border-radius: 50%;
  background: #e5e5e7;
  border: 4rpx solid #fff;
  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
  &.active {
    background: #007aff;
  }
}
.timeline-content {
  padding-bottom: 20rpx;
}
.step-title {
  display: block;
  font-size: 28rpx;
  font-weight: 600;
  color: #1d1d1f;
  margin-bottom: 8rpx;
}
.step-time, .step-person {
  display: block;
/* 小型按钮 */
.small-btn {
  padding: 8rpx 20rpx;
  font-size: 24rpx;
  color: #86868b;
  margin-bottom: 4rpx;
  border-radius: 6rpx;
  border: none;
  &.primary {
    background: #007aff;
    color: #fff;
  }
  &.secondary {
    background: #ecf5ff;
    color: #007aff;
    border: 1rpx solid #007aff;
  }
  &:active {
    opacity: 0.8;
  }
}
.action-bar {
  display: flex;
  gap: 20rpx;
  padding: 40rpx 0;
  background: transparent;
  padding: 20rpx 0;
  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: 100;
}
.action-btn {
@@ -509,6 +950,24 @@
      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);
    }
  }
}
.fade-in-up {