react 项目 计算列表金额 数据 汇总并实时变更

来,先看需求,
在这里插入图片描述
在这里插入图片描述
总体来说简单归纳一下需求:
就是表头数据的一些特定值变更后会影响明细的税率值,实时变更,明细变更后影响会总行数据,然后表头也变更。如果单独改了明细的数据值,那么相应的汇总,和表头都要自动进行计算。

明细行和会总行是可编辑表格,这个直接用公司封装的组件就行。编辑表格的需求就是: 明细行可编辑的字段涉及到计算的就是:数量。单价。含税单价。

需要注意的是,会总行数据来源源自于明细,且明细行相同的商品带出的数据,(也就是相同商品所在的数据,每个金额计算以及数量都要相加后赋值到会总行展示)要计算结果后展示在汇总数据行。
并且在新增、编辑、删除的时候不会影响每行的数据变化。

会总行 的结果是明细行的计算,表头金额值是会总行的计算。(是不是觉得,把明细行的每行值结果加起来以后直接给表头就行了,会总行要不要都行,但是人家就非要这样,相同的在一块,不想同的自己站一行。)

新增的原型
在这里插入图片描述

在这里插入图片描述
来看代码吧。
提示,相对于所有的金额计算方面的最后都单独做个封装,一般计算逻辑都是一样的。一个公司的一个项目不会出现2个以上的计算逻辑。(出现两个的情况也是区分的不通的状态下,并且应该也是计算的反值计算。其实都一样。)

1,现在我们已经点击了新增,跳转到了新增页面

  handleCreate = (keys, rows, type) => {
    if (type === 'create') {
      this.props.push('/fin/payablesManagement/payable/BillsPayable/create');  // 跳转到新增页面
    }
  };

2 看下边的布局解释在这里插入图片描述

修改的原型
在这里插入图片描述
代码有点多 最外层的inde文件,切记要引入我们的组件哦。

class AdjustEdit extends React.Component<Props, State> {
  constructor(props) {
    super(props);
    this.state = {
      loading: false,
      formRef: null,
      editTableRef: null,
      editTotTableRef: null,
      formData: {}, //表单值
      editDataSource: [], //表格值
      editTotDataSource: [], //汇总表格值
      type: this.props.match.path.includes('/edit/') ? 'edit' : 'create',
      id: this.props.match.params?.id,
      basicRef: null
    };
  }

  componentDidMount() {  //  钩子函数
    if (this.state.type === 'create') {
      this.initAddNewData(); // 新增逻辑
    } else {
      this.getDetail(this.state.id); // 编辑赋值逻辑
    }
  }
  // 新增
  initAddNewData = async () => {
    const initData: any = {
      apOrderNo: '', //应付单号
      sourceNo: '', //来源系统单号
      createMode: 'SG', //来源单据 默认为手动
      taxFlag: true, //是否含税单价 默认为是
      orderState: 'DRAFT', //状态 默认为草稿
      buDate: dayjs().format('YYYY-MM-DD'), // 业务日期 默认为当前日期
      initFlag: false, //是否期初 默认为否
      taxRate: 0, //税率
      exchangeRate: 1, //汇率
      totalAmt: 0, //含税金额
      exclTaxAmt: 0, //不含税金额
      totalCurAmt: 0, //含税金额本位币
      exclTaxCurAmt: 0, //不含税金额本位币
      taxAmt: 0, //税额
      taxCurAmt: 0 //税额本位币
    };
    this.setState({
      loading: true
    });
     // 获取默认应付单类型接口,需要就用不要就删
    const res = await service.getApTypeDefault();
    this.setState({
      loading: false
    });
    if (res.success) {
      const data = res.data;
      initData.apTypeId = {
        id: data.id,
        apTypeCode: data.apTypeCode,
        apTypeName: data.apTypeName
      };
    } else {
      ElNotification({
        type: 'error',
        message: res.msg || '获取默认应付单类型失败'
      });
    }
    this.setState({ formData: initData });
  };

