@alifd/next react+form+table combined writing

foreword

Recently, the front-end masters @alifd/nextused to write the background management system, and assigned me some pages similar to the following pages
insert image description here
, in which the branch office, storage address, and storage area are linked at three levels. Every item in the table can be deleted. The page of the pop-up box for adding a button looks like the following.
insert image description here
Click the row of the pop-up box, multiple selection, all selection can be updated to the page of the first picture

problems encountered

  • How to control this table change, how to proceed, the input box can be operated alone, and the drop-down box can be operated alone. If a data element is changed back and forth, then the consumption of the page is very large. Wait a minute, here are a few examples

accomplish

I looked at the official fieldexamples of similar solutions before , and found that the writing is relatively concise. It is also not easy to operate. So I tried form+ tablecombined writing magic changed a bit

1. Use the form to wrap the table, because the form is embedded field, so it should be more convenient.

   const fieldTable = Field.useField(); //初始化fieldTable
    <Form field={
    
    fieldTable}>
         <Table.StickyLock
           size="medium"
           primaryKey="id" // 识别的唯一值
           fixedHeader
           maxBodyHeight={
    
    400} //和fixedHeader结合,设置table盒子的高度
           dataSource={
    
    dataSource} //数据源
           columns={
    
    columns} // 列表
           rowSelection={
    
    rowSelection} // 选择每一行的操作,默认多选
         />
       </Form>

select rowSelectionsettings

 const [selectedRowKeys, setSelectedRowKeys] = useState([]);
 const [selectedRows, setSelectedRows] = useState([]);
 const rowSelection = {
    
    
    selectedRowKeys,
    onChange: (_selectedRowKeys, _selectedRows) => {
    
     // 单选时的操作
      setSelectedRowKeys(_selectedRowKeys);
      setSelectedRows(_selectedRows);
    },
    onSelectAll: (selected, _selectedRows) => {
    
     //全选时的操作
      setSelectedRowKeys(_selectedRows.map((item) => item.cid));
    },
    getProps: (record) => {
    
     // 禁用的☑️的操作
      return {
    
    
        disabled: mode === 'view',
      };
    },
  };

2. Encapsulate the columns, (delete several repeated types, roughly understandable)

const columns = [
      // 新增/编辑用到的id,itemId,不显示但是可以区分(用是否有itemId来区分是点击编辑时拿到的数据,还是编辑时新增还是新增时新增的数据)
      {
    
    
        width: '0px', // 不会显示在页面
        dataIndex: 'id',
        align: 'center',
        lock: 'left',
        cell: (value, index, record) => {
    
    
          return <div style={
    
    {
    
     display: 'none' }}>{
    
    FormChildItem(value, index, record, 'id')}</div>;
        },
      },
      {
    
    
        width: '0px',// 不会显示在页面
        dataIndex: 'itemId',
        align: 'center',
        lock: 'left',
        cell: (value, index, record) => {
    
    
          return <div style={
    
    {
    
     display: 'none' }}>{
    
    FormChildItem(value, index, record, 'itemId')}</div>;
        },
      },
      {
    
    
        title: '类型',
        dataIndex: 'wirType',
        align: 'center',
        lock: 'left',
        cell: (value, index, record) => {
    
    
          return value ? (
            <Tag size="small" type="normal" color={
    
    wirTypeColor[value]}>
              {
    
    wirTypeName[value] || 'BTS备货'}
            </Tag>
          ) : (
            <Tag size="small" type="normal" color="turquoise">
              BTS备货
            </Tag>
          );
        },
      },
      // 下面是formItem进行嵌套的
      mode === 'add' ? isNull() : isRequireNo(), // 根据是编辑还是新增,动态显示table的列
      {
    
    
        title: '物料名称',
        dataIndex: 'itemName',
        align: 'center',
        lock: 'left',
        cell: (value, index, record) => {
    
    
          return FormChildItem(value, index, record, 'itemName');
        },
      },
      {
    
    
        title: '键号',
        dataIndex: 'itemCode',
        align: 'center',
        cell: (value, index, record) => {
    
    
          return FormChildItem(value, index, record, 'itemCode');
        },
      },
      {
    
    
        title: '申请数量',
        dataIndex: 'applyQuantity',
        align: 'center',
        cell: (value, index, record) => {
    
    
          return FormChildItemRequire(value, index, record, 'applyQuantity');
        },
      },
      {
    
    
        title: '存储区',
        dataIndex: 'areaId',
        align: 'center',
        cell: (value, index, record) => {
    
    
          return FormChildItemSelect(value, index, record, 'areaId');
        },
      },
    ];

