【LowCode低代码平台/物料平台】自定义拖拽表单

一、业务需求描述

  1. 能够拖动表单组件(不限制样式)到指定区域后,渲染成指定的组件
  2. 能支持自定义标签名和属性,以及默认值
  3. 能实现可支持预览、可排序、可编辑等功能
  4. SON数据格式前后端相互传递

二、相关包

  1. 主要包:react-sortablejs
  2. 辅助包:lodash,immutability-helper

二、成果展示
在这里插入图片描述
三、代码实现
1.CustomForm/index.js

 
import React, {
    useEffect, useState } from 'react';
import {
    Tag, Layout, Button, Modal, Divider, Form, Input, Select } from 'antd';
import Sortable from 'react-sortablejs';
import _ from 'lodash';
import uniqueId from 'lodash/uniqueId';
import update from 'immutability-helper';
import './index.less';
import {
    indexToArray, getItem, setInfo, isPath, getCloneItem, itemRemove, itemAdd } from './utils';
import {
    formItemData, GlobalComponent } from './config';
import EditableTable from '../../components/EditableTable';
 
const {
    Header, Sider, Content, Footer } = Layout;
const {
    Option } = Select;
const sortableOption = {
   
  animation: 150,
  fallbackOnBody: true,
  swapThreshold: 0.65,
  group: {
   
    name: 'formItem',
    pull: true,
    put: true,
  },
};
 
const CustomForm = () => {
   
 
  const [itemData, setItemData] = useState(Array);                  // 组件数据
  const [isShowModal, setIsShowModal] = useState(false);            // 弹框是否显示
  const [curItemKey, setCurItemKey] = useState(String);             // 当前选中组件的Key
  const [curItemName, setCurItemName] = useState(String);           // 当前选中组件的Name
  const [curItemType, setCurItemType] = useState(String);           // 当前选中组件的Type
  const [isChoose, setIsChoose] = useState(false);                  // 组件是否处于选中状态
 
  useEffect(() => {
   
 
  }, []);
 
  const handleSubmit = () => {
   
 
  };
 
  const handlePreview = () => {
   
    console.log('itemData:'+JSON.stringify(itemData))
    setIsChoose(false)
    setIsShowModal(true)
  };
 
  const handleLabelChange = (e) => {
   
    const val = e.target.value;
    setCurItemName(val);
    itemData[curItemKey].label = val;
    setItemData(...[itemData]);
  }
 
  const handleDel = () => {
   
    let newTreeData = itemRemove(curItemKey, itemData);
    setCurItemKey('');
    setCurItemName('');
    setCurItemType('');
    setItemData([...newTreeData])
  }
 
  const sortableChoose = (e) => {
   
    console.log(e)
    setIsChoose(true);
    const curKey = e.item.getAttribute('data-id');
    const curName = e.item.firstChild.innerText;
    const curType = e.item.getAttribute('type');
    setCurItemKey(curKey);
    setCurItemName(curName);
    setCurItemType(curType)
  };
 
  // 拖拽的添加方法
  const sortableAdd = e => {
   
    // 组件名或路径
    const nameOrIndex = e.clone.getAttribute('data-id');
    // 父节点路径
    const parentPath = e.path[1].getAttribute('data-id');
    // 拖拽元素的目标路径
    const {
    newIndex } = e;
    // 新路径 为根节点时直接使用index
    const newPath = parentPath ? `${
     parentPath}-${
     newIndex}` : newIndex;
    console.log('nameOrIndex:'+nameOrIndex,'parentPath:'+parentPath,'newIndex:'+newIndex,'newPath:'+newPath)
    // 判断是否为路径 路径执行移动,非路径为新增
    if (isPath(nameOrIndex)) {
   
      // 旧的路径index
      const oldIndex = nameOrIndex;
      // 克隆要移动的元素
      const dragItem = getCloneItem(oldIndex, itemData)
      // 比较路径的上下位置 先执行靠下的数据 再执行靠上数据
      if (indexToArray(oldIndex) > indexToArray(newPath)) {
   
        // 删除元素 获得新数据
        let newTreeData = itemRemove(oldIndex, itemData);
        // 添加拖拽元素
        newTreeData = itemAdd(newPath, newTreeData, dragItem)
        // 更新视图
        setItemData([...newTreeData])
        return
      }
      // 添加拖拽元素
      let newData = itemAdd(newPath, itemData, dragItem)
      // 删除元素 获得新数据
      newData = itemRemove(oldIndex, newData);
      setItemData([...newData])
      return
    }
 
    // 新增流程 创建元素 => 插入元素 => 更新视图
    const id = nameOrIndex
    const newItem = _.cloneDeep(formItemData.find(item => (item.name === id)))
    // 为容器或者弹框时增加子元素
    if ( newItem.name === 'Containers') {
   
      const ComponentsInfo = _.cloneDeep(GlobalComponent[newItem.name])
      // 判断是否包含默认数据
      newItem.children = [ComponentsInfo]
    }
    let Data = itemAdd(newPath, itemData, newItem)
    setItemData([...Data])
  };
 
  // 拖拽的排序方法
  const sortableUpdate = e => {
   
    // 交换数组
    const {
    newIndex, oldIndex } = e;
    // 父节点路径
    const parentPath = e.path[1].getAttribute('data-id');
    // 父元素 根节点时直接调用data
    let parent = parentPath ? getItem(parentPath, itemData) : itemData;
    // 当前拖拽元素
    const dragItem = parent[oldIndex];
    // 更新后的父节点
    parent = update(parent, {
   
      $splice: [[oldIndex, 1], [newIndex, 0, dragItem]],
    });
    // 最新的数据 根节点时直接调用data
    const Data = parentPath ? setInfo(parentPath, itemData, parent) : parent
    // 调用父组件更新方法
    setItemData([...Data])
  };
 
  // 递归函数
  const loop = (arr, index) => {
   
    return (
      arr.map((item, i) => {
   
        const indexs = index === '' ? String(i) : `${
     index}-${
     i}`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值