事先声明:在此我用到了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: "结束",
},
],
})
效果