<div className="main">
<ReducerContext>
<DndProvider backend={
HTML5Backend}>
<Left />
<Right />
</DndProvider>
</ReducerContext>
</div>
import React, {
useContext } from 'react';
import {
Menu } from 'cloud-react';
import SourceBox from './SourceBox';
import {
Context } from '../reducer'
const {
MenuItem } = Menu;
const types = ['View', 'Text', 'Button', 'Button3'];
export default function Left() {
const {
dispatch, state } = useContext(Context);
return (
<Menu>
{
types.map((type, index) => {
return (
<SourceBox dispatch={
dispatch} name={
type} key={
index}>
<MenuItem>{
type}</MenuItem>
</SourceBox>
)
})
}
</Menu>
)
}
import React, {
Component } from 'react';
import {
findDOMNode } from 'react-dom';
import {
observer } from 'mobx-react';
import {
DragSource, DropTarget } from 'react-dnd';
import './index.css'
import List from './list';
import Components from './components';
const source = {
/**
* 拖拽前为组件增加一些属性
* @param {*} props
*/
beginDrag(props) {
const {
parentId, item } = props;
// console.log(props);
const {
id, type, childrens } = item;
return {
id,
parentId,
type,
items: childrens
};
},
/**
* 限制组件是否可拖拽
* @param {*} props
*/
canDrag(props) {
if (props.item.id === 0) return false;
return true;
},
/**
* 当前组件是否处于拖拽中
* @param {*} props
* @param {*} monitor
*/
isDragging(props, monitor) {
return props.item.id === monitor.getItem().id;
},
/**
*
* @param {*} props
* @param {*} monitor
*/
endDrag(props, monitor) {
const result = monitor.getDropResult();
if (result.dragItem) {
const {
dragItem, overItem } = result;
const {
draggedId, dragParentId } = dragItem;
const {
overId, overParentId } = overItem;
if (result.moveShift) {
//同级拖拽
props.dispatch({
type: 'moveShift',
payload: {
dragItem, overItem }
});
} else {
//view与box之间拖拽
props.dispatch({
type: 'moveItem',
payload: {
dragItem, overItem }
});
}
}
}
};
function sourceCollect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview(),
isDragging: monitor.isDragging()
};
}
const target = {
/**
* 是否可以将拖拽的元素放置
* @param {*} props
* @param {*} monitor
*/
canDrop(props, monitor) {
// 在此处可以获取到拖拽的组件类型,从而增加一些是否可以放置的条件
return true;
},
/**
* 使用drop而未使用hover是不想一直更改数据结构
* @param {*} props
* @param {*} monitor
*/
drop(props, monitor, component) {
const didDrop = monitor.didDrop();
if (didDrop) {
return undefined;
}
const {
id: draggedId, parentId: dragParentId } = monitor.getItem();
const {
parentId: overParentId } = props;
const {
id: overId, type: targetType } = props.item;
console.log("-------------drop-props", props);
/*
monitor.getItem(): {type: "View"} 来自beginDrag return的
props : {parentId: null, item: Proxy, move: ƒ} 来自父级属性
*/
console.log("draggedId:", draggedId, "dragParentId:", dragParentId); //来自beginDrag return的 ----即 拖拽节点的属性
console.log("overParentId:", overParentId, "overId:", overId); //来自父级属性 ----即 被接收容器的属性
if (draggedId) {
//右侧内部拖拽
if (draggedId === overId || draggedId === overParentId || dragParentId === overId) return undefined;
if (dragParentId === overId) return undefined;
if (dragParentId == overParentId && targetType != "View") {
//同层切换顺序
const hoverBoundingRect = (findDOMNode(component)).getBoundingClientRect();
// 获取中点垂直坐标
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;//放置目标节点中心点
// 确定鼠标位置
const clientOffset = monitor.getClientOffset();
// 获取距顶部距离
const hoverClientY = (clientOffset).y - hoverBoundingRect.top; //鼠标距放置节点top的距离
console.log(hoverClientY, hoverMiddleY);
if (hoverClientY != hoverMiddleY && dragParentId == overParentId) {
//同级拖拽
return {
dragItem: {
draggedId, dragParentId },
overItem: {
overId, overParentId },
moveShift: 1
}
}
} else {
//非切换顺序
return {
dragItem: {
draggedId, dragParentId },
overItem: {
overId, overParentId }
};
}
}
// 左侧拖右侧
return {
id: overId, overParentId: overParentId, targetType: targetType }; //来自上一级 data数据里的item的id-----即被接收容器的id
}
};
function targetCollect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver({
shallow: true }),
canDrop: monitor.canDrop()
};
}
class Item extends Component {
constructor(props) {
super(props)
this.state = {
hover: false
}
}
render() {
const {
connectDropTarget, connectDragSource, canDrop, isOver, item, dispatch } = this.props;
const {
id, type, childrens } = item;
const CurrentComponet = Components[type];
const classes = (canDrop && isOver) ? 'activeHover' : '';
return (
<div >
<CurrentComponet
id={
id}
type={
type}
className={
`item ${
classes}`}
ref={
instance => {
connectDragSource(findDOMNode(instance));
connectDropTarget(findDOMNode(instance));
}}>
<List dispatch={
dispatch} parentId={
id} items={
childrens} />
</CurrentComponet>
</div>
);
}
}
export default DropTarget('ITEM', target, targetCollect)(DragSource('ITEM', source, sourceCollect)(Item));
reducer.js 数据处理
import React, {
createContext, useReducer } from 'react'
const initState = [
{
id: 0,
type: 'Box',
childrens: []
}
];
function findItem(dataList, id) {
let result = null;
dataList.forEach(item => {
const loop = data => {
if (data.id === id) {
result = data;
return result;
}
const childs = data.childrens;
if (childs) {
for (let i = 0; i < childs.length; i += 1) {
loop(childs[i]);
}
}
};
loop(item);
});
return result;
}
const removeNode = (state, removeId, parentId) => {
const item = findItem(state, parentId);
const index = item.childrens.findIndex(child => child.id === removeId);
item.childrens.splice(index, 1);
}
function reducer(state, action) {
switch (action.type) {
case 'additem':
const {
targetId, type, over_ParentId, targetType } = action.payload;
// console.log(type, targetId, over_ParentId, targetType);
let item;
if (targetType == "Box" || (targetType == "View" && type !== targetType)) {
//push 到box
item = findItem(state, targetId);
} else {
item = findItem(state, over_ParentId);
if (item.type == "View" && type == "View") {
//阻止嵌套View组件
return state
}
}
const obj = {
id: Math.ceil(Math.random() * 10000),
type
};
if (type == "View") {
obj.childrens = []
}
item.childrens.push(obj)
console.log(state);
// 进行深拷贝 分配新数组
const a = JSON.parse(JSON.stringify(state))
return a
case 'moveItem':
var {
dragItem, overItem } = action.payload;
var {
draggedId, dragParentId } = dragItem;
var {
overId, overParentId } = overItem;
const Item = {
...findItem(state, draggedId) };
const target = findItem(state, overId);
const overParentITem = findItem(state, overParentId);
const dragParentIdITem = findItem(state, dragParentId);
// console.log(JSON.stringify(Item));
// console.log(JSON.stringify(target));
// console.log(overParentITem);
// console.log(dragParentIdITem);
if (Item.type == "View") {
return state
}
removeNode(state, draggedId, dragParentId);
//push到当前view/BOx层
if (target.type == "View" || target.type == "Box") {
target.childrens.push(Item)
} else {
overParentITem.childrens.push(Item)
}
const b = JSON.parse(JSON.stringify(state))
return b
case 'moveShift':
console.log(state);
var {
dragItem, overItem } = action.payload;
var {
draggedId, dragParentId } = dragItem;
var {
overId, overParentId } = overItem;
const moveShiftItem1 = {
...findItem(state, draggedId) };
const moveShiftItem2 = {
...findItem(state, overId) };
const moveShiftAtrr = {
...findItem(state, dragParentId) }.childrens;
const moveShiftItem1Index = moveShiftAtrr.findIndex(v => v.id === moveShiftItem1.id);
const moveShiftItem2Index = moveShiftAtrr.findIndex(v => v.id === moveShiftItem2.id);
console.log(moveShiftItem1Index, moveShiftItem2Index);
const newM = moveShiftAtrr.splice(moveShiftItem1Index, 1)[0];
// moveShiftAtrr[moveShiftItem2Index] = moveShiftAtrr.splice(moveShiftItem1Index, 1, moveShiftAtrr[moveShiftItem2Index])[0];
moveShiftAtrr.splice(moveShiftItem2Index, 0, newM);
return JSON.parse(JSON.stringify(state))
case 'remove':
return {
...state,
}
default:
return state;
}
}
export const Context = createContext({
});
export const ReducerContext = (props) => {
//利用useReducer,将当前reducer中需要处理的方法进行导出,useReducer的第一个参数表示要处理的相关逻辑,第二个参数表示初始值
const [state, dispatch] = useReducer(reducer, initState)
return (
//在这里我们使用了useContext进行了状态的共享
<Context.Provider value={
{
state, dispatch }}>
{
props.children}
</Context.Provider>
)
}
具体代码可参考地址https://github.com/xiahaox/react-dnd-Hook.git