  // 查详情
  getDetail = async (id) => {
    this.setState({ loading: true });
    const res = await service.searchEditInfo({ id }); // 获取所有的返回数据
    this.setState({ loading: false });
    if (res.success) {
      const { apOrderDtlVOList, apOrderDtlGroupVOList, ...masterData } =
        res.data;
       // 表单信息
      const transMasterData = this.transBasicFormData(masterData);
      // 明细数据
      const transDetailData = this.transDetailData(apOrderDtlVOList);
      // 汇总数据
      const transTotalData = this.transTotalData(apOrderDtlGroupVOList);
      this.setState({
        formData: transMasterData,
        editDataSource: transDetailData,
        editTotDataSource: transTotalData
      });
    } else {
      ElNotification({
        type: 'error',
        message: res.msg || '操作失败!'
      });
    }
  };
  // 查询返回数据后 特殊处理
  // 表单数据处理
  transBasicFormData = (data) => {
    return {
      ...data,
      currCode: {
        currCode: data.currCode,
        currName: data.currName
      },
      operUserId: {
        id: data.operUserId,
        empName: data.operator
      },
      ouId: {
        id: data.ouId,
        ouCode: data.ouCode,
        ouName: data.ouName
      },
      suppId: {
        id: data.suppId,
        suppCode: data.suppCode,
        suppName: data.suppName
      },
      // 付款条件
      payMentCode: {
        // id: data.payMentId,
        payMentCode: data.payMentCode,
        payMentName: data.payMentName
      },
      apTypeId: {
        id: data.apTypeId,
        apTypeCode: data.apTypeCode,
        apTypeName: data.apTypeName
      },
      buId: {
        id: data.suppId,
        buCode: data.buCode,
        buName: data.buName
      },
      buDate: dayjs(data.buDate).format('YYYY-MM-DD')
    };
  };
  //明细数据处理
  transDetailData = (dataArr) => {
    const tempData = dataArr.map((item) => {
      return {
        ...item,
        itemCode: {
          spuCode: item.itemCode,
          spuId: item.itemId,
          spuName: item.itemName,
          spec: item.itemType,
          itemSource: item.smallCateCode,
          itemSourceName: item.smallCateName,
          uom: item.uom,
          uomName: item.uomName
        }
      };
    });
    return tempData;
  };
  // 汇总数据处理
  transTotalData = (dataArr) => {
    const tempData = dataArr.map((item) => {
      return {
        ...item,
        buId: {
          buId: item.buId,
          buName: item.buName
        },
        expensesType: {
          udcVal: item.expensesType,
          valDesc: item.expensesTypeName
        }
      };
    });
    return tempData;
  };

  // 保存按钮触发
  beforeSave = async () => {
    const data = await this.proceData();
    data && this.presave(data);
  };
  // 保存
  presave = async (data) => {
    this.setState({ loading: true });
    let res = null;
    if (this.state.type === 'create') {
      res = await service.addNewSave(data);
    } else {
      res = await service.editSave(data);
    }
    this.setState({ loading: false });
    this.handSave(res)
  };
  // 提交按钮触发
  beforeSubmit = async () => {
    const data = await this.proceData();
    data && this.submit(data);
  };
  // 提交
  submit = async (data) => {
    this.setState({ loading: true });
    await this.state.editTableRef.quitEditState();
    const res = await service.submit(data);
    this.setState({ loading: false });
    this.handSave(res)
  };
  // 保存或者提交后的页面返回封装 
  handSave = (res) =>{
   if (res.success) {
      ElNotification({ type: 'success', message: '操作成功!' });
      const { store } = this.props;
      store.MultiTabMobx.closeCurrentToPath(
        '/fin/payablesManagement/payable/billsPayable'
      );
    } else {
      ElNotification({
        type: 'error',
        message: res.msg || res.data || '操作失败!'
      });
    }
}

  // 处理数据
  proceData = async () => {
    const { formRef, editTableRef, editTotTableRef } = this.state;
    const formDataRes = await formRef.validateFields();  // 表头数据
    await editTableRef.quitEditState(); // 明细行保存退出编辑状态, 这个editTableRef.quitEditState()是哥哥项目里封装的方法,看看自己的项目怎么退出编辑状态的
    const detailTableValues = await editTableRef.validateTableRows(); //  明细行数据
    if (detailTableValues.data.length === 0) {
      // 判断是否新增行
      ElNotification({
        type: 'error',
        message: '请添加明细信息!'
      });
      return false;
    }
    if (!detailTableValues.success) {
      ElNotification({
        type: 'error',
        message:
          '明细数据有误:' + this.getDetailErrorMsg(detailTableValues.msg)
      });
      return false;
    }
    let tableArr = detailTableValues.data;
    tableArr = tableArr.map((item) => {
      return {
        ...item,
        itemCode: item.itemCode.spuCode
      };
    });
    await editTotTableRef.quitEditState(); // 会总行保存退出编辑状态
    const totalTableValues = await editTotTableRef.validateTableRows(); // 汇总行数据处理
    // 以为只要新增就必须增加明细,要不然就无法保存,所有必有明细和汇总信息,这里就不会在去验证汇总行是否有数据了,但是明细还是要给的,因为,明细里面是有必填校验的。
    if (!totalTableValues.success) {
      ElNotification({
        type: 'error',
        message: '汇总数据有误:' + this.getDetailErrorMsg(totalTableValues.msg)
      });
      return false;
    }
    let totalTableArr = totalTableValues.data;
    totalTableArr = totalTableArr.map((item) => {
      return {
        ...item,
        buId: item.buId?.buId,
        buName: item.buId?.buName,
        expensesType: item?.expensesType?.udcVal
      };
    });
    
	// 后台接口所需参数 解析
    const params = {
      ...this.state.formData, // 表单数据 新增时的默认或者是编辑时的赋值信息
      ...formDataRes, // 处理所有表单头的数据
      // 币种
      currCode: formDataRes.currCode?.currCode,
      currName: formDataRes.currCode?.currName,
      // 经办人信息 员工信息
      operUserId: formDataRes.operUserId?.id || '',
      operator: formDataRes.operUserId?.empName || '',
      // 部门信息
      buId: formDataRes.buId?.id || '',
      buCode: formDataRes.buId?.buCode || '',
      buName: formDataRes.buId?.buName || '',
      // 公司
      ouId: formDataRes.ouId?.id,
      ouCode: formDataRes.ouId?.ouCode,
      ouName: formDataRes.ouId?.ouName,
      // 供应商
      suppId: formDataRes.suppId?.id,
      suppCode: formDataRes.suppId?.suppCode,
      suppName: formDataRes.suppId?.suppName,
      // 付款条件
      payMentCode:
        formDataRes.payMentCode?.ptCode || formDataRes.suppId?.paymentTerm,
      payMentName:
        formDataRes.payMentCode?.ptName || formDataRes.suppId?.paymentTermName,
      // 应付款类型
      apTypeId: formDataRes.apTypeId?.id,
      apTypeCode: formDataRes.apTypeId?.apTypeCode,
      apTypeName: formDataRes.apTypeId?.apTypeName,
      // 业务日期
      buDate: dayjs(formDataRes.buDate).format('YYYY-MM-DD hh:mm:ss'),
      // 编辑表格信息行内容
      apOrderDtlSaveParamList: tableArr,
      //汇总行信息内容
      apOrderDtlGroupSaveParamList: totalTableArr
    };
    return params;
  };

