WXL
2026-05-17 3453ba7e5243022ad4388da1515dc75ad8d81f94
src/views/business/ethicalReview/ethicalReviewInfo.vue
@@ -234,14 +234,6 @@
          >
            发送主委专家
          </el-button>
          <el-button
            size="mini"
            type="warning"
            @click="handleBatchSend"
            :disabled="!canBatchSend"
          >
            批量发送
          </el-button>
        </div>
      </div>
@@ -373,6 +365,14 @@
          </template>
        </el-table-column>
        <el-table-column label="截止时间" width="160" align="center">
          <template slot-scope="scope">
            <span>{{
              scope.row.endTime ? formatDateTime(scope.row.endTime) : "未设置"
            }}</span>
          </template>
        </el-table-column>
        <el-table-column label="审查时间" width="160" align="center">
          <template slot-scope="scope">
            <span>{{
@@ -392,7 +392,7 @@
          </template>
        </el-table-column>
        <el-table-column label="操作" width="280" align="center" fixed="right">
        <el-table-column label="操作" width="180" align="center" fixed="right">
          <template slot-scope="scope">
            <el-button
              size="mini"
@@ -417,27 +417,11 @@
              }}
            </el-button>
            <el-button
              size="mini"
              type="text"
              icon="el-icon-edit"
              @click="handleEditExpertReview(scope.row)"
              :disabled="!['2', '3'].includes(scope.row.receiveStatus)"
            >
              编辑
            </el-button>
            <el-button
              size="mini"
              type="text"
              icon="el-icon-view"
              @click="handleViewExpertReview(scope.row)"
            >
              详情
            </el-button>
            <el-button
              v-if="scope.row.receiveStatus == 0"
              size="mini"
              type="text"
              icon="el-icon-delete"
              @click="handleDeleteExpertReview(scope.$index)"
              @click="handleDeleteExpertReview(scope.row, scope.$index)"
              style="color: #f56c6c;"
            >
              删除
@@ -482,7 +466,7 @@
      </div>
      <el-table
        :data="expertList"
        :data="filteredExpertList"
        v-loading="expertListLoading"
        style="width: 100%"
        max-height="400"
@@ -534,7 +518,7 @@
          :page-sizes="[10, 20, 50, 100]"
          :page-size="expertPage.pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="expertTotal"
          :total="filteredExpertTotal"
        ></el-pagination>
      </div>
@@ -554,6 +538,7 @@
      :title="sendDialogTitle"
      :visible.sync="sendDialogVisible"
      width="500px"
      @close="handleSendDialogClose"
    >
      <el-form :model="sendForm" ref="sendForm" label-width="100px">
        <el-form-item label="专家类型" prop="expertType">
@@ -589,6 +574,16 @@
            style="width: 100%"
            :disabled="sendForm.expertType === 'chief'"
          />
          <div v-if="sendForm.expertType !== 'chief'" style="margin-top: 5px;">
            <el-button-group>
              <el-button size="mini" @click="setEndTime(0.5)"
                >半小时后</el-button
              >
              <el-button size="mini" @click="setEndTime(1)">一小时后</el-button>
              <el-button size="mini" @click="setEndTime(2)">两小时后</el-button>
              <el-button size="mini" @click="setEndTime(24)">一天后</el-button>
            </el-button-group>
          </div>
          <div
            v-if="sendForm.expertType === 'chief'"
            style="font-size: 12px; color: #999; margin-top: 5px;"
@@ -610,36 +605,23 @@
          </el-select>
        </el-form-item>
        <el-form-item
          label="选择专家"
          prop="expertIds"
          v-if="sendForm.expertType == 'normal'"
        >
          <el-select
            v-model="sendForm.expertIds"
            multiple
            placeholder="请选择专家"
            style="width: 100%"
          >
            <el-option
              v-for="expert in availableNormalExperts"
              :key="getExpertKey(expert)"
              :label="
                `${expert.expertname}${
                  expert.expertNo ? '(' + expert.expertNo + ')' : ''
                }`
              "
              :value="getExpertKey(expert)"
            />
          </el-select>
        <el-form-item label="发送标题" prop="title" required>
          <el-input v-model="sendForm.title" placeholder="请输入发送标题" />
        </el-form-item>
        <el-form-item label="发送内容" prop="content">
        <el-form-item label="发送内容" prop="content" required>
          <el-input
            type="textarea"
            :rows="4"
            v-model="sendForm.content"
            placeholder="请输入发送给专家的审查内容说明"
          />
        </el-form-item>
        <el-form-item label="跳转链接" prop="url">
          <el-input
            v-model="sendForm.url"
            placeholder="请输入跳转链接(可选)"
          />
        </el-form-item>
      </el-form>