isNullUsed to operate on ternary expressions, otherwise an error will be reported when writing a table

 const isNull = () => {
    
    
    return {
    
    
      align: 'center',
      width: '0px',
    };
  };
  const isRequireNo = () => {
    
    
    return {
    
    
      title: '订单编号',
      dataIndex: 'requireNo',
      align: 'center',
      lock: 'left',
      cell: (value, index, record) => {
    
    
        return <div>{
    
    value ? `${
      
      value}⚠️` : ''}</div>;
      },
    };
  };

3 Encapsulated FormChildItemcommon form display, FormChildItemRequireverifiable Input, FormChildItemSelectverifiable select. Why do you want to FormChildItemwrap it in a form for ordinary people, mainly because the back-end students need to use the data inside. To deal with, the following reason should be clear.

  • idThe following is used to distinguish each row , ⚠️Do not use the index index instead, because there will be problems when deleting
 const FormChildItem = (value, index, record, _name) => {
    
    
    return (
      <FormItem
        isPreview // 查看的时候不能修改的一个属性
        name={
    
    `${
      
      _name}-${
      
      record.id}`}
        labelAlign="left"
      >
        <Input defaultValue={
    
    value} />
      </FormItem>
    );
  };
  const FormChildItemSelect = (value, index, record, _name) => {
    
    
    return (
      <FormItem
        isPreview={
    
    mode === 'view'}
        asterisk
        name={
    
    `${
      
      _name}-${
      
      record.id}`}
        required
        requiredMessage="请选择存储区"
        labelAlign="left"
      >
        <Select style={
    
    {
    
     width: '110px' }} defaultValue={
    
    value} dataSource={
    
    storageType} />
      </FormItem>
    );
  };
  const FormChildItemRequire = (value, index, record, _name) => {
    
    
    return (
      <FormItem
        isPreview={
    
    mode === 'view'}. 
        asterisk
        name={
    
    `${
      
      _name}-${
      
      record.id}`}
        required
        requiredMessage="请输入申请数量"
        labelAlign="left"
      >
        <NumberPicker defaultValue={
    
    value} min={
    
    0} />
      </FormItem>
    );
  };

4. Now that we talked about deletion, let’s write the method of deletion (I won’t write new ones, it’s relatively simple, and the subcomponent of the pop-up box uses setDatasource to set the passed value)

  // 点击删除按钮删除
  const clickDeleteDataObj = () => {
    
    
    if (!selectedRowKeys?.length) {
    
    
      Message.error('请选择物料明细进行删除');
    } else {
    
    
      const newArr = removeDuplicates(dataSource, selectedRows); //dataSource:新增时通过从弹框选中拿到的数据数组;selectedRows:第一个页面删除时拿到的行数数组
      setDataSource(newArr); // 重新设置table的源数据
      // 重置key
      setSelectedRowKeys([]); //清空被第一个页面被选中的框
    }
  };
  // removeDuplicates删除的方法, 删除table数据,通过id来删除,简单有效。
  const removeDuplicates = (arr1, arr2) => {
    
    
    const ids = new Set(arr2.map((item) => item.id));
    return arr1.filter((item) => !ids.has(item.id));
  };

5. How to verify when saving?