 // 这个是封装的报错信息,因为有的时候必填校验比较多,这个可以拿到那个地方有错,当然还要和后台接口配合,用不到就不要看了。
  getDetailErrorMsg = (msg: ValidatorResult) => {
    const map = new Map();
    msg.errors.forEach((err) => {
      if (err.path.length == 2) {
        if (!map.has(err.path[0])) map.set(err.path[0], []);
        map.get(err.path[0]).push((err.schema as any)?.message);
      }
    });
    const str = [...map.entries()]
      .map(([index, errArr]) => {
        return `第${index + 1}行${errArr.join(',')};`;
      })
      .join();
    return str;
  };
// 返回按钮
  onBack = () => {
    const { push } = this.props;
    push('/fin/payablesManagement/payable/billsPayable', () => false);
  };
//  父子组件传值,接收表头的信息改变值处理联动
  onBaseFormValuesChange = async (changedFields) => {
    const changeField = Object.keys(changedFields)[0];
    switch (changeField) {
      case 'currCode':
        this.currChange(changedFields);
        break;
      case 'suppId':
        this.suppIdChange(changedFields);
        break;
      case 'ouId':
        this.ouIdChange(changedFields);
        break;
    }
  };
  // 表头币别改变
  currChange = async (changedFields) => {
    await this.state.editTableRef.quitEditState();
    this.calcDetailCurrAmt(); //计算明细本位币类金额
    setTimeout(() => {
      this.updateTotTable(); //更新汇总行
      this.calcMasterCurrAmt(); //计算表头本位币类金额
    }, 0);
  };

  // 表头供应商改变
  suppIdChange = async (changedFields) => {
    let taxRate = 0;
    if (changedFields.suppId) {
      taxRate = changedFields.suppId?.taxRateValue || '';
    }
    this.setState({
      formData: {
        ...this.state.formData,
        ...this.state.formRef.getFieldsValue(),
        taxRate
      }
    });
    // 表头税率改变 明细需要重新计算
    const taxFlag = this.state.formRef.getFieldValue('taxFlag'); //是否单价含税
    const exchangeRate = this.state.formRef.getFieldValue('exchangeRate'); //是否单价含税
    await this.state.editTableRef.quitEditState();
    this.state.editTableRef.updateTableData((record) => {
      let updateObj: any = {};
      const { qty } = record;
      let { exclTaxPrice, price } = record;
      if (taxFlag) {
        //是否含税单价为是 以含税单价为准 算未税单价
        exclTaxPrice = calcService.Price2exclTaxPrice(price, taxRate);
        updateObj.exclTaxPrice = exclTaxPrice;
      } else {
        //是都含税单价为否 以未税单价为准 算含税单价
        // 封装的计算方法calcService.exclTaxPrice2Price(xx,yy);
        updateObj.price = calcService.exclTaxPrice2Price(exclTaxPrice, taxRate);
      }
      updateObj = {
        taxRate,
        ...updateObj,
        ...calcService.calcDetailRow(qty, exclTaxPrice, taxRate, exchangeRate)
      };
      return updateObj;
    });
    setTimeout(() => {
      this.updateTotTable(); //更新汇总行
      this.calcMaster(); //表头计算
    }, 0);
  };

