为了实现这个功能我们和后端定义了数据结构
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;