一、业务需求描述
- 能够拖动表单组件(不限制样式)到指定区域后,渲染成指定的组件
- 能支持自定义标签名和属性,以及默认值
- 能实现可支持预览、可排序、可编辑等功能
- SON数据格式前后端相互传递
二、相关包
- 主要包:react-sortablejs
- 辅助包: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}`