  // 表头公司改变 ==》 本位币变化 ==》 汇率变化
  ouIdChange = async (changedFields) => {
    await this.state.editTableRef.quitEditState();
    this.calcDetailCurrAmt(); //计算明细本位币类金额
    setTimeout(() => {
      this.updateTotTable(); //更新汇总行
      this.calcMasterCurrAmt(); //计算表头本位币类金额
    }, 0);
  };
  
  // 父子传值事件, 明细行数据变化处理联动
  onDetailValuesChange = async (
    changedValues,
    allValues,
    record,
    formRef,
    editTableRef,
    extraData
  ) => {
    const changeField = Object.keys(changedValues)[0];
    switch (changeField) {
      case 'itemCode':
        this.updateTotTable(true, extraData); //更新汇总行
        break;
      case 'exclTaxPrice':
      case 'price':
      case 'qty':
        this.updateTotTable(true, extraData); //更新汇总行
        this.calcMaster(true, extraData);
        break;
    }
  };

// 明细行删除按钮
  onDetailRowsDelete = async () => {
    await this.state.editTableRef.quitEditState();
    this.updateTotTable(); //更新汇总行
    this.calcMaster();
  };

  //计算明细本位币类金额
  calcDetailCurrAmt = () => {
    let exchangeRate = this.state.formRef.getFieldValue('exchangeRate');
    this.state.editTableRef.updateTableData((record) => {
      const { exclTaxAmt, taxAmt } = record;
      return calcService.calcDetailRowCurrAmt(exclTaxAmt, taxAmt, exchangeRate);
    });
  };

  //计算表头本位币类金额
  calcMasterCurrAmt = async () => {
    const fields = ['exclTaxCurAmt', 'taxCurAmt', 'totalCurAmt'];
    const initCurrAmtObj = {};
    fields.forEach((field) => {
      initCurrAmtObj[field] = 0;
    });
    const masCurrAmtObj = this.state.editTableRef
      .getRows()
      .reduce((totalAmtObj, curr) => {
        fields.forEach((field) => {
          totalAmtObj[field] = add(totalAmtObj[field], curr[field]);
        });
        return totalAmtObj;
      }, initCurrAmtObj);
    this.state.formRef.setFieldsValue(masCurrAmtObj);
  };

  //计算表头金额
  calcMaster = async (isDetailEditing = false, extraData: any = {}) => {
    const fields = [
      'exclTaxAmt',
      'exclTaxCurAmt',
      'taxAmt',
      'taxCurAmt',
      'totalAmt',
      'totalCurAmt'
    ];
    const initAmtObj = {};
    fields.forEach((field) => {
      initAmtObj[field] = 0;
    });
    const masAmtObj = this.state.editTableRef
      .getRows()
      .reduce((totalAmtObj, curr) => {
        if (isDetailEditing && extraData.id == curr.id) {
          curr = extraData;
        }
        fields.forEach((field) => {
          totalAmtObj[field] = add(totalAmtObj[field], curr[field]);
        });
        return totalAmtObj;
      }, initAmtObj);
    this.state.formRef.setFieldsValue(masAmtObj);
  };
	
	// 更新汇总行数据处理
  updateTotTable = async (isDetailEditing = false, extraData: any = {}) => {
    const fields = [
      'qty',
      'exclTaxAmt',
      'exclTaxCurAmt',
      'taxAmt',
      'taxCurAmt',
      'totalAmt',
      'totalCurAmt'
    ];
    const newRowFields = [
      'itemId',
      'itemName',
      'smallCateCode',
      'smallCateName'
    ];
    const sumItemCodeRecords = this.state.editTableRef
      .getRows()
      .reduce((totalByItemCode, curr) => {
        if (isDetailEditing && extraData.id == curr.id) {
          curr = extraData;
        }
        const { itemCode } = curr;
        const spuCode = itemCode.spuCode;
        let currItemRecord: any = totalByItemCode[spuCode];
        if (!currItemRecord) {
          currItemRecord = totalByItemCode[spuCode] = {
            record0: curr
          };
          fields.forEach((field) => {
            currItemRecord[field] = 0;
          });
        }
        fields.forEach((field) => {
          currItemRecord[field] = add(currItemRecord[field], curr[field]);
        });
        return totalByItemCode;
      }, {});
    await this.state.editTotTableRef.quitEditState();
    const deleteRowIds = [];
    this.state.editTotTableRef.updateTableData((record) => {
      const { itemCode } = record;
      const sumItemCodeRow = sumItemCodeRecords[itemCode];
      if (!sumItemCodeRow) {
        //说明是要删除的行项
        deleteRowIds.push(record.id);
        return {};
      } else {
        delete sumItemCodeRecords[itemCode]; //更新过了之后就删除掉 sumItemCodeRecords剩下的就是要新增的
        delete sumItemCodeRow.record0; //删除这个属性 是不需要更新的 新增行的时候用
        return sumItemCodeRow;
      }
    });
    if (deleteRowIds.length > 0) {
      this.state.editTotTableRef.removeRowsByKeys(deleteRowIds);
    }
    const newRows = Object.values(sumItemCodeRecords).map((item: any) => {
      const newRow = item;
      newRowFields.forEach((field) => [(newRow[field] = item.record0[field])]);
      newRow.itemCode = item.record0.itemCode.spuCode;
      newRow.id = maths.genFakeId(-1);
      delete newRow.record0;
      return newRow;
    });
    if (newRows.length > 0) {
      this.state.editTotTableRef.addRows(newRows);
    }
  };
  // 制单基本信息ref
  basicRef = (ref) => {
    this.setState({
      basicRef: ref
    });
  };