@@ -781,7 +763,8 @@
  ethicalreviewedit,
  ethicalreviewadd,
  ethicalreviewInfo,
  ethicalreExpertTotal
  ethicalreExpertTotal,
  sendNotification
} from "@/api/businessApi";
import { listExternalperson } from "@/api/project/externalperson";
import CaseBasicInfo from "@/components/CaseBasicInfo";
@@ -917,7 +900,9 @@
        startTime: "",
        endTime: "",
        sendType: "0",
        content: ""
        title: "伦理审查任务通知",
        content: "",
        url: ""
      },
      // 专家历史审批情况
@@ -926,8 +911,8 @@
      expertHistoryData: null,
      currentExpertInfo: {},
      // 内部状态跟踪
      internalExpertList: []
      // 当前发送的专家
      currentSendExperts: []
    };
  },
  computed: {
@@ -1028,12 +1013,43 @@
      return this.availableChiefExperts.length > 0 && normalApprovedCount >= 12;
    },
    // 是否可以批量发送
    canBatchSend() {
      return (
        this.availableNormalExperts.length > 0 ||
        this.availableChiefExperts.length > 0
      );
    // 已存在的专家编号列表
    existingExpertNos() {
      return this.ethicalreviewopinionsList
        .map(expert => expert.expertNo)
        .filter(no => no);
    },
    // 已存在的专家姓名列表
    existingExpertNames() {
      return this.ethicalreviewopinionsList
        .map(expert => expert.expertname)
        .filter(name => name);
    },
    // 过滤后的专家列表(排除已存在的专家)
    filteredExpertList() {
      if (!this.expertList.length) return [];
      return this.expertList.filter(expert => {
        // 如果专家有编号,检查编号是否已存在
        if (expert.userno && this.existingExpertNos.includes(expert.userno)) {
          return false;
        }
        // 如果专家有姓名,检查姓名是否已存在
        if (
          expert.username &&
          this.existingExpertNames.includes(expert.username)
        ) {
          return false;
        }
        return true;
      });
    },
    // 过滤后的专家总数
    filteredExpertTotal() {
      return this.filteredExpertList.length;
    },
    // 当前用户信息
@@ -1057,8 +1073,6 @@
    "form.ethicalreviewopinionsList": {
      handler(newVal) {
        console.log("专家列表变化:", newVal);
        // 强制触发计算属性更新
        this.$forceUpdate();
      },
      deep: true
    }
@@ -1371,7 +1385,20 @@
      if (this.sendForm.expertType === "chief") {
        // 主委专家无需设置截止时间
        this.sendForm.endTime = "";
      } else {
        // 普通专家重置截止时间为当前时间
        this.sendForm.endTime = "";
      }
    },
    // 设置截止时间快捷键
    setEndTime(hours) {
      const now = new Date();
      const endTime = new Date(now.getTime() + hours * 60 * 60 * 1000);
      this.sendForm.endTime = endTime
        .toISOString()
        .replace("T", " ")
        .substring(0, 19);
    },
    // 保存信息
