ソースを参照

feat: 添加应付信息组件,整合应付明细展示与编辑功能

xieyong 1 日 前
コミット
c850246fad

+ 15 - 1
src/views/bpm/handleTask/components/purchaseOrder/accountstatement/page.vue

@@ -6,6 +6,15 @@
                    :dataForm.sync="dataForm"
                    :datasource.sync="datasource"
                    :dialogType="permissionType"></purchase-form>
+    <headerTitle title="应付信息" style="margin-top: 30px"></headerTitle>
+    <payablesInfo
+      ref="payablesInfoRef"
+      :dataForm.sync="dataForm"
+      :payablesList="payablesList"
+      :dialogType="permissionType"
+      statementAdvanceTotalPriceTitle="总对账预付款确付金额"
+      columnsTitle="本次对账预付款确付金额"
+    ></payablesInfo>
     <headerTitle title="对账明细" style="margin-top: 30px">
       <template v-slot>
         <el-row style="font-weight: 700; color: red">
@@ -46,6 +55,8 @@ import purchaseForm from "./purchaseForm.vue";
 import {getSearchMergeListByTypeAPI, infoAccountStatementAPI, accountstatementInfoAPI} from "@/api/bpm/components/saleManage/saleorder";
 import recorpayTableList from '@/views/bpm/handleTask/components/saleOrder/accountstatement/recorpayTableList.vue';
 import AccountDetailTable from '@/views/bpm/handleTask/components/saleOrder/accountstatement/accountDetailTable.vue';