  // form表单ref
  formRef = (ref) => {
    this.setState({
      formRef: ref
    });
  };
  // 可编辑表格ref
  editTableRef = (ref) => {
    this.setState({
      editTableRef: ref
    });
  };

  // 汇总表格ref
  editTotTableRef = (ref) => {
    this.setState({
      editTotTableRef: ref
    });
  };

  render() {
    return (
      <ElPage spinning={this.state.loading}>
        <ElRowContainer
          blocks={[
            {
              key: 'save',
              text: '保存',
              handleClick: this.beforeSave,
              icon: <SaveBlue />,
              authCode: 'fin-pur-tf-save'
            },
            {
              key: 'submit',
              text: '提交',
              handleClick: this.beforeSubmit,
              icon: <SubmitBlue />
            }
          ]}
          onBack={this.onBack}
          position='top'
        />
        <ElCard key='base' id='base' title='应付单'>
          <BaseForm
            editTableRef={this.state.editTableRef}
            onRef={this.formRef}
            formData={this.state.formData}
            type={this.state.type}
            onValuesChange={this.onBaseFormValuesChange}
          />
        </ElCard>

        <ElCard title='制单信息'>
          <ControlForm
            onRef={this.basicRef}
            formData={this.state.formData}
            type={this.state.type}
          />
        </ElCard>

        <ElCard key='detail' id='detail' title='明细信息'>
          <EditTable
            onRef={this.editTableRef}
            editTableRef={this.state.editTableRef}
            formRef={this.state.formRef}
            dataSource={this.state.editDataSource}
            type={this.state.type}
            baseFormData={this.state.formData}
            onvaluesChange={this.onDetailValuesChange}
            onRowsDelete={this.onDetailRowsDelete}
          />
        </ElCard>
        <ElCard title='汇总信息'>
          <EditTotTable
            dataSource={this.state.editTotDataSource} //数据展示
            onRef={this.editTotTableRef}
          />
        </ElCard>
      </ElPage>
    );
  }
}
export default AdjustEdit;

3。然后看看表头自己的内部处理
在这里插入图片描述

import React from 'react';
import ElForm from '@/components/el/ElForm';
import { getFormItems } from './config';
import { ElNotification } from '@/components/el';
import { FormInstance } from 'antd';

interface Props {
  onRef: Function;
  editTableRef: any;
  formData: any;
  type: any;
  onValuesChange?: Function;
}

class BaseForm extends React.Component<Props> {
  formRef: FormInstance = null;
  constructor(props) {
    super(props);
  }

  /**
   * 基本信息改变changedFields 改变的字段信息
   */
  onValuesChange = async (changedFields) => {
    const changeField = Object.keys(changedFields)[0];
    switch (changeField) {
      case 'currCode':
        this.currcyChange(changedFields);
        break;
      case 'ouId':
        this.ouIdChange(changedFields);
        break;
      case 'suppId':
        this.suppIdChange(changedFields);
        break;
      // case 'payMentCode':
      //   this.payMentCodeChange(changedFields);
    }
    this.props?.onValuesChange(changedFields);
  };

  currcyChange = async (changedFields) => {
    if (changedFields.currCode) {
      //todo 根据币别设置汇率
      this.formRef.setFieldsValue({
        exchangeRate: changedFields.currCode?.dispDecimal
      });
    } else {
      this.formRef.setFieldsValue({
        exchangeRate: 1
      });
    }
  };

  ouIdChange = (changedFields) => {
    if (changedFields.ouId) {
      console.log(changedFields.ouId);
      // 根据币别设置汇率
      this.formRef.setFieldsValue({
        exchangeRate: changedFields.ouId?.exchangeRate || 7
      });
    } else {
      this.formRef.setFieldsValue({
        exchangeRate: 1
      });
    }
  };