@@ -1379,6 +1406,10 @@
      this.$refs.form.validate(async valid => {
        if (valid) {
          this.saveLoading = true;
          // 保存清空id便于后端整体删除新增
          this.form.ethicalreviewopinionsList.forEach(item=>{
            item.id=null
          })
          try {
            const submitData = {
              ...this.form,
@@ -1495,9 +1526,6 @@
            if (response.code === 200) {
              this.$message.success("审查已中止,所有专家状态已更新");
              this.form.status = "2";
              // 触发计算属性更新
              this.$forceUpdate();
            } else {
              this.$message.error("操作失败:" + (response.msg || "未知错误"));
            }
@@ -1575,8 +1603,8 @@
        const response = await listExternalperson(params);
        if (response.code === 200) {
          this.expertList = response.rows;
          this.expertTotal = response.total;
          this.expertList = response.rows || [];
          this.expertTotal = response.total || 0;
        } else {
          this.$message.error(
            "加载专家列表失败:" + (response.msg || "未知错误")
@@ -1621,21 +1649,8 @@
        this.$set(this.form, "ethicalreviewopinionsList", []);
      }
      // 过滤已存在的专家
      const existingExpertIds = this.form.ethicalreviewopinionsList.map(
        expert => expert.expertNo || expert.expertname
      );
      const newExperts = this.selectedExperts.filter(expert => {
        return !existingExpertIds.includes(expert.userno || expert.username);
      });
      if (newExperts.length === 0) {
        this.$message.warning("选择的专家已存在");
        return;
      }
      // 添加专家到列表
      newExperts.forEach(expert => {
      this.selectedExperts.forEach(expert => {
        // 判断是否为主任委员
        const isChief = this.getIsChiefExpert(expert);
@@ -1649,7 +1664,7 @@
          expertType: isChief ? "1" : "0", // 主任委员设置为主委专家
          deptName: expert.unitname || "",
          title: expert.title || "",
          telephone: expert.telephone || "",
          deptname: expert.telephone || "",
          receiveStatus: "0", // 待接收
          expertconclusion: "",
          expertopinion: "",
@@ -1672,22 +1687,11 @@
          delFlag: "0"
        };
        // 使用Vue.set确保响应式
        // 使用push添加,确保响应式
        this.form.ethicalreviewopinionsList.push(expertReview);
      });
      // 触发计算属性更新
      this.$forceUpdate();
      console.log(
        "添加专家后,当前专家列表:",
        this.form.ethicalreviewopinionsList
      );
      console.log("普通专家数量:", this.normalExpertsCount);
      console.log("主委专家数量:", this.chiefExpertsCount);
      console.log("可发送普通专家:", this.availableNormalExperts);
      this.$message.success(`成功添加 ${newExperts.length} 位专家`);
      this.$message.success(`成功添加 ${this.selectedExperts.length} 位专家`);
      this.expertDialogVisible = false;
      this.selectedExperts = [];
    },
@@ -1715,9 +1719,7 @@
    // 发送给普通专家
    handleSendToNormalExperts() {
      this.sendForm.expertIds = this.availableNormalExperts.map(expert =>
        this.getExpertKey(expert)
      );
      this.currentSendExperts = this.availableNormalExperts;
      this.sendForm.expertType = "normal";
      this.sendForm.endTime = ""; // 重置截止时间
      this.sendDialogVisible = true;
@@ -1725,34 +1727,33 @@
    // 发送给主委专家
    handleSendToChiefExpert() {
      this.sendForm.expertIds = this.availableChiefExperts.map(expert =>
        this.getExpertKey(expert)
      );
      this.currentSendExperts = this.availableChiefExperts;
      this.sendForm.expertType = "chief";
      this.sendForm.endTime = ""; // 主委专家无需截止时间
      this.sendDialogVisible = true;
    },
    // 批量发送
    handleBatchSend() {
      const allAvailableExperts = [
        ...this.availableNormalExperts,
        ...this.availableChiefExperts
      ];
      this.sendForm.expertIds = allAvailableExperts.map(expert =>
        this.getExpertKey(expert)
      );
      this.sendForm.expertType = "batch";
      this.sendForm.endTime = ""; // 重置截止时间
      this.sendDialogVisible = true;
    },
    // 发送给单个专家
    handleSendToExpert(expert) {
      this.sendForm.expertIds = [this.getExpertKey(expert)];
      this.currentSendExperts = [expert];
      this.sendForm.expertType = expert.expertType === "1" ? "chief" : "normal";
      this.sendForm.endTime = expert.expertType === "1" ? "" : ""; // 主委专家无需截止时间
      this.sendDialogVisible = true;
    },
    // 发送对话框关闭
    handleSendDialogClose() {
      this.sendForm = {
        expertType: "normal",
        expertIds: [],
        startTime: "",
        endTime: "",
        sendType: "0",
        title: "伦理审查任务通知",
        content: "",
        url: ""
      };
      this.currentSendExperts = [];
    },
    // 确认发送
@@ -1773,23 +1774,51 @@
        return;
      }
      if (this.sendForm.expertIds.length === 0) {
        this.$message.warning("请选择要发送的专家");
      if (!this.sendForm.title) {
        this.$message.warning("请输入发送标题");
        return;
      }
      if (!this.sendForm.content) {
        this.$message.warning("请输入发送内容");
        return;
      }
      if (this.currentSendExperts.length === 0) {
        this.$message.warning("没有找到可发送的专家");
        return;
      }
      this.sending = true;
      try {
        // 模拟发送过程
        await new Promise(resolve => setTimeout(resolve, 1000));
        // 发送给每个专家
        const sendPromises = this.currentSendExperts.map(async expert => {
          try {
            // 构建发送数据
            const sendData = {
              number: expert.deptname || "", // 用户手机号
              title: this.sendForm.title,
              url: this.sendForm.url || "",
              createTime: new Date()
                .toISOString()
                .replace("T", " ")
                .substring(0, 19)
            };
            // 调用发送通知接口
            const response = await sendNotification(sendData);
            if (response.code === 200) {
        // 更新专家状态
        this.sendForm.expertIds.forEach(expertKey => {
          const index = this.form.ethicalreviewopinionsList.findIndex(
            expert => this.getExpertKey(expert) === expertKey
                e =>
                  e.expertNo === expert.expertNo ||
                  e.expertname === expert.expertname
          );
          if (index !== -1) {
            this.form.ethicalreviewopinionsList[index].receiveStatus = "2"; // 已接收
                this.form.ethicalreviewopinionsList[index].receiveStatus = "1"; // 已接收
            this.form.ethicalreviewopinionsList[
              index
            ].startTime = this.sendForm.startTime;
@@ -1799,21 +1828,56 @@
            this.form.ethicalreviewopinionsList[
              index
            ].sendType = this.sendForm.sendType;
            this.form.ethicalreviewopinionsList[index].updateTime = new Date()
                this.form.ethicalreviewopinionsList[
                  index
                ].updateTime = new Date()
              .toISOString()
              .replace("T", " ")
              .substring(0, 19);
            // 使用Vue.set确保响应式
                // 使用Vue.set确保响应式更新
            this.$set(
              this.form.ethicalreviewopinionsList,
              index,
              this.form.ethicalreviewopinionsList[index]
            );
          }
              return { success: true, expert: expert.expertname };
            } else {
              return {
                success: false,
                expert: expert.expertname,
                error: response.msg
              };
            }
          } catch (error) {
            console.error(`发送给专家 ${expert.expertname} 失败:`, error);
            return {
              success: false,
              expert: expert.expertname,
              error: error.message
            };
          }
        });
        this.$message.success("发送成功");
        // 等待所有发送完成
        const results = await Promise.all(sendPromises);
        // 统计发送结果
        const successCount = results.filter(r => r.success).length;
        const failCount = results.filter(r => !r.success).length;
        if (failCount === 0) {
          this.$message.success(`成功发送给 ${successCount} 位专家`);
        } else if (successCount > 0) {
          this.$message.warning(
            `成功发送给 ${successCount} 位专家,失败 ${failCount} 位`
          );
        } else {
          this.$message.error("发送失败,请稍后重试");
        }
        this.sendDialogVisible = false;
        this.sendForm = {
          expertType: "normal",
@@ -1821,11 +1885,11 @@
          startTime: "",
          endTime: "",
          sendType: "0",
          content: ""
          title: "伦理审查任务通知",
          content: "",
          url: ""
        };
        // 触发计算属性更新
        this.$forceUpdate();
        this.currentSendExperts = [];
      } catch (error) {
        console.error("发送失败:", error);
        this.$message.error("发送失败,请重试");
@@ -1834,48 +1898,23 @@
      }
    },
    // 编辑专家审查
    handleEditExpertReview(expert) {
      this.$prompt("请输入审查意见", "编辑专家审查", {
    // 删除专家审查
    handleDeleteExpertReview(expert, index) {
      this.$confirm("确定要删除该专家的审查记录吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        inputValue: expert.expertopinion || "",
        inputPlaceholder: "请输入审查意见",
        inputValidator: value => {
          if (!value || value.trim() === "") {
            return "审查意见不能为空";
          }
          return true;
        }
        type: "warning"
      })
        .then(({ value }) => {
          const index = this.form.ethicalreviewopinionsList.findIndex(
            e => e.id === expert.id || e.expertNo === expert.expertNo
          );
          if (index !== -1) {
            this.form.ethicalreviewopinionsList[index].expertopinion = value;
            this.form.ethicalreviewopinionsList[index].updateTime = new Date()
              .toISOString()
              .replace("T", " ")
              .substring(0, 19);
            // 使用Vue.set确保响应式
            this.$set(
              this.form.ethicalreviewopinionsList,
              index,
              this.form.ethicalreviewopinionsList[index]
            );
            this.$message.success("审查意见已更新");
          }
        .then(() => {
          // 从数组中删除专家
          this.form.ethicalreviewopinionsList.splice(index, 1);
          this.$message.success("专家审查记录已删除");
        })
        .catch(() => {});
    },
    // 查看专家历史审批情况
    async handleViewExpertHistory(expert) {
      console.log(12);
      if (!expert.expertNo) {
        this.$message.warning("该专家没有编号,无法查询历史审批情况");
        return;
@@ -1889,16 +1928,14 @@
        const params = {
          expertNo: expert.expertNo
        };
        console.log(11);
        const response = await ethicalreExpertTotal(params);
        console.log(response);
        if (response) {
          this.expertHistoryData = response[0];
        if (response && response.code === 200) {
          this.expertHistoryData = response.data || response[0] || null;
        } else {
          this.$message.error(
            "查询专家历史审批情况失败:" + (response.msg || "未知错误")
            "查询专家历史审批情况失败:" + (response?.msg || "未知错误")
          );
          this.expertHistoryData = null;
        }
@@ -1911,78 +1948,12 @@
      }
    },
    // 查看专家审查详情
    handleViewExpertReview(expert) {
      this.$alert(
        `
        <div style="line-height: 1.6;">
          <p><strong>专家姓名:</strong>${expert.expertname}</p>
          <p><strong>专家编号:</strong>${expert.expertNo || "-"}</p>
          <p><strong>专家类型:</strong>${this.getExpertTypeText(
            expert.expertType
          )}</p>
          <p><strong>科室名称:</strong>${expert.deptName || "-"}</p>
          <p><strong>职称:</strong>${expert.title || "-"}</p>
          <p><strong>联系电话:</strong>${expert.telephone || "-"}</p>
          <p><strong>审查状态:</strong>${this.getReviewStatusText(
            expert.receiveStatus
          )}</p>
          <p><strong>专家结论:</strong>${
            expert.expertconclusion
              ? this.getConclusionText(expert.expertconclusion)
              : "未提交"
          }</p>
          <p><strong>审查意见:</strong>${expert.expertopinion || "无"}</p>
          <p><strong>结论顺序:</strong>${expert.conclusionorder || "-"}</p>
          <p><strong>审查时间:</strong>${expert.conclusiontime || "未审查"}</p>
          <p><strong>发送时间:</strong>${expert.startTime || "未发送"}</p>
          <p><strong>截止时间:</strong>${expert.endTime || "-"}</p>
          <p><strong>发送方式:</strong>${
            expert.sendType
              ? expert.sendType === "0"
                ? "系统发送"
                : expert.sendType === "1"
                ? "邮件发送"
                : expert.sendType === "2"
                ? "短信发送"
                : "其他方式"
              : "-"
          }</p>
        </div>
      `,
        "专家审查详情",
        {
          dangerouslyUseHTMLString: true,
          customClass: "expert-review-detail-dialog",
          showConfirmButton: false,
          showCancelButton: true,
          cancelButtonText: "关闭"
        }
      );
    },
    // 删除专家审查
    handleDeleteExpertReview(index) {
      this.$confirm("确定要删除该专家的审查记录吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          this.form.ethicalreviewopinionsList.splice(index, 1);
          // 触发计算属性更新
          this.$forceUpdate();
          this.$message.success("专家审查记录已删除");
        })
        .catch(() => {});
    },
    // 时间格式化
    parseTime(time) {
      if (!time) return "";
      const date = new Date(time);
      if (isNaN(date.getTime())) return time;
      return `${date.getFullYear()}-${(date.getMonth() + 1)
        .toString()
        .padStart(2, "0")}-${date