
 为了实现这个功能我们和后端定义了数据结构
data:{
	id:‘’,
	formInfo:'',
	formInfo2:'',
	formInfo3:'',
	formInfo4:'', 
	......
	deailData:[ // 明细数据  // saleData 查询带出的对应明细序列号数据
		{	id:'',	ocopyId:'',	copyId:'',	odoId:'',	......,  saleData:[ {	id:'',	ocopyId:'', odoId:'',	......,} ] },
		{	id:'',	ocopyId:'',	copyId:'',	odoId:'',	......,  saleData:[ {	id:'',	ocopyId:'', odoId:'',	......,} ] },
		{	id:'',	ocopyId:'',	copyId:'',	odoId:'',	......,  saleData:[ {	id:'',	ocopyId:'', odoId:'',	......,} ] }
	]
}
代码入下
/**
 * 编辑页面
 */
import React from 'react';
import { TForm, ELImport_3, ElNotification, ElRowContainer, TCollapse } from '@/cloudt/component';
import { TEditTable } from '@/cloudt/pro-component';
import {
  getDetailColumns,
  getSerialButtons,
  getDetailButtons,
  getTopButtons,
  getBasicForm,
  getControlForm,
  getReceivingForm,
  getSerialEdittabColumns,
} from './config';
import dayjs from 'dayjs';
import { asserts } from '@/utils';
import { save, getDetailById, getFileCode } from './service';
import { FileUpload } from '@/cloudt/component/ItemComponent';
import { maths } from '@/utils';
import { Spin } from 'antd';
import { closeCurrentToPath, frameHistoryPush } from '@/cloudt/frame-history';
interface Props {
  match: any;
}
interface State {
  loading: boolean;
  pageType: string;
  id: string | number;
  bascicFormData: any;
  OrderInfoData: Array<any>;
  recvCountryCode: any;
  fileList: Array<any>;
  preSelectedRow: Record<string, any> | null; //记录上次选中的明细行
  importRequestParams: Record<string, any>;
}
class SaleOrderEdit extends React.Component<Props, State> {
  basicFormRef: any; //基础信息表单ref
  controlFormRef: any; //制单信息
  receiveFormRef: any; //客户收货信息表单ref
  deatilTableRef: any; //明细信息可编辑表格ref
  serialTableRef: any; // 序列号ref
  importModalRef: any; //导入组件ref
  constructor(props) {
    super(props);
    this.state = {
      fileList: [],
      id: this.props.match?.params?.id, //单据id
      pageType: this.props.match.path.includes('/edit/') ? 'edit' : 'view', // 编辑或详情页面
      loading: false, // 页面级loading
      bascicFormData: {}, //基本信息
      OrderInfoData: [], //明细数据
      recvCountryCode: '', // 国家
      preSelectedRow: null,
      importRequestParams: {},
    };
  }
  async componentDidMount() {
    await this.getDetail(this.state.id);
  }
  getDetail = async (id) => {
    this.setState({
      loading: true,
    });
    const res = await getDetailById(id);
    if (res && res.success) {
      const bascicFormData = await this.handleBasicFormData(res?.data);
      const newSalSoDSaveVOList = this.handleDetailEditData(res?.data?.odoDList);
      // 查询附件
      const fileCode = res.data?.fileCode;
      if (fileCode) {
        await this.handleFilesData(res.data?.fileCode.split(','));
      }
      this.setState({
        bascicFormData,
        OrderInfoData: newSalSoDSaveVOList,
      });
    } else {
      ElNotification({
        type: 'error',
        message: res.msg || '查询失败',
      });
    }
    this.setState({
      loading: false,
    });
  };
  handleBasicFormData = async (data) => {
    const basicData = {
      ...data,
      recvArea: [data?.recvProvince, data?.recvCity, data?.recvCounty],
      whId: {
        whId: data?.whId,
        whCode: data?.whCode,
        whName: data?.whName,
        // deter2: data?.deter2,
        // deter2Name: data?.deter2Name,
      },
    };
    this.setState({
      recvCountryCode: data?.recvCountry,
    });
    return basicData;
  };
  handleFilesData = async (fileCode) => {
    const res = await getFileCode(fileCode);
    if (res && res.success) {
      this.setState({
        fileList: res.data,
      });
    }
  };
  /**
   * 查详情明细行数据处理 -- 明细行原始数据
   */
  handleDetailEditData = (odoDSaveVOList) => {
    const newSalSoDSaveVOList =
      odoDSaveVOList &&
      Array.isArray(odoDSaveVOList) &&
      odoDSaveVOList.map((item) => {
        return {
          ...item,
          // createTime: asserts.isExist(item?.createTime) ? dayjs(item?.createTime).format('YYYY-MM-DD') : null,
          // modifyTime: asserts.isExist(item?.modifyTime) ? dayjs(item?.modifyTime).format('YYYY-MM-DD') : null,
          serialRespVOs: item.serialRespVOs ? item.serialRespVOs : [],
          lotNo: item?.lotNo,
        };
      });
    return newSalSoDSaveVOList;
  };
  // 明细信息行选择事件 更新下边的序号编辑
  handleOrderInfoSelect = async (selectedRowKeys, selectedRows) => {
    await this.deatilTableRef.quitEditState(); // 明细行退出编辑状态
    await this.serialTableRef.quitEditState(); // 序列号退出编辑状态
    const TableValues = await this.serialTableRef.getRows(); // 获取序列号数据
    const detailSelectedRow = selectedRows[0];
    if (this.state.preSelectedRow) {
      this.deatilTableRef.updateRows({ serialRespVOs: TableValues }, [this.state.preSelectedRow.id]);
    }
    const nowSerialData = detailSelectedRow.serialRespVOs;
    await this.serialTableRef?.clearRows(); //清除可编辑表格的数据
    this.serialTableRef?.addRows(nowSerialData); //重新添加查询的数据
    this.setState({
      preSelectedRow: detailSelectedRow,
    });
  };
  // 明细行复制
  handleCopy = async (selectedRowKeys, selectedRows) => {
    const selectedRow = selectedRows[0];
    const isCopyedRow = selectedRow.ocopyId;
    if (isCopyedRow) {
      return ElNotification({
        type: 'warning',
        message: '不能复制已经复制的信息,请重新选择',
      });
    }
    await this.deatilTableRef.quitEditState();
    const dataSouce = await this.deatilTableRef.getRows();
    const index = dataSouce?.findLastIndex((v) => selectedRow?.relateDocLineno === v.relateDocLineno);
    const copyRow = {
      ...selectedRow,
      odoId: this.state.id, // 单头id
      ocopyId: selectedRow.id, // 复制行时把id给ocopyId,
      id: maths.genFakeId(-1),
      demandQty: null,
      qty: 0,
      serialRespVOs: [],
    };
    dataSouce.splice(index + 1, 0, copyRow);
    await this.deatilTableRef.clearRows();
    await this.deatilTableRef.addRows(dataSouce);
  };
  // 明细信息行删除事件
  handleDelClick = async (selectedRowKeys, selectedRows) => {
    const selectedRow = selectedRows[0];
    const isCopyedRow = selectedRow.ocopyId;
    if (!isCopyedRow) {
      return ElNotification({
        type: 'warning',
        message: '只能删除复制行,请重新选择',
      });
    }
    this.deatilTableRef.removeRowsByKeys(selectedRowKeys, 'rowKey'); // 删除明细信息
    await this.serialTableRef.quitEditState(); // 序列号退出编辑状态
    await this.serialTableRef?.clearRows(); //清除可编辑表格的数据
  };
  // 序列号信息添加
  handleAddDataRow = async () => {
    const selectionData = await this.deatilTableRef?.getSelectionData();
    if (selectionData.selectedRows.length !== 1) {
      return ElNotification({
        type: 'warning',
        message: '请选中一行明细',
      });
    }
    const detailSelectedRow = selectionData.selectedRows[0];
    const newRow = {
      id: maths.genFakeId(-1),
      odoId: this.state.id, // 关联单id
      odoDid: detailSelectedRow.id, // 序列号新增时,赋值选中的明细行id
    };
    this.serialTableRef?.addRow(newRow); //重新添加查询的数据
  };
  // 序列号信息删除
  handleDistributionDelete = async (selectedRowKeys) => {
    await this.serialTableRef.quitEditState();
    this.serialTableRef.removeRowsByKeys(selectedRowKeys, 'rowKey');
  };
  // 序列号导入
  handleImport = async () => {
    const selectionData = await this.deatilTableRef?.getSelectionData();
    if (selectionData.selectedRows.length !== 1) {
      return ElNotification({
        type: 'warning',
        message: '请选中一行明细',
      });
    }
    this.setState({
      importRequestParams: { odoDid: selectionData.selectedRows[0].id, odoId: this.state.id },
    });
    this.importModalRef.openModal();
  };
  // 导入方法回调
  importCallBack = async (res) => {
    if (res && res.success) {
      const importSuccessData = res.data.map((x) => {
        return {
          ...x,
          id: maths.genFakeId(-1),
        };
      });
      this.serialTableRef?.addRows(importSuccessData);
      ElNotification({
        type: 'success',
        message: '操作成功!',
      });
    } else if (res && !res.success) {
      ElNotification({
        type: 'error',
        message: res?.msg || '操作失败',
      });
    }
  };
  /**
   * 返回
   */
  onBack = () => {
    closeCurrentToPath('/inventory/outboundDelivery/searchOrder');
  };
  // 保存
  saveParams = async () => {
    //保存之前先把序列号信息退出编辑状态,同步到明细数据中
    const selectionData = await this.deatilTableRef?.getSelectionData();
    const detailSelectedRow = selectionData?.selectedRows[0];
    await this.serialTableRef.quitEditState(); // 序列号退出编辑状态
    const serialEditTableValues = await this.serialTableRef.validateTableRows();
    if (!serialEditTableValues?.msg?.success) {
      ElNotification({
        type: 'warning',
        message: '请添加序列号必填信息!',
      });
      return;
    }
    const TableValues = await this.serialTableRef.getRows(); // 获取序列号数据
    if (this.state.preSelectedRow) {
      this.deatilTableRef.updateRows({ serialRespVOs: TableValues }, [detailSelectedRow.id]);
    }
    const data = await this.processData();
    if (data?.odoDSaveVOList?.length === 0) {
      ElNotification({
        type: 'warning',
        message: '明细信息不允许为空',
      });
      return;
    }
    if (data) {
      this.setState({ loading: true });
      const res = await save(data);
      this.setState({ loading: false });
      if (res.success) {
        ElNotification({
          type: 'success',
          message: res.msg || '操作成功',
        });
        this.onBack();
      } else {
        ElNotification({
          type: 'error',
          message: res.msg || res.data || '操作失败!',
        });
      }
    }
  };
  /*
  入参基本信息处理
  baseFormDataRes 基本信息
  custFormDataRes 客户信息
  */
  paramsBasicFormData = (baseFormDataRes, custFormDataRes) => {
    baseFormDataRes.whId = baseFormDataRes.whId?.whId;
    baseFormDataRes.whCode = baseFormDataRes.whId?.whCode;
    baseFormDataRes.whName = baseFormDataRes.whId?.whName;
    custFormDataRes.recvArea = undefined;
  };
  /*  delSameObjValue 数组对象相同值相加去重
    arr 需要处理的数组   -- editTableValues?.data
    resultNum 最终计算结果的键名 -- sumConfirmQty
    keyName  用于计算判断的键名 这里是以数组的方式传过来的可以这支持接收多个参数判断】 -- relateDocLineno
    keyValue 用于计算结果的键名 这里是以数组的方式传过来的可以这支持接收多个参数判断】 对应的键值为number类型 -- qty
    */
  delSameObjValue = (arr, resultNum, keyName, keyValue) => {
    const warp = new Map();
    arr.forEach((i) => {
      const str = keyName.map((v) => i[v]).join('_');
      i[resultNum] = keyValue.reduce((p, c) => (p += i[c]), 0);
      warp.has(str) ? (warp.get(str)[resultNum] += i[resultNum]) : warp.set(str, i);
    });
    return Array.from(warp).map(([, v]) => v);
  };
  // 校验表单信息
  processData = async () => {
    // 基本信息
    const baseFormDataRes = await this.basicFormRef?.validateFields().catch(() => {
      ElNotification({
        type: 'warning',
        message: '请检查【基本信息】必填信息',
      });
      return Promise.reject();
    });
    // 客户信息
    const custFormDataRes = await this.receiveFormRef?.getFieldsValue();
    // 制单信息
    const otherFormDataRes = await this.controlFormRef?.getFieldsValue();
    this.paramsBasicFormData(baseFormDataRes, custFormDataRes);
    // 附件信息
    const { fileList } = this.state;
    const fileCodes =
      Array.isArray(fileList) &&
      fileList?.map((x) => {
        return x.fileCode;
      });
    const fileCode = fileCodes.toString();
    // 商品明细信息
    await this.deatilTableRef.quitEditState(); //保存退出明细行编辑状态
    const editTableValues = await this.deatilTableRef.validateTableRows();
    const sum = this.delSameObjValue(editTableValues?.data, 'sumConfirmQty', ['relateDocLineno'], ['qty']);
    const flag = sum.some((val) => val.sumConfirmQty !== val.demandQty);
    if (flag) {
      return ElNotification({
        type: 'warning',
        message: '相同【来源单据行号】的数据信息【实发数量】必须等于【要求发货数量】!',
      });
    }
    if (!editTableValues?.msg?.success) {
      return ElNotification({
        type: 'warning',
        message: '请添加明细必填信息!',
      });
    }
    const ruleLotFlagAddlotNo = editTableValues?.data.some(
      (item) => item.lotFlag && (item.lotNo === null || item.lotNo === ''),
    );
    if (ruleLotFlagAddlotNo) {
      return ElNotification({
        type: 'warning',
        message: '明细商品中包含【启用批次】,请检查并完善【批次】',
      });
    }
    const itemDataSource = editTableValues?.data?.map((item) => {
      return {
        ...item,
        odoId: this.state.id,
        lotNo: item?.lotNo?.lotNo,
        olotNo: item?.orlotNo,
        odoSerialSaveVOs: item.serialRespVOs ? item.serialRespVOs : [],
        sumConfirmQty: undefined, // 提示汇总字段不要
        serialRespVOs: undefined, // 返回的数组字段不要
        orlotNo: undefined,
      };
    });
    // 根据公司查询组织
    return {
      ...this.state.bascicFormData,
      id: this.state.bascicFormData?.id, //复制按钮时,入参需要处理
      ...baseFormDataRes, //基础信息
      ...custFormDataRes, //客户信息
      ...otherFormDataRes, // 制单信息
      odoDSaveVOList: itemDataSource, // 明细
      fileCode, // 附件
      odoDList: undefined,
    };
  };
  render() {
    const {
      pageType: type,
      bascicFormData: bascicFormDate,
      loading,
      OrderInfoData,
      fileList,
      importRequestParams,
    } = this.state;
    return (
      <Spin spinning={loading}>
        <div className="inner-content">
          <ElRowContainer
            onBack={this.onBack}
            blocks={getTopButtons(this.saveParams, this.state.pageType)}
            position="top"
          />
          <TCollapse className="formAlignment" title="基本信息" levelOne>
            <TForm
              data={bascicFormDate}
              onRef={(ref) => {
                this.basicFormRef = ref;
              }}
              formProps={getBasicForm(type)}
            />
            <TCollapse title="收货信息" defaultOpen={false} className="formAlignment">
              <TForm
                data={bascicFormDate}
                onRef={(ref) => {
                  this.receiveFormRef = ref;
                }}
                formProps={getReceivingForm(this.state.recvCountryCode)}
              />
            </TCollapse>
            <TCollapse title="制单信息" defaultOpen={false} className="formAlignment">
              <TForm
                data={bascicFormDate}
                onRef={(ref) => {
                  this.controlFormRef = ref;
                }}
                formProps={getControlForm('create')}
              />
            </TCollapse>
          </TCollapse>
          <TCollapse title="明细信息">
            <TEditTable
              tableId="middleGround_inventoryCenter_supply_saleorder_orderDetail"
              bordered
              rowKey="id"
              scroll={{ y: 20000 }}
              onRef={(ref) => (this.deatilTableRef = ref)}
              dataSource={OrderInfoData} //
              actionButtons={getDetailButtons({
                type,
                handleCopy: this.handleCopy,
                handleDelete: this.handleDelClick,
              })}
              needToolBar={true}
              columns={getDetailColumns(this)}
              rowSelectionConfig={{
                type: 'radio',
                fixed: true,
                disabledRowIds: [],
                onChange: ({ selectedRowKeys, selectedRows }) => {
                  this.handleOrderInfoSelect(selectedRowKeys, selectedRows);
                },
              }}
              defaultTableConfig={{
                onBottomPressEnter: 'trigger',
                onTableIntoEdit: 'dbclick',
              }}
            />
          </TCollapse>
          <TCollapse title="序列号信息">
            <TEditTable
              tableId="middleGround_inventoryCenter_supply_saleorder_distributionInformation"
              rowKey="id"
              bordered
              scroll={{ y: 'calc(100vh - 430px)' }}
              dataSource={[]}
              actionButtons={getSerialButtons(
                type,
                this.handleAddDataRow,
                this.handleDistributionDelete,
                this.handleImport,
              )}
              onRef={(ref) => (this.serialTableRef = ref)}
              columns={getSerialEdittabColumns()}
            />
          </TCollapse>
          <TCollapse title="相关附件" levelOne={true}>
            <FileUpload
              useType="outTable"
              fileListLen={999}
              uploadInfo="支持所有类型文件,文件不能超过10M"
              value={fileList}
              onUploadErr={(err) => {
                ElNotification({
                  type: 'error',
                  message: err?.msg || '上传失败',
                });
              }}
              onRemoveErr={(err) => {
                ElNotification({
                  type: 'error',
                  message: err?.msg || '删除失败',
                });
              }}
              onChange={(value) => {
                this.setState({
                  fileList: value,
                });
              }}
              multiple={true}
              fileList={fileList}
              prefix="/cloudt/system"
            />
          </TCollapse>
          <ELImport_3
            push={frameHistoryPush}
            prefix="/yst/inv"
            templateCode="INV_ODO_SERIAL_IMPORT"
            downTemplateConfig={{
              mode: 'templateCode',
              fileName: '序列号导入模版',
            }}
            importConfig={{
              mode: 'request',
              url: '/yst/inv/inv/odod/serial/importOdoSerial',
              maxSize: 1,
              sizeUnit: 'MB',
              note: '仅支持xlsx文件,不能大于20mb',
              accept: 'xlsx|xls|xlsx',
              onSuccess: this.importCallBack,
            }}
            requestParams={importRequestParams}
            onRef={(ref) => (this.importModalRef = ref)}
          />
        </div>
      </Spin>
    );
  }
}
export default SaleOrderEdit;



