  suppIdChange = (changedFields) => {
    if (changedFields.suppId) {
      this.formRef.setFieldsValue({
        payMentCode: changedFields.suppId.paymentTermName
      });
    } else {
      this.formRef.setFieldsValue({
        payMentCode: ''
      });
    }
  };
  // payMentCodeChange = (changedFields) => {
  //   if (changedFields.payMentCode) {
  //     this.formRef.setFieldsValue({
  //       payMentCode: changedFields.payMentCode.ptCode
  //     });
  //   } else {
  //     this.formRef.setFieldsValue({
  //       payMentCode: ''
  //     });
  //   }
  // };
  onRef = (form) => {
    this.formRef = form;
    this.props?.onRef(form);
  };

  render() {
    return (
      <ElForm
        onRef={this.onRef}
        formProps={
   
   {
          onValuesChange: this.onValuesChange,
          items: getFormItems(this.props.type, this.props.formData)
        }}
        data={this.props.formData}
      />
    );
  }
}
export default BaseForm;

4.然后看看明细的,这个确实有点多的多。
先看基础数据的传值,他是组件内部之间的传值,也就是自己给自己引入的。
config文件,好多数据是由商品编码带出来的,所以这个文件少不了。但是你们自己公司肯定也有自己的方法。
在这里插入图片描述
代码如下

// 明细信息 基础数据展示处理
import React from 'react';
import { AddBlue, DeleteRed, ImportBlue } from '@/components/el/ElIcon';
import { ElEditTableColumns } from '@/components/el/ElEditTable';
import { ActionButtonProps } from '@/components/el/ElSearchTable';
import { ElNotification } from '@/components/el';
import * as calcService from '../calc'; // 封装的计算金额方法,