Two form verifications are done here. It can be observed through page 1 that there is also a form at the top to be verified, and its fieldname isfield

 // 保存草稿| 提交申请
  const handleOk = (type: number) => {
    
    
    // type 1为保存草稿,2为提交申请
    field.validate(field.getNames(), async (error) => {
    
    
      if (error === null) {
    
    
        const paramsValue: Object = field.getValues(); // 获取顶部的form数据
        if (!dataSource?.length) {
    
    
          return Message.error('请添加物料信息');
        }
        // 对table进行校验
        fieldTable.validate(fieldTable.getNames(), async (errorIn) => {
    
    
          const paramsValueIn: Object = fieldTable.getValues(); // 获取table的数据,这里获取的数据就是被FormItem包裹的数据,
          //但是拿到的是对象,而且是所有`name-id`的对象,所以下面的方法将对象变为数组
          const detailVoList = objChangeArr(paramsValueIn); // 
          if (errorIn === null) {
    
    
            let TCode = 0;
            let TMsg = '';
            if (mode === 'add') {
    
    
              // 新增,submitImmediately:保存草稿(false)和提交申请(true)
              const {
    
     code = 0, msg = '' } = await services.fetchAddReplenishmentDraftAndApply({
    
    
                ...paramsValue,
                detailVoList,
                submitImmediately: type === 2,
              });
              TCode = code;
              TMsg = msg;
            } else {
    
    
              // 编辑
              // 只需要editDataSource被标记删除的数据
              const _editDetailVoList = editDataSource.filter((v: any) => v.delete);
              console.log(_editDetailVoList, '_editDetaVoList');
              const {
    
     code = 0, msg = '' } = await services.fetchEditReplenishmentDraftAndApply({
    
    
                ...paramsValue,
                detailVoList: [...detailVoList, ...(_editDetailVoList || [])],
                submitImmediately: type === 2,
              });
              TCode = code;
              TMsg = msg;
            }
            if (TCode === 101) {
    
    
              Message.success(TMsg);
              onOk?.(); // 父组件传入的值,不用管
              field.resetToDefault();
            }
          } else {
    
    
            // console.log(error);
          }
        });
      } else {
    
    
        // console.log(error);
      }
    });
  };
  • Objects to arrays
    Objects like this insert image description here
    are converted to arraysinsert image description here
 // 将对象转换成数组
  const objChangeArr = (obj) => {
    
    
    const _detailVoList = Object.keys(obj)
      .reduce((acc: any, key: string) => {
    
    
        const [prefix, id] = key.split('-');
        if (prefix === 'id') {
    
    
          acc.push({
    
    
            // 编辑时,在有名为itemId的前提下,拿到id,否则(编辑时新增的,新增时新增的)就不传id,
            id: obj[`itemId-${
      
      id}`] ? obj[`id-${
      
      id}`] : undefined, // 这个属于业务逻辑,不注重
            itemId: obj[`itemId-${
      
      id}`] || obj[`id-${
      
      id}`],
            itemCode: obj[`itemCode-${
      
      id}`],
            applyQuantity: obj[`applyQuantity-${
      
      id}`],
            areaId: obj[`areaId-${
      
      id}`],
          });
        }
        return acc;
      }, [])
      .filter(Boolean);
    return _detailVoList;
  };

there are other questions

  • 1. Save, echo the deleted data when editing, and mark delete. You
    can set another datasource data source to save when the page is initialized, and then merge it into it
  • 2. For the three-level linkage problem, refer to the first page and re-select the branch or storage address. The data in the table must be cleared, so I wrote it like this
 // 清空所有存储区 // 这里利用dataSource来进行遍历。通过field的setValue,重新设置为空字符串来清空
  const clearAreaId = () => {
    
    
    dataSource?.map((itemClear: any) => {
    
    
      const areaIdNum = `areaId-${
      
      itemClear.id}`;
      fieldTable.setValue(areaIdNum, '');
      return itemClear;
    });
  };

postscript

After writing in this way, there is no problem with my function here, but the boss said that what I wrote is too complicated and difficult to maintain. I need to change it to field writing, but I don’t want to write field writing. I don't know if other big guys have any suggestions.

Share a lot and grow fast.

Guess you like

Origin blog.csdn.net/weixin_45701199/article/details/129885442