yxh
19 小时以前 48118e7c69c14b86b06abbabb4c910cb59c12cfa
Merge branch 'master' of http://116.62.18.175:6699/r/~yxh/opo-web
已修改2个文件
612 ■■■■■ 文件已修改
src/views/project/invoice/index.vue 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/travelexpenseapply/travelexpensedetail/index.vue 424 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/invoice/index.vue
@@ -27,7 +27,7 @@
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="报销状态" prop="reimburseState">
      <el-form-item v-if="!selectMode" label="报销状态" prop="reimburseState">
        <el-select
          v-model="queryParams.reimburseState"
          placeholder="请选择报销状态"
@@ -54,7 +54,7 @@
    </el-form>
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
      <el-col v-if="!selectMode" :span="1.5">
        <el-button
          type="primary"
          plain
@@ -72,7 +72,7 @@
    </el-row>
    <!-- 手机端:卡片列表 -->
    <div v-if="isMobile" class="invoice-cards" v-loading="loading">
    <div v-if="isMobile && !selectMode" class="invoice-cards" v-loading="loading">
      <div
        v-for="row in invoiceList"
        :key="row.id"
@@ -151,7 +151,21 @@
      </div>
    </div>
    <el-table v-else v-loading="loading" border :data="invoiceList">
    <el-table
      v-else
      v-loading="loading"
      border
      row-key="id"
      :data="invoiceList"
      @selection-change="handleInvoiceSelectionChange"
    >
      <el-table-column
        v-if="selectMode"
        type="selection"
        width="50"
        align="center"
        :reserve-selection="true"
      />
      <el-table-column type="index" label="序号" width="55" align="center" />
      <el-table-column label="案例" align="center" prop="caseId"  width="100">
        
@@ -220,28 +234,30 @@
            @click="openPreview(scope.row.invoiceUrl, scope.row.invoiceName)"
            >文件预览</el-button
          >
          <el-button
            v-if="String(scope.row.reimburseState) !== '1'"
            type="text"
            icon="el-icon-check"
            @click="handleReimburse(scope.row)"
            v-hasPermi="['project:invoice:edit']"
            >已报销</el-button
          >
          <el-button
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['project:invoice:edit']"
            >修改</el-button
          >
          <el-button
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['project:invoice:remove']"
            >删除</el-button
          >
          <template v-if="!selectMode">
            <el-button
              v-if="String(scope.row.reimburseState) !== '1'"
              type="text"
              icon="el-icon-check"
              @click="handleReimburse(scope.row)"
              v-hasPermi="['project:invoice:edit']"
              >已报销</el-button
            >
            <el-button
              type="text"
              icon="el-icon-edit"
              @click="handleUpdate(scope.row)"
              v-hasPermi="['project:invoice:edit']"
              >修改</el-button
            >
            <el-button
              type="text"
              icon="el-icon-delete"
              @click="handleDelete(scope.row)"
              v-hasPermi="['project:invoice:remove']"
              >删除</el-button
            >
          </template>
        </template>
      </el-table-column>
    </el-table>
@@ -253,6 +269,20 @@
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />
    <div v-if="selectMode" class="invoice-select-footer">
      <span class="invoice-select-footer__tip">
        已选择 {{ selectedInvoices.length }} 张发票
      </span>
      <el-button size="mini" @click="$emit('cancel')">取 消</el-button>
      <el-button
        type="primary"
        size="mini"
        :disabled="!selectedInvoices.length"
        @click="confirmSelect"
        >确定选择</el-button
      >
    </div>
    <!-- 上传发票 / 发票信息确认 -->
    <el-dialog
@@ -281,7 +311,11 @@
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="凭证">
                <el-checkbox v-model="uploadForm.voucher">凭证</el-checkbox>
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="发票文件">
            <el-upload
@@ -373,10 +407,30 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="发票类型" prop="invoiceType">
              <el-input
              <el-select
                v-model="form.invoiceType"
                placeholder="请输入发票类型"
              />
                placeholder="请选择发票类型"
                filterable
                clearable
                style="width: 100%"
              >
                <el-option
                  v-for="item in invoiceTypeOptions"
                  :key="item"
                  :label="item"
                  :value="item"
                />
                <!-- OCR 识别出的其它类型保留回显,避免直接丢失 -->
                <el-option
                  v-if="
                    form.invoiceType &&
                    invoiceTypeOptions.indexOf(form.invoiceType) === -1
                  "
                  :key="form.invoiceType"
                  :label="form.invoiceType"
                  :value="form.invoiceType"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -554,6 +608,13 @@