const getTableColumns = (type, that): Array<ElEditTableColumns> => [
  {
    title: '来源单号',
    dataIndex: 'sourceNo',
    width: 160
  },
  {
    title: '来源行号',
    dataIndex: 'sourceLine',
    width: 160
  },
  {
    title: '行号',
    dataIndex: 'index',
    width: 60,
    editable: true,
    align: 'left',
    cellRender: (text, record, index) => index + 1
  },
  {
    title: '商品编码',
    dataIndex: 'itemCode',
    align: 'left',
    width: 180,
    editable: true,
    rule: {
      required: true,
      message: '商品编码必填'
    },
    cellRender: (text) => text?.spuCode,
    field: () => {
      return {
        formOption: {
          type: '$fin-Goods-list',
          props: {
            placeholder: '请选择商品编码',
            paramData: {}
          }
        },
        name: 'itemCode'
      };
    },
    selectMapping: async ({
      changedValues,
      allValues,
      record,
      editTableRef,
      formRef
    }) => {
      const itemCode = allValues.itemCode || {};
      const {
        spuId = '',
        spuName = '',
        spec = '',
        itemSource = '',
        itemSourceName = '',
        uom = 'ge',
        uomName = '个'
      } = itemCode;
      //todo 单位取值

      const initObj = calcService.initDetailRow();
      const updateObj = {
        itemId: spuId,
        itemName: spuName,
        itemType: spec,
        smallCateCode: itemSource,
        smallCateName: itemSourceName,
        uom: uom,
        uomName: uomName,

        totalAmt: initObj.totalAmt,
        totalCurAmt: initObj.totalCurAmt,
        taxAmt: initObj.taxAmt,
        taxCurAmt: initObj.taxCurAmt,
        exclTaxAmt: initObj.exclTaxAmt,
        exclTaxCurAmt: initObj.exclTaxCurAmt
      };
      editTableRef.updateRows(updateObj, [record.id]);
      const returnObj = {
        qty: initObj.qty,
        exclTaxPrice: initObj.exclTaxPrice,
        price: initObj.price
      };
      setTimeout(() => {
        that.props?.onvaluesChange(
          changedValues,
          allValues,
          record,
          formRef,
          editTableRef,
          {
            ...record,
            ...allValues,
            ...updateObj,
            ...returnObj
          }
        );
      }, 0);
      return returnObj;
    }
  },
  {
    title: '商品名称',
    dataIndex: 'itemName',
    width: 160
  },
  {
    title: '规格型号',
    dataIndex: 'itemType',
    width: 160
  },
  {
    title: '小类编码',
    dataIndex: 'smallCateCode',
    width: 160
  },
  {
    title: '小类名称',
    dataIndex: 'smallCateName',
    width: 160
  },
  {
    title: '单位',
    dataIndex: 'uomName',
    width: 70
  },
  {
    title: '数量',
    width: 100,
    align: 'left',
    dataIndex: 'qty',
    editable: true,
    selectMapping: ({
      changedValues,
      allValues,
      editTableRef,
      formRef,
      record
    }) => {
      if (allValues.itemCode == '' || allValues.itemCode == undefined) {
        ElNotification({
          type: 'warning',
          message: '请优先处理商品信息'
        });
        return {
          qty: 0,
          price: 0,
          exclTaxPrice: 0
        };
      } else {
        let { qty, exclTaxPrice } = allValues;
        let { taxRate } = record;
        let exchangeRate = that.props.formRef.getFieldValue('exchangeRate');
        const updateObj = calcService.calcDetailRow(
          qty,
          exclTaxPrice,
          taxRate,
          exchangeRate
        );
        editTableRef.updateRows(updateObj, [record.id]);
        setTimeout(() => {
          that.props?.onvaluesChange(
            changedValues,
            allValues,
            record,
            formRef,
            editTableRef,
            {
              ...record,
              ...allValues,
              ...updateObj
            }
          );
        }, 0);
        return {};
      }
    },
    field: () => {
      return {
        formOption: {
          props: {
            // min: 1
            // precision: 0,
          },
          type: '$inputNumber'
        },
        name: 'qty'
      };
    }
  },
  {
    title: '单价',
    dataIndex: 'exclTaxPrice',
    width: 160,
    rule: {
      required: true,
      message: '必填'
    },
    editable: true,
    selectMapping: ({
      changedValues,
      allValues,
      editTableRef,
      formRef,
      record
    }) => {
      if (allValues.itemCode == '' || allValues.itemCode == undefined) {
        ElNotification({
          type: 'warning',
          message: '请优先处理商品信息'
        });
        return {
          qty: 0,
          price: 0,
          exclTaxPrice: 0
        };
      } else {
        let { qty, exclTaxPrice } = allValues;
        let { taxRate } = record;
        let exchangeRate = that.props.formRef.getFieldValue('exchangeRate');
        let price = calcService.exclTaxPrice2Price(exclTaxPrice, taxRate);
        const updateObj = calcService.calcDetailRow(
          qty,
          exclTaxPrice,
          taxRate,
          exchangeRate
        );
        editTableRef.updateRows(updateObj, [record.id]);
        const returnObj = {
          price
        };
        setTimeout(() => {
          that.props?.onvaluesChange(
            changedValues,
            allValues,
            record,
            formRef,
            editTableRef,
            {
              ...record,
              ...allValues,
              ...updateObj,
              ...returnObj
            }
          );
        }, 0);
        return returnObj;
      }
    },
    field: ({}) => {
      return {
        formOption: {
          type: '$inputNumber',
          props: {
            placeholder: '请输入单价'
          }
        },
        name: 'exclTaxPrice'
      };
    }
  },
  {
    title: '含税单价',
    dataIndex: 'price',
    width: 160,
    rule: {
      required: true,
      message: '必填'
    },
    editable: true,
    selectMapping: ({
      changedValues,
      allValues,
      editTableRef,
      formRef,
      record
    }) => {
      if (allValues.itemCode == '' || allValues.itemCode == undefined) {
        ElNotification({
          type: 'warning',
          message: '请优先处理商品信息'
        });
        return {
          qty: 0,
          price: 0,
          exclTaxPrice: 0
        };
      } else {
        let { qty, price } = allValues;
        let { taxRate } = record;
        let exchangeRate = that.props.formRef.getFieldValue('exchangeRate');
        let exclTaxPrice = calcService.Price2exclTaxPrice(price, taxRate);

        const updateObj = calcService.calcDetailRow(
          qty,
          exclTaxPrice,
          taxRate,
          exchangeRate
        );
        editTableRef.updateRows(updateObj, [record.id]);
        const returnObj = {
          exclTaxPrice
        };
        setTimeout(() => {
          that.props?.onvaluesChange(
            changedValues,
            allValues,
            record,
            formRef,
            editTableRef,
            {
              ...record,
              ...allValues,
              ...updateObj,
              ...returnObj
            }
          );
        }, 0);
        return returnObj;
      }
    },
    field: ({ record }) => {
      return {
        formOption: {
          type: '$inputNumber',
          props: {
            placeholder: ''
          }
        },
        name: 'price'
      };
    }
  },
  {
    title: '含税金额', // 含税金额=含税单价*数量;
    dataIndex: 'totalAmt',
    width: 160,
    align: 'right'
  },
  {
    title: '含税金额本位币', //含税金额本位币=含税金额*汇率;
    dataIndex: 'totalCurAmt',
    width: 160
  },
  {
    title: '税率', // 表头供应商带出
    dataIndex: 'taxRate',
    width: 160
  },
  {
    title: '税额', // 税额=四舍五入 ((数量*含税单价)*税率/(1+税率)),2);
    dataIndex: 'taxAmt',
    width: 160
  },
  {
    title: '税额本位币', //   税额本位币=税额*汇率;
    dataIndex: 'taxCurAmt',
    width: 160
  },
  {
    title: '不含税金额', // 不含税金额=含税金额-税额;
    dataIndex: 'exclTaxAmt',
    width: 160
  },
  {
    title: '不含税金额本位币', //  不含税金额本位币=含税金额本位币-税额本位币;
    dataIndex: 'exclTaxCurAmt',
    width: 160
  },
  {
    title: '备注',
    dataIndex: 'remark',
    width: 160,
    editable: true,
    field: () => ({
      name: 'remark',
      formOption: {
        type: '$input',
        props: {
          placeholder: '请输入备注'
        }
      }
    })
  }
];
const getTableActionButtons = (
  PayCreate,
  PaydDel
): Array<ActionButtonProps> => [
  {
    text: '新增',
    key: 'fin-withdraw-create',
    handleClick: PayCreate,
    location: 'left',
    icon: <AddBlue />,
    authCode: 'fin-pay-tf-add'
  },
  {
    text: '删除',
    key: 'fin-withdraw-del',
    handleClick: PaydDel,
    location: 'left',
    minSelection: 1,
    needConfirm: true,
    icon: <DeleteRed />,
    authCode: 'fin-pay-tf-del'
  }
];