+import payablesInfo from './payablesInfo.vue';
+
 export default {
   name: 'page',
   props: ['permissionType', 'businessId', 'taskDefinitionKey'],
@@ -53,11 +64,13 @@ export default {
     InventoryTable,
     purchaseForm,
     recorpayTableList,
-    AccountDetailTable
+    AccountDetailTable,
+    payablesInfo,
   },
   data() {
     return {
       datasource: [],
+      payablesList: [],
       dataForm: {
         sourceType: '',
         dateType: '',
@@ -135,6 +148,7 @@ export default {
       // data.amountCompletePrice = data.orderList.reduce((pre, cur) => pre + Math.round(+cur.statementedAmount * 100), 0) / 100;
       // data.amountUnCompletePrice = data.orderList.reduce((pre, cur) => pre + Math.round(+cur.unStatementAmount * 100), 0) / 100;
       this.dataForm = data
+      this.payablesList = data?.statPayable?.statPayableDetailList || []
       // this.priceObj.amountNotInvoicedPrice = data.amountNotInvoicedPrice
       switch (this.dataForm.dateType) {
         case 1:

+ 480 - 0
src/views/bpm/handleTask/components/purchaseOrder/accountstatement/payablesInfo.vue

@@ -0,0 +1,480 @@
+<template>
+  <div class="payables-info">
+    <el-form ref="form" :model="dataForm" :rules="rules" label-width="170px">
+      <el-row>
+        <el-col :span="8">
+          <el-form-item :label="statementAdvanceTotalPriceTitle">
+            <el-input disabled v-model="statementAdvanceTotalPrice">
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="本次对账应付总金额">
+            <el-input disabled v-model="receivedTotalPrice">
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <el-form :model="{ payablesList: localList }" ref="tableForm">
+      <ele-pro-table
+        ref="payablesTable"
+        row-key="id"
+        :needPage="false"
+        :columns="columns"
+        max-height="400px"
+        :datasource="localList"
+        :cache-key="cacheKeyUrl"
+        class="time-form"
+      >
+        <template v-slot:fundType="scope">
+          <el-form-item
+            :prop="'payablesList.' + scope.$index + '.fundType'"
+            label-width="0"
+          >
+            <el-select
+              v-model="scope.row.fundType"
+              placeholder="请选择"
+              class="ele-block"
+              :disabled="isView"
+              @change="(val) => updateRowField(scope.$index, 'fundType', val)"
+            >
+              <el-option
+                v-for="item in paymentTypeOp"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              />
+            </el-select>
+          </el-form-item>
+        </template>
+
+        <template v-slot:transactionMode="scope">
+          <el-form-item
+            :prop="'payablesList.' + scope.$index + '.transactionMode'"
+            label-width="0"
+          >
+            <el-select
+              v-model="scope.row.transactionMode"
+              placeholder="请选择"
+              class="ele-block"
+              :disabled="isView"
+              @change="(val) => updateRowField(scope.$index, 'transactionMode', val)"
+            >
+              <el-option
+                v-for="item in transactionMethodsOp"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              />
+            </el-select>
+          </el-form-item>
+        </template>
+
+        <template v-slot:paymentConditionName="scope">
+          <el-input
+            clearable
+            :value="scope.row.paymentConditionName"
+            @click.native="openPaymentTerms(scope.$index)"
+            :placeholder="isView ? '' : '请选择'"
+            :disabled="isView"
+            readonly
+          />
+        </template>
+
+        <template v-slot:nowConfirmedPrepayAmt="scope">
+          <el-form-item
+            :prop="'payablesList.' + scope.$index + '.nowConfirmedPrepayAmt'"
+            :rules="{
+              validator: (rule, value, cb) =>
+                validateConfirmAmount(rule, value, cb, scope.row),
+              trigger: 'change'
+            }"
+          >
+            <el-input
+              v-model="scope.row.nowConfirmedPrepayAmt"
+              type="number"
+              :disabled="true"
+              @input="handleConfirmAmountChange(scope.row, scope.$index)"
+            />
+          </el-form-item>
+        </template>
+
+        <template v-slot:action="scope">
+          <el-link
+            v-if="!isView"
+            type="danger"
+            :underline="false"
+            icon="el-icon-delete"
+            @click="removeRow(scope.$index)"
+            >删除</el-link
+          >
+        </template>
+
+        <template v-slot:columnRequired="{ column }">
+          <span class="is-required">{{ column.label }}</span>
+        </template>
+      </ele-pro-table>
+    </el-form>
+  </div>
+</template>
+
+<script>
+  import {
+    paymentTypeOp,
+    transactionMethodsOp,
+    paymentStatus
+  } from '@/enum/dict';
+
+  export default {
+    name: 'payablesInfo',
+    components: {  },
+    props: {
+      dataForm: {
+        type: Object,
+        default: () => ({})
+      },
+      // 父组件传入的初始应付明细,仅作为初始化值,修改不再回写父
+      payablesList: {
+        type: Array,
+        default: () => []
+      },
+      dialogType: {
+        type: String,
+        default: 'add'
+      },
+      statementAdvanceTotalPriceTitle: {
+        type: String,
+        default: '总对账预付款确收金额'
+      },
+      columnsTitle: {
+        type: String,
+        default: '本次对账预付款确收金额'
+      }
+    },
+    data() {
+      return {
+        cacheKeyUrl: 'eom-202608181000-purchase-payables-table',
+        paymentTypeOp,
+        transactionMethodsOp,
+        paymentStatusOp: paymentStatus,
+        rules: {},
+        // 本地维护的应付明细,避免每次编辑都同步回父组件
+        localList: [],
+        columns: [
+          {
+            width: 60,
+            label: '序号',
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+          {
+            minWidth: 140,
+            prop: 'purchaseCode',
+            label: '采购订单编码',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 140,
+            prop: 'prepayCodes',
+            label: '预付编码',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'fundType',
+            minWidth: 140,
+            label: '款项类型',
+            align: 'center',
+            slot: 'fundType',
+            headerSlot: 'columnRequired'
+          },
+          {
+            prop: 'transactionMode',
+            minWidth: 140,
+            label: '交易方式',
+            align: 'center',
+            slot: 'transactionMode',
+            headerSlot: 'columnRequired'
+          },
+          {
+            prop: 'paymentConditionName',
+            minWidth: 160,
+            label: '付款条件',
+            align: 'center',
+            slot: 'paymentConditionName'
+          },
+          {
+            prop: 'prepayTotalAmt',
+            minWidth: 160,
+            label: '预付合计金额',
+            align: 'center',
+          },
+          {
+            prop: 'confirmedPrepayAmt',
+            minWidth: 160,
+            label: '累计预付确付金额',
+            align: 'center',
+          },
+          {
+            prop: 'unpaidPrepayAmt',
+            minWidth: 160,
+            label: '未付预付金额',
+            align: 'center',
+          },
+          // {
+          //   minWidth: 170,
+          //   prop: 'unreconciledAdvanceConfirmAmount',
+          //   label: '未对账预付款确收金额',
+          //   align: 'center',
+          //   showOverflowTooltip: true
+          // },
+          {
+            minWidth: 190,
+            headerSlot: 'columnRequired',
+            prop: 'nowConfirmedPrepayAmt',
+            label: this.columnsTitle,
+            align: 'center',
+            slot: 'nowConfirmedPrepayAmt'
+          },
+          {
+            minWidth: 160,
+            prop: 'lastPayableAmt',
+            label: '上期累计应付金额',
+            align: 'center'
+          },
+          {
+            minWidth: 120,
+            prop: 'statementAmount',
+            label: '本次对账金额',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 120,
+            prop: 'statementAmt',
+            label: '本次对账应付总金额',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 160,
+            prop: 'payableAmt',
+            label: '累计应付金额',
+            align: 'center',
+            showOverflowTooltip: true
+          }
+        ]
+      };
+    },
+    computed: {
+      isView() {
+        return this.dialogType === 'view';
+      },
+      statementAdvanceTotalPrice() {
+        return this.dataForm?.statPayable?.statementPrepayTotalPrice || 0
+        // return (
+        //   this.localList.reduce(
+        //     (pre, cur) =>
+        //       pre + Math.round((+cur.confirmedPrepayAmt || 0) * 100),
+        //     0
+        //   ) / 100
+        // );
+      },
+      receivedTotalPrice() {
+        return (
+          // Math.round(
+          //   ((+this.dataForm.amountTotalPrice || 0) -
+          //     this.statementAdvanceTotalPrice) *
+          //     100
+          // ) / 100
+          this.localList.reduce(
+            (pre, cur) =>
+              pre + Math.round((+cur.payablePrice || 0) * 100),
+            0
+          ) / 100
+        );
+      }
+    },
+    watch: {
+      // 父传入初始值(查询/编辑回填)时同步到本地副本
+      payablesList: {
+        handler(val) {
+          this.localList = (val || []).map((item) => ({ ...item }));
+          this.recalcAllRows();
+        },
+        immediate: true
+      },
+      'dataForm.amountTotalPrice': {
+        handler() {
+          this.recalcAllRows();
+        },
+        immediate: true
+      }
+    },
+    methods: {
+      openPaymentTerms(index) {
+        this.currentRowIndex = index;
+        this.$refs.receiveTermsDialogRef.open();
+      },
+      changeReceiveTerms(row, index) {
+        const targetIndex = index != null ? index : this.currentRowIndex;
+        if (targetIndex == null) return;
+        this.updateRowField(targetIndex, 'collectionConditionId', row.id);
+        this.updateRowField(targetIndex, 'collectionConditionName', row.name);
+      },
+      // 行内字段修改:仅更新本地副本
+      // 注意:必须用 $set 直接修改原对象属性,保持引用不变,
+      // 否则 ele-pro-table 内部缓存的 tableData 仍引用旧对象,导致表格不更新
+      updateRowField(index, key, val) {
+        const item = this.localList[index];
+        if (!item) return;
+        this.$set(item, key, val);
+      },
+      // 新增一行空白预付
+      addRow() {
+        this.localList.push({
+          id: 'local_' + Date.now() + '_' + this.localList.length,
+          purchaseCode: '',
+          advanceCode: '',
+          fundType: '',
+          transactionMode: '',
+          paymentConditionId: '',
+          paymentConditionName: '',
+          unreconciledAdvanceConfirmAmount: 0,
+          nowConfirmedPrepayAmt: 0,
+          lastPayableAmt: 0,
+          statementAmount: 0,
+          payablePrice: 0,
+          payableAmt: 0
+        });
+      },
+      // 由父组件预付款选择器回调调用:追加一行已映射好的应付明细(本地维护)
+      addPayableRow(row) {
+        this.localList.push({ ...row });
+      },
+      removeRow(index) {
+        if (index < 0 || index >= this.localList.length) return;
+        this.localList.splice(index, 1);
+      },
+      // 本次对账预付款确收金额上限 = min(未对账预付款确收金额, 本次对账金额)
+      getMaxConfirmAmount(row) {
+        return Math.min(
+          +row.unreconciledAdvanceConfirmAmount || 0,
+          +row.statementAmount || 0
+        );
+      },
+      handleConfirmAmountChange(row, index) {
+        // 限制本次确收金额不能超过未对账金额和本次对账金额
+        let val = +row.nowConfirmedPrepayAmt || 0;
+        const max = this.getMaxConfirmAmount(row);
+        if (val > max) {
+          val = max;
+          // this.$set(row, 'nowConfirmedPrepayAmt', val);
+          this.$message.warning(
+            '本次对账预付款确收金额不能超过未对账金额和本次对账金额'
+          );
+        }
+        this.recalcRow(row);
+      },
+      // 根据本次对账金额重算单行:
+      // 确收金额 = min(未对账预付款确收金额, 本次对账金额)
+      // 本次应付金额 = 本次对账金额 - 确收金额
+      // 累计应付金额 = 本次对账金额 - 累计预付确付金额 + 上期累计应付金额
+      recalcRow(row) {
+        const statementAmount = +row.statementAmount || 0;
+        let val = +row.nowConfirmedPrepayAmt || 0;
+        const max = this.getMaxConfirmAmount(row);
+        if (val > max) val = max;
+        // this.$set(row, 'nowConfirmedPrepayAmt', val);
+        // 本次应付金额 = 本次对账金额 - 确收金额
+        this.$set(
+          row,
+          'payablePrice',
+          Math.round((statementAmount - val) * 100) / 100
+        );
+        // 累计应付金额=本次对账金额-累计预付确付金额+上期累计应付金额
+        const totalPayableAmount =
+          statementAmount -
+          (+row.confirmedPrepayAmt || 0) +
+          (+row.lastPayableAmt || 0);
+        this.$set(
+          row,
+          'payableAmt',
+          Math.round(totalPayableAmount * 100) / 100
+        );
+      },
+      // 明细表按采购单号汇总本次对账金额后,同步到对应采购单号的应付行
+      syncStatementAmount(summaryMap) {
+        if (!summaryMap || !Object.keys(summaryMap).length) return;
+        this.localList.forEach((row) => {
+          const amount = summaryMap[row.purchaseCode];
+          if (amount !== undefined) {
+            this.$set(row, 'statementAmount', amount);
+            this.recalcRow(row);
+          }
+        });
+      },
+      recalcAllRows() {
+        this.localList.forEach((row) => {
+          this.recalcRow(row);
+        });
+      },
+      validateConfirmAmount(rule, value, callback, row) {
+        return callback();
+        const val = +value || 0;
+        const max = this.getMaxConfirmAmount(row);
+        if (val < 0) {
+          callback(new Error('不能小于0'));
+        } else if (val > max) {
+          callback(new Error(`不能超过${max}`));
+        } else {
+          callback();
+        }
+      },
+      // 提交时由父组件调用,一次性取回整个应付表单(本地明细 + 汇总)
+      getSubmitData() {
+        return {
+          payablesList: this.localList.map((item) => ({ ...item })),
+          statementAdvanceTotalPrice: this.statementAdvanceTotalPrice,
+          receivedTotalPrice: this.receivedTotalPrice
+        };
+      },
+      validate() {
+        return new Promise((resolve, reject) => {
+          const promises = [this.$refs.form.validate()];
+          if (this.$refs.tableForm) {
+            promises.push(this.$refs.tableForm.validate());
+          }
+          Promise.all(promises)
+            .then(() => resolve(true))
+            .catch(() => reject(false));
+        });
+      }
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .payables-info {
+    .payables-table-header {
+      display: flex;
+      justify-content: flex-end;
+      margin-bottom: 10px;
+    }
+    .el-form-item {
+      margin-bottom: 0 !important;
+    }
+  }
+  ::v-deep .is-required::before {
+    content: '*';
+    color: #f56c6c;
+    margin-right: 4px;
+  }
+</style>