WXL
2025-12-27 05e6b08007a86b5b10c680babc9c3bcc3a1a201b
上报转运单部分数据接入
已重命名1个文件
已修改9个文件
已添加4个文件
1756 ■■■■ 文件已修改
.env.development 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App.vue 81 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/op-select/index.vue 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
index.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
manifest.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/case/CaseDetails.vue 915 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/case/CaseInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/case/index.vue 605 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/login/Login.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
static/avatar/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
static/avatar/logo1.jpg 补丁 | 查看 | 原始文档 | blame | 历史
static/avatar/yisn.jpg 补丁 | 查看 | 原始文档 | blame | 历史
static/avatar/yisna.jpg 补丁 | 查看 | 原始文档 | blame | 历史
uni_modules/uview-plus/libs/util/route.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.development
@@ -1,4 +1,4 @@
# å¼€å‘环境API地址
VITE_APP_BASE_API = 'http://192.168.100.10:8080'
# VITE_APP_BASE_API = 'http://192.168.100.125:8080'
# VITE_APP_BASE_API = 'http://192.168.100.10:8080'
VITE_APP_BASE_API = 'http://localhost:8080'
VUE_APP_PLATFORM = 'h5'
App.vue
@@ -1,35 +1,74 @@
<script setup>
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import { onLaunch } from '@dcloudio/uni-app'
import { getToken } from '@/utils/auth'
import { useUserStore } from '@/stores/user'
const { t } = useI18n()
// å®šä¹‰é¡µé¢ç™½åå• - è¿™äº›é¡µé¢ä¸éœ€è¦token校验
const pageWhiteList = [
  'pages/login/Login',
  'pages/login/DingTalkLogin'
]
onLaunch(() => {
    console.log('App Launch')
    try {
        const currentLang = uni.getStorageSync('language')
        console.log('当前语言:', currentLang)
    } catch (error) {
        console.error('语言配置错误:', error)
    }
})
// æ”¹è¿›çš„白名单检查方法
const isPageInWhiteList = (currentPage) => {
  return pageWhiteList.some(path => currentPage.includes(path))
}
onShow(() => {
    console.log('App Show')
})
onHide(() => {
    console.log('App Hide')
onLaunch(async () => {
  console.log('App Launch')
  const userStore = useUserStore()
  try {
    const token = getToken()
    const launchOptions = uni.getLaunchOptionsSync()
    const currentPage = launchOptions.path || ''
    console.log(launchOptions);
    console.log(launchOptions.path);
    if (!token) {
      if (!isPageInWhiteList(currentPage)) {
        console.log('未通过白名单跳转登录页')
        return uni.redirectTo({ url: '/pages/login/Login' })
      }
      return
    }
    // æ ¡éªŒtoken有效性:通过调用/current/user/current_roles接口
    const current = await uni.$uapi.get("/system/user/profile");
    // å¦‚果接口返回成功,说明token有效,继续获取用户信息
    if (current ) {
      // const resuser = await uni.$uapi.get("/system/user/profile");
      userStore.setUserInfo(current);
      // userStore.setroleKey(current[0].roleKey);
      // å¦‚果当前是登录页,跳转首页
      if (isPageInWhiteList(currentPage)) {
        uni.switchTab({ url: '/pages/index/index' })
      }
    } else {
      // æŽ¥å£è¿”回但角色信息为空,视为token无效
      console.error('角色信息获取失败,token可能无效')
      userStore.clearUser() // æ¸…除本地用户信息
      uni.redirectTo({ url: '/pages/login/Login' })
    }
  } catch (error) {
    console.error('初始化失败:', error)
    // token无效或其他错误,清除本地用户信息并跳转登录页
    userStore.clearUser()
    uni.redirectTo({ url: '/pages/login/Login' })
  }
})
</script>
<style lang="scss">
@import "@/uni_modules/uview-plus/index.scss";
// ä¸»é¢˜é¢œè‰²
$primary-color: #0f95b0;
$primary-color: #67AFAB;
$primary-light: rgba($primary-color, 0.1);
$primary-gradient: linear-gradient(135deg, #0f95b0, #89C4C1);
$primary-gradient: linear-gradient(135deg, #67AFAB, #89C4C1);
// æ–‡å­—颜色
$text-primary: #333333;
components/op-select/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,137 @@
<!-- components/fs-select/fs-select.vue -->
<template>
  <view style="width: 100%">
    <u-input
      :input-align="inputaling"
      :placeholder="placeholderText"
      v-model="valueLable"
      type="select"
      :select-open="show"
      @click="show = true"
      :border="border"
      readonly
    />
    <u-select
      v-model="show"
      :mode="mode"
      :list="list"
      @confirm="confirm"
      :value-name="valuename"
      :label-name="labelname"
      :safe-area-inset-bottom="true"
    ></u-select>
  </view>
</template>
<script>
export default {
  name: "fs-select",
  props: {
    placeholder: {
      type: String,
      default: ''
    },
    // æ˜¾ç¤ºæ–‡æœ¬çš„字段名
    labelname: {
      type: String,
      default: 'label'
    },
    // å€¼çš„字段名
    valuename: {
      type: String,
      default: 'value'
    },
    // é€‰æ‹©å™¨æ¨¡å¼
    mode: {
      type: String,
      default: 'single-column'
    },
    // æ•°æ®åˆ—表
    list: {
      type: Array,
      default() {
        return []
      }
    },
    // é€‰ä¸­çš„值
    value: {
      type: [String, Number],
      required: true
    },
    // æ˜¯å¦æ˜¾ç¤ºè¾¹æ¡†
    border: {
      type: Boolean,
      default: false
    },
    // æ–‡æœ¬å¯¹é½æ–¹å¼
    inputaling: {
      type: String,
      default: 'right'
    }
  },
  data() {
    return {
      show: false,
      valueLable: ''
    };
  },
  computed: {
    placeholderText() {
      return this.placeholder || `请选择`;
    }
  },
  watch: {
    // ç›‘听value值变化,更新显示文本
    value: {
      handler(newValue) {
        this.updateDisplayLabel(newValue);
      },
      immediate: true
    },
    // ç›‘听list数据变化,更新显示文本
    list: {
      handler(newList) {
        this.updateDisplayLabel(this.value);
      },
      deep: true
    }
  },
  methods: {
    // æ›´æ–°æ˜¾ç¤ºæ–‡æœ¬
    updateDisplayLabel(currentValue) {
      if (!currentValue && currentValue !== 0) {
        this.valueLable = '';
        return;
      }
      const foundItem = this.list.find(item =>
        String(item[this.valuename]) === String(currentValue)
      );
      if (foundItem) {
        this.valueLable = foundItem[this.labelname];
      } else {
        this.valueLable = '';
      }
    },
    // ç¡®è®¤é€‰æ‹©
    confirm(e) {
      if (e.length > 0) {
        this.valueLable = e[0].label;
        // æŠ›å‡ºé€‰ä¸­çš„value值
        this.$emit('input', e[0].value);
        // å¦‚果需要更详细的数据,可以额外抛出事件
        this.$emit('change', {
          value: e[0].value,
          label: e[0].label,
          item: e[0]
        });
      }
    }
  }
}
</script>
<style lang="scss" scoped>
// å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ è‡ªå®šä¹‰æ ·å¼
</style>
index.html
@@ -10,7 +10,7 @@
        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
    </script>
    <title>青岛OPO管理系统</title>
    <link rel="icon" href="/static/avatar/logo.jpg">  <!-- å¼•用根目录的图标 -->
    <link rel="icon" href="/static/avatar/logo.png">  <!-- å¼•用根目录的图标 -->
    <!--preload-links-->
    <!--app-context-->
  </head>
manifest.json
@@ -73,7 +73,7 @@
            "disableHostCheck" : true,
            "proxy" : {
                "/api" : {
                    "target" : "http://192.168.100.10:8080",
                    "target" : "http://localhost:8080",
                    "changeOrigin" : true
                }
            }
pages/case/CaseDetails.vue
@@ -3,6 +3,11 @@
    <!-- è¡¨å•内容 -->
    <scroll-view scroll-y class="form-scroll" :show-scrollbar="false">
      <view class="form-content">
        <view class="page-header">
          <text class="page-title">{{
            isEditMode ? "修改案例" : "上报案例"
          }}</text>
        </view>
        <!-- åŸºæœ¬ä¿¡æ¯å¡ç‰‡ -->
        <view class="form-section">
          <view class="section-header">
@@ -12,53 +17,56 @@
          <view class="form-grid">
            <view class="form-item">
              <text class="item-label">捐献编号</text>
              <text class="item-label">案例编号</text>
              <u-input
                v-model="form.donorno"
                placeholder="系统自动生成"
                v-model="form.caseNo"
                placeholder="上报后自动生成"
                disabled
                :disabledColor="disabledColor"
                border="none"
              />
            </view>
            <!-- æ”¹é€ åŽçš„医疗机构选择 -->
            <view class="form-item">
              <text class="item-label">医疗机构</text>
              <u-input
                v-model="form.treatmenthospitalno"
                placeholder="请选择医疗机构"
                readonly
                border="none"
                @click="showHospitalPicker = true"
              <picker
                mode="selector"
                :range="hospitalLabels"
                :value="hospitalIndex"
                @change="onHospitalChange"
              >
                <template #suffix>
                  <u-icon name="arrow-down" color="#86868b"></u-icon>
                </template>
              </u-input>
                <view class="picker">
                  <text>{{ currentHospital }}</text>
                  <text class="icon-arrow">›</text>
                </view>
              </picker>
            </view>
            <!-- æ”¹é€ åŽçš„科室选择 -->
            <view class="form-item">
              <text class="item-label">科室</text>
              <u-input
                v-model="form.treatmentdeptname"
                placeholder="请选择科室"
                readonly
                border="none"
                @click="selectShow = true"
              <picker
                mode="selector"
                :range="deptLabels"
                :value="deptIndex"
                @change="onDeptChange"
              >
                <template #suffix>
                  <u-icon name="arrow-down" color="#86868b"></u-icon>
                </template>
              </u-input>
                <view class="picker">
                  <text>{{ currentDept }}</text>
                  <text class="icon-arrow">›</text>
                </view>
              </picker>
            </view>
            <view class="form-item">
              <text class="item-label required">姓名</text>
              <u-input
                type="text"
                v-model="form.name"
                placeholder="请输入姓名"
                border="none"
                :customStyle="inputStyle(!form.name)"
                maxlength="20"
                class="custom-input"
              />
            </view>
          </view>
@@ -72,93 +80,99 @@
          </view>
          <view class="form-grid">
            <!-- æ”¹é€ åŽçš„æ°‘族选择 -->
            <view class="form-item">
              <text class="item-label">民族</text>
              <u-input
                v-model="form.nation"
                placeholder="请选择民族"
                readonly
                border="none"
                @click="showNationPicker = true"
              <picker
                mode="selector"
                :range="nationLabels"
                :value="nationIndex"
                @change="onNationChange"
              >
                <template #suffix>
                  <u-icon name="arrow-down" color="#86868b"></u-icon>
                </template>
              </u-input>
                <view class="picker">
                  <text>{{ currentNation }}</text>
                  <text class="icon-arrow">›</text>
                </view>
              </picker>
            </view>
            <view class="form-item">
              <text class="item-label">国籍</text>
              <u-input
                type="text"
                v-model="form.nationality"
                placeholder="请输入国籍"
                border="none"
                class="custom-input"
              />
            </view>
            <!-- æ”¹é€ åŽçš„证件类型选择 -->
            <view class="form-item">
              <text class="item-label">证件类型</text>
              <u-input
                v-model="form.idcardtype"
                placeholder="请选择证件类型"
                readonly
                border="none"
                @click="showIdCardTypePicker = true"
              <picker
                mode="selector"
                :range="idCardTypeLabels"
                :value="idCardTypeIndex"
                @change="onIdCardTypeChange"
              >
                <template #suffix>
                  <u-icon name="arrow-down" color="#86868b"></u-icon>
                </template>
              </u-input>
                <view class="picker">
                  <text>{{ currentIdCardType }}</text>
                  <text class="icon-arrow">›</text>
                </view>
              </picker>
            </view>
            <view class="form-item">
              <text class="item-label required">证件号码</text>
              <u-input
                type="idcard"
                v-model="form.idcardno"
                placeholder="请输入证件号码"
                border="none"
                :customStyle="inputStyle(!form.idcardno)"
                maxlength="18"
                class="custom-input"
                @blur="validateIdCard"
              />
              <text class="error-text" v-if="idCardError">{{
                idCardError
              }}</text>
            </view>
            <!-- æ”¹é€ åŽçš„æ€§åˆ«é€‰æ‹© -->
            <view class="form-item">
              <text class="item-label">性别</text>
              <view class="radio-group">
              <view class="radio-options">
                <view
                  v-for="gender in genderOptions"
                  :key="gender.value"
                  class="radio-item"
                  class="option-item"
                  :class="{ active: form.sex === gender.value }"
                  @click="form.sex = gender.value"
                >
                  <view
                    class="radio-dot"
                    :class="{ active: form.sex === gender.value }"
                  ></view>
                  <text class="radio-label">{{ gender.label }}</text>
                  <text class="radio-dot"></text>
                  <text class="option-label">{{ gender.label }}</text>
                </view>
              </view>
            </view>
            <!-- æ”¹é€ åŽçš„出生日期选择 -->
            <view class="form-item">
              <text class="item-label">出生日期</text>
              <u-input
                v-model="form.birthday"
                placeholder="选择出生日期"
                readonly
                border="none"
                @click="showDatePicker = true"
              <picker
                mode="date"
                :value="form.birthday"
                @change="onBirthdayChange"
              >
                <template #suffix>
                  <u-icon name="arrow-down" color="#86868b"></u-icon>
                </template>
              </u-input>
                <view class="picker">
                  <text>{{ form.birthday || "选择出生日期" }}</text>
                  <text class="icon-arrow">›</text>
                </view>
              </picker>
            </view>
            <view class="form-item">
              <text class="item-label">年龄</text>
              <u-input
                v-model="form.age"
                v-model="ageDisplay"
                placeholder="自动计算"
                disabled
                :disabledColor="disabledColor"
@@ -181,6 +195,14 @@
              <u-input
                v-model="form.inpatientno"
                placeholder="请输入住院号"
                border="none"
              />
            </view>
            <view class="form-item">
              <text class="item-label">GCS评分</text>
              <u-input
                v-model="form.gscScore"
                placeholder="请输入GCS评分"
                border="none"
              />
            </view>
@@ -212,6 +234,31 @@
                  <text class="radio-label">{{ bloodType.label }}</text>
                </view>
              </view>
            </view>
            <view class="form-item">
              <text class="item-label">传染病</text>
              <view class="radio-group horizontal">
                <view
                  v-for="bloodType in infectiousDiseaselist"
                  :key="bloodType.value"
                  class="radio-item"
                  @click="form.infectious = bloodType.value"
                >
                  <view
                    class="radio-dot"
                    :class="{ active: form.bloodtype === bloodType.value }"
                  ></view>
                  <text class="radio-label">{{ bloodType.label }}</text>
                </view>
              </view>
            </view>
            <view class="form-item">
              <text class="item-label">其他</text>
              <u-input
                v-model="form.infectiousOther"
                placeholder="请输入住院号"
                border="none"
              />
            </view>
            <view class="form-item">
@@ -261,7 +308,7 @@
              />
            </view>
            <view class="form-item">
            <!-- <view class="form-item">
              <text class="item-label">报告人</text>
              <u-input
                v-model="form.reporterno"
@@ -274,7 +321,7 @@
                  <u-icon name="arrow-down" color="#86868b"></u-icon>
                </template>
              </u-input>
            </view>
            </view> -->
            <view class="form-item">
              <text class="item-label">报告时间</text>
@@ -290,13 +337,15 @@
        <!-- æ“ä½œæŒ‰é’® -->
        <view class="action-buttons">
          <u-button class="btn secondary" @click="handleCancel">取消</u-button>
          <u-button class="btn secondary" @click="resetForm">重置表单</u-button>
          <u-button
            class="btn primary"
            :disabled="!isFormValid"
            @click="submitForm"
            >提交上报</u-button
            :disabled="!isFormValid || loading"
            @click="handleSubmit"
          >
            {{ loading ? "提交中..." : isEditMode ? "保存修改" : "提交上报" }}
          </u-button>
        </view>
        <attachment-upload
          ref="attachment"
@@ -342,14 +391,14 @@
    ></u-picker>
    <!-- è¯ä»¶ç±»åž‹é€‰æ‹©å™¨ -->
    <u-picker
    <!-- <u-picker
      :show="showIdCardTypePicker"
      :columns="[idCardTypeOptions]"
      keyName="label"
      @confirm="onIdCardTypeConfirm"
      @cancel="showIdCardTypePicker = false"
      title="请选择证件类型"
    ></u-picker>
    ></u-picker> -->
    <!-- æ—¥æœŸé€‰æ‹©å™¨ -->
    <u-datetime-picker
@@ -381,11 +430,16 @@
import { onLoad } from "@dcloudio/uni-app";
import attachmentUpload from "@/components/attachment";
import { useUserStore } from "@/stores/user";
import { useDict } from "@/utils/dict";
const dict = ref({});
const userStore = useUserStore();
const isEditMode = ref(false);
const currentId = ref(null);
// è¡¨å•数据
const form = ref({
  donorno: "",
  treatmenthospitalno: "",
  caseNo: "",
  treatmenthospitalname: "",
  treatmentdeptname: "",
  name: "",
  nation: "",
@@ -395,19 +449,52 @@
  sex: "",
  birthday: "",
  age: "",
  ageunit: "", // æ–°å¢žï¼šå¹´é¾„单位
  inpatientno: "",
  diagnosisname: "",
  bloodtype: "",
  rhyin: "",
  infoname: "",
  infophone: "",
  reporterno: "",
  reportername: userStore.name || "",
  reporterno: userStore.userId || "",
  reporterphone: "", // æŠ¥å‘Šè€…联系电话
  reporttime: "",
  contactperson: "", // æ–°å¢žï¼šè”系人(协调员)
  education: "", // æ–°å¢žï¼šå­¦åކ
  illnessoverview: "", // æ–°å¢žï¼šç—…情概况
  infectious: "", // ä¼ æŸ“病情况
  infectiousOther: "", // ä¼ æŸ“ç—…å…¶ä»–
  isTransport: "1", // æ–°å¢žï¼šæ˜¯å¦éœ€è¦è½¬è¿ï¼Œé»˜è®¤1不需要
  nativeplace: "", // æ–°å¢žï¼šç±è´¯
  occupation: "", // æ–°å¢žï¼šèŒä¸š
  patientstate: "", // æ–°å¢žï¼šç—…人状况
  phone: "", // æ–°å¢žï¼šè”系电话
  registeraddress: "", // æ–°å¢žï¼šæˆ·ç±åœ°å€
  registerprovince: "", // æ–°å¢žï¼šæˆ·ç±åœ°å€çœç¼–号
  registerprovincename: "", // æ–°å¢žï¼šæˆ·ç±åœ°å€çœåç§°
  registercityname: "", // æ–°å¢žï¼šå¸‚名称
  registertownname: "", // æ–°å¢žï¼šæ‰€å±žè¡—道(镇)名称
  registercommunityname: "", // æ–°å¢žï¼šç¤¾åŒºï¼ˆæ‘)名称
  residenceaddress: "", // æ–°å¢žï¼šçŽ°ä½åœ°å€
  residenceprovince: "", // æ–°å¢žï¼šçŽ°ä½åœ°å€çœä»£ç 
  residenceprovincename: "", // æ–°å¢žï¼šçŽ°ä½åœ°å€çœåç§°
  residencecountycode: "", // æ–°å¢žï¼šæ‰€å±žåŒºåŸŸç¼–号
  residencecountyname: "", // æ–°å¢žï¼šæ‰€å±žåŒºåŸŸåç§°
  residencetownname: "", // æ–°å¢žï¼šæ‰€å±žè¡—道(镇)名称
  residencecommunity: "", // æ–°å¢žï¼šç¤¾åŒºï¼ˆæ‘)编号
  residencecommunityname: "", // æ–°å¢žï¼šç¤¾åŒºï¼ˆæ‘)名称
  remark: "", // æ–°å¢žï¼šå¤‡æ³¨
  reportStatus: "1", // æ–°å¢žï¼šä¸ŠæŠ¥çŠ¶æ€ï¼Œé»˜è®¤1已上报
  terminationCase: 0, // æ–°å¢žï¼šæ˜¯å¦ç»ˆæ­¢æ¡ˆä¾‹ï¼Œé»˜è®¤0开启
  annexfilesList: [], // é™„件文件地址集合
});
// é€‰æ‹©å™¨çŠ¶æ€
const attachments = ref([]);
const infectiousDiseaselist = ref([]);
const isReadonly = ref(false);
const id = ref(null);
const selectShow = ref(false);
const showHospitalPicker = ref(false);
const showNationPicker = ref(false);
@@ -481,7 +568,114 @@
const isFormValid = computed(() => {
  return form.value.name && form.value.idcardno && form.value.diagnosisname;
});
// é€‰æ‹©å™¨ç´¢å¼•
const hospitalIndex = ref(-1);
const deptIndex = ref(-1);
const nationIndex = ref(-1);
const idCardTypeIndex = ref(-1);
// è®¡ç®—属性 - æ ‡ç­¾æ•°ç»„
const hospitalLabels = computed(() =>
  hospitalOptions.value.map((item) => item.label)
);
const ageDisplay = computed(() => {
  if (!form.value.age || !form.value.ageunit) {
    return '自动计算';
  }
  return `${form.value.age}${form.value.ageunit}`;
});
const deptLabels = computed(() => {
  return pickerColumns.value[0].map((item) => item.label);
});
const nationLabels = computed(() =>
  nationOptions.value.map((item) => item.label)
);
const idCardTypeLabels = computed(() =>
  idCardTypeOptions.value.map((item) => item.label)
);
// è®¡ç®—属性 - å½“前选中显示文本
const currentHospital = computed(() => {
  return hospitalIndex.value >= 0
    ? hospitalLabels.value[hospitalIndex.value]
    : "请选择医疗机构";
});
const currentDept = computed(() => {
  return deptIndex.value >= 0
    ? deptLabels.value[deptIndex.value]
    : "请选择科室";
});
const currentNation = computed(() => {
  return nationIndex.value >= 0
    ? nationLabels.value[nationIndex.value]
    : "请选择民族";
});
const currentIdCardType = computed(() => {
  return idCardTypeIndex.value >= 0
    ? idCardTypeLabels.value[idCardTypeIndex.value]
    : "请选择证件类型";
});
// é€‰æ‹©å™¨å˜æ›´äº‹ä»¶
const onHospitalChange = (e) => {
  const index = parseInt(e.detail.value);
  hospitalIndex.value = index;
  form.value.treatmenthospitalname = hospitalOptions.value[index].label;
};
const onDeptChange = (e) => {
  const index = parseInt(e.detail.value);
  deptIndex.value = index;
  form.value.treatmentdeptname = pickerColumns.value[0][index].label;
};
const onNationChange = (e) => {
  const index = parseInt(e.detail.value);
  nationIndex.value = index;
  form.value.nation = nationOptions.value[index].label;
};
const onIdCardTypeChange = (e) => {
  const index = parseInt(e.detail.value);
  idCardTypeIndex.value = index;
  form.value.idcardtype = idCardTypeOptions.value[index].value;
};
const onBirthdayChange = (e) => {
  form.value.birthday = e.detail.value;
  calculateAge();
};
const onDateConfirm = (e) => {
  const date = new Date(e.value);
  // æ ¼å¼åŒ–日期为 YYYY-MM-DD
  form.value.birthday = `${date.getFullYear()}-${(date.getMonth() + 1)
    .toString()
    .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
  calculateAge();
  showDatePicker.value = false;
};
// èº«ä»½è¯éªŒè¯
const idCardError = ref("");
const validateIdCard = () => {
  if (!form.value.idcardno) {
    idCardError.value = "请输入证件号码";
    return false;
  }
  if (form.value.idcardno.length !== 18) {
    idCardError.value = "请输入18位身份证号码";
    return false;
  }
  idCardError.value = "";
  return true;
};
// æ ·å¼æ–¹æ³•
const inputStyle = (isError) => {
  return isError
@@ -497,62 +691,130 @@
// ç”Ÿå‘½å‘¨æœŸ
onMounted(() => {
  updateCurrentTime();
  generateDonorNo();
  // updateCurrentTime();
  // generateDonorNo();
  setInterval(updateCurrentTime, 1000);
});
onLoad((options) => {
  if (options.edit && options.id) {
    loadCaseData(options.id);
onLoad(async (options) => {
  id.value = options.id;
  if (options.id) {
    currentId.value = options.id;
    isEditMode.value = true;
    await loadCaseData(options.id);
  } else {
    isEditMode.value = false;
    generateDonorNo();
  }
  // èŽ·å–å­—å…¸æ•°æ®
  dict.value = await useDict(
    "sys_IDType",
    "sys_user_sex",
    "sys_BloodType",
    "sys_Infectious",
    "sys_AgeUnit", // æ–°å¢žå¹´é¾„单位
    "sys_education", // æ–°å¢žå­¦åކ
    "sys_nation", // æ–°å¢žæ°‘族
    "sys_occupation" // æ–°å¢žèŒä¸š
  );
  initOptions();
  updateCurrentTime();
});
// åˆå§‹åŒ–选项数据的方法
const initOptions = () => {
  infectiousDiseaselist.value = dict.value.sys_Infectious || [];
  idCardTypeOptions.value = dict.value.sys_IDType || [];
};
// æ–¹æ³•定义
const updateCurrentTime = () => {
  const now = new Date();
  currentTime.value = now.toLocaleString("zh-CN", {
  // å…ˆèŽ·å–æ ¼å¼åŒ–çš„å­—ç¬¦ä¸²ï¼Œç„¶åŽæ›¿æ¢åˆ†éš”ç¬¦å’Œè°ƒæ•´é¡ºåº
  const localString = now.toLocaleString("zh-CN", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
    hour12: false,
  });
  // å°† "yyyy/mm/dd hh:mm:ss" è½¬æ¢ä¸º "yyyy-mm-dd hh:mm:ss"
  currentTime.value = localString.replace(/\//g, "-");
  form.value.reporttime = currentTime.value;
};
const generateDonorNo = () => {
  const date = new Date();
  const timestamp = date.getTime().toString().slice(-6);
  form.value.donorno = `DON${date.getFullYear()}${(date.getMonth() + 1)
    .toString()
    .padStart(2, "0")}${timestamp}`;
  // form.value.caseNo = `DON${date.getFullYear()}${(date.getMonth() + 1)
  //   .toString()
  //   .padStart(2, "0")}${timestamp}`;
};
const calculateAge = () => {
  if (!form.value.birthday) return;
  if (!form.value.birthday) {
    form.value.age = '';
    form.value.ageunit = '';
    return;
  }
  const birthDate = new Date(form.value.birthday);
  const today = new Date();
  let age = today.getFullYear() - birthDate.getFullYear();
  const monthDiff = today.getMonth() - birthDate.getMonth();
  if (
    monthDiff < 0 ||
    (monthDiff === 0 && today.getDate() < birthDate.getDate())
  ) {
    age--;
  // æ£€æŸ¥æ—¥æœŸæœ‰æ•ˆæ€§
  if (isNaN(birthDate.getTime())) {
    form.value.age = '';
    form.value.ageunit = '';
    return;
  }
  form.value.age = age.toString();
  // è®¡ç®—总天数差
  const timeDiff = today.getTime() - birthDate.getTime();
  const daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
  if (daysDiff < 0) {
    // æœªæ¥æ—¥æœŸå¤„理
    form.value.age = '';
    form.value.ageunit = '';
    return;
  }
  // è®¡ç®—年、月、日
  const years = today.getFullYear() - birthDate.getFullYear();
  const months = today.getMonth() - birthDate.getMonth();
  const days = today.getDate() - birthDate.getDate();
  let ageValue, ageUnit;
  if (years >= 1) {
    // å¤§äºŽç­‰äºŽ1年:显示年
    let actualYears = years;
    // å¤„理月份和日期的边界情况
    if (months < 0 || (months === 0 && days < 0)) {
      actualYears = years - 1;
    }
    ageValue = actualYears.toString();
    ageUnit = '岁';
  } else if (daysDiff >= 30) {
    // å¤§äºŽç­‰äºŽ30天:显示月
    let totalMonths = years * 12 + months;
    if (days < 0) {
      totalMonths--;
    }
    ageValue = Math.max(1, totalMonths).toString(); // ç¡®ä¿è‡³å°‘1个月
    ageUnit = '个月';
  } else {
    // å°äºŽ30天:显示天
    ageValue = Math.max(1, daysDiff).toString(); // ç¡®ä¿è‡³å°‘1天
    ageUnit = '天';
  }
  form.value.age = ageValue;
  form.value.ageunit = ageUnit;
};
const validateIdCard = () => {
  if (form.value.idcardno && form.value.idcardno.length !== 18) {
    uni.showToast({
      title: "请输入18位身份证号码",
      icon: "none",
    });
  }
};
// å¤„理基础附件上传
const handleBaseUpload = (file) => {
  console.log("基础附件上传成功:", file);
@@ -615,7 +877,7 @@
const onHospitalConfirm = (e) => {
  if (e.value && e.value[0]) {
    form.value.treatmenthospitalno = e.value[0].label;
    form.value.treatmenthospitalname = e.value[0].label;
  }
  showHospitalPicker.value = false;
};
@@ -629,19 +891,19 @@
const onIdCardTypeConfirm = (e) => {
  if (e.value && e.value[0]) {
    form.value.idcardtype = e.value[0].label;
    form.value.idcardtype = e.value[0].value;
  }
  showIdCardTypePicker.value = false;
};
const onDateConfirm = (e) => {
  const date = new Date(e.value);
  form.value.birthday = `${date.getFullYear()}-${(date.getMonth() + 1)
    .toString()
    .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
  calculateAge();
  showDatePicker.value = false;
};
// const onDateConfirm = (e) => {
//   const date = new Date(e.value);
//   form.value.birthday = `${date.getFullYear()}-${(date.getMonth() + 1)
//     .toString()
//     .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
//   calculateAge();
//   showDatePicker.value = false;
// };
const onReporterConfirm = (e) => {
  if (e.value && e.value[0]) {
@@ -661,19 +923,33 @@
    success: (res) => {
      if (res.confirm) {
        Object.keys(form.value).forEach((key) => {
          if (key !== "donorno") {
          if (!["id", "caseNo"].includes(key)) {
            form.value[key] = "";
          }
        });
        // é‡ç½®é€‰æ‹©å™¨ç´¢å¼•
        hospitalIndex.value = -1;
        deptIndex.value = -1;
        nationIndex.value = -1;
        idCardTypeIndex.value = -1;
        attachments.value = [];
        form.value.nationality = "中国";
        generateDonorNo();
        form.value.isTransport = "1";
        form.value.terminationCase = 0;
        form.value.reportStatus = "1";
        if (!isEditMode.value) {
          generateDonorNo();
        }
        uni.showToast({ title: "表单已重置", icon: "success" });
      }
    },
  });
};
const submitForm = async () => {
const handleSubmit = async () => {
  if (!isFormValid.value) {
    uni.showToast({
      title: "请填写姓名、证件号码和疾病诊断",
@@ -682,52 +958,162 @@
    return;
  }
  loading.value = true;
  try {
    await new Promise((resolve) => setTimeout(resolve, 1500));
    uni.showLoading({ title: isEditMode.value ? "修改中..." : "提交中..." });
    uni.showToast({
      title: "上报成功",
      icon: "success",
    });
    // å‡†å¤‡æäº¤æ•°æ®
    const submitData = {
      ...form.value,
      age: parseInt(form.value.age) || 0,
      annexfilesList: attachments.value.map((file) => ({
        url: file.url,
        name: file.name,
        type: file.type,
      })),
      phone: form.value.infophone,
      isTransport: form.value.isTransport || "1",
      terminationCase: form.value.terminationCase || 0,
      reportStatus: form.value.reportStatus || "1",
    };
    setTimeout(() => {
      uni.navigateBack();
    }, 1500);
    let res;
    if (isEditMode.value) {
      // ä¿®æ”¹æŽ¥å£
      res = await uni.$uapi.post(
        "/system/donatebaseinforeport/edit",
        submitData
      );
    } else {
      // æ–°å¢žæŽ¥å£
      res = await uni.$uapi.post(
        "/system/donatebaseinforeport/add",
        submitData
      );
    }
    uni.hideLoading();
    if (res.code === 200) {
      uni.showToast({
        title: isEditMode.value ? "修改成功" : "上报成功",
        icon: "success",
      });
      // æ¸…空本地存储的草稿
      removeDraft();
      setTimeout(() => {
        uni.navigateBack();
      }, 1500);
    } else {
      throw new Error(res.msg || "操作失败");
    }
  } catch (error) {
    console.error("操作失败:", error);
    uni.showToast({
      title: "上报失败,请重试",
      title: error.message || (isEditMode.value ? "修改失败" : "上报失败"),
      icon: "none",
    });
  } finally {
    loading.value = false;
  }
};
const removeDraft = () => {
  localStorage.removeItem("caseReportDraft");
};
// å–消处理
const handleCancel = () => {
  uni.navigateBack();
};
const loadCaseData = (id) => {
const loadCaseData = async (id) => {
  // æ¨¡æ‹ŸåŠ è½½ç¼–è¾‘æ•°æ®
  form.value = {
    donorno: "DON20241216001",
    treatmenthospitalno: "青岛镜湖医院",
    treatmentdeptname: "神经外科",
    name: "张三",
    nation: "汉族",
    nationality: "中国",
    idcardtype: "居民身份证",
    idcardno: "370203198510123456",
    sex: "1",
    birthday: "1985-10-12",
    age: "38",
    inpatientno: "ZY20241216001",
    diagnosisname: "脑外伤导致脑死亡",
    bloodtype: "A",
    rhyin: "positive",
    infoname: "李医生",
    infophone: "13800138000",
    reporterno: "张医生",
    reporttime: currentTime.value,
  };
  // form.value = {
  //   caseNo: "DON20241216001",
  //   treatmenthospitalname: "青岛镜湖医院",
  //   treatmentdeptname: "神经外科",
  //   name: "张三",
  //   nation: "汉族",
  //   nationality: "中国",
  //   idcardtype: "居民身份证",
  //   idcardno: "370203198510123456",
  //   sex: "1",
  //   birthday: "1985-10-12",
  //   age: "38",
  //   inpatientno: "ZY20241216001",
  //   diagnosisname: "脑外伤导致脑死亡",
  //   bloodtype: "A",
  //   rhyin: "positive",
  //   infoname: "李医生",
  //   infophone: "13800138000",
  //   reporterno: "张医生",
  //   reporttime: currentTime.value,
  // };
  try {
    loading.value = true;
    const res = await uni.$uapi.get(
      `/system/donatebaseinforeport/getInfo/${id}`
    );
    if (res.code === 200 && res.data) {
      // å¡«å……表单数据
      Object.keys(form.value).forEach((key) => {
        if (res.data[key] !== undefined && res.data[key] !== null) {
          form.value[key] = res.data[key];
        }
      });
      // å¤„理选择器索引
      updatePickerIndexes();
      // å¤„理附件
      if (res.data.annexfilesList) {
        attachments.value = res.data.annexfilesList;
      }
      uni.showToast({
        title: "数据加载成功",
        icon: "success",
      });
    } else {
      throw new Error(res.msg || "数据加载失败");
    }
  } catch (error) {
    console.error("加载案例数据失败:", error);
    uni.showToast({
      title: "数据加载失败,请重试",
      icon: "none",
    });
  } finally {
    loading.value = false;
  }
};
// æ›´æ–°é€‰æ‹©å™¨ç´¢å¼•
const updatePickerIndexes = () => {
  // åŒ»ç–—机构索引
  const hospitalIndex = hospitalOptions.value.findIndex(
    (item) => item.label === form.value.treatmenthospitalname
  );
  if (hospitalIndex !== -1) hospitalIndex.value = hospitalIndex;
  // ç§‘室索引
  const deptIndex = pickerColumns.value[0].findIndex(
    (item) => item.label === form.value.treatmentdeptname
  );
  if (deptIndex !== -1) deptIndex.value = deptIndex;
  // æ°‘族索引
  const nationIndex = nationOptions.value.findIndex(
    (item) => item.label === form.value.nation
  );
  if (nationIndex !== -1) nationIndex.value = nationIndex;
  // è¯ä»¶ç±»åž‹ç´¢å¼•
  const idCardTypeIndex = idCardTypeOptions.value.findIndex(
    (item) => item.value === form.value.idcardtype
  );
  if (idCardTypeIndex !== -1) idCardTypeIndex.value = idCardTypeIndex;
};
</script>
<style lang="scss" scoped>
@@ -887,4 +1273,233 @@
    }
  }
}
.case-report-container {
  min-height: 100vh;
  background: linear-gradient(135deg, #f8fdff 0%, #e8f7f6 100%);
}
.form-scroll {
  height: 100vh;
}
.form-content {
  padding: 30rpx;
}
.form-section {
  background: #fff;
  border-radius: 20rpx;
  padding: 30rpx;
  margin-bottom: 30rpx;
  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
}
.section-header {
  display: flex;
  align-items: center;
  margin-bottom: 30rpx;
  padding-bottom: 20rpx;
  border-bottom: 2rpx solid #f0f0f0;
}
.section-icon {
  font-size: 32rpx;
  margin-right: 16rpx;
}
.section-title {
  font-size: 32rpx;
  font-weight: 600;
  color: #1d1d1f;
}
.form-grid {
  display: flex;
  flex-direction: column;
  gap: 24rpx;
}
.form-item {
  display: flex;
  flex-direction: column;
}
.item-label {
  font-size: 28rpx;
  color: #1d1d1f;
  font-weight: 500;
  margin-bottom: 12rpx;
  &.required::after {
    content: "*";
    color: #ff4757;
    margin-left: 4rpx;
  }
}
/* æ”¹é€ åŽçš„选择器样式 */
.picker {
  height: 88rpx;
  background: #f5f5f7;
  border-radius: 12rpx;
  padding: 0 24rpx;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border: 2rpx solid #e5e5e7;
  text {
    font-size: 28rpx;
    color: #1d1d1f;
    &.icon-arrow {
      font-size: 32rpx;
      color: #86868b;
      transform: rotate(90deg);
    }
  }
}
.page-header {
  padding: 30rpx 0;
  text-align: center;
  margin-bottom: 20rpx;
}
.page-title {
  font-size: 36rpx;
  font-weight: 600;
  color: #1d1d1f;
}
/* åœ¨åŽŸæœ‰æ ·å¼åŸºç¡€ä¸Šæ·»åŠ  */
.form-section {
  position: relative;
}
.edit-badge {
  position: absolute;
  top: 30rpx;
  right: 30rpx;
  background: #ff6b35;
  color: white;
  padding: 8rpx 16rpx;
  border-radius: 20rpx;
  font-size: 24rpx;
}
/* åŠ è½½çŠ¶æ€æ ·å¼ */
.loading-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}
/* å“åº”式调整 */
@media (max-width: 768px) {
  .form-content {
    padding: 20rpx;
  }
  .form-section {
    padding: 20rpx;
  }
}
/* æ”¹é€ åŽçš„单选按钮样式 */
.radio-options {
  display: flex;
  gap: 40rpx;
}
.option-item {
  display: flex;
  align-items: center;
  gap: 16rpx;
  .radio-dot {
    width: 32rpx;
    height: 32rpx;
    border: 2rpx solid #e5e5e7;
    border-radius: 50%;
    position: relative;
  }
  .option-label {
    font-size: 28rpx;
    color: #1d1d1f;
  }
  &.active {
    .radio-dot {
      border-color: #0f95b0;
      &::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 16rpx;
        height: 16rpx;
        background: #0f95b0;
        border-radius: 50%;
      }
    }
    .option-label {
      color: #0f95b0;
    }
  }
}
.error-text {
  font-size: 24rpx;
  color: #ff4757;
  margin-top: 8rpx;
}
/* åŽŸæœ‰æ ·å¼è°ƒæ•´ */
:deep(.u-input) {
  border: 2rpx solid #e5e5e7 !important;
  border-radius: 12rpx !important;
  padding: 20rpx 24rpx !important;
  background: #fff !important;
}
.action-buttons {
  display: flex;
  gap: 20rpx;
  margin-top: 40rpx;
}
.btn {
  flex: 1;
  height: 80rpx;
  border-radius: 16rpx;
  font-size: 32rpx;
  font-weight: 500;
  &.secondary {
    background: #f5f5f7 !important;
    color: #1d1d1f !important;
  }
  &.primary {
    background: linear-gradient(135deg, #0f95b0, #89c4c1) !important;
    color: #fff !important;
    &:disabled {
      background: #c0c0c0 !important;
      opacity: 0.6;
    }
  }
}
</style>
pages/case/CaseInfo.vue
@@ -123,7 +123,7 @@
              <text class="step-person" v-if="step.person">经办人:{{ step.person }}</text>
            </view>
          </view>
        </view>
        </view>  +
      </view>
    </view>
pages/case/index.vue
@@ -124,8 +124,8 @@
            <text class="value">{{ caseItem.reviewerName }}</text>
          </view>
        </view>
        <!-- æ˜¾ç¤ºè½¬è¿è¯¦æƒ… -->
        <!-- åœ¨ .detail-info éƒ¨åˆ†ä¹‹åŽï¼Œ.footer éƒ¨åˆ†ä¹‹å‰æ·»åŠ è½¬è¿ä¿¡æ¯åŒºåŸŸ -->
        <!-- è½¬è¿ä¿¡æ¯åŒºåŸŸ -->
        <view class="transport-section">
          <view class="transport-info">
            <text class="label">转运状态</text>
@@ -137,31 +137,80 @@
            </text>
          </view>
          <!-- æ˜¾ç¤ºè½¬è¿è¯¦æƒ… -->
          <!-- æ˜¾ç¤ºè½¬è¿è¯¦æƒ…:只要有转运单号就显示 -->
          <view
            class="transport-details"
            v-if="caseItem.transportStatus !== 'not_transported'"
            v-if="caseItem.transportOrderNo && caseItem.transportOrderNo !== ''"
          >
            <view class="detail-item">
              <text class="detail-label">转运单号</text>
              <text class="detail-value">{{ caseItem.transportOrderNo }}</text>
            </view>
            <view class="detail-item" v-if="caseItem.transportDetails">
            <view class="detail-item" v-if="caseItem.transportTime">
              <text class="detail-label">计划转运时间</text>
              <text class="detail-value">{{ caseItem.transportTime }}</text>
            </view>
            <view class="detail-item" v-if="caseItem.transportDetails?.vehicle">
              <text class="detail-label">转运车辆</text>
              <text class="detail-value">{{
                caseItem.transportDetails.vehicle
              }}</text>
            </view>
            <view class="detail-item" v-if="caseItem.transportDetails?.driver">
              <text class="detail-label">驾驶员</text>
              <text class="detail-value">{{
                caseItem.transportDetails.driver
              }}</text>
            </view>
            <view class="detail-item" v-if="caseItem.transportDetails?.phone">
              <text class="detail-label">联系电话</text>
              <text class="detail-value">{{
                caseItem.transportDetails.phone
              }}</text>
            </view>
            <!-- è½¬è¿ä¸­æ‰æ˜¾ç¤ºçš„动态信息 -->
            <view
              class="detail-item"
              v-if="
                caseItem.transportDetails &&
                caseItem.transportStatus === 'transporting'
                caseItem.transportStatus === 'transporting' &&
                caseItem.transportDetails?.currentLocation
              "
            >
              <text class="detail-label">当前位置</text>
              <text class="detail-value">{{
                caseItem.transportDetails.currentLocation
              }}</text>
            </view>
            <view
              class="detail-item"
              v-if="
                caseItem.transportStatus === 'transporting' &&
                caseItem.transportDetails?.estimatedTime
              "
            >
              <text class="detail-label">预计到达</text>
              <text class="detail-value">{{
                caseItem.transportDetails.estimatedTime
              }}</text>
            </view>
            <!-- å®ŒæˆåŽæ˜¾ç¤ºçš„信息 -->
            <view
              class="detail-item"
              v-if="
                caseItem.transportStatus === 'completed' &&
                caseItem.transportDetails?.completedTime
              "
            >
              <text class="detail-label">完成时间</text>
              <text class="detail-value">{{
                caseItem.transportDetails.completedTime
              }}</text>
            </view>
          </view>
@@ -170,7 +219,7 @@
          <view class="transport-actions">
            <button
              v-if="
                caseItem.transportStatus === 'not_transported' &&
                !caseItem.transportOrderNo &&
                caseItem.status === 'agreed'
              "
              class="transport-btn primary"
@@ -190,8 +239,11 @@
              éœ€å…ˆåŒæ„æ¡ˆä¾‹
            </button>
            <!-- åªè¦æœ‰è½¬è¿å•号就可以查看详情 -->
            <button
              v-if="caseItem.transportStatus !== 'not_transported'"
              v-if="
                caseItem.transportOrderNo && caseItem.transportOrderNo !== ''
              "
              class="transport-btn secondary"
              @tap.stop="viewTransportDetail(caseItem)"
            >
@@ -207,6 +259,7 @@
            </button>
          </view>
        </view>
        <view class="footer">
          <view class="action-info">
            <text class="label">案例状态</text>
@@ -248,9 +301,14 @@
      </view>
      <!-- ç©ºçŠ¶æ€ -->
      <view class="empty-state" v-if="filteredCases.length === 0">
      <view class="empty-state" v-if="!loading && filteredCases.length === 0">
        <image src="/static/empty/no-case.png" mode="aspectFit" />
        <text>暂无器官捐献案例记录</text>
      </view>
      <!-- åŠ è½½çŠ¶æ€ -->
      <view class="empty-state" v-if="loading">
        <text>数据加载中...</text>
      </view>
    </scroll-view>
@@ -275,70 +333,31 @@
</template>
<script setup>
import { ref, computed } from "vue";
import { ref, computed, onMounted } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { useDict } from "@/utils/dict";
// å“åº”式数据
const dict = ref({});
const genderOptions = ref([]);
const educationOptions = ref([]);
const loading = ref(false);
const cases = ref([]);
const total = ref(0);
const pageNum = ref(1);
const pageSize = ref(10);
const hasMore = ref(true);
const refreshing = ref(false);
// ç»Ÿè®¡æ•°æ®
const stats = ref({
  totalCases: 8,
  readCases: 4,
  agreedCases: 2,
  totalCases: 0,
  readCases: 0,
  agreedCases: 0,
});
// æ’¤å›žç¡®è®¤å¼¹çª—相关
const showWithdrawModal = ref(false);
const currentCase = ref({});
onLoad(async (options) => {
  // èŽ·å–å­—å…¸æ•°æ®
  dict.value = await useDict(
    "sys_IDType",
    "sys_user_sex",
    "sys_BloodType",
    "sys_DiseaseType"
  );
  // åˆå§‹åŒ–选项数据
  initOptions();
  donatebaseinforeportList();
  donateTotal();
});
onShow(() => {
  // è¿™é‡Œå¯ä»¥æ·»åŠ ä»Žè½¬è¿é¡µé¢è¿”å›žæ—¶çš„æ•°æ®åˆ·æ–°é€»è¾‘
  // ä¾‹å¦‚检查本地存储中是否有需要更新的转运状态
  const transportUpdate = uni.getStorageSync("transportStatusUpdate");
  if (transportUpdate) {
    updateTransportStatus(transportUpdate.orderNo, transportUpdate.status);
    uni.removeStorageSync("transportStatusUpdate");
  }
});
// åˆå§‹åŒ–选项数据的方法
const initOptions = () => {
  genderOptions.value = dict.value.sys_IDType || [];
  educationOptions.value = dict.value.sys_user_sex || [];
};
const donatebaseinforeportList = async () => {
  const params = {
    pageNum: pageNum.value,
    pageSize: pageSize.value,
  };
  const res = await uni.$uapi.post("/system/donatebaseinforeport/list", params);
  total.value = res.total;
  console.log(res.records, "11");
};
const donateTotal = async () => {
  const res = await uni.$uapi.post("/system/donatebaseinforeport/getTotal");
  console.log(res);
};
// ç­›é€‰ç›¸å…³
const caseTypes = [
  { label: "全部", value: "all" },
@@ -350,186 +369,7 @@
const currentType = ref("all");
const startDate = ref("");
const endDate = ref("");
// æ¡ˆä¾‹è®°å½•数据 - å¢žåŠ æ›´å¤šæ•°æ®[1,4](@ref)
const cases = ref([
  {
    id: 1,
    donorNo: "DON20240325001",
    hospitalName: "青岛镜湖医院",
    hospitalLogo: "/static/hospital/kiang-wu.jpg",
    caseType: "器官捐献案例",
    donorName: "张三",
    idCardNo: "370203198510123456",
    gender: "男",
    age: 38,
    bloodType: "A型",
    diagnosis: "脑外伤导致脑死亡",
    reportTime: "2024-03-25 09:30",
    reporterName: "李医生",
    status: "agreed",
    statusText: "已同意",
    departmentName: "神经外科",
    contactPerson: "王护士",
    contactPhone: "13800138000",
    redCrossOrg: "青岛市红十字会",
    acquisitionOrg: "青岛市器官获取组织",
    transportStatus: "not_transported", // è½¬è¿çŠ¶æ€ï¼šnot_transported-未转运, transporting-转运中, completed-已完成
    transportOrderNo: "", // è½¬è¿å•号
    transportTime: "", // è½¬è¿æ—¶é—´
    transportDetails: null, // è½¬è¿è¯¦æƒ…
  },
  {
    id: 2,
    donorNo: "DON20240320002",
    hospitalName: "青岛科大医院",
    hospitalLogo: "/static/hospital/must.jpg",
    caseType: "器官捐献案例",
    donorName: "李四",
    idCardNo: "370205197805207890",
    gender: "女",
    age: 45,
    bloodType: "O型",
    diagnosis: "急性心肌梗死",
    reportTime: "2024-03-20 15:00",
    reporterName: "张医生",
    status: "read",
    statusText: "已阅读",
    reviewTime: "2024-03-21 10:15",
    reviewerName: "审核专员A",
    departmentName: "心内科",
    contactPerson: "赵护士",
    contactPhone: "13900139000",
    redCrossOrg: "青岛市红十字会",
    acquisitionOrg: "青岛市器官获取组织",
    transportStatus: "transporting", // è½¬è¿ä¸­
    transportOrderNo: "TR20240321001",
    transportTime: "2024-03-21 14:30",
    transportDetails: {
      driver: "张师傅",
      vehicle: "鲁B12345",
      phone: "13800138000",
      estimatedTime: "2小时",
      currentLocation: "青岛市南区",
    },
  },
  {
    id: 3,
    donorNo: "DON20240318003",
    hospitalName: "青岛大学附属医院",
    hospitalLogo: "/static/hospital/qingda.jpg",
    caseType: "器官捐献案例",
    donorName: "王五",
    idCardNo: "370211197212153214",
    gender: "男",
    age: 51,
    bloodType: "B型",
    diagnosis: "颅内出血",
    reportTime: "2024-03-18 14:20",
    reporterName: "刘医生",
    status: "agreed",
    statusText: "已同意",
    reviewTime: "2024-03-19 09:45",
    reviewerName: "审核专员B",
    departmentName: "神经内科",
    contactPerson: "钱护士",
    contactPhone: "13600136000",
    redCrossOrg: "青岛市红十字会",
    acquisitionOrg: "青岛市器官获取组织",
    transportStatus: "completed", // å·²å®Œæˆ
    transportOrderNo: "TR20240319001",
    transportTime: "2024-03-19 11:20",
    transportDetails: {
      driver: "李师傅",
      vehicle: "鲁B67890",
      phone: "13900139000",
      completedTime: "2024-03-19 13:45",
      distance: "156公里",
    },
  },
  {
    id: 4,
    donorNo: "DON20240315004",
    hospitalName: "青岛市立医院",
    hospitalLogo: "/static/hospital/shili.jpg",
    caseType: "器官捐献案例",
    donorName: "赵六",
    idCardNo: "370205198803274561",
    gender: "女",
    age: 36,
    bloodType: "AB型",
    diagnosis: "多器官功能衰竭",
    reportTime: "2024-03-15 16:40",
    reporterName: "陈医生",
    status: "rejected",
    statusText: "已驳回",
    reviewTime: "2024-03-16 11:20",
    reviewerName: "审核专员C",
    rejectReason: "资料不完整,需补充家属同意书",
    departmentName: "ICU",
    contactPerson: "孙护士",
    contactPhone: "13700137000",
    redCrossOrg: "青岛市红十字会",
    acquisitionOrg: "青岛市器官获取组织",
    transportStatus: "not_transported", // è½¬è¿çŠ¶æ€ï¼šnot_transported-未转运, transporting-转运中, completed-已完成
    transportOrderNo: "", // è½¬è¿å•号
    transportTime: "", // è½¬è¿æ—¶é—´
    transportDetails: null, // è½¬è¿è¯¦æƒ…
  },
  {
    id: 5,
    donorNo: "DON20240310005",
    hospitalName: "青岛眼科医院",
    hospitalLogo: "/static/hospital/yanke.jpg",
    caseType: "角膜捐献案例",
    donorName: "孙七",
    idCardNo: "370203199205187896",
    gender: "男",
    age: 32,
    bloodType: "O型",
    diagnosis: "视网膜母细胞瘤",
    reportTime: "2024-03-10 08:15",
    reporterName: "眼科张医生",
    status: "read",
    statusText: "已阅读",
    reviewTime: "2024-03-11 14:30",
    reviewerName: "审核专员A",
    departmentName: "眼科",
    contactPerson: "周护士",
    contactPhone: "13500135000",
    redCrossOrg: "青岛市红十字会",
    acquisitionOrg: "青岛市眼库",
    transportStatus: "not_transported", // è½¬è¿çŠ¶æ€ï¼šnot_transported-未转运, transporting-转运中, completed-已完成
    transportOrderNo: "", // è½¬è¿å•号
    transportTime: "", // è½¬è¿æ—¶é—´
    transportDetails: null, // è½¬è¿è¯¦æƒ…
  },
  {
    id: 6,
    donorNo: "DON20240305006",
    hospitalName: "青岛儿童医院",
    hospitalLogo: "/static/hospital/children.jpg",
    caseType: "器官捐献案例",
    donorName: "周八",
    idCardNo: "370211201802153248",
    gender: "女",
    age: 6,
    bloodType: "A型",
    diagnosis: "先天性心脏病",
    reportTime: "2024-03-05 11:25",
    reporterName: "儿科李医生",
    status: "reported",
    statusText: "已上报",
    departmentName: "儿科ICU",
    contactPerson: "吴护士",
    contactPhone: "13400134000",
    redCrossOrg: "青岛市红十字会",
    acquisitionOrg: "青岛市器官获取组织",
    transportStatus: "not_transported", // è½¬è¿çŠ¶æ€ï¼šnot_transported-未转运, transporting-转运中, completed-已完成
    transportOrderNo: "", // è½¬è¿å•号
    transportTime: "", // è½¬è¿æ—¶é—´
    transportDetails: null, // è½¬è¿è¯¦æƒ…
  },
]);
// è½¬è¿çŠ¶æ€æ˜ å°„
const transportStatusMap = {
  not_transported: {
@@ -541,16 +381,186 @@
  completed: { text: "已完成", color: "success", class: "completed" },
};
// ç­›é€‰è®°å½•[3](@ref)
// æ•°æ®æ˜ å°„函数
// æ•°æ®æ˜ å°„函数
const mapApiDataToCaseItem = (apiData) => {
  const statusMap = {
    1: { status: "reported", statusText: "已上报" },
    2: { status: "read", statusText: "已阅读" },
    3: { status: "agreed", statusText: "已同意" },
    4: { status: "rejected", statusText: "已驳回" },
  };
  const transportStatusMap = {
    1: "not_transported",
    2: "transporting",
    3: "completed",
    4: "not_transported",
    5: "not_transported",
  };
  const statusInfo = statusMap[apiData.reportStatus] || statusMap["1"];
  // å¤„理转运信息 - æ›´åŠ å¥å£®çš„å¤„ç†
  let transportDetails = null;
  let transportStatus = "not_transported";
  let transportOrderNo = "";
  let transportTime = "";
  // ä¼˜å…ˆä»Ž serviceTransport èŽ·å–è½¬è¿ä¿¡æ¯
  if (
    apiData.serviceTransport &&
    Array.isArray(apiData.serviceTransport) &&
    apiData.serviceTransport.length > 0
  ) {
    const transport = apiData.serviceTransport[0];
    transportDetails = {
      driver: transport.driver || "",
      vehicle: transport.vehicle || "",
      phone: transport.driverPhone || "",
      currentLocation: transport.transportStartPlace || "",
      estimatedTime: transport.estimatedTime || "",
      completedTime: transport.completedTime || "",
    };
    transportStatus =
      transportStatusMap[transport.transitStatus] || "not_transported";
    transportOrderNo = transport.id || "";
    transportTime = transport.transportStartTime || "";
  }
  // å¦‚果直接有转运信息,也进行处理
  if (apiData.transportOrderNo) {
    transportOrderNo = apiData.transportOrderNo;
  }
  if (apiData.transportStartTime) {
    transportTime = apiData.transportStartTime;
  }
  if (apiData.transitStatus) {
    transportStatus =
      transportStatusMap[apiData.transitStatus] || "not_transported";
  }
  return {
    id: apiData.id || apiData.donatebaseinfoReportId,
    donorNo: apiData.caseNo,
    hospitalName: apiData.treatmenthospitalname,
    hospitalLogo: "/static/hospital/default.jpg",
    caseType: "器官捐献案例",
    donorName: apiData.name,
    idCardNo: apiData.idcardno,
    gender:
      dict.value.sys_user_sex?.find((item) => item.dictValue === apiData.sex)
        ?.dictLabel || apiData.sex,
    age: apiData.age,
    bloodType:
      dict.value.sys_BloodType?.find(
        (item) => item.dictValue === apiData.bloodType
      )?.dictLabel || apiData.bloodType,
    diagnosis: apiData.diagnosisname,
    reportTime: apiData.reporttime,
    reporterName: apiData.reportername,
    reviewTime: apiData.reviewTime,
    reviewerName: apiData.reviewerName,
    departmentName: apiData.treatmentdeptname,
    contactPerson: apiData.userName || apiData.contactPerson,
    contactPhone: apiData.phone,
    ...statusInfo,
    transportStatus: transportStatus,
    transportOrderNo: transportOrderNo,
    transportTime: transportTime,
    transportDetails: transportDetails,
  };
};
// ç”Ÿå‘½å‘¨æœŸ
onLoad(async (options) => {
  // èŽ·å–å­—å…¸æ•°æ®
  dict.value = await useDict(
    "sys_IDType",
    "sys_user_sex",
    "sys_BloodType",
    "sys_DiseaseType"
  );
  // åŠ è½½æ•°æ®
  await loadInitialData();
});
onShow(() => {
  const transportUpdate = uni.getStorageSync("transportStatusUpdate");
  if (transportUpdate) {
    updateTransportStatus(transportUpdate.orderNo, transportUpdate.status);
    uni.removeStorageSync("transportStatusUpdate");
  }
});
// æ•°æ®åŠ è½½å‡½æ•°
const loadInitialData = async () => {
  loading.value = true;
  try {
    await Promise.all([donatebaseinforeportList(), donateTotal()]);
  } catch (error) {
    console.error("初始化数据失败:", error);
  } finally {
    loading.value = false;
  }
};
// èŽ·å–æ¡ˆä¾‹åˆ—è¡¨
const donatebaseinforeportList = async () => {
  try {
    const params = {
      pageNum: pageNum.value,
      pageSize: pageSize.value,
    };
    const res = await uni.$uapi.post(
      "/system/donatebaseinforeport/list",
      params
    );
    console.log(res, "res");
    const mappedData = res.map((item) => mapApiDataToCaseItem(item));
    if (pageNum.value === 1) {
      cases.value = mappedData;
    } else {
      cases.value = [...cases.value, ...mappedData];
    }
    hasMore.value = pageNum.value * pageSize.value < res.total;
    total.value = res.total;
  } catch (error) {
    console.error("获取案例列表失败:", error);
    uni.showToast({
      title: "网络请求失败",
      icon: "none",
    });
  }
};
// èŽ·å–ç»Ÿè®¡æ•°æ®
const donateTotal = async () => {
  try {
    const res = await uni.$uapi.post("/system/donatebaseinforeport/getTotal");
    stats.value = {
      totalCases: res["1"] || 0,
      readCases: res["2"] || 0,
      agreedCases: res["3"] || 0,
    };
  } catch (error) {
    console.error("获取统计数据失败:", error);
  }
};
// è®¡ç®—属性:筛选记录
const filteredCases = computed(() => {
  let result = cases.value;
  // çŠ¶æ€ç­›é€‰
  if (currentType.value !== "all") {
    result = result.filter((caseItem) => caseItem.status === currentType.value);
  }
  // æ—¥æœŸç­›é€‰
  if (startDate.value && endDate.value) {
    result = result.filter((caseItem) => {
      const caseDate = caseItem.reportTime.split(" ")[0];
@@ -561,47 +571,11 @@
  return result;
});
// åˆ†é¡µç›¸å…³
const hasMore = ref(true);
const refreshing = ref(false);
// åˆ›å»ºè½¬è¿å•
const createTransportOrder = (caseItem) => {
  console.log(caseItem);
  uni.navigateTo({
    url: `/pages/case/transferinfo?caseId=${caseItem.id}&donorNo=${caseItem.donorNo}`,
  });
};
// æŸ¥çœ‹è½¬è¿è¯¦æƒ…
const viewTransportDetail = (caseItem) => {
  uni.navigateTo({
    url: `/pages/transport/detail?orderNo=${caseItem.transportOrderNo}`,
  });
};
// å®žæ—¶è·Ÿè¸ª
const trackTransport = (caseItem) => {
  uni.navigateTo({
    url: `/pages/transport/track?orderNo=${caseItem.transportOrderNo}`,
  });
};
// æ›´æ–°è½¬è¿çŠ¶æ€ï¼ˆç”¨äºŽä»Žè½¬è¿é¡µé¢è¿”å›žæ—¶åˆ·æ–°æ•°æ®ï¼‰
const updateTransportStatus = (orderNo, newStatus) => {
  const caseItem = cases.value.find(
    (item) => item.transportOrderNo === orderNo
  );
  if (caseItem) {
    caseItem.transportStatus = newStatus;
  }
};
// é€‰æ‹©ç±»åž‹
// äº‹ä»¶å¤„理函数
const selectType = (type) => {
  currentType.value = type;
};
// æ—¥æœŸé€‰æ‹©
const onStartDateChange = (e) => {
  startDate.value = e.detail.value;
};
@@ -610,28 +584,67 @@
  endDate.value = e.detail.value;
};
// æŸ¥çœ‹è¯¦æƒ…
// åˆ·æ–°å’ŒåŠ è½½æ›´å¤š
const onRefresh = async () => {
  refreshing.value = true;
  pageNum.value = 1;
  await donatebaseinforeportList();
  setTimeout(() => {
    refreshing.value = false;
  }, 1000);
};
const onLoadMore = async () => {
  if (!hasMore.value || loading.value) return;
  pageNum.value += 1;
  await donatebaseinforeportList();
};
// å…¶ä»–功能函数(保持原有逻辑)
const createTransportOrder = (caseItem) => {
  uni.navigateTo({
    url: `/pages/case/transferinfo?caseId=${caseItem.id}&donorNo=${caseItem.donorNo}`,
  });
};
const viewTransportDetail = (caseItem) => {
  uni.navigateTo({
    url: `/pages/transport/detail?orderNo=${caseItem.transportOrderNo}`,
  });
};
const trackTransport = (caseItem) => {
  uni.navigateTo({
    url: `/pages/transport/track?orderNo=${caseItem.transportOrderNo}`,
  });
};
const updateTransportStatus = (orderNo, newStatus) => {
  const caseItem = cases.value.find(
    (item) => item.transportOrderNo === orderNo
  );
  if (caseItem) {
    caseItem.transportStatus = newStatus;
  }
};
const viewDetail = (caseItem) => {
  uni.navigateTo({
    url: `/pages/case/detail?id=${caseItem.id}`,
  });
};
// æ’¤å›žæ¡ˆä¾‹[7](@ref)
const withdrawCase = (caseItem) => {
  currentCase.value = caseItem;
  showWithdrawModal.value = true;
};
// ç¡®è®¤æ’¤å›ž
const confirmWithdraw = () => {
  const index = cases.value.findIndex(
    (item) => item.id === currentCase.value.id
  );
  if (index !== -1) {
    // ä»Žåˆ—表中移除已撤回的案例[4](@ref)
    cases.value.splice(index, 1);
    // æ›´æ–°ç»Ÿè®¡æ•°æ®
    stats.value.totalCases -= 1;
    uni.showToast({
      title: "撤回成功",
@@ -641,46 +654,20 @@
  showWithdrawModal.value = false;
};
// å–消撤回
const cancelWithdraw = () => {
  showWithdrawModal.value = false;
};
// æŸ¥çœ‹è¯¦æƒ…
const viewDetails = (caseItem) => {
  uni.navigateTo({
    url: `/pages/case/detail?id=${caseItem.id}`,
  });
};
// ç¼–辑案例
const editCase = (caseItem) => {
  uni.navigateTo({
    url: `/pages/case/edit?id=${caseItem.id}`,
  });
};
// ä¸‹æ‹‰åˆ·æ–°
const onRefresh = () => {
  refreshing.value = true;
  loadCases();
  setTimeout(() => {
    refreshing.value = false;
  }, 1000);
};
// åŠ è½½æ›´å¤š
const onLoadMore = () => {
  if (!hasMore.value) return;
  loadCases();
};
// åŠ è½½è®°å½•
const loadCases = () => {
  // è¿™é‡Œè°ƒç”¨API加载数据
  setTimeout(() => {
    hasMore.value = false;
  }, 1000);
};
</script>
pages/login/Login.vue
@@ -1,7 +1,7 @@
<template>
  <view class="login-container">
    <view class="header">
      <image src="/static/avatar/logo.jpg" class="logo" />
      <image src="/static/avatar/logo.png" class="logo" />
      <text class="hospital-name">青岛OPO</text>
    </view>
@@ -110,8 +110,10 @@
    // åˆ¤æ–­æ˜¯å¦ä¸ºtabBar页面
    const tabBarPages = ["/pages/index/index", "/pages/appointment/index", "/pages/consultation/index", "/pages/my/index"];
    if (tabBarPages.includes(redirects)) {
      console.log(redirects,'预路由1');
      uni.switchTab({ url: redirects });
    } else {
      console.log(redirects,'预路由2');
      uni.redirectTo({ url: redirects });
    }
  } catch (err) {
static/avatar/logo.png
static/avatar/logo1.jpg

static/avatar/yisn.jpg
static/avatar/yisna.jpg
uni_modules/uview-plus/libs/util/route.js
@@ -99,11 +99,15 @@
            })
        }
        if (config.type == 'redirectTo' || config.type == 'redirect') {
            console.log(url,'进入路由');
            uni.redirectTo({
                url
            })
        }
        if (config.type == 'switchTab' || config.type == 'tab') {
            console.log(url,'进入路由');
            uni.switchTab({
                url
            })