// 非手工不展示按钮
const SpaceshipButton = (): Array<ActionButtonProps> => [];

export { getTableColumns, getTableActionButtons, SpaceshipButton };

5,再看 明细表格的index文件,总共就下边图那么多,就不写了。
在这里插入图片描述
6 最最主要的计算封装来了。这个封装你可以拿去直接用,换换字段就好了。

import { maths } from '@/utils';
const { add, mul, div } = maths;
// 保留小数位
const toFixedPrice = 6;
const toFixedAmt = 2;

// 客户输入(导入)含税单价/单价和数量后的计算逻辑:
//含税单价 = 未税单价 * (1+税率)   输入未税单价时执行
//未税单价 = 含税单价 / (1+税率)   输入含税单价时执行
//未税金额 = 未税单价 * 数量
//税额 = 未税金额 * 税率
//含税金额 = 未税金额 + 税额

export function initDetailRow() {
  return {
    qty: 0, // 数量 4
    exclTaxPrice: 0, // 单价 6
    price: 0, // 含税单价 6
    totalAmt: 0, // 含税金额 2
    totalCurAmt: 0, //含税金额本位币2
    taxAmt: 0, // 税额2
    taxCurAmt: 0, // 税额本位币2
    exclTaxAmt: 0, // 不含税金额2
    exclTaxCurAmt: 0 // 不含税金额本位币2
  };
}

export function exclTaxPrice2Price(exclTaxPrice, taxRate) {
  return maths.rounds(mul(exclTaxPrice, 1 + taxRate), toFixedPrice);
}

export function Price2exclTaxPrice(price, taxRate) {
  return maths.rounds(div(price, 1 + taxRate), toFixedPrice);
}
export function calcDetailRow(qty, exclTaxPrice, taxRate, exchangeRate) {
  if (!Number.isFinite(Number(qty))) qty = 0;
  if (!Number.isFinite(Number(exclTaxPrice))) exclTaxPrice = 0;
  if (!Number.isFinite(Number(exchangeRate))) exchangeRate = 1;
  if (!Number.isFinite(Number(taxRate))) exchangeRate = 0;
  const exclTaxAmt = maths.rounds(mul(exclTaxPrice, qty), toFixedAmt); // 不含税金额
  const exclTaxCurAmt = maths.rounds(mul(exclTaxAmt, exchangeRate), toFixedAmt); // 不含税金额本位币
  const taxAmt = maths.rounds(mul(exclTaxAmt, taxRate), toFixedAmt); //税额
  const taxCurAmt = maths.rounds(mul(taxAmt, exchangeRate), toFixedAmt); // 税额本位币
  const totalAmt = maths.rounds(add(exclTaxAmt, taxAmt), toFixedAmt); //含税金额
  const totalCurAmt = maths.rounds(add(exclTaxCurAmt, taxCurAmt), toFixedAmt); //含税金额本位币

  return {
    exclTaxAmt,
    exclTaxCurAmt,
    taxAmt,
    taxCurAmt,
    totalAmt,
    totalCurAmt
  };
}

export function calcDetailRowCurrAmt(exclTaxAmt, taxAmt, exchangeRate) {
  if (!Number.isFinite(Number(exclTaxAmt))) exclTaxAmt = 0;
  if (!Number.isFinite(Number(taxAmt))) taxAmt = 0;
  if (!Number.isFinite(Number(exchangeRate))) exchangeRate = 1;

  const exclTaxCurAmt = maths.rounds(mul(exclTaxAmt, exchangeRate), toFixedAmt); // 不含税金额本位币
  const taxCurAmt = maths.rounds(mul(taxAmt, exchangeRate), toFixedAmt); // 税额本位币
  const totalCurAmt = maths.rounds(add(exclTaxCurAmt, taxCurAmt), toFixedAmt); // 含税金额本位币

  return {
    exclTaxCurAmt,
    taxCurAmt,
    totalCurAmt
  };
}

猜你喜欢

转载自blog.csdn.net/lzfengquan/article/details/124174142