yxh
13 小时以前 38c26c3049d55ffda598ce0263498448b7c60834
src/views/project/fund/applyDetail/index.vue
@@ -68,15 +68,7 @@
            <el-form-item label="捐献案例" prop="name">
              <el-input :disabled="true" v-model="form.donorname" placeholder="请输入捐献者姓名" />
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <div style="margin-left: 10px;">
              <dict-tag
                :options="dict.type.sys_DonationStatus"
                :value="form.invoicecount"
              />
            </div>
          </el-col>
          </el-col>
          <el-col :span="10">
            <el-form-item label="申请说明" prop="remark">
              <el-input v-model="form.remark" placeholder="申请说明" />
@@ -186,18 +178,18 @@
                <el-input v-model="scope.row.amount" placeholder="金额" />
              </template>
            </el-table-column>
            <el-table-column prop="taxamount" align="center" label="扣税金额" width="120" v-if="
            <el-table-column prop="taxamount" align="center" label="劳务个税" width="120" v-if="
              ismanager == true &&
              (businessType == '1' || businessType == '2')
            ">
              <template slot-scope="scope">
                <el-input v-model="scope.row.taxamount" placeholder="扣税金额" />
                <el-input v-model="scope.row.taxamount" placeholder="劳务个税" />
              </template>
            </el-table-column>
            <el-table-column prop="taxedamount" align="center" label="税后金额" width="120"
            <el-table-column prop="taxedamount" align="center" label="劳务税后" width="120"
              v-if="businessType == '1' || businessType == '2'">
              <template slot-scope="scope">
                <el-input v-model="scope.row.taxedamount" placeholder="税后金额" />
                <el-input v-model="scope.row.taxedamount" placeholder="劳务税后" />
              </template>
            </el-table-column>
            <el-table-column prop="beneficiaryname" column-key="sdad" align="center" :label="beneficiarylabel"
@@ -524,7 +516,24 @@
      </span>
    </el-dialog>
    <el-dialog v-dialogDrags :modal="false" :close-on-click-modal="false" :title="pdftitle" :visible.sync="pdfVisible"
      width="60%">
      :fullscreen="pdfFullscreen" width="60%" custom-class="pdf-resize-dialog" @closed="pdfFullscreen = false">
      <template slot="title">
        <span>{{ pdftitle }}</span>
        <el-button class="pdf-fullscreen-btn" size="mini"
          :icon="pdfFullscreen ? 'el-icon-close' : 'el-icon-full-screen'"
          @click="pdfFullscreen = !pdfFullscreen">{{ pdfFullscreen ? "还原" : "全屏" }}</el-button>
      </template>
      <el-alert
        :title="
          activeName == 'invoice'
            ? '当前正在操作:发票文件(请确认票据属于发票)'
            : '当前正在操作:普通附件(请勿在此上传发票)'
        "
        :type="activeName == 'invoice' ? 'success' : 'warning'"
        :closable="false"
        show-icon
        style="margin-bottom: 10px;"
      />
      <div style="margin-bottom: 10px;">
        <el-table :data="tableDatatop" style="width: 100%">
          <el-table-column v-if="businessType == 4" prop="price" align="center" label="单价">
@@ -533,7 +542,7 @@
          </el-table-column>
          <el-table-column prop="amount" align="center" :label="amountlabel">
          </el-table-column>
          <el-table-column prop="taxedamount" align="center" label="税后金额" v-if="
          <el-table-column prop="taxedamount" align="center" label="劳务税后" v-if="
            (this.ismanager == true) &
            (form.applytype == '1' || form.applytype == '2')
          ">
@@ -609,18 +618,7 @@
              </div>
            </div>
            <div v-if="this.previewpdf && pdfimgsrcList.length" class="pdfimgmin">
              <!-- <img :src="pdfimg" /> -->
              <el-image ref="imagePreview" style="width: 95%; height: 90%" :src="pdfimg"
                @click="handleImageClick(initialIndex)">
                <!-- <div slot="error" class="image-slot">
                <i class="el-icon-picture-outline"></i>
              </div> -->
              </el-image>
              <custom-image-viewer :url-list="pdfimgsrcList" :initial-index="currentIndex" :visible="viewerVisible"
                @update:visible="viewerVisible = $event" @close="handleViewerClose" />
            </div>
            <div v-else class="pdfimgmins">{{ hintitle }}</div>
            <attachment-preview-pane :file="embeddedFile" style="flex: 1; min-width: 0;" />
          </div>
        </el-tab-pane>
        <el-tab-pane label="发票文件" name="invoice">