export default {
  name: "ServiceInvoice",
  props: {
    // 作为其他单据的发票选择器嵌入时传 true:显示多选列,仅展示未报销发票
    selectMode: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      // 遮罩层
@@ -564,6 +625,8 @@
      total: 0,
      // 发票列表
      invoiceList: [],
      // 选择模式下已勾选的发票
      selectedInvoices: [],
      // 弹窗
      open: false,
      // upload:上传发票;edit:识别结果确认
@@ -579,7 +642,9 @@
      },
      // 上传阶段表单(用户ID取当前登录用户,不在此处编辑)
      uploadForm: {
        caseId: ""
        caseId: "",
        // 勾选后传 voucher=1,后端按“凭证”处理
        voucher: false
      },
      uploadRules: {
       
@@ -588,6 +653,21 @@
      uploadedList: [],
      // 正在上传的文件数,用于判断一批上传是否结束
      uploadingCount: 0,
      // 发票类型下拉选项
      invoiceTypeOptions: [
        "交通费",
        "市内交通费",
        "住宿费",
        "伙食补助",
        "公杂费补助",
        "其他费用",
        "医护团队专家费",
        "评估专家费",
        "转运护理劳务费",
        "获取专家劳务费",
        "伦理审核专家费",
        "凭证"
      ],
      // 识别结果 / 修改表单
      form: {},
      rules: {
@@ -622,7 +702,8 @@
    uploadData() {
      return {
        userId: this.currentUserId,
        caseId: this.uploadForm.caseId
        caseId: this.uploadForm.caseId,
        voucher: this.uploadForm.voucher ? "1" : "0"
      };
    }
  },
@@ -634,7 +715,11 @@
    /** 查询发票列表 */
    getList() {
      this.loading = true;
      this.queryParams.userId=this.currentUserId
      this.queryParams.userId = this.currentUserId;
      // 选择模式只允许引用未报销发票,避免同一张发票重复报销
      if (this.selectMode) {
        this.queryParams.reimburseState = "0";
      }
      listInvoice(this.queryParams)
        .then(response => {
          this.invoiceList = response.rows;
@@ -693,9 +778,24 @@
    resetQuery() {
      this.queryParams.caseId = "";
      this.queryParams.invoiceName = "";
      this.queryParams.reimburseState = "";
      this.queryParams.reimburseState = this.selectMode ? "0" : "";
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 选择模式:记录勾选的发票 */
    handleInvoiceSelectionChange(selection) {
      this.selectedInvoices = selection;
    },
    /** 选择模式:把勾选的发票交给调用方 */
    confirmSelect() {
      if (!this.selectedInvoices.length) {
        this.$modal.msgWarning("请先勾选需要引用的发票");
        return;
      }
      this.$emit(
        "confirm",
        this.selectedInvoices.map(row => Object.assign({}, row))
      );
    },
    /** 上传发票按钮操作 */
    async handleUpload() {
@@ -703,7 +803,8 @@
        await this.getCurrentUser();
      }
      this.uploadForm = {
        caseId: ""
        caseId: "",
        voucher: false
      };
      this.uploadedList = [];
      this.uploadingCount = 0;
@@ -744,6 +845,8 @@
      this.getList();
      if (!invoiceList.length) {
        this.$modal.msgWarning("该文件未识别到发票信息,请确认文件清晰");
      } else if (this.uploadForm.voucher) {
        this.$modal.msgSuccess("凭证上传成功");
      } else {
        this.$modal.msgSuccess(
          "发票上传成功,识别到 " + invoiceList.length + " 张发票"
@@ -786,7 +889,7 @@
        remark: record.remark
      };
      this.mode = "edit";
      this.title = "发票信息确认";
      this.title = record.invoiceType === "凭证" ? "凭证信息确认" : "发票信息确认";
      this.open = true;
      this.refreshThumb();
    },
@@ -1057,6 +1160,19 @@
  color: #909399;
}
.invoice-select-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 12px 0 0;
  &__tip {
    margin-right: 16px;
    font-size: 13px;
    color: #606266;
  }
}
/* 手机端适配 */
@media (max-width: 992px) {
  .app-container {
src/views/project/travelexpenseapply/travelexpensedetail/index.vue
@@ -631,8 +631,13 @@
        </el-tab-pane>
        <el-tab-pane label="发票文件" :name="2">
          <div class="pdfimg">
            <div class="box-pdf">
            <div class="box-pdf box-pdf--invoice">
              <div>
                <div class="invoice-select-bar">
                  <el-button type="primary" size="mini" icon="el-icon-plus"
                    :disabled="operationType == 'detail'" @click="openInvoiceSelect">引用发票</el-button>
                  <span class="invoice-select-bar__tip">引用后自动标记为已报销,删除后恢复未报销</span>
                </div>
                <el-upload size="mini" class="upload-demo" :action="uploadFileUrl" :file-list="invoicefileList"
                  :show-file-list="false" multiple drag :headers="headers" :on-success="(response, file, fileList) =>
                      uploadSccess(response, file, fileList)
@@ -646,15 +651,31 @@
                  </div>
                </el-upload>
                <el-table :data="invoicefileListto" @row-click="downFile" style="width: 100%" height="400">
                  <el-table-column prop="name" :show-overflow-tooltip="true" label="名称">
                  <el-table-column prop="name" :show-overflow-tooltip="true" label="文件名称">
                    <template slot-scope="scope">
                      <i style="color:#409EFF" class=" el-icon-s-order" />{{
                        scope.row.name
                      }}
                    </template>
                  </el-table-column>
                  <el-table-column prop="invoiceType" width="90" align="center" :show-overflow-tooltip="true"
                    label="发票类型">
                    <template slot-scope="scope">
                      {{ scope.row.invoiceType || "-" }}
                    </template>
                  </el-table-column>
                  <el-table-column prop="invoiceName" width="120" :show-overflow-tooltip="true" label="发票名称">
                    <template slot-scope="scope">
                      {{ scope.row.invoiceName || "-" }}
                    </template>
                  </el-table-column>
                  <el-table-column prop="invoiceAmount" width="95" align="center" label="发票金额">
                    <template slot-scope="scope">
                      {{ scope.row.invoiceAmount || "-" }}
                    </template>
                  </el-table-column>
                  <el-table-column prop="name" width="180" :show-overflow-tooltip="true" label="功能">
                  <el-table-column prop="name" width="140" :show-overflow-tooltip="true" label="功能">
                    <template slot-scope="scope">
                      <el-button type="primary" size="mini"
                        @click.native.prevent.stop="deletedowfile(scope.row)">删除</el-button>
@@ -670,6 +691,11 @@
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-dialog>
    <el-dialog title="选择发票" :visible.sync="invoiceSelectOpen" width="1100px" append-to-body
      :fullscreen="isMobile" :close-on-click-modal="false">
      <invoice-index v-if="invoiceSelectOpen" select-mode @confirm="handleInvoiceSelected"
        @cancel="invoiceSelectOpen = false" />
    </el-dialog>
    <el-dialog title="提示" :visible.sync="Savereminder" width="30%">
      <el-alert :title="'您已添加' +
@@ -727,15 +753,18 @@
import { treeselect } from "@/api/system/dept";
import { getSubsidy } from "@/api/project/travelcity";
import { listReportname, listUser } from "@/api/project/organization";
import { updateInvoice } from "@/api/project/invoice";
import InvoiceIndex from "@/views/project/invoice/index.vue";
import { getToken } from "@/utils/auth";
import debounce from "lodash/debounce";
import { moneySum, moneyAdd, moneyRound } from "@/utils/money";
import { moneySum, moneyAdd, moneySub, moneyRound } from "@/utils/money";
import { checkFileName, checkDetails } from "@/utils/fileNameCheck";
export default {
  components: {
    Treeselect,
    Li_area_select,
    CustomImageViewer,
    InvoiceIndex,
    pdf
  },
  name: "Funddetail",
@@ -905,6 +934,9 @@
      initialIndex: 0, // 初始索引
      viewerVisible: false, // 控制预览组件显示
      invoiceviewerVisible: false, // 控制预览组件显示
      // 发票管理引用弹窗
      invoiceSelectOpen: false,
      invoiceSelecting: false,
      //人员类别
      persontype: null,
@@ -927,6 +959,12 @@
      //保存按钮控制
      idisabled: false
    };
  },
  computed: {
    isMobile() {
      return this.$store.state.app.device === "mobile";
    }
  },
  created() {
@@ -1078,6 +1116,213 @@
      console.log("图片加载成功");
    },
    handleUploadError() { },
    // 打开发票管理引用弹窗(多选未报销发票)
    openInvoiceSelect() {
      if (this.operationType == "detail") {
        return;
      }
      if (!this.rbDetails[this.atpresent]) {
        this.$modal.msgWarning("请先选择费用明细行");
        return;
      }
      this.invoiceSelectOpen = true;
    },
    // 引用发票:写入当前明细的发票集合,并把发票标记为已报销
    handleInvoiceSelected(rows) {
      if (this.invoiceSelecting) {
        return;
      }
      const detail = this.rbDetails[this.atpresent];
      if (!detail) {
        this.$modal.msgWarning("请先选择费用明细行");
        return;
      }
      const existed = (detail.invoicefilesList || [])
        .map(item => item && item.invoiceId)
        .filter(id => id !== null && id !== undefined && id !== "")
        .map(id => String(id));
      const selected = (rows || []).filter(
        row => !existed.includes(String(row.id))
      );
      if (!selected.length) {
        this.$modal.msgWarning("所选发票已引用,请勿重复添加");
        return;
      }
      this.invoiceSelecting = true;
      Promise.all(
        selected.map(row =>
          updateInvoice({ id: row.id, reimburseState: "1" })
        )
      )
        .then(() => {
          if (!detail.invoicefilesList) {
            this.$set(detail, "invoicefilesList", []);
          }
          selected.forEach(row => {
            const file = this.buildInvoiceFile(row);
            // 按发票类型把金额计入对应费用科目
            const applied = this.applyInvoiceExpense(detail, file, 1);
            // 记录实际计入金额,负数发票被0截断时删除不会反向增加
            file.expenseAmount = moneyRound(applied).toFixed(2);
            // 明细内保存服务端地址,列表内保存同一份数据供展示/删除
            detail.invoicefilesList.push(Object.assign({}, file));
            this.invoicefileListto.push(file);
          });
          this.sumRowFee(detail);
          this.invoicepdfimgsrcList = this.invoicefileListto.map(
            item => item.url
          );
          this.pdftitle = "共" + this.invoicefileListto.length + "项";
          this.invoiceSelectOpen = false;
          this.$modal.msgSuccess(
            "已引用 " + selected.length + " 张发票,费用已按类型计入"
          );
        })
        .catch(() => {
          this.$modal.msgError("发票标记已报销失败,请稍后重试");
        })
        .finally(() => {
          this.invoiceSelecting = false;
        });
    },
    // 发票管理记录 -> 发票附件(保留发票业务字段,随明细一起保存)
    buildInvoiceFile(row) {
      const fileUrl = this.toRelativeFileUrl(row.invoiceUrl);
      const ext = this.getFileExt(fileUrl);
      return {
        name: this.buildInvoiceFileName(row, ext),
        url: fileUrl,
        invoiceId: row.id,
        invoiceNo: row.invoiceNo,
        invoiceType: row.invoiceType,
        invoiceName: row.invoiceName,
        invoiceAmount: row.invoiceAmount,
        // 记录计入的费用科目和金额,删除发票时按同一口径扣减
        expenseField: this.resolveInvoiceExpenseField(row),
        expenseAmount: moneyRound(
          this.invoiceAmountValue(row.invoiceAmount)
        ).toFixed(2),
        reimburseState: "1"
      };
    },
    // 发票类型 -> 报销明细费用科目
    resolveInvoiceExpenseField(invoice) {
      const type = String(invoice.invoiceType || "");
      let ext = {};
      try {
        ext = invoice.extContent ? JSON.parse(invoice.extContent) : {};
        if (!ext || typeof ext !== "object") {
          ext = {};
        }
      } catch (e) {
        ext = {};
      }
      const text = [
        type,
        invoice.invoiceName,
        invoice.sellerName,
        ext["销售方名称"],
        ext["项目名称"],
        ext["货物或应税劳务名称"]
      ]
        .filter(Boolean)
        .join(" ");
      // 优先识别发票类型里直接写明的费用科目
      if (type.includes("市内交通")) return "cityfee";
      if (type.includes("交通")) return "trafficexpense";
      if (type.includes("住宿")) return "hotelexpense";
      if (type.includes("伙食")) return "foodallowance";
      if (type.includes("公杂")) return "otherexpense";
      if (type.includes("其他费用")) return "otherfeeamount";
      // OCR 类型通常是发票种类,再结合发票名称/销售方/项目名称识别
      if (/出租|网约|滴滴|地铁|公交|停车|过路|通行费/.test(text)) {
        return "cityfee";
      }
      if (
        /火车|高铁|动车|铁路|飞机|航空|机票|行程单|汽车票|客运|船票|运输/.test(
          text
        )
      ) {
        return "trafficexpense";
      }
      if (/住宿|酒店|宾馆|旅馆/.test(text)) return "hotelexpense";
      if (/伙食|餐饮|餐费|饭店|食品/.test(text)) return "foodallowance";
      if (/公杂|办公|日用品|通讯|电话费|快递/.test(text)) {
        return "otherexpense";
      }
      // 无法归类的发票统一计入其他费用
      return "otherfeeamount";
    },
    // 发票金额可能是带千分位/货币符号的文本,抽出可计算部分
    invoiceAmountValue(value) {
      const text = String(value === null || value === undefined ? "" : value)
        .replace(/,/g, "")
        .trim();
      const match = text.match(/-?\d*\.?\d+/);
      return match ? match[0] : 0;
    },
    // sign=1 计入,sign=-1 扣减,金额统一走精确运算
    applyInvoiceExpense(detail, invoiceFile, sign) {
      const field = invoiceFile.expenseField;
      if (!detail || !field) {
        return 0;
      }
      const amount = moneyRound(
        this.invoiceAmountValue(
          invoiceFile.expenseAmount !== undefined
            ? invoiceFile.expenseAmount
            : invoiceFile.invoiceAmount
        )
      );
      if (!amount) {
        return 0;
      }
      const before = moneyRound(detail[field]);
      const next =
        sign > 0
          ? moneyAdd(before, amount)
          : moneySub(before, amount);
      const after = moneyRound(Math.max(0, next));
      detail[field] = after.toFixed(2);
      return moneyRound(after - before);
    },
    // 汇总一行明细中计入指定费用科目的发票金额
    sumInvoiceExpenseByField(row, field) {
      const list = (row && row.invoicefilesList) || [];
      return moneySum(
        list.filter(item => item && item.expenseField === field),
        item =>
          item.expenseAmount !== undefined
            ? item.expenseAmount
            : item.invoiceAmount
      );
    },
    // 附件名校验不允许空格/特殊字符且主干≤20字,用发票号码生成安全文件名
    buildInvoiceFileName(row, ext) {
      const raw = String(row.invoiceNo || "").replace(/[\s/\\:*?"<>|#%]/g, "");
      const main = [...(raw || "发票" + row.id)].slice(0, 20).join("");
      return main + ext;
    },
    // 发票地址入库时统一成接口相对路径,避免服务器地址变更后预览失效
    toRelativeFileUrl(url) {
      const text = String(url || "");
      const index = text.indexOf("/profile/upload");
      return index > -1 ? text.slice(index) : text;
    },
    getFileExt(url) {
      const path = String(url || "").split("?")[0];
      const index = path.lastIndexOf(".");
      return index > -1 ? path.slice(index) : "";
    },
    // 预览框依赖文件名后缀判断文件类型,缺少后缀时用地址补齐
    withFileExt(name, url) {
      const text = String(name || "");
      if (/\.[a-z0-9]{2,5}$/i.test(text)) {
        return text;
      }
      const ext = this.getFileExt(url);
      return ext ? text + ext : text;
    },
    // 获取请求头
    Getnetworkheader() {
      // 以当前页面 origin 为准,兼容 80/8032/9091 及后续端口调整
@@ -1267,19 +1512,24 @@
      //发票
      if (this.rbDetails[index].invoicefilesList) {
        const fetchPromises = this.rbDetails[index].invoicefilesList.map(
          (value, indexson) => {
            const pdfimg = this.Networkheader + process.env.VUE_APP_BASE_API + value.url;
          value => {
            // 保留发票业务字段(invoiceId/类型/名称/金额),重新打开时仍可展示和删除
            const kept = Object.assign({}, value);
            // 发票管理中的地址是完整 URL,不能再拼接口前缀
            if (/^https?:\/\//i.test(value.url)) {
              return Promise.resolve(kept);
            }
            const pdfimg =
              this.Networkheader + process.env.VUE_APP_BASE_API + value.url;
            return fetch(pdfimg, config)
              .then(response => response.blob())
              .then(blob => {
                return {
                  name: value.name,
                  url: URL.createObjectURL(blob)
                };
                kept.url = URL.createObjectURL(blob);
                return kept;
              })
              .catch(error => {
                console.error("Error loading image", error);
                return null;
                return kept;
              });
          }
        );
@@ -1288,7 +1538,9 @@
          this.invoicefileListto = invoicefileListto.filter(
            item => item !== null
          );
          this.invoicepdfimg = this.invoicefileListto[0].url;
          this.invoicepdfimg = this.invoicefileListto.length
            ? this.invoicefileListto[0].url
            : "";
          console.log(this.invoicepdfimg, "invoicepdfimg");
          this.invoicepdfimgsrcList = this.invoicefileListto.map(
            item => item.url
@@ -1315,10 +1567,13 @@
    downFile(item) {
      const source =
        this.activeName == 1 ? this.fileListto : this.invoicefileListto;
      let list = (source || []).map(f => ({ name: f.name, url: f.url }));
      let list = (source || []).map(f => ({
        name: this.withFileExt(f.name, f.url),
        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 }];
        list = [{ name: this.withFileExt(item.name, item.url), url: item.url }];
        index = 0;
      }
      this.$previewFile(list, index > -1 ? index : 0);
@@ -1362,26 +1617,53 @@
          this.invoicepdfimgsrcList,
          row.url
        );
        this.invoicepdfimgsrcList.splice(indexlist, 1);
        if (indexlist > -1) {
          this.invoicepdfimgsrcList.splice(indexlist, 1);
        }
        const index = this.getIndexInArray(this.invoicefileListto, row);
        this.invoicefileListto.splice(index, 1);
        console.log(this.rbDetails[this.atpresent].invoicefilesList);
        const targetIndex = this.rbDetails[
          this.atpresent
        ].invoicefilesList.findIndex(item => {
          return item.name === row.name; // 建议使用严格相等 ===
        if (index > -1) {
          this.invoicefileListto.splice(index, 1);
        }
        const invoiceFiles =
          this.rbDetails[this.atpresent].invoicefilesList || [];
        // 引用发票按发票ID匹配,避免同名发票删错;上传文件按名称兼容旧数据
        const targetIndex = invoiceFiles.findIndex(item => {
          return row.invoiceId
            ? String(item.invoiceId) === String(row.invoiceId)
            : item.name === row.name;
        });
        console.log(targetIndex, "删除索引"); // 现在打印的是索引号,例如 0, 1, 2...
        console.log(targetIndex, "删除索引");
        let removed = null;
        if (targetIndex > -1) {
          // 确保找到了元素
          this.rbDetails[this.atpresent].invoicefilesList.splice(
            targetIndex,
            1
          );
          removed = invoiceFiles.splice(targetIndex, 1)[0];
          console.log("删除成功");
        } else {
          console.log("未找到对应项目,无法删除");
        }
        // 删除已引用发票时,按引用时计入的科目和金额扣减
        if (removed && removed.expenseField) {
          this.applyInvoiceExpense(
            this.rbDetails[this.atpresent],
            removed,
            -1
          );
          this.sumRowFee(this.rbDetails[this.atpresent]);
        }
        // 删除已引用的发票后,把发票恢复为未报销
        const invoiceId = row.invoiceId || (removed && removed.invoiceId);
        if (invoiceId) {
          updateInvoice({ id: invoiceId, reimburseState: "0" })
            .then(() => {
              this.$modal.msgSuccess(
                "发票已删除,费用已扣减并恢复为未报销"
              );
            })
            .catch(() => {
              this.$modal.msgError(
                "发票状态恢复失败,请到发票管理中手动标记为未报销"
              );
            });
        }
      }
    },
@@ -1406,21 +1688,24 @@
        ); // 将itemann插入到索引位置的前一位
      } else {
        const index = this.getIndexInArray(this.invoicefileListto, row);
        if (index < 1) {
          return;
        }
        const item = this.invoicefileListto.splice(index, 1)[0]; // 移除指定索引处的元素,并保存到item变量中
        this.invoicefileListto.splice(index - 1, 0, item); // 将item插入到索引位置的前一位
        const indexinvo = this.rbDetails[
          this.atpresent
        ].invoicefilesList.findIndex(item => item.name == row.name);
        const iteminvo = this.rbDetails[this.atpresent].invoicefilesList.splice(
          indexinvo,
          1
        )[0]; // 移除指定索引处的元素,并保存到item变量中
        this.rbDetails[this.atpresent].invoicefilesList.splice(
          indexinvo - 1,
          0,
          iteminvo
        ); // 将iteminvo插入到索引位置的前一位
        const invoiceFiles =
          this.rbDetails[this.atpresent].invoicefilesList || [];
        const indexinvo = invoiceFiles.findIndex(file =>
          row.invoiceId
            ? String(file.invoiceId) === String(row.invoiceId)
            : file.name == row.name
        );
        if (indexinvo < 1) {
          return;
        }
        const iteminvo = invoiceFiles.splice(indexinvo, 1)[0];
        invoiceFiles.splice(indexinvo - 1, 0, iteminvo);
      }
    },
@@ -1549,35 +1834,58 @@
        row.persontype == "管理人员"
      ) {
        //公杂费补助:杭州桐庐,建德,淳安60,杭州其他地区0,非杭州地区80
        const otherInvoice = this.sumInvoiceExpenseByField(
          row,
          "otherexpense"
        );
        const foodInvoice = this.sumInvoiceExpenseByField(
          row,
          "foodallowance"
        );
        // 先算补助基数,再叠加餐饮/办公类发票,避免补助重算把发票金额覆盖
        let baseOther = row.otherexpense;
        if (otherInvoice) {
          baseOther = moneySub(row.otherexpense, otherInvoice);
        }
        if (row.destination != "") {
          var tempStr = row.destination;
          //验证城市
          if (tempStr == "杭州") {
            row.otherexpense = "";
            baseOther = "";
          } else if (
            tempStr.includes("桐庐") ||
            tempStr.includes("建德") ||
            tempStr.includes("淳安")
          ) {
            row.otherexpense = (parseInt(row.days) * 60).toString();
            baseOther = (parseInt(row.days) * 60).toString();
          } else {
            row.otherexpense = (parseInt(row.days) * 80).toString();
            baseOther = (parseInt(row.days) * 80).toString();
          }
        }
        //伙食费补助:城市(西藏,新疆,青海.120)(其他100)
        let baseFood = "";
        if (tempStr == "杭州") {
          row.foodallowance = "";
          baseFood = "";
        } else if (
          tempStr == "西藏" ||
          tempStr == "新疆" ||
          tempStr == "青海"
        ) {
          row.foodallowance = (parseInt(row.days) * 120).toString();
          baseFood = (parseInt(row.days) * 120).toString();
        } else {
          row.foodallowance = (parseInt(row.days) * 100).toString();
          baseFood = (parseInt(row.days) * 100).toString();
        }
        row.otherexpense =
          baseOther === "" && !otherInvoice
            ? ""
            : moneyAdd(baseOther, otherInvoice).toFixed(2);
        row.foodallowance =
          baseFood === "" && !foodInvoice
            ? ""
            : moneyAdd(baseFood, foodInvoice).toFixed(2);
        //计算合计
        this.sumRowFee(row);
@@ -1611,10 +1919,14 @@
            for (let i = 0; i < response.data.length; i++) {
              fee += response.data[i].reimbursementamout;
            }
            row.hotelexpense = fee;
            const hotelInvoice = this.sumInvoiceExpenseByField(
              row,
              "hotelexpense"
            );
            row.hotelexpense = moneyAdd(fee, hotelInvoice).toFixed(2);
          })
          .finally(() => {
            this.sumTotalFee();
            this.sumRowFee(row);
          });
      }
    },
@@ -2927,6 +3239,19 @@
  text-align: center;
}
.invoice-select-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 10px;
  &__tip {
    margin-left: 10px;
    font-size: 12px;
    color: #909399;
  }
}
.pdfimg {
  display: flex; // text-align: center;
  width: 100%;
@@ -2942,6 +3267,11 @@
    box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1); // <- Add this to fix.
  }
  // 发票页多出类型/名称/金额三列,给表格留出横向空间
  .box-pdf--invoice {
    width: 560px;
  }
  .pdftit {
    width: 200px;
    padding: 20px;