react-dnd 详细教程(安装,使用)

事先声明:在此我用到了antd的组件和mock假数据,如果需要就提前安装一下。

1. 安装

npm i react-dnd react-dnd-html5-backend

2. 使用

        2.1 首先需要将DndProvider注入

import React, { useState, createContext } from "react"
import Tree from "./pages/Tree"
import Box from "./pages/Box"
import Content from "./pages/Content"
import { DndProvider } from "react-dnd"
import { HTML5Backend } from "react-dnd-html5-backend"
import "./App.css"
const CountContext = createContext([])
function App() {
	const [count, setCount] = useState([])
	const arr = []
	return (
		<div className="App">
			<CountContext.Provider value={count}>
				<DndProvider backend={HTML5Backend}>
					<div style={{ display: "flex" }}>
						<div>
							<Tree></Tree>
						</div>
						<div>
							<Content arrList={arr} />
						</div>
					</div>
				</DndProvider>
			</CountContext.Provider>
		</div>
	)
}

export default App

        2.2 要记住useDrag是拖拽源,useDrop是放置源

          注意:并且两者互有关联的标识,在useDrag里声明的是type,在useDrop里接收的是accept。

                2.2.1 拖拽源文件 Tree.jsx

import { MailOutlined } from "@ant-design/icons"
import { Menu } from "antd"
import { useDrag } from "react-dnd"
import axios from "axios"

import "../../mock"
import React, { useState, useEffect } from "react"
function getItem(label, key, icon, children, type) {
	return {
		label,
		key,
		icon,
		children,
		type,
	}
}
function TableItem({ selected, tableName, dbName, remark, dataset, onClick = () => {}, onDoubleClick }) {
	const [, drag] = useDrag({
		type: "Box",
		item: { data: { ...dataset, dbName: dbName } },
	})
	return <p ref={drag}>{tableName}</p>
}

// submenu keys of first level
const rootSubmenuKeys = ["sub1", "sub2", "sub4"]
const App = () => {
	const [list, setList] = useState([])

	useEffect(() => {
		axios.get("test/list").then((res) => {
			// console.log(res.data.objectResult)
			if ((res.status = 200)) {
				// console.log(res)
				setList(res.data.objectResult.slice(0, 3))
			}
		})
	}, [])

	// const items = [getItem("Navigation One", "sub1", <MailOutlined />, [getItem("Option 1", "1"), getItem("Option 2", "2"), getItem("Option 3", "3"), getItem("Option 4", "4")])]
	// console.log(list)
	const items = [
		getItem(
			"Navigation One",
			"sub1",
			<MailOutlined />,

			list.map((item, index) =>
				getItem(
					<TableItem
						key={item.srcTableName}
						onClick={() => {
							// handleTbClick(dataset.name, item.srcTableName, item.taskId)
						}}
						dataset={item}
						tableName={item.srcTableName}
						remark={item.remark}
					/>,
				),
			),
		),
	]
	const [openKeys, setOpenKeys] = useState(["sub1"])
	const onOpenChange = (keys) => {
		const latestOpenKey = keys.find((key) => openKeys.indexOf(key) === -1)
		if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
			setOpenKeys(keys)
		} else {
			setOpenKeys(latestOpenKey ? [latestOpenKey] : [])
		}
	}
	return (
		<Menu
			mode="inline"
			openKeys={openKeys}
			onOpenChange={onOpenChange}
			style={{
				width: 256,
			}}
			items={items}
		/>
	)
}
export default App

                  2.2.2 放置源文件 Content.jsx

import React, { useState, useEffect, useCallback, useRef, forwardRef, useContext, createContext } from "react"
import { useDrop } from "react-dnd"
import { Button, Drawer, Table, Input } from "antd"

const style = {
	width: 900,
	height: 400,
	display: "flex",
	flexWrap: "wrap",
	position: "relative",
	overflow: "hidden",
	lineHeight: "60px",
	border: "1px dashed black",
}

const CountContext = createContext([])