@@ -661,19 +659,7 @@
              </div>
            </div>
            <div v-if="this.previewpdf && invoicepdfimgsrcList.length" class="pdfimgmin">
              <!-- <img :src="pdfimg" /> -->
              <el-image style="width: 95%; height: 90%" :src="invoicepdfimg"
                @click="invoicehandleImageClick(initialIndex)">
                <!-- <div slot="error" class="image-slot">
                <i class="el-icon-picture-outline"></i>
              </div> -->
              </el-image>
              <custom-image-viewer :url-list="invoicepdfimgsrcList" :initial-index="invoicecurrentIndex"
                :visible="invoiceviewerVisible" @update:visible="invoiceviewerVisible = $event"
                @close="invoicehandleViewerClose" />
            </div>
            <div v-else class="pdfimgmins">{{ hintitle }}</div>
            <attachment-preview-pane :file="invoiceEmbeddedFile" style="flex: 1; min-width: 0;" />
          </div>
        </el-tab-pane>
      </el-tabs>
@@ -1129,7 +1115,7 @@
      invoicepdfimg: "",
      invoicepdfimgsrcList: [],
      invoDatatop: [],
      activeName: "common", //文件类型
      activeName: "invoice", //文件类型:默认发票文件
      //上传附件路径
      uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload",
      ICDtoken: getToken(),
@@ -1140,6 +1126,7 @@
      //票据文件
      pdftitle: "",
      pdfimg: "",
      invoiceEmbeddedFile: null,
      pdfimgsrcList: [],
      currentIndex: 0, // 初始索引
      invoicecurrentIndex: 0, // 初始索引
