写在前边,二维数组可以拖拽,但是不可以编辑+拖拽,如果想要实现编辑+拖拽,还是需要转换成一维数组。原因是因为插件的官方规定,在拖拽过程中不可以编辑Droppable层的Props。
相关地址:
中文文档地址
react-beautiful-dnd - 《react-beautiful-dnd 中文文档帮助手册教程》 - 极客文档 (geekdaxue.co)
git源码
GitHub - chinanf-boy/react-beautiful-dnd-zh: 🇨🇳翻译: react-beautiful-dnd 文档 ❤️ 更新 ✅
使用
安装
yarn
yarn add react-beautiful-dnd
npm
npm install react-beautiful-dnd --save
引入
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
引用
<DraggableList data={listDemo}></DraggableList>
一维数组使用
传参data是一维数组
TypeScript
import React, { useEffect, useState } from 'react';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
interface Props {
data: any[];
}
const DraggableList: React.FC<Props> = ({ data }) => {
const [sortList, setSortList] = useState([]);
const getItems = () => {
const arr = Array.from({ length: 10 }, (v, k) => k).map((k) => ({
id: `item-${k}`,
content: `item ${k}`,
}));
setSortList(arr);
};
useEffect(() => {
getItems();
}, []);
const grid = 8;
const getItemStyle = (isDragging, draggableStyle) => ({
// some basic styles to make the items look a bit nicer
userSelect: 'none',
padding: grid * 2,
margin: `0 ${grid}px 0 0`,
// change background colour if dragging
background: isDragging ? 'lightgreen' : 'grey',
// styles we need to apply on draggables
...draggableStyle,
});
const getListStyle = (isDraggingOver: any) => ({
background: isDraggingOver ? 'lightblue' : 'lightgrey',
display: 'flex',
padding: grid,
overflow: 'auto',
});
const onDragEnd = (result) => {
if (!result.destination) {
return;
}
console.log('end', sortList, result);
const res = sortList.filter((item) => item); // 更改引用地址
console.log('移动前res', res);
const [removed] = res.splice(result.source.index, 1);
console.log('删除???', removed);
res.splice(result.destination.index, 0, removed);
console.log('添加后', res);
setSortList(res);
};
console.log('data', data);
/**
* Draggable组件可以拖动并拖放到其Droppables上. 一个Draggable必须始终包含在一个Droppable.
* 它是 可能重新排序Draggable在其Droppable家中或移动到另一个Droppable.
* 一个Draggable必须包含在一个Droppable.
* */
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable" direction="horizontal">
{(provided, snapshot) => (
<div ref={provided.innerRef} style={getListStyle(snapshot.isDraggingOver)} {...provided.droppableProps}>
{sortList.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
>
{item.content}
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
};
export default DraggableList;
二维数组的使用
TypeScript
import React, { useEffect, useState } from 'react';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
interface Props {
data: any[];
}
const DraggableList: React.FC<Props> = ({ data = [] }) => {
const [sortList, setSortList] = useState(data);
const grid = 8;
const getItemStyle = (isDragging, draggableStyle) => ({
// some basic styles to make the items look a bit nicer
userSelect: 'none',
padding: grid * 2,
margin: `0 ${grid}px 0 0`,
// change background colour if dragging
background: isDragging ? 'lightgreen' : 'grey',
// styles we need to apply on draggables
...draggableStyle,
});
const getListStyle = (isDraggingOver) => ({
background: isDraggingOver ? 'lightblue' : 'lightgrey',
display: 'flex',
padding: grid,
overflow: 'auto',
});
const onDragEnd = (result) => {
if (!result.destination) {
return;
}
console.log('end', sortList, result);
const res = sortList.filter((item) => item); //修改引用地址
console.log('res', res);
const [removed] = res.splice(result.source.index, 1);
console.log('删除???', removed);
res.splice(result.destination.index, 0, removed);
console.log('添加后', res);
setSortList(res);
};
useEffect(() => {
setSortList(data);
}, [data]);
console.log('data', data);
return (
<DragDropContext onDragEnd={onDragEnd}>
{sortList.map((item, index) => {
return (
<Droppable droppableId={'droppable' + index} key={index} direction="vertical">
{(provided, snapshot) => (
<div ref={provided.innerRef} style={getListStyle(snapshot.isDraggingOver)} {...provided.droppableProps}>
{/*{data.map((item, index) => (*/}
<Draggable key={item[0].value} draggableId={item[0].value} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
>
{66666 + item[0].label}
</div>
)}
</Draggable>
{/*))}*/}
{provided.placeholder}
</div>
)}
</Droppable>
);
})}
</DragDropContext>
);
};
export default DraggableList;
组件传值的数组内容
TypeScript
const [options, setOptions] = useState([
{
label: '延时时间',
value: 'delayTime',
children: [
{
label: '时',
value: 'hour',
disabled: false,
},
{
label: '分',
value: 'minute',
disabled: false,
},
{
label: '秒',
value: 'second',
disabled: false,
},
],
},
{
label: '限制类型',
value: 'limitType',
children: [
{
label: '前置点位1',
value: '1',
disabled: false,
},
{
label: '前置点位2',
value: '2',
disabled: false,
},
{
label: '前置点位3',
value: '3',
disabled: false,
},
],
},
{
label: '温度',
value: 'templete',
},
]);
案列
案例是通过级联的组件选择条件,新增条件时,前端重新定义数据格式,将二维的结构改成一维数组的结构。遍历填充内容时,是在Droppable的下一级,所以可以修改内容。
TypeScript
const onDispatchValue = (res: any) => {
dispatch({
type: `${MODEL_NAME}/save`,
payload: {
proTypeList: res,
},
});
};
// 新增、删除前置条件
const [inputFlag, setInputFlag] = useState(false);
const [listDemo, setListDemo] = useState([]);
const changeCondition = (ids, option) => {
let arr2 = [];
// 第三层关系选中两个时的判断
if (ids && ids.length > 1) {
// 二维数组结构成一维数组,方便去重
arr2 = ids.reduce((a, b) => {
return a.concat(b);
});
const arr3 = Array.from(new Set(arr2));
if (arr2.length !== arr3.length) {
setRepeatFlag(true);
return message.warning('前置条件重复,请删除!');
} else {
setRepeatFlag(false);
}
}
// 没有子级或者全选的判断
ids.map((item, index) => {
if (item.length === 1 && option[index][0].value === item[0] && option[index][0]?.children?.length > 0) {
setRepeatFlag(true);
return message.warning('前置条件重复,请删除!');
} else {
setRepeatFlag(false);
}
});
const arr = option.map((item) => {
let obj = {
typeName: '', // 类型名称
typeValue: '', // 类型id
unitName: '', // 单位名称
unitValue: '', // 单位id
value: '', // 值
};
item.map((i, index) => {
if (item.length === 1) {
obj.typeName = i.label;
obj.typeValue = i.value;
}
if (item.length === 2) {
if (index === 1) {
obj.unitName = i.label;
obj.unitValue = i.value;
} else {
obj.typeName = i.label;
obj.typeValue = i.value;
}
}
});
return obj;
});
setListDemo(arr);
// 保存定义好的数据,用于组件之间传值
onDispatchValue(arr);
};
TypeScript
// 父组件引用
<DraggableList data={proTypeList}></DraggableList>
TypeScript
// 子组件
import { ConnectState } from '@/typing/connect';
import { connect } from '@@/exports';
import { Input } from 'antd';
import React, { useEffect, useState } from 'react';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import { Dispatch } from 'umi';
interface Props {
data: any[];
dispatch: Dispatch;
}
const MODEL_NAME = 'mainConfig';
const DraggableList: React.FC<Props> = ({ data = [], dispatch }) => {
const [sortList, setSortList] = useState(data);
// 拖拽时的样式
const getListStyle = () => ({
overflow: 'auto',
width: '100%',
});
// 拖拽后的样式
const getItemStyle = (isDragging, draggableStyle) => ({
// some basic styles to make the items look a bit nicer
width: '100%',
userSelect: 'none',
...draggableStyle,
});
const onDragEnd = (result) => {
if (!result.destination) {
return;
}
const res = sortList.filter((item) => item); //修改引用地址
const [removed] = res.splice(result.source.index, 1);
res.splice(result.destination.index, 0, removed);
// console.log('添加后', res);
setSortList(res);
dispatch({
type: `${MODEL_NAME}/save`,
payload: {
proTypeList: res,
},
});
console.log('拖拽后', res);
};
// 校验输入框内容
const regInputValue = (e: any, index: number) => {
// 输入框聚焦时
const arr = data.filter((item) => item);
arr[index].value = e.target.value;
console.log('arr', arr);
setSortList(arr);
dispatch({
type: `${MODEL_NAME}/save`,
payload: {
proTypeList: arr,
},
});
};
useEffect(() => {
setSortList(data);
}, [data]);
// console.log('弹窗起落data', data);
/**
* Draggable组件可以拖动并拖放到其Droppables上. 一个Draggable必须始终包含在一个Droppable.
* 它是 可能重新排序Draggable在其Droppable家中或移动到另一个Droppable.
* 一个Draggable必须包含在一个Droppable.
* */
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable" direction="horizontal">
{(provided, snapshot) => (
<div ref={provided.innerRef} style={getListStyle(snapshot.isDraggingOver)} {...provided.droppableProps}>
{data.map((item, index) => (
<Draggable key={item.typeValue} draggableId={item.typeValue} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
>
<div style={{ width: '100%', display: 'flex', justifyContent: 'flex-start', textAlign: 'center' }}>
<div style={{ width: '33%', backgroundColor: '#f2f2f2', padding: '8px 0' }}>条件名称</div>
<div style={{ width: '33%', backgroundColor: '#f2f2f2', padding: '8px 0' }}>条件值</div>
<div style={{ width: '33%', backgroundColor: '#f2f2f2', padding: '8px 0' }}>单位名称</div>
</div>
<div
style={{
width: '100%',
display: 'flex',
justifyContent: 'flex-start',
padding: '6px',
textAlign: 'center',
marginBottom: 16,
}}
>
<div style={{ width: '33%', padding: '8px 0' }}>{item.typeName}</div>
<div style={{ width: '33%', padding: '8px 0' }}>
<Input
placeholder="请输入内容"
onChange={(e) => {
regInputValue(e, index);
}}
/>
</div>
<div style={{ width: '33%', padding: '8px 0' }}>{item.unitName}</div>
</div>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
};
export default connect(({ mainConfig }: ConnectState) => ({
mainConfig ,
}))(DraggableList);