WXL
2 天以前 2cc85c64f1c64a2dbaeae276a3e2ca8420de76b7
上报转运调试
已修改9个文件
986 ■■■■■ 文件已修改
components/attachment/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/case/CaseDetails.vue 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/case/CaseInfo.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/case/index.vue 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/case/transfer.vue 226 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/case/transferinfo.vue 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/dict.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/request.js 154 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/attachment/index.vue
@@ -451,6 +451,8 @@
      success: (res) => {
        if (res.statusCode === 200) {
          const data = JSON.parse(res.data);
          console.log(data,'文件');
          if (data.code === 200) {
            resolve({
              ...data,
@@ -509,11 +511,12 @@
        file.status = 'uploading';
        const res = await uploadFile(file.file, 'base');
        Object.assign(file, {
          url: res.fileName,
          fileName: res.fileName,
          url: res.url,
          fileName: res.name,
          newFileName: res.newFileName,
          originalFilename: res.originalFilename,
          status: 'success'
          status: 'success',
          size:res.size
        });
        emit("upload-base", file);
      } catch (error) {
pages.json
@@ -181,6 +181,7 @@
      "path": "pages/case/index",
      "style": {
        "navigationBarTitleText": "我的上报"
      }
    },
    {
@@ -199,6 +200,7 @@
      "path": "pages/case/transfer",
      "style": {
        "navigationBarTitleText": "转运登记"
      }
    },
     {
pages/case/CaseDetails.vue
@@ -822,16 +822,19 @@
// 处理基础附件上传
const handleBaseUpload = (file) => {
  console.log("基础附件上传成功:", file);
  attachments.value.push(file);
};
// 处理其他附件上传
const handleFilesUpdate = (files) => {
  console.log(files, "files");
  attachments.value = files.map((file) => ({
    ...file,
    // 确保只存储半路径
    url: file.url.startsWith("http")
      ? file.url.replace(baseUrlHt, "")
      : file.url,
    // url: file.url.startsWith("http")
    //   ? file.url.replace(baseUrlHt, "")
    //   : file.url,
  }));
};
@@ -963,16 +966,14 @@
  }
  try {
    console.log(attachments.value);
    uni.showLoading({ title: isEditMode.value ? "修改中..." : "提交中..." });
    // 准备提交数据
    const submitData = {
      ...form.value,
      age: parseInt(form.value.age) || 0,
      annexfilesList: attachments.value.map((file) => ({
        url: file.url,
        name: file.name,
        path: file.url,
        fileName: file.name,
        type: file.type,
      })),
      phone: form.value.infophone,
@@ -1032,47 +1033,26 @@
};
const loadCaseData = async (id) => {
  // 模拟加载编辑数据
  // 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(
      `/project/donatebaseinforeport/getInfo/${id}`,
    );
    if (res) {
      console.log(res, "res");
      form.value = res;
      console.log(1, "res");
    if (res.code) {
      form.value = res.data;
      // 处理选择器索引
      // updatePickerIndexes();
      console.log(2, "res");
      // 处理附件
      if (res.annexfilesList) {
        attachments.value = res.annexfilesList;
      if (res.data.annexfilesList) {
        attachments.value = res.data.annexfilesList;
        attachments.value.forEach((item) => {
          item.url = item.path;
          item.name = item.fileName;
        });
      }
      console.log(3, "res");
@@ -1253,6 +1233,16 @@
  display: flex;
  gap: 20rpx;
  margin-top: 40rpx;
  // background: #fff;
  // padding: 20rpx 0;
  // 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;
}
.btn {
@@ -1476,12 +1466,6 @@
  border-radius: 12rpx !important;
  padding: 20rpx 24rpx !important;
  background: #fff !important;
}
.action-buttons {
  display: flex;
  gap: 20rpx;
  margin-top: 40rpx;
}
.btn {
pages/case/CaseInfo.vue
@@ -365,8 +365,8 @@
  try {
    const res = await uni.$uapi.get(`/project/donatebaseinforeport/getInfo/${id}`)
    
    if (res) {
      caseDetail.value = res
    if (res.data) {
      caseDetail.value = res.data
      
      // 如果状态是"已上报"(1),自动更新为"已阅读"(2)
      if (caseDetail.value.reportStatus === '1') {
pages/case/index.vue
@@ -77,7 +77,7 @@
          </view>
          <text
            class="status"
            :class="caseItem.reportStatus.replace('ed', '')"
            :class="getStatusClass(caseItem.reportStatus)"
            >{{ caseItem.statusText }}</text
          >
        </view>
@@ -197,15 +197,6 @@
              创建转运单
            </button>
            <!-- 需转运但未同意 -->
            <!-- <button
              v-if="caseItem.isTransport == '2' && !hasTransport(caseItem)"
              class="transport-btn disabled"
              disabled
            >
              需先同意案例
            </button> -->
            <!-- 有转运单可查看 -->
            <button
              v-if="hasTransport(caseItem)"
@@ -233,7 +224,7 @@
          <view class="actions">
            <button
              class="action-btn"
              v-if="caseItem.reportStatus != '1'"
              v-if="caseItem.reportStatus == '1' || caseItem.reportStatus == '2'"
              @tap.stop="withdrawCase(caseItem)"
            >
              撤回
@@ -243,7 +234,7 @@
            </button>
            <button
              class="action-btn secondary"
              v-if="caseItem.reportStatus != '1'"
              v-if="caseItem.reportStatus == '1' || caseItem.reportStatus == '2'"
              @tap.stop="editCase(caseItem)"
            >
              编辑
@@ -253,19 +244,19 @@
      </view>
      <!-- 加载更多 -->
      <view class="load-more" v-if="hasMore">
        <text>加载中...</text>
      <view class="load-more" v-if="loadingMore">
        <u-loading-icon text="加载中..."></u-loading-icon>
      </view>
      <!-- 无更多数据 -->
      <view class="no-more" v-if="!hasMore && filteredCases.length > 0">
        <text>已加载全部数据</text>
      </view>
      <!-- 空状态 -->
      <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>
@@ -286,17 +277,25 @@
        </view>
      </view>
    </view>
    <!-- 加载状态 -->
    <u-loading-page v-if="initLoading" :loading="true" text="数据加载中..." />
  </view>
   <view class="float-button" @tap="createNewCase">
    <u-icon name="plus" color="#fff" size="24"></u-icon>
  </view>
</template>
<script setup>
import { ref, computed, onMounted } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { onLoad, onShow, onReachBottom } from "@dcloudio/uni-app";
import { useDict } from "@/utils/dict";
// 响应式数据
const dict = ref({});
const loading = ref(false);
const initLoading = ref(false);
const loadingMore = ref(false);
const cases = ref([]);
const total = ref(0);
const pageNum = ref(1);
@@ -324,22 +323,6 @@
  };
  const statusInfo = statusMap[apiData.reportStatus] || statusMap["1"];
  // 转运状态映射
  const getTransportStatusClass = (caseData) => {
    if (!hasTransport(caseData)) {
      return "not-transported";
    }
    const status = getTransportStatus(caseData);
    const map = {
      1: "pending",
      2: "transporting",
      3: "completed",
      4: "cancelled",
      5: "draft",
    };
    return map[status] || "pending";
  };
  return {
    id: apiData.id || apiData.donatebaseinfoReportId,
@@ -393,13 +376,16 @@
};
// 转运判断方法 - 与PC端保持一致
const hasTransport = (caseData) => {
  console.log(caseData.serviceTransport);
  return caseData.serviceTransport && caseData.serviceTransport.length > 0;
};
const createNewCase = () => {
  uni.navigateTo({
    url: '/pages/case/CaseDetails'
  });
};
const canCreateTransport = (caseData) => {
  return (
    (caseData.isTransport != "4" || caseData.isTransport == 2) && // 需要转运
    (caseData.isTransport == "2" || caseData.isTransport == 2) && // 需要转运
    !hasTransport(caseData)
  ); // 无转运单
};
@@ -490,7 +476,15 @@
    url: `/pages/case/transferinfo?caseId=${caseItem.id}&caseNo=${caseItem.donorNo}`,
  });
};
const getStatusClass = (status) => {
  const map = {
    '1': 'reported',     // 已上报
    '2': 'read',         // 已阅读
    '3': 'agreed',       // 已同意
    '4': 'rejected'      // 已驳回
  };
  return map[status] || 'reported';
}
const viewTransportDetail = (caseItem) => {
  if (hasTransport(caseItem)) {
    const transport = caseItem.serviceTransport[0];
@@ -502,7 +496,7 @@
// 筛选相关
const caseTypes = [
  { label: "全部", value: "" },
  { label: "全部", value: "all" },
  { label: "已上报", value: "1" },
  { label: "已阅读", value: "2" },
  { label: "已同意", value: "3" },
@@ -511,17 +505,6 @@
const currentType = ref("all");
const startDate = ref("");
const endDate = ref("");
// 转运状态映射
const transportStatusMap = {
  not_transported: {
    text: "未转运",
    color: "warning",
    class: "not-transported",
  },
  transporting: { text: "转运中", color: "primary", class: "transporting" },
  completed: { text: "已完成", color: "success", class: "completed" },
};
// 生命周期
onLoad(async (options) => {
@@ -537,6 +520,12 @@
  await loadInitialData();
});
onReachBottom(() => {
  if (hasMore.value && !loading.value && !loadingMore.value) {
    onLoadMore();
  }
});
onShow(() => {
  const transportUpdate = uni.getStorageSync("transportStatusUpdate");
  if (transportUpdate) {
@@ -547,46 +536,90 @@
// 数据加载函数
const loadInitialData = async () => {
  loading.value = true;
  initLoading.value = true;
  try {
    await Promise.all([donatebaseinforeportList(), donateTotal()]);
    await Promise.all([donatebaseinforeportList(true), donateTotal()]);
  } catch (error) {
    console.error("初始化数据失败:", error);
  } finally {
    loading.value = false;
    initLoading.value = false;
  }
};
// 获取案例列表
const donatebaseinforeportList = async () => {
const donatebaseinforeportList = async (reset = false) => {
  if (reset) {
    pageNum.value = 1;
    hasMore.value = true;
  }
  if (loading.value || loadingMore.value || !hasMore.value) return;
  if (reset) {
    loading.value = true;
  } else {
    loadingMore.value = true;
  }
  try {
    const params = {
      pageNum: pageNum.value,
      pageSize: pageSize.value,
    };
    // 筛选条件 - 如果选择了"全部",不传reportStatus参数
    if (currentType.value !== "all") {
      params.reportStatus = currentType.value;
    }
    // 时间筛选条件
    if (startDate.value && endDate.value) {
      // 将时间格式转换为接口需要的格式
      params.startDate = startDate.value;
      params.endDate = endDate.value;
    }
    console.log("请求参数:", params);
    const res = await uni.$uapi.post(
      "/project/donatebaseinforeport/list",
      params,
    );
    console.log(res, "res");
    const mappedData = res.map((item) => mapApiDataToCaseItem(item));
    if (pageNum.value == 1) {
      cases.value = mappedData;
    console.log("接口返回数据:", res);
    if (res.code == 200) {
      const data =res.data || [];
      const mappedData = data.map((item) => mapApiDataToCaseItem(item));
      if (reset || pageNum.value == 1) {
        console.log(mappedData,'mappedData');
        cases.value = mappedData;
      } else {
        cases.value = [...cases.value, ...mappedData];
      }
      // 判断是否还有更多数据
      hasMore.value = data.length >= pageSize.value;
      total.value = res.total || 0;
      // 如果有数据加载成功,增加页码
      if (data.length > 0) {
        pageNum.value++;
      }
    } else {
      cases.value = [...cases.value, ...mappedData];
      throw new Error(res.msg || "加载失败");
    }
    hasMore.value = pageNum.value * pageSize.value < res.total;
    total.value = res.total;
  } catch (error) {
    console.error("获取案例列表失败:", error);
    uni.showToast({
      title: "网络请求失败",
      icon: "none",
    });
  } finally {
    loading.value = false;
    loadingMore.value = false;
  }
};
@@ -594,62 +627,73 @@
const donateTotal = async () => {
  try {
    const res = await uni.$uapi.post("/project/donatebaseinforeport/getTotal");
    stats.value = {
      totalCases: res["all"] || 0,
      readCases: res["2"] || 0,
      agreedCases: res["3"] || 0,
    };
    if (res.code === 200) {
      stats.value = {
        totalCases: res.data["all"] || 0,
        readCases: res.data["2"] || 0,
        agreedCases: res.data["3"] || 0,
      };
    } else {
      throw new Error(res.msg || "获取统计失败");
    }
  } catch (error) {
    console.error("获取统计数据失败:", error);
  }
};
// 计算属性:筛选记录
// 计算属性:筛选记录 - 现在只做前端状态筛选
const filteredCases = computed(() => {
  let result = cases.value;
  if (currentType.value !== "all") {
    result = result.filter(
      (caseItem) => caseItem.reportStatus == currentType.value,
    );
  }
  if (startDate.value && endDate.value) {
    result = result.filter((caseItem) => {
      const caseDate = caseItem.reportTime.split(" ")[0];
      return caseDate >= startDate.value && caseDate <= endDate.value;
    });
  }
  // 只做前端状态筛选,时间筛选已经通过接口实现
  // if (currentType.value !== "all") {
  //   result = result.filter(
  //     (caseItem) => caseItem.reportStatus == currentType.value,
  //   );
  // }
  return result;
});
// 事件处理函数
const selectType = (type) => {
  currentType.value = type;
  // 重置分页,重新加载数据
  pageNum.value = 1;
  donatebaseinforeportList(true);
};
const onStartDateChange = (e) => {
  startDate.value = e.detail.value;
  // 如果结束日期已选择,立即触发筛选
  if (endDate.value) {
    pageNum.value = 1;
    donatebaseinforeportList(true);
  }
};
const onEndDateChange = (e) => {
  endDate.value = e.detail.value;
  // 如果开始日期已选择,立即触发筛选
  if (startDate.value) {
    pageNum.value = 1;
    donatebaseinforeportList(true);
  }
};
// 刷新和加载更多
const onRefresh = async () => {
  refreshing.value = true;
  pageNum.value = 1;
  await donatebaseinforeportList();
  await donatebaseinforeportList(true);
  await donateTotal();
  setTimeout(() => {
    refreshing.value = false;
  }, 1000);
};
const onLoadMore = async () => {
  if (!hasMore.value || loading.value) return;
  pageNum.value += 1;
  if (!hasMore.value || loading.value || loadingMore.value) return;
  await donatebaseinforeportList();
};
@@ -677,19 +721,36 @@
  showWithdrawModal.value = true;
};
const confirmWithdraw = () => {
  const index = cases.value.findIndex(
    (item) => item.id == currentCase.value.id,
  );
  if (index !== -1) {
    cases.value.splice(index, 1);
    stats.value.totalCases -= 1;
const confirmWithdraw = async () => {
  try {
    const res = await uni.$uapi.delete(`/project/donatebaseinforeport/${currentCase.value.id}`);
    if (res.code === 200) {
      // 从列表中移除
      const index = cases.value.findIndex(
        (item) => item.id == currentCase.value.id
      );
      if (index !== -1) {
        cases.value.splice(index, 1);
        stats.value.totalCases -= 1;
      }
      uni.showToast({
        title: "撤回成功",
        icon: "success",
      });
    } else {
      throw new Error(res.msg || "撤回失败");
    }
  } catch (error) {
    console.error("撤回失败:", error);
    uni.showToast({
      title: "撤回成功",
      icon: "success",
      title: "撤回失败,请重试",
      icon: "none",
    });
  } finally {
    showWithdrawModal.value = false;
  }
  showWithdrawModal.value = false;
};
const cancelWithdraw = () => {
@@ -1070,6 +1131,14 @@
      }
    }
  }
  .load-more, .no-more {
    text-align: center;
    padding: 40rpx 0;
    color: $text-secondary;
    font-size: 28rpx;
  }
  .empty-state {
    padding: 120rpx 0;
    text-align: center;
@@ -1154,4 +1223,25 @@
    }
  }
}
</style>
/* 浮动按钮样式 */
.float-button {
  position: fixed;
  bottom: 120rpx;
  right: 40rpx;
  width: 100rpx;
  height: 100rpx;
  background: $primary-color;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8rpx 20rpx rgba($primary-color, 0.3);
  z-index: 100;
  transition: all 0.3s ease;
  &:active {
    opacity: 0.8;
    transform: scale(0.95);
  }
}
</style>
pages/case/transfer.vue
@@ -24,7 +24,7 @@
        <text
          v-for="(type, index) in transportTypes"
          :key="index"
          :class="{ active: currentType === type.value }"
          :class="{ active: currentType == type.value }"
          @tap="selectType(type.value)"
          >{{ type.label }}</text
        >
@@ -131,7 +131,7 @@
            </button>
            <button
              class="action-btn secondary"
              v-if="item.transitStatus === 1 || item.transitStatus === 5"
              v-if="item.transitStatus != 3"
              @tap.stop="editTransport(item)"
            >
              编辑
@@ -144,8 +144,13 @@
      </view>
      <!-- 加载更多 -->
      <view class="load-more" v-if="loading">
      <view class="load-more" v-if="loadingMore">
        <u-loading-icon text="加载中..."></u-loading-icon>
      </view>
      <!-- 无更多数据 -->
      <view class="no-more" v-if="!hasMore && filteredTransports.length > 0">
        <text>已加载全部数据</text>
      </view>
      <!-- 空状态 -->
@@ -176,12 +181,20 @@
        </view>
      </view>
    </view>
    <!-- 加载状态 -->
    <u-loading-page v-if="initLoading" :loading="true" text="数据加载中..." />
    <!-- 浮动按钮 -->
    <!-- <view class="float-button" @tap="createTransport">
      <u-icon name="plus" color="#fff" size="24"></u-icon>
    </view> -->
  </view>
</template>
<script setup>
import { ref, computed, onMounted } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { onLoad, onShow, onReachBottom } from "@dcloudio/uni-app";
import { useDict } from "@/utils/dict";
// 字典数据
@@ -197,6 +210,8 @@
// 转运单数据
const transports = ref([]);
const loading = ref(false);
const initLoading = ref(false);
const loadingMore = ref(false);
const refreshing = ref(false);
const hasMore = ref(true);
const pageNum = ref(1);
@@ -225,9 +240,13 @@
  // 获取字典数据
  dict.value = await useDict("sys_user_sex");
  // 初始化数据
  loadTransports();
  // 加载统计数据
  loadStats();
  await loadInitialData();
});
onReachBottom(() => {
  if (hasMore.value && !loading.value && !loadingMore.value) {
    onLoadMore();
  }
});
onShow(() => {
@@ -264,25 +283,15 @@
  };
};
// 筛选记录
// 计算属性:筛选记录
const filteredTransports = computed(() => {
  let result = transports.value;
  // 状态筛选
  // 前端状态筛选
  if (currentType.value !== "") {
    result = result.filter((item) => item.transitStatus === currentType.value);
  }
  // 日期筛选
  if (startDate.value && endDate.value) {
    result = result.filter((item) => {
      const transportDate = item.createTime
        ? item.createTime.split(" ")[0]
        : "";
      return transportDate >= startDate.value && transportDate <= endDate.value;
    });
  }
  return result;
});
@@ -329,15 +338,28 @@
// 选择类型
const selectType = (type) => {
  currentType.value = type;
  // 重置分页,重新加载数据
  pageNum.value = 1;
  loadTransports(true);
};
// 日期选择
const onStartDateChange = (e) => {
  startDate.value = e.detail.value;
  // 如果结束日期已选择,立即触发筛选
  if (endDate.value) {
    pageNum.value = 1;
    loadTransports(true);
  }
};
const onEndDateChange = (e) => {
  endDate.value = e.detail.value;
  // 如果开始日期已选择,立即触发筛选
  if (startDate.value) {
    pageNum.value = 1;
    loadTransports(true);
  }
};
// 查看详情
@@ -348,14 +370,21 @@
// 查看详情
const viewDetails = (item) => {
  uni.navigateTo({
    url: `/pages/transport/detail?id=${item.id}`,
    url: `/pages/case/transferinfo?id=${item.id}`,
  });
};
// 编辑转运单
const editTransport = (item) => {
  uni.navigateTo({
    url: `/pages/transport/detail?id=${item.id}&edit=true`,
    url: `/pages/case/transferinfo?id=${item.id}&edit=true`,
  });
};
// 创建转运单
const createTransport = () => {
  uni.navigateTo({
    url: '/pages/transport/create'
  });
};
@@ -404,59 +433,105 @@
  showActionModal.value = false;
};
// 数据加载函数
const loadInitialData = async () => {
  initLoading.value = true;
  try {
    await Promise.all([loadTransports(true), loadStats()]);
  } catch (error) {
    console.error("初始化数据失败:", error);
  } finally {
    initLoading.value = false;
  }
};
// 下拉刷新
const onRefresh = () => {
const onRefresh = async () => {
  refreshing.value = true;
  pageNum.value = 1;
  loadTransports();
  loadStats();
  await loadTransports(true);
  await loadStats();
  setTimeout(() => {
    refreshing.value = false;
  }, 1000);
};
// 加载更多
const onLoadMore = () => {
  if (!hasMore.value || loading.value) return;
  pageNum.value++;
  loadTransports();
const onLoadMore = async () => {
  if (!hasMore.value || loading.value || loadingMore.value) return;
  await loadTransports();
};
// 加载记录
const loadTransports = async () => {
  if (loading.value) return;
  loading.value = true;
// 加载转运单列表
const loadTransports = async (reset = false) => {
  if (reset) {
    pageNum.value = 1;
    hasMore.value = true;
  }
  if (loading.value || loadingMore.value || !hasMore.value) return;
  if (reset) {
    loading.value = true;
  } else {
    loadingMore.value = true;
  }
  try {
    const params = {
      pageNum: pageNum.value,
      pageSize: pageSize.value,
    };
    if (currentType.value) {
    // 筛选条件
    if (currentType.value !== "") {
      params.transitStatus = currentType.value;
    }
    // 时间筛选条件
    if (startDate.value && endDate.value) {
      params.startDate = startDate.value;
      params.endDate = endDate.value;
    }
    console.log("请求参数:", params);
    const res = await uni.$uapi.post("/project/transport/list", params);
    if (res) {
      const data = res || [];
    console.log("接口返回数据:", res);
    if (res && res.code === 200) {
      // 处理不同的数据结构
      let data = [];
      if (res.rows && Array.isArray(res.rows)) {
        data = res.rows;
      } else if (res.data && Array.isArray(res.data)) {
        data = res.data;
      } else if (Array.isArray(res)) {
        data = res;
      } else {
        console.error("接口返回格式不正确:", res);
        throw new Error("接口返回格式不正确");
      }
      const mappedData = data.map((item) => mapApiDataToTransportItem(item));
      if (pageNum.value === 1) {
      if (reset || pageNum.value === 1) {
        transports.value = mappedData;
      } else {
        transports.value = [...transports.value, ...mappedData];
      }
      hasMore.value = (res.rows || []).length >= pageSize.value;
      // 判断是否还有更多数据
      hasMore.value = data.length >= pageSize.value;
      // 如果有数据加载成功,增加页码
      if (data.length > 0) {
        pageNum.value++;
      }
    } else {
      throw new Error(res.msg || "加载失败");
      throw new Error(res?.msg || "加载失败");
    }
  } catch (error) {
    console.error("加载转运单列表失败:", error);
@@ -466,6 +541,7 @@
    });
  } finally {
    loading.value = false;
    loadingMore.value = false;
  }
};
@@ -473,11 +549,12 @@
const loadStats = async () => {
  try {
    const res = await uni.$uapi.post("/api/totalServiceTransportState");
    if (res) {
    if (res.data) {
      stats.value = {
        totalTransports: res.reduce((sum, item) => sum + item.count, 0) || 0,
        pendingTransports: res[0].count || 0,
        completedTransports: res[3].count || 0,
        totalTransports:
          res.data.reduce((sum, item) => sum + item.count, 0) || 0,
        pendingTransports: res.data[0].count || 0,
        completedTransports: res.data[3].count || 0,
      };
    }
  } catch (error) {
@@ -492,10 +569,10 @@
      id: currentTransport.value.id,
      transitStatus: newStatus,
    };
    const res = await uni.$uapi.post("/project/transport/edit", updateData);
    if (res.code === 200) {
    if (res && res.code === 200) {
      // 更新本地数据
      const index = transports.value.findIndex(
        (item) => item.id === currentTransport.value.id,
@@ -503,19 +580,19 @@
      if (index !== -1) {
        transports.value[index].transitStatus = newStatus;
      }
      // 更新统计数据
      await loadStats();
      // 存储状态更新
      uni.setStorageSync("transportStatusUpdate", {
        orderId: currentTransport.value.id,
        status: newStatus,
      });
      return true;
    } else {
      throw new Error(res.msg || `${actionName}失败`);
      throw new Error(res?.msg || `${actionName}失败`);
    }
  } catch (error) {
    console.error(`${actionName}失败:`, error);
@@ -535,10 +612,10 @@
</script>
<style lang="scss" scoped>
/* 保持原有样式完全不变 */
.transport-record {
  min-height: 100vh;
  background: linear-gradient(135deg, #fafdff 0%, #e3f0ff 100%);
  padding-bottom: 100rpx;
  .stats-card {
    margin: 20rpx;
@@ -809,6 +886,13 @@
      }
    }
    .load-more, .no-more {
      text-align: center;
      padding: 40rpx 0;
      color: #86868b;
      font-size: 28rpx;
    }
    .empty-state {
      padding: 120rpx 0;
      text-align: center;
@@ -901,5 +985,27 @@
      }
    }
  }
  /* 浮动按钮 */
  .float-button {
    position: fixed;
    bottom: 100rpx;
    right: 40rpx;
    width: 100rpx;
    height: 100rpx;
    background: #0071e3;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 8rpx 20rpx rgba(0, 113, 227, 0.3);
    z-index: 100;
    transition: all 0.3s ease;
    &:active {
      opacity: 0.8;
      transform: scale(0.95);
    }
  }
}
</style>
</style>
pages/case/transferinfo.vue
@@ -4,7 +4,7 @@
    <scroll-view scroll-y class="form-scroll" :show-scrollbar="false">
      <view class="form-content">
        <!-- 案例信息卡片 -->
        <view class="form-section" v-if="transportData.caseNo">
        <view class="form-section" v-if="transportData.reportId">
          <view class="section-header">
            <view class="section-icon">📋</view>
            <text class="section-title">关联案例信息</text>
@@ -54,7 +54,7 @@
          </view>
          <view class="form-grid">
            <view class="form-item">
            <!-- <view class="form-item">
              <text class="item-label">转运单号</text>
              <u-input
                v-model="transportData.reportId"
@@ -63,7 +63,7 @@
                :disabledColor="disabledColor"
                border="none"
              />
            </view>
            </view> -->
            <view class="form-item">
              <text class="item-label required">出发地点</text>
@@ -249,47 +249,6 @@
          </view>
        </view>
        <!-- 附件信息 -->
        <view
          class="form-section"
          v-if="
            transportData.annexfilesList &&
            transportData.annexfilesList.length > 0
          "
        >
          <view class="section-header">
            <view class="section-icon">📎</view>
            <text class="section-title"
              >附件信息 ({{ transportData.annexfilesList.length }})</text
            >
          </view>
          <view class="attachment-list">
            <view
              v-for="(file, index) in transportData.annexfilesList"
              :key="index"
              class="attachment-item"
              @click="previewFile(file)"
            >
              <view class="file-info">
                <u-icon
                  name="file-text"
                  color="#409EFF"
                  size="20"
                  class="file-icon"
                ></u-icon>
                <view class="file-details">
                  <text class="file-name">{{ file.fileName }}</text>
                  <text class="file-time">{{
                    formatTime(file.createTime)
                  }}</text>
                </view>
              </view>
              <u-icon name="arrow-right" color="#999" size="16"></u-icon>
            </view>
          </view>
        </view>
        <!-- 备注信息 -->
        <view class="form-section">
          <view class="section-header">
@@ -334,9 +293,9 @@
            >
              完成转运
            </u-button>
            <u-button v-if="canDelete" class="btn error" @click="handleDelete">
            <!-- <u-button v-if="canDelete" class="btn error" @click="handleDelete">
              删除
            </u-button>
            </u-button> -->
          </template>
          <!-- 编辑模式 -->
@@ -354,7 +313,15 @@
        </view>
      </view>
    </scroll-view>
    <attachment-upload
      ref="attachment"
      :files="attachments"
      :readonly="isReadonly"
      :maxCount="5"
      @update:files="handleFilesUpdate"
      @upload-base="handleBaseUpload"
      @preview="handlePreview"
    />
    <!-- 时间选择器 -->
    <u-datetime-picker
      :show="showTimePicker"
@@ -372,13 +339,18 @@
<script setup>
import { ref, computed, onMounted } from "vue";
import attachmentUpload from "@/components/attachment";
import { useUserStore } from "@/stores/user";
import { onLoad } from "@dcloudio/uni-app";
// 字典数据
const dict = ref({});
// 数据
const transportData = ref({});
const transportData = ref({
  annexfilesList: [], // 附件文件地址集合
});
const loading = ref(false);
const saving = ref(false);
const isEdit = ref(false);
@@ -386,6 +358,9 @@
const showTimePicker = ref(false);
const departureTimeValue = ref(0);
const statusSelectVisible = ref(false);
const isReadonly = ref(false);
const attachments = ref([]);
// 字典选项
const statusOptions = ref([
@@ -459,10 +434,18 @@
const loadTransportData = async (id) => {
  loading.value = true;
  try {
    const res = await uni.$uapi.post(`/project/transport/list`, { id: id });
    const res = await uni.$uapi.get(`/project/transport/getInfo/${id}`);
    if (res) {
      transportData.value = res[0];
    if (res.data) {
      transportData.value = res.data;
        if (res.data.annexfilesList) {
           attachments.value = res.data.annexfilesList;
        attachments.value.forEach(item=>{
          item.url=item.opath,
          item.name=item.fileName
        })
        }
    } else {
      throw new Error(res.msg || "数据加载失败");
    }
@@ -482,26 +465,28 @@
  try {
    // 如果有案例ID,加载案例信息
    if (caseId) {
      const res = await uni.$uapi.post(
        `/project/transport/list`,
        { id: caseId },
      const res = await uni.$uapi.get(
        `/project/donatebaseinforeport/getInfo/${caseId}`,
      );
      if (res) {
        const caseData = res[0];
      if (res.data) {
        const caseData = res.data;
        console.log(caseData);
        transportData.value = {
          ...transportData.value,
          caseNo: caseNo || caseData.caseNo,
          patName: caseData.patName,
          sex: caseData.sex,
          age: caseData.age,
          diagnosisname: caseData.diagnosisname,
          treatmentHospitalName: caseData.treatmenthospitalname,
          transitStatus: 1, // 待转运
          createTime: new Date()
            .toISOString()
            .replace("T", " ")
            .substring(0, 19),
          caseNo: caseData.caseNo || "",
          patName: caseData.name || "",
          sex: caseData.sex || "",
          age: caseData.age || "",
          reportId: caseData.id || null,
          diagnosisname: caseData.diagnosisname || "",
          treatmentHospitalName: caseData.treatmenthospitalname || "",
          treatmentDeptName: caseData.treatmentdeptname || "",
          // 可以设置一些默认值
          transportStartPlace: caseData.treatmenthospitalname || "",
          contactPerson: caseData.coordinatorName || "",
        };
        console.log(transportData.value, "transportData.value");
      }
    } else {
      transportData.value = {
@@ -560,6 +545,7 @@
// 时间确认
const onTimeConfirm = (e) => {
  const date = new Date(e.value);
  // 添加秒的部分
  transportData.value.transportStartTime = `${date.getFullYear()}-${(
    date.getMonth() + 1
  )
@@ -567,7 +553,11 @@
    .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")} ${date
    .getHours()
    .toString()
    .padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
    .padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date
    .getSeconds()
    .toString()
    .padStart(2, "0")}`; // 添加秒
  showTimePicker.value = false;
};
@@ -641,10 +631,18 @@
  saving.value = true;
  try {
    // const submitData = {
    //   ...transportData.value,
    // };
    const submitData = {
      ...transportData.value,
      annexfilesList: attachments.value.map((file) => ({
        path: file.url,
        fileName: file.name,
        type: file.type,
      })),
    };
    let res;
    if (transportId.value) {
      // 修改
@@ -737,9 +735,15 @@
    const updateData = {
      id: transportId.value,
      transitStatus: newStatus,
      ...transportData.value,
       annexfilesList: attachments.value.map((file) => ({
        path: file.url,
        fileName: file.name,
        type: file.type,
      })),
    };
    const res = await uni.$uapi.post("/system/transport/edit", updateData);
    const res = await uni.$uapi.post("/project/transport/edit", updateData);
    if (res.code === 200) {
      uni.showToast({ title: `${actionName}成功`, icon: "success" });
@@ -753,32 +757,63 @@
  }
};
// 删除转运单
const handleDelete = () => {
  uni.showModal({
    title: "确认删除",
    content: `确定要删除转运单 ${transportData.value.reportId} 吗?`,
    success: async (res) => {
      if (res.confirm) {
        try {
          const result = await uni.$uapi.delete(
            `/system/transport/${transportId.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 handleBaseUpload = (file) => {
  console.log("基础附件上传成功:", file);
  attachments.value.push(file);
};
// 处理其他附件上传
const handleFilesUpdate = (files) => {
  console.log(files,'files');
  attachments.value = files.map((file) => ({
    ...file,
    // 确保只存储半路径
    // url: file.url.startsWith("http")
    //   ? file.url.replace(baseUrlHt, "")
    //   : file.url,
  }));
  console.log(attachments.value);
};
// 预览文件 - 修改为使用完整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" });
  }
};
</script>
@@ -1006,7 +1041,7 @@
  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;
  z-index: 9;
}
.btn {
utils/dict.js
@@ -16,7 +16,7 @@
    } else {
      try {
        const resp = await uni.$uapi.get("/system/dict/data/type/" + dictType);
        res.value[dictType] = resp.map(p => ({
        res.value[dictType] = resp.data.map(p => ({
          label: p.dictLabel,
          value: p.dictValue,
          elTagType: p.listClass,
utils/request.js
@@ -6,35 +6,34 @@
 */
const showToast = (message) => {
  if (uni.$u?.toast) {
    uni.$u.toast(message)
    uni.$u.toast(message);
  } else {
    uni.showToast({
      title: message,
      icon: 'none'
    })
      icon: "none",
    });
  }
}
};
// 基础配置 - 从环境变量中获取baseURL
const config = {
  baseURL: '/api', // 使用环境变量中的配置
  baseURL: "/api", // 使用环境变量中的配置
  timeout: 60000,
  header: {
    'Content-Type': 'application/json',
    'X-Business-System': 'medical-system',
    "Content-Type": "application/json",
    "X-Business-System": "medical-system",
  },
  // 新增配置项
  loginPage: '/pages/login/Login', // 登录页路径
  loginPage: "/pages/login/Login", // 登录页路径
  tokenExpiredCode: 401, // 后端返回的Token过期状态码
  noPermissionCode: 403, // 无权限状态码
  // 添加白名单配置(不需要校验token的接口)
  whiteList: [
    '/login', // 登录接口
    "/login", // 登录接口
    // '/dingtalk/auth/login', // 钉钉授权登录接口
    // '/dingtalk/auth/bind' // 钉钉授权登录绑定接口
  ]
}
  ],
};
/**
 * 检查请求是否在白名单中
@@ -42,33 +41,36 @@
 */
const isInWhiteList = (url) => {
  // 提取相对路径(移除baseURL)并忽略查询参数
  const relativeUrl = url.replace(config.baseURL, '').split('?')[0];
  return config.whiteList.some(path => {
  const relativeUrl = url.replace(config.baseURL, "").split("?")[0];
  return config.whiteList.some((path) => {
    // 处理通配符情况
    if (path.endsWith('/')) {
      return relativeUrl.startsWith(path)
    if (path.endsWith("/")) {
      return relativeUrl.startsWith(path);
    }
    // 精确匹配路径
    return relativeUrl === path
  })
}
    return relativeUrl === path;
  });
};
/**
 * 跳转到登录页
 */
const navigateToLogin = () => {
  uni.redirectTo({
    url: config.loginPage + '?redirect=' + encodeURIComponent(getCurrentPagePath())
  })
}
    url:
      config.loginPage +
      "?redirect=" +
      encodeURIComponent(getCurrentPagePath()),
  });
};
/**
 * 获取当前页面路径(用于登录后跳回)
 */
const getCurrentPagePath = () => {
  const pages = getCurrentPages()
  return pages[pages.length - 1]?.route || ''
}
  const pages = getCurrentPages();
  return pages[pages.length - 1]?.route || "";
};
/**
 * 检查Token是否过期
@@ -76,72 +78,72 @@
 */
const checkTokenExpired = (statusCode) => {
  if (statusCode === config.tokenExpiredCode) {
    showToast('登录已过期,请重新登录')
    navigateToLogin()
    return true
    showToast("登录已过期,请重新登录");
    navigateToLogin();
    return true;
  }
  return false
}
  return false;
};
/**
 * 请求拦截器
 */
const requestInterceptor = (options) => {
  const token = uni.getStorageSync('token')
  const token = uni.getStorageSync("token");
  // 如果请求在白名单中,直接放行
  if (isInWhiteList(options.url)) {
    return options
    return options;
  }
  // 如果未登录且不是白名单接口,跳转登录
  // console.log(token,'token');
  if (!token) {
    navigateToLogin()
    throw new Error('未登录')
    navigateToLogin();
    throw new Error("未登录");
  }
  // 添加Token到Header
  options.header = {
    ...options.header,
    'Authorization': `Bearer ${token}`
  }
  return options
}
    Authorization: `Bearer ${token}`,
  };
  return options;
};
/**
 * 响应拦截器
 */
const responseInterceptor = (response) => {
  const { statusCode, data } = response
  const { statusCode, data } = response;
  // Token过期处理
  if (checkTokenExpired(statusCode)) {
    return Promise.reject(data)
    return Promise.reject(data);
  }
  // 无权限处理
  if (statusCode === config.noPermissionCode) {
    showToast('无权限访问')
    return Promise.reject(data)
    showToast("无权限访问");
    return Promise.reject(data);
  }
  // 其他错误状态码
  if (statusCode !== 200) {
    showToast(`请求失败: ${statusCode}`)
    return Promise.reject(data)
    showToast(`请求失败: ${statusCode}`);
    return Promise.reject(data);
  }
  // 接口自定义错误码处理(假设data.code为0表示成功)
  if (data?.code !== 200) {
    showToast(data?.msg || '操作失败')
    return Promise.reject(data)
    showToast(data?.msg || "操作失败");
    return Promise.reject(data);
  }
  return data.data || data // 返回实际业务数据
}
  return data; // 返回实际业务数据
};
/**
 * 核心请求方法
@@ -153,58 +155,58 @@
      ...config,
      ...options,
      url: config.baseURL + options.url,
      header: { ...config.header, ...options.header }
    }
      header: { ...config.header, ...options.header },
    };
    // 请求拦截
    options = await requestInterceptor(options)
    options = await requestInterceptor(options);
    // 发起请求
    return new Promise((resolve, reject) => {
      uni.request({
        ...options,
        success: (res) => {
          resolve(responseInterceptor(res))
          resolve(responseInterceptor(res));
        },
        fail: (err) => {
          showToast('网络错误,请重试')
          reject(err)
        }
      })
    })
          showToast("网络错误,请重试");
          reject(err);
        },
      });
    });
  } catch (err) {
    return Promise.reject(err)
    return Promise.reject(err);
  }
}
};
// 快捷方法封装
const http = {
  get(url, data = {}, options = {}) {
    return request({ url, data, method: 'GET', ...options })
    return request({ url, data, method: "GET", ...options });
  },
  post(url, data = {}, options = {}) {
    return request({ url, data, method: 'POST', ...options })
    return request({ url, data, method: "POST", ...options });
  },
  put(url, data = {}, options = {}) {
    return request({ url, data, method: 'PUT', ...options })
    return request({ url, data, method: "PUT", ...options });
  },
  delete(url, data = {}, options = {}) {
    return request({ url, data, method: 'DELETE', ...options })
    return request({ url, data, method: "DELETE", ...options });
  },
  // 新增:上传文件方法
  upload(url, filePath, name = 'file', formData = {}) {
  upload(url, filePath, name = "file", formData = {}) {
    return request({
      url,
      method: 'POST',
      method: "POST",
      filePath,
      name,
      formData,
      header: { 'Content-Type': 'multipart/form-data' }
    })
  }
}
      header: { "Content-Type": "multipart/form-data" },
    });
  },
};
// 挂载到全局
uni.$uapi = http
uni.$uapi = http;
export default http
export default http;