function ERGraph() {
	const [open, setOpen] = useState(false)
	const [list, setList] = useState([])
	const [current, setCurrent] = useState(1)
	useEffect(() => {
		document.addEventListener("click", () => {
			setOpen(false)
		})
	}, [])
	const arr = useContext(CountContext)
	const graphRef = useRef(null)

	const onItem = (e) => {
		setList(e.columns)
		setOpen(!open)
		document.onkeyup = function (event) {
			if (event.key == "Backspace") {
				arr.splice(
					arr.findIndex((item) => item == e),
					1,
				)
				setCurrent(current + 1)
				console.log(arr)
			}
		}
	}
	useEffect(() => {
		setOpen(false)
	}, [current])
	const stopPropagation = (e) => {
		e.nativeEvent.stopImmediatePropagation()
	}
	const isOver = (e) => {
		console.log(e)
	}
	const onClose = () => {
		setOpen(false)
	}
	const handleAction = (e, a, b, value) => {
		console.log(e, a, b, value)
	}

	const columns = [
		{
			title: "字段",
			dataIndex: "columnName",
			key: "columnName",
		},
		{
			title: "别名",

			key: "name",
			render: (value = "", row) => <Input name="alias" onChange={(e) => handleAction("aliasChange", row, "alias", e.target.value)} />,
		},
		{
			title: "备注",
			width: 120,
			dataIndex: "remark",
			key: "remark",
		},
	]
	const [collectProps, drop] = useDrop({
		accept: "Box",
		collect: (monitor) => ({
			isActive: monitor.isOver(),
			canDrop: monitor.canDrop(),
		}),
		drop: (item, monitor) => {
			const dropPoint = monitor.getClientOffset()
			

			const dragData = item.data
			

			arr.push({ ...dragData, id: Math.floor(Math.random() * 1000 + 1) })
			console.log(arr)
			it.srcTableName))
			
		},
	})
	
	const content = collectProps.isActive ? "将 Box 组件拖动到这里" : "1"
	return (
		<div ref={drop} style={{ ...style }}>
			{arr.map((its, index) => {
				return (
					<Button
						key={index}
						style={{ width: "115px", height: "120px", border: "1px solid #000", margin: "10px 20px" }}
						onClick={(e) => {
							stopPropagation(e)
							onItem(its)
						}}
					>
						<p>{its.srcTableName}</p>
					</Button>
				)
			})}

			<Drawer
				title="Basic Drawer"
				placement="right"
				closable={true}
				onClose={onClose}
				open={open}
				getContainer={false}
				mask={false}
				style={{
					position: "absolute",
				}}
			>
				<Table dataSource={list} columns={columns} pagination={false}></Table>
				<div style={{ float: "right" }}>
					<Button type="error" onClick={onClose}>
						取消
					</Button>
					<Button type="primary" onClick={onClose}>
						保存
					</Button>
				</div>
			</Drawer>
		</div>
	)
}
export default forwardRef(ERGraph)

        Mock.js (供参考)

import Mock from "mockjs"
const TestMock = Mock.mock("test/list", "get", {
	success: true,
	"objectResult|1-10": [
		{
			srcTableName: "固定人审批",
			columns: [
				{ columnName: "model_id", columnTypeName: "BIGINT", remark: "数据模型id" },
				{ columnName: "model_name", columnTypeName: "VARCHAR", remark: "数据对象名称" },
				{ columnName: "description", columnTypeName: "VARCHAR", remark: "数据对象的描述" },
			],

			remark: "数据模型",
		},
		{
			srcTableName: "流动审批",
			columns: [
				{ columnName: "api_id", columnTypeName: "BIGINT", remark: "数据源id" },
				{ columnName: "api_name", columnTypeName: "VARCHAR", remark: "数据源名称" },
				{ columnName: "host", columnTypeName: "VARCHAR", remark: "用户元网络" },
				{ columnName: "protocol", columnTypeName: "VARCHAR", remark: "接口的协议" },
				{ columnName: "method", columnTypeName: "VARCHAR", remark: "接口调用的方法" },
				{ columnName: "path", columnTypeName: "VARCHAR", remark: "接口的调用路径" },
			],
			remark: "管理员表",
		},
		{
			srcTableName: "结束",

			remark: "结束",
		},
	],
})

        效果

 

 

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值