@@ -1242,14 +1229,8 @@
    },
    // 获取请求头
    Getnetworkheader() {
      let string = window.location.href;
      if (string.includes("9091")) {
        const index = string.indexOf("9091");
        this.Networkheader = string.slice(0, index + 4); // 截取9091及其前部字符
      } else {
        const index = string.indexOf("8032");
        this.Networkheader = string.slice(0, index + 4); // 截取8032及其前部字符
      }
      // 以当前页面 origin 为准,兼容 80/8032/9091 及后续端口调整
      this.Networkheader = window.location.origin;
    },
    /** 通过参数获取业务类型 */
    getroute() {
@@ -1673,7 +1654,7 @@
            }
            this.rbDetails[i] = tempDetail;
          }
          //计算当前行报销内容费用合计,税前金额(amount)、扣税金额(taxamount)、税后金额(taxedamount)
          //计算当前行报销内容费用合计,税前金额(amount)、劳务个税(taxamount)、劳务税后(taxedamount)
          if (this.businessType == "2") {
            for (let j = 0; j < this.rbDetails.length; j++) {
              let tempDetail = this.rbDetails[j];
@@ -2276,7 +2257,7 @@
      });
    },
    //计算个税:type(A:已知税前金额,计算税后金额;B:已知税后金额,计算税前金额)
    //计算个税:type(A:已知税前金额,计算劳务税后;B:已知劳务税后,计算税前金额)
    calculateTax(row, type) {
      for (let i = 0; i < this.rbDetails.length; i++) {
        if (!this.rbDetails[i].amount) {
@@ -2452,7 +2433,7 @@
              }
              this.rbDetails[i] = tempDetail;
            }
            //计算当前行报销内容费用合计,税前金额(amount)、扣税金额(taxamount)、税后金额(taxedamount)
            //计算当前行报销内容费用合计,税前金额(amount)、劳务个税(taxamount)、劳务税后(taxedamount)
            if (this.businessType == "2") {
              for (let j = 0; j < this.rbDetails.length; j++) {
                let tempDetail = this.rbDetails[j];
@@ -2606,6 +2587,23 @@
      });
      this.batchdialogVisible = true;
    },
    /**
     * 明细行是否为「空记录」:服务项目、专家、专家所在单位 是否都为空
     * 用于批量录入专家时判断"复用最后一行"还是"追加新行"
     */
    isBlankDetailRow(row) {
      if (!row) {
        return false;
      }
      const isEmpty = v =>
        v === null || v === undefined || String(v).trim() === "";
      return (
        isEmpty(row.itemid) &&
        isEmpty(row.beneficiaryname) &&
        isEmpty(row.unitno) &&
        isEmpty(row.unitname)
      );
    },
    /** 批量录入弹框:姓名/单位/工号/身份证后4位 命中判断 */
    personMatchKeyword(item, kw) {
      const idcard = String(item.idcrad || "");
@@ -2631,6 +2629,17 @@
          existed[String(row.beneficiaryname).trim()] = true;
        }
      });
      // 2026-10-22:修正"导入后表格多出一行空记录"。
      // 规则:先看最后一条记录,若它的"服务项目 / 专家 / 专家所在单位"都是空的,
      // 说明是一条空记录,就从这一行开始填;否则追加新行。
      let writeIndex = this.rbDetails.length;
      if (
        writeIndex > 0 &&
        this.isBlankDetailRow(this.rbDetails[writeIndex - 1])
      ) {
        writeIndex -= 1;
      }
      const duplicated = [];
      this.checkedCities.forEach(item => {
@@ -2690,7 +2699,13 @@
          annexfiles: null
        };
        this.rbDetails.push(rowData);
        if (writeIndex < this.rbDetails.length) {
          // 复用最后那条空记录
          this.rbDetails.splice(writeIndex, 1, rowData);
        } else {
          this.rbDetails.push(rowData);
        }
        writeIndex++;
      });
      for (let i = 0; i < this.rbDetails.length; i++) {
        this.rbDetails[i].orderno = i + 1;
@@ -2949,7 +2964,7 @@
    /**
     * 2026-10-15:上传时校验附件文件名(业务要求:不合格立即提示且不加入列表)。
     * 规则与后端 AttachmentNameValidator 同口径:必须含中文 / 去掉扩展名后主干 ≤20 字 /
     * 规则与后端 AttachmentNameValidator 同口径:不强制含中文 / 去掉扩展名后主干 ≤20 字 /
     * 不含空格及 / \ : * ? " < > | # % 等特殊字符。
     */
    checkUploadFileName(file) {
@@ -2988,27 +3003,27 @@
      const config = {
        headers: { Authorization: "Bearer " + this.ICDtoken }
      };
      const pdfimg = this.Networkheader + "/prod-api" + response.fileName;
      const pdfimg = this.Networkheader + process.env.VUE_APP_BASE_API + response.fileName;
      //获取票据信息位置
      if (this.activeName == "common") {
        if (response.code == 200) {
          this.previewpdf = true;
          fetch(pdfimg, config)
            .then(response => response.blob())
            .then(blob => {
              // 将获取的数据流转换为URL
              this.pdfimg = URL.createObjectURL(blob);
              this.pdfimgsrcList.push(URL.createObjectURL(blob));
              this.fileListto.push({
                name: file.name,
                url: URL.createObjectURL(blob)
              });
            })
            .catch(error => {
              console.error("Error loading image", error);
              return;
            });
          // 新上传文件直接使用浏览器本地 File,避免服务端文件地址/Token
          // 异常时把 JSON 错误响应当成图片或 PDF 预览。
          const localBlob = file.raw;
          const localUrl = URL.createObjectURL(localBlob);
          this.pdfimg = localUrl;
          this.embeddedFile = {
            name: file.name,
            url: localUrl,
            type: /\.pdf$/i.test(file.name) ? "pdf" : "image"
          };
          this.pdfimgsrcList.push(localUrl);
          this.fileListto.push({
            name: file.name,
            url: localUrl
          });
          this.$modal.msgSuccess(response.msg);
@@ -3027,21 +3042,19 @@
      } else {
        if (response.code == 200) {
          this.previewpdf = true;
          fetch(pdfimg, config)
            .then(response => response.blob())
            .then(blob => {
              // 将获取的数据流转换为URL
              this.invoicepdfimg = URL.createObjectURL(blob);
              this.invoicepdfimgsrcList.push(URL.createObjectURL(blob));
              this.invoicefileListto.push({
                name: file.name,
                url: URL.createObjectURL(blob)
              });
            })
            .catch(error => {
              console.error("Error loading image", error);
              return;
            });
          const localBlob = file.raw;
          const localUrl = URL.createObjectURL(localBlob);
          this.invoicepdfimg = localUrl;
          this.invoiceEmbeddedFile = {
            name: file.name,
            url: localUrl,
            type: /\.pdf$/i.test(file.name) ? "pdf" : "image"
          };
          this.invoicepdfimgsrcList.push(localUrl);
          this.invoicefileListto.push({
            name: file.name,
            url: localUrl
          });
          this.$modal.msgSuccess(response.msg);
@@ -3077,7 +3090,7 @@
      if (this.rbDetails[index].annexfilesList) {
        // this.rbDetails[index].annexfilesList.forEach((value, indexson) => {
        //   const pdfimg = this.Networkheader + "/prod-api" + value.url;
        //   const pdfimg = this.Networkheader + process.env.VUE_APP_BASE_API + value.url;
        //   fetch(pdfimg, config)
        //     .then(response => response.blob())
        //     .then(blob => {
@@ -3099,7 +3112,7 @@
        const fetchPromises = this.rbDetails[index].annexfilesList.map(
          (value, indexson) => {
            const pdfimg = this.Networkheader + "/prod-api" + value.url;
            const pdfimg = this.Networkheader + process.env.VUE_APP_BASE_API + value.url;
            return fetch(pdfimg, config)
              .then(response => response.blob())
              .then(blob => {
@@ -3132,7 +3145,7 @@
      if (this.rbDetails[index].invoicefilesList) {
        const fetchPromises = this.rbDetails[index].invoicefilesList.map(
          (value, indexson) => {
            const pdfimg = this.Networkheader + "/prod-api" + value.url;
            const pdfimg = this.Networkheader + process.env.VUE_APP_BASE_API + value.url;
            return fetch(pdfimg, config)
              .then(response => response.blob())
              .then(blob => {
@@ -3175,50 +3188,17 @@
      console.log(this.rbDetails[index].annexfilesList, "annexfilesList");
    },
    // 点击已上传文件
    // 费用报销:附件统一使用独立 FilePreview(录入、审批、详情一致)
    downFile(item) {
      if (this.activeName == "common") {
        this.pdftitle =
          "共" + this.pdfimgsrcList.length + "项,当前选中" + item.name;
        let name = item.name.split(".");
        if (name[1] == "pdf") {
          this.$modal.msgWarning("当前文件暂不支持预览");
          this.previewpdf = false;
          this.hintitle = "当前文件暂不支持预览";
        } else if (name[1] == "jpg" || "png") {
          console.log(item, "展示");
          this.previewpdf = true;
          if (item.url) {
            this.pdfimg = item.url;
          } else {
            this.pdfimg = "";
          }
        } else {
          this.hintitle = "当前文件暂不支持预览";
          this.$modal.msgWarning("当前文件暂不支持预览");
          this.previewpdf = false;
        }
      } else {
        this.pdftitle =
          "共" + this.invoicepdfimgsrcList.length + "项,当前选中" + item.name;
        let name = item.name.split(".");
        if (name[1] == "pdf") {
          this.$modal.msgWarning("当前文件暂不支持预览");
          this.previewpdf = false;
          this.hintitle = "当前文件暂不支持预览";
        } else if (name[1] == "jpg" || "png") {
          this.previewpdf = true;
          if (item.url) {
            this.invoicepdfimg = item.url;
          } else {
            this.invoicepdfimg = "";
          }
        } else {
          this.hintitle = "当前文件暂不支持预览";
          this.$modal.msgWarning("当前文件暂不支持预览");
          this.previewpdf = false;
        }
      const source =
        this.activeName == "common" ? this.fileListto : this.invoicefileListto;
      let list = (source || []).map(f => ({ name: f.name, url: f.url }));
      let index = list.findIndex(f => f.url === item.url);
      if (index < 0 && item && item.url) {
        list = [{ name: item.name, url: item.url }];
        index = 0;
      }
      this.$previewFile(list, index > -1 ? index : 0);
    },
    getIndexInArray(arr, obj) {
      return arr.indexOf(obj);