WXL
5 天以前 2140c5aae2032673cef7055df9738b9bacf37816
pagesSub/ethicalReview/index.vue
@@ -14,12 +14,12 @@
      <view class="divider"></view>
      <view class="stat-item">
        <text class="count">{{ stats.rejectCount }}</text>
        <text class="label">审查驳回</text>
        <text class="label">不同意</text>
      </view>
      <view class="divider"></view>
      <view class="stat-item">
        <text class="count">{{ stats.waiveCount }}</text>
        <text class="label">已放弃</text>
        <text class="label">弃权</text>
      </view>
    </view>
@@ -132,7 +132,7 @@
        <!-- 审查结论 -->
        <view
          class="conclusion-section"
          v-if="review.expertconclusion && review.expertconclusion !== 2"
          v-if="review.expertconclusion !== '' && review.expertconclusion != null && review.expertconclusion != 2"
        >
          <text class="conclusion-label">审查结论:</text>
          <text class="conclusion-content">{{
@@ -163,7 +163,7 @@
          </button>
          <button
            v-if="review.expertconclusion == 2"
            v-if="review.expertconclusion == 0"
            class="action-btn appeal-btn"
            @tap.stop="submitAppeal(review)"
          >
@@ -172,7 +172,7 @@
          </button>
          <button
            v-if="review.expertconclusion == 2 || review.expertconclusion == 3"
            v-if="review.expertconclusion == 0 || review.expertconclusion == 3"
            class="action-btn restart-btn"
            @tap.stop="restartReview(review)"
          >
@@ -212,6 +212,7 @@
  onReachBottom,
} from "@dcloudio/uni-app";
import { useDict } from "@/utils/dict";
import { useUserStore } from "@/stores/user";
// 响应式数据
const loading = ref(false);
@@ -220,6 +221,7 @@
const pageNum = ref(1);
const pageSize = ref(10);
const dict = ref({});
const userStore = useUserStore();
// 筛选条件
const currentStatus = ref("all");
@@ -242,24 +244,24 @@
  { label: "全部", value: "all" },
  { label: "待审查", value: "pending" },
  { label: "审查通过", value: "approved" },
  { label: "审查驳回", value: "rejected" },
  { label: "已放弃", value: "abandoned" },
  { label: "不同意", value: "rejected" },
  { label: "弃权", value: "abandoned" },
]);
// 字典映射
const statusDict = {
  pending: "待审查",
  approved: "审查通过",
  rejected: "审查驳回",
  abandoned: "已放弃",
  rejected: "不同意",
  abandoned: "弃权",
};
// 结论映射
const conclusionDict = {
  0: "未审核",
  1: "审查通过",
  2: "审查驳回",
  3: "放弃",
  0: "不同意",
  1: "同意",
  2: "审查中",
  3: "弃权",
  4: "修改后同意",
};
@@ -360,10 +362,10 @@
        params.expertconclusion = 1; // 同意
        break;
      case "rejected":
        params.expertconclusion = 2; // 不同意
        params.expertconclusion = 0; // 不同意
        break;
      case "abandoned":
        params.expertconclusion = 3; // 放弃
        params.expertconclusion = 3; // 弃权
        break;
    }
  }
@@ -459,45 +461,75 @@
  });
};
// 下载报告
const downloadReport = async (review) => {
  if (!review.conclusionannex) {
    uni.showToast({
      title: "暂无报告可下载",
      icon: "none",
    });
    return;
  }
// 获取文件下载的基础地址:
// H5 端走同源地址(由部署该页面的 nginx 反向代理 /profile/ 到后端,规避跨域);
// 小程序 / App 端使用 userStore 中的文件服务器地址
const getDownloadBaseUrl = () => {
  // #ifdef H5
  try {
    const annexes = review.conclusionannex
      .split(";")
      .filter((item) => item.trim());
    return window.location.origin;
  } catch {
    return userStore.baseUrlHt;
  }
  // #endif
  // #ifndef H5
  return userStore.baseUrlHt;
  // #endif
};
    if (annexes.length == 0) {
// 下载报告:调用后端档案下载接口获取下载地址,再下载文件
const downloadReport = async (review) => {
  try {
    // 先调用后端接口获取档案下载地址(与 web 端一致:参数 nitiateId 对应伦理审查发起 id)
    const res = await uni.$uapi.get(
      "/project/ethicalreviewopinions/download",
      { nitiateId: review.nitiateId || review.fcid || review.id },
    );
    if (res.code != 200 || !res.data || !res.data.downloadUrl) {
      uni.showToast({
        title: "暂无报告可下载",
        title: res.msg || "暂无报告可下载",
        icon: "none",
      });
      return;
    }
    const downloadUrl = res.data.downloadUrl;
    const downloadName =
      res.data.downloadName || downloadUrl.split("/").pop() || "伦理审查报告.doc";
    // 拼接完整下载地址:base + /profile + downloadUrl(后端 downloadUrl 缺少 /profile 前缀)
    const fullUrl = getDownloadBaseUrl() + "/profile" + downloadUrl;
    // #ifdef H5
    // H5 端:直接触发浏览器下载
    const link = document.createElement("a");
    link.href = fullUrl;
    link.download = downloadName;
    link.target = "_blank";
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    return;
    // #endif
    // #ifndef H5
    // 小程序 / App 端:下载后保存并打开
    uni.showLoading({
      title: "下载中...",
      mask: true,
    });
    // 下载第一个附件
    const fileUrl = annexes[0];
    const token = uni.getStorageSync("token") || "";
    const downloadTask = uni.downloadFile({
      url: fileUrl,
      url: fullUrl,
      header: {
        Authorization: token ? `Bearer ${token}` : "",
      },
      success: (downloadRes) => {
        if (downloadRes.statusCode == 200) {
          const tempFilePath = downloadRes.tempFilePath;
          // 保存到本地
          uni.saveFile({
            tempFilePath: tempFilePath,
            tempFilePath: downloadRes.tempFilePath,
            success: (saveRes) => {
              uni.hideLoading();
              uni.showToast({
@@ -506,8 +538,6 @@
                duration: 2000,
              });
              // 在微信小程序中可以打开文件
              if (uni.getSystemInfoSync().platform == "weixin") {
                uni.openDocument({
                  filePath: saveRes.savedFilePath,
                  showMenu: true,
@@ -518,9 +548,8 @@
                    console.error("打开文档失败", err);
                  },
                });
              }
            },
            fail: (saveErr) => {
            fail: () => {
              uni.hideLoading();
              uni.showToast({
                title: "保存文件失败",
@@ -553,6 +582,7 @@
    downloadTask.onProgressUpdate((res) => {
      console.log("下载进度:", res.progress);
    });
    // #endif
  } catch (error) {
    uni.hideLoading();
    console.error("下载报告失败:", error);