初始化代码
This commit is contained in:
commit
8da1996605
|
|
@ -0,0 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# clay-top-test
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"name": "clay-top-test",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/g6": "^4.2.7",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.0-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "^4.5.13",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/compiler-sfc": "^3.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^7.0.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<!-- <div id="nav">-->
|
||||
<!-- <router-link to="/">Home</router-link> |-->
|
||||
<!-- <router-link to="/about">About</router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- <router-view/>-->
|
||||
|
||||
|
||||
<Index/>
|
||||
<div id="container"/>
|
||||
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import Index from './views/Index'
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
Index
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
createApp(App).use(router).mount('#app')
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
// import Home from '../views/Home.vue'
|
||||
|
||||
const routes = [
|
||||
// {
|
||||
// path: '/',
|
||||
// name: 'Home',
|
||||
// component: Home
|
||||
// },
|
||||
// {
|
||||
// path: '/about',
|
||||
// name: 'About',
|
||||
// // route level code-splitting
|
||||
// // this generates a separate chunk (about.[hash].js) for this route
|
||||
// // which is lazy-loaded when the route is visited.
|
||||
// component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
||||
// }
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
<template>
|
||||
|
||||
<div id="container"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {renderMap} from "@/views/create";
|
||||
import G6 from "@antv/g6";
|
||||
//注册监听事件,线条,已经节点
|
||||
import Behavior from './behavior'
|
||||
import Edge from './edge'
|
||||
import Node from './node'
|
||||
Behavior.register(G6)
|
||||
Edge.register(G6)
|
||||
Node.register(G6)
|
||||
// import initGraph from "@/views/graph";
|
||||
export default {
|
||||
name: "Index",
|
||||
data(){
|
||||
return{
|
||||
graphConfig:{
|
||||
container: 'container',
|
||||
defaultNode: {
|
||||
size: [300, 400],
|
||||
type: 'dice-er-box',
|
||||
color: '#5B8FF9',
|
||||
style: {
|
||||
fill: '#9EC9FF',
|
||||
lineWidth: 3,
|
||||
},
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: 'black',
|
||||
fontSize: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
//设置edge默认属性
|
||||
defaultEdge: {
|
||||
type: 'dice-er-edge',
|
||||
style: {
|
||||
stroke: '#e2e2e2',
|
||||
lineWidth: 4,
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: ['dice-er-scroll', 'drag-node', 'drag-canvas'],
|
||||
},
|
||||
layout: {
|
||||
type: 'dagre',
|
||||
rankdir: 'LR',
|
||||
align: 'UL',
|
||||
controlPoints: true,
|
||||
nodesepFunc: () => 0.2,
|
||||
ranksepFunc: () => 0.5,
|
||||
},
|
||||
animate: true,
|
||||
},
|
||||
graphData: {
|
||||
nodes: [
|
||||
{
|
||||
id: "info",
|
||||
label: "Employee",
|
||||
attrs: [
|
||||
{
|
||||
key: "id",
|
||||
type: "number(6)"
|
||||
},
|
||||
{
|
||||
key: "key",
|
||||
type: "varchar(255)"
|
||||
},
|
||||
{
|
||||
key: "gender",
|
||||
type: "enum(M, F)"
|
||||
},
|
||||
{
|
||||
key: "birthday",
|
||||
type: "date"
|
||||
},
|
||||
{
|
||||
key: "hometown",
|
||||
type: "varchar(255)"
|
||||
},
|
||||
{
|
||||
key: "country",
|
||||
type: "varchar(255)"
|
||||
},
|
||||
{
|
||||
key: "nation",
|
||||
type: "varchar(255)"
|
||||
},
|
||||
{
|
||||
key: "jobId",
|
||||
type: "number(3)",
|
||||
relation: [
|
||||
{
|
||||
key: "id",
|
||||
nodeId: "job"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "phone",
|
||||
type: "varchar(255)"
|
||||
},
|
||||
{
|
||||
key: "deptId",
|
||||
type: "number(6)",
|
||||
relation: [
|
||||
{
|
||||
key: "id",
|
||||
nodeId: "dept"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "startTime",
|
||||
type: "date"
|
||||
},
|
||||
{
|
||||
key: "leaveTime",
|
||||
type: "date"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "job",
|
||||
label: "Job",
|
||||
attrs: [
|
||||
{
|
||||
key: "id",
|
||||
type: "number(3)"
|
||||
},
|
||||
{
|
||||
key: "title",
|
||||
type: "varchar(255)"
|
||||
},
|
||||
{
|
||||
key: "level",
|
||||
type: "number(3)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "dept",
|
||||
label: "Department",
|
||||
attrs: [
|
||||
{
|
||||
key: "id",
|
||||
type: "number(6)"
|
||||
},
|
||||
{
|
||||
key: "title",
|
||||
type: "varchar(255)"
|
||||
},
|
||||
{
|
||||
key: "desc",
|
||||
type: "text"
|
||||
},
|
||||
{
|
||||
key: "parent",
|
||||
type: "number(6)",
|
||||
relation: [
|
||||
{
|
||||
key: "id",
|
||||
nodeId: "dept"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "manager",
|
||||
type: "number(6)"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: "info",
|
||||
target: "job",
|
||||
sourceKey: "jobId",
|
||||
targetKey: "id"
|
||||
},
|
||||
{
|
||||
source: "info",
|
||||
target: "dept",
|
||||
sourceKey: "deptId",
|
||||
targetKey: "id"
|
||||
},
|
||||
{
|
||||
source: "dept",
|
||||
target: "dept",
|
||||
sourceKey: "parent",
|
||||
targetKey: "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initTopo()
|
||||
},
|
||||
methods:{
|
||||
initTopo(){
|
||||
const container = document.getElementById('container');
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 800) - 20;
|
||||
const graph = new G6.Graph({
|
||||
...this.graphConfig,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
renderMap(graph,this.graphData)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/14 23:20
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
const isInBBox = (point, bbox) => {
|
||||
const {
|
||||
x,
|
||||
y
|
||||
} = point;
|
||||
const {
|
||||
minX,
|
||||
minY,
|
||||
maxX,
|
||||
maxY
|
||||
} = bbox;
|
||||
|
||||
return x < maxX && x > minX && y > minY && y < maxY;
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'dice-er-scroll',
|
||||
options: {
|
||||
getDefaultCfg() {
|
||||
return {
|
||||
multiple: true,
|
||||
};
|
||||
},
|
||||
getEvents() {
|
||||
return {
|
||||
itemHeight: 50,
|
||||
wheel: "scroll",
|
||||
click: "click",
|
||||
"node:mousemove": "moves",
|
||||
};
|
||||
},
|
||||
scroll(e) {
|
||||
console.log(454)
|
||||
e.preventDefault();
|
||||
const {
|
||||
graph
|
||||
} = this;
|
||||
const nodes = graph.getNodes().filter((n) => {
|
||||
const bbox = n.getBBox();
|
||||
|
||||
return isInBBox(graph.getPointByClient(e.clientX, e.clientY), bbox);
|
||||
});
|
||||
if (nodes) {
|
||||
nodes.forEach((node) => {
|
||||
const model = node.getModel();
|
||||
if (model.attrs.length < 9) {
|
||||
return;
|
||||
}
|
||||
const idx = model.startIndex || 0;
|
||||
let startX = model.startX || 0.5;
|
||||
let startIndex = idx + e.deltaY * 0.02;
|
||||
startX -= e.deltaX;
|
||||
if (startIndex < 0) {
|
||||
startIndex = 0;
|
||||
}
|
||||
if (startX > 0) {
|
||||
startX = 0;
|
||||
}
|
||||
if (startIndex > model.attrs.length - 1) {
|
||||
startIndex = model.attrs.length - 1;
|
||||
}
|
||||
graph.update(node, {
|
||||
startIndex,
|
||||
startX,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
click(e) {
|
||||
console.log(789)
|
||||
const {
|
||||
graph
|
||||
} = this;
|
||||
const {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
y
|
||||
} = e;
|
||||
const item = e.item;
|
||||
const shape = e.shape;
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const model = item.getModel();
|
||||
|
||||
if (shape.get("name") === "collapse") {
|
||||
graph.updateItem(item, {
|
||||
collapsed: true,
|
||||
size: [300, 50],
|
||||
});
|
||||
setTimeout(() => graph.layout(), 100);
|
||||
} else if (shape.get("name") === "expand") {
|
||||
graph.updateItem(item, {
|
||||
collapsed: false,
|
||||
size: [300, 500],
|
||||
});
|
||||
setTimeout(() => graph.layout(), 100);
|
||||
}
|
||||
},
|
||||
moves(e) {
|
||||
const name = e.shape.get("name");
|
||||
console.log(name)
|
||||
const item = e.item;
|
||||
console.log(name)
|
||||
|
||||
if (name && name.startsWith("item")) {
|
||||
this.graph.updateItem(item, {
|
||||
selectedIndex: Number(name.split("-")[1]),
|
||||
});
|
||||
} else {
|
||||
this.graph.updateItem(item, {
|
||||
selectedIndex: NaN,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/14 23:19
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
import diceErScroll from './dice-er-scroll'
|
||||
const obj={
|
||||
diceErScroll
|
||||
}
|
||||
export default {
|
||||
obj,
|
||||
register(G6) {
|
||||
Object.values(obj).map(item => {
|
||||
G6.registerBehavior(item.name,item.options)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/14 23:58
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
export default {
|
||||
type: 'dice-er-edge',
|
||||
style: {
|
||||
stroke: '#e2e2e2',
|
||||
lineWidth: 4,
|
||||
endArrow: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/14 23:06
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
export function renderMap(graph,graphData){
|
||||
// const rawData = [
|
||||
// {
|
||||
// "id": "info",
|
||||
// "label": "Employee",
|
||||
// "attrs": [{
|
||||
// "key": "id",
|
||||
// "type": "number(6)"
|
||||
// },
|
||||
// {
|
||||
// "key": "key",
|
||||
// "type": "varchar(255)"
|
||||
// },
|
||||
// {
|
||||
// "key": "gender",
|
||||
// "type": "enum(M, F)"
|
||||
// },
|
||||
// {
|
||||
// "key": "birthday",
|
||||
// "type": "date"
|
||||
// },
|
||||
// {
|
||||
// "key": "hometown",
|
||||
// "type": "varchar(255)"
|
||||
// },
|
||||
// {
|
||||
// "key": "country",
|
||||
// "type": "varchar(255)"
|
||||
// },
|
||||
// {
|
||||
// "key": "nation",
|
||||
// "type": "varchar(255)"
|
||||
// },
|
||||
// {
|
||||
// "key": "jobId",
|
||||
// "type": "number(3)",
|
||||
// "relation": [{
|
||||
// "key": "id",
|
||||
// "nodeId": "job"
|
||||
// }]
|
||||
// },
|
||||
// {
|
||||
// "key": "phone",
|
||||
// "type": "varchar(255)"
|
||||
// },
|
||||
// {
|
||||
// "key": "deptId",
|
||||
// "type": "number(6)",
|
||||
// "relation": [{
|
||||
// "key": "id",
|
||||
// "nodeId": "dept"
|
||||
// }]
|
||||
// },
|
||||
// {
|
||||
// "key": "startTime",
|
||||
// "type": "date"
|
||||
// },
|
||||
// {
|
||||
// "key": "leaveTime",
|
||||
// "type": "date"
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// "id": "job",
|
||||
// "label": "Job",
|
||||
// "attrs": [{
|
||||
// "key": "id",
|
||||
// "type": "number(3)"
|
||||
// },
|
||||
// {
|
||||
// "key": "title",
|
||||
// "type": "varchar(255)"
|
||||
// },
|
||||
// {
|
||||
// "key": "level",
|
||||
// "type": "number(3)"
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// "id": "dept",
|
||||
// "label": "Department",
|
||||
// "attrs": [{
|
||||
// "key": "id",
|
||||
// "type": "number(6)"
|
||||
// },
|
||||
// {
|
||||
// "key": "title",
|
||||
// "type": "varchar(255)"
|
||||
// },
|
||||
// {
|
||||
// "key": "desc",
|
||||
// "type": "text"
|
||||
// },
|
||||
// {
|
||||
// "key": "parent",
|
||||
// "type": "number(6)",
|
||||
// "relation": [{
|
||||
// "key": "id",
|
||||
// "nodeId": "dept"
|
||||
// }]
|
||||
// },
|
||||
// {
|
||||
// "key": "manager",
|
||||
// "type": "number(6)"
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
// const dataTransform = (data) => {
|
||||
// const nodes = [];
|
||||
// const edges = [];
|
||||
//
|
||||
// data.map((node) => {
|
||||
// nodes.push({
|
||||
// ...node
|
||||
// });
|
||||
// if (node.attrs) {
|
||||
// node.attrs.forEach((attr) => {
|
||||
// if (attr.relation) {
|
||||
// attr.relation.forEach((relation) => {
|
||||
// edges.push({
|
||||
// source: node.id,
|
||||
// target: relation.nodeId,
|
||||
// sourceKey: attr.key,
|
||||
// targetKey: relation.key,
|
||||
// label: relation.label,
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// return {
|
||||
// nodes,
|
||||
// edges,
|
||||
// };
|
||||
// }
|
||||
graph.data(graphData);
|
||||
graph.render();
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/14 23:46
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
import {Util} from '@antv/g6'
|
||||
export default {
|
||||
name: 'dice-er-edge',
|
||||
options: {
|
||||
draw(cfg, group) {
|
||||
//todo 画线
|
||||
// console.log("cfg",cfg,"group",group)
|
||||
const edge = group.cfg.item;
|
||||
const sourceNode = edge.getSource().getModel();
|
||||
const targetNode = edge.getTarget().getModel();
|
||||
|
||||
const sourceIndex = sourceNode.attrs.findIndex(
|
||||
(e) => e.key === cfg.sourceKey
|
||||
);
|
||||
|
||||
const sourceStartIndex = sourceNode.startIndex || 0;
|
||||
|
||||
let sourceY = 15;
|
||||
|
||||
if (!sourceNode.collapsed && sourceIndex > sourceStartIndex - 1) {
|
||||
sourceY = 30 + (sourceIndex - sourceStartIndex + 0.5) * 30;
|
||||
sourceY = Math.min(sourceY, 300);
|
||||
}
|
||||
|
||||
const targetIndex = targetNode.attrs.findIndex(
|
||||
(e) => e.key === cfg.targetKey
|
||||
);
|
||||
|
||||
const targetStartIndex = targetNode.startIndex || 0;
|
||||
|
||||
let targetY = 15;
|
||||
|
||||
if (!targetNode.collapsed && targetIndex > targetStartIndex - 1) {
|
||||
targetY = (targetIndex - targetStartIndex + 0.5) * 30 + 30;
|
||||
targetY = Math.min(targetY, 300);
|
||||
}
|
||||
|
||||
const startPoint = {
|
||||
...cfg.startPoint
|
||||
};
|
||||
const endPoint = {
|
||||
...cfg.endPoint
|
||||
};
|
||||
|
||||
startPoint.y = startPoint.y + sourceY;
|
||||
endPoint.y = endPoint.y + targetY;
|
||||
|
||||
let shape;
|
||||
if (sourceNode.id !== targetNode.id) {
|
||||
shape = group.addShape("path", {
|
||||
attrs: {
|
||||
stroke: "#5B8FF9",
|
||||
path: [
|
||||
["M", startPoint.x, startPoint.y],
|
||||
[
|
||||
"C",
|
||||
endPoint.x / 3 + (2 / 3) * startPoint.x,
|
||||
startPoint.y,
|
||||
endPoint.x / 3 + (2 / 3) * startPoint.x,
|
||||
endPoint.y,
|
||||
endPoint.x,
|
||||
endPoint.y,
|
||||
],
|
||||
],
|
||||
endArrow: true,
|
||||
},
|
||||
name: "path-shape",
|
||||
});
|
||||
} else if (!sourceNode.collapsed) {
|
||||
let gap = Math.abs((startPoint.y - endPoint.y) / 3);
|
||||
if (startPoint["index"] === 1) {
|
||||
gap = -gap;
|
||||
}
|
||||
shape = group.addShape("path", {
|
||||
attrs: {
|
||||
stroke: "#5B8FF9",
|
||||
path: [
|
||||
["M", startPoint.x, startPoint.y],
|
||||
[
|
||||
"C",
|
||||
startPoint.x - gap,
|
||||
startPoint.y,
|
||||
startPoint.x - gap,
|
||||
endPoint.y,
|
||||
startPoint.x,
|
||||
endPoint.y,
|
||||
],
|
||||
],
|
||||
endArrow: true,
|
||||
},
|
||||
name: "path-shape",
|
||||
});
|
||||
}
|
||||
|
||||
return shape;
|
||||
},
|
||||
afterDraw(cfg, group) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const labelCfg = cfg.labelCfg || {};
|
||||
const edge = group.cfg.item;
|
||||
const sourceNode = edge.getSource().getModel();
|
||||
const targetNode = edge.getTarget().getModel();
|
||||
if (sourceNode.collapsed && targetNode.collapsed) {
|
||||
return;
|
||||
}
|
||||
const path = group.find(
|
||||
(element) => element.get("name") === "path-shape"
|
||||
);
|
||||
|
||||
const labelStyle = Util.getLabelPosition(path, 0.5, 0, 0, true);
|
||||
const label = group.addShape("text", {
|
||||
attrs: {
|
||||
...labelStyle,
|
||||
text: cfg.label || '',
|
||||
fill: "#000",
|
||||
textAlign: "center",
|
||||
stroke: "#fff",
|
||||
lineWidth: 1,
|
||||
},
|
||||
});
|
||||
label.rotateAtStart(labelStyle.rotate);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/14 23:49
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
import diceErEdge from './dice-er-edge'
|
||||
|
||||
const obj = {
|
||||
diceErEdge
|
||||
}
|
||||
|
||||
export default {
|
||||
obj,
|
||||
register(G6) {
|
||||
Object.values(obj).map(item =>{
|
||||
G6.registerEdge(item.name,item.options)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/15 0:01
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
import * as d3 from 'd3-force/dist/d3-force'
|
||||
import {renderMap} from '../create'
|
||||
const initGraph = {
|
||||
commonGraph:function(G6,options){
|
||||
var graphData = options.graphData
|
||||
const graph = new G6.Graph({
|
||||
container:options.container,
|
||||
defaultNode: {
|
||||
size: [300, 400],
|
||||
type: 'dice-er-box',
|
||||
color: '#5B8FF9',
|
||||
style: {
|
||||
fill: '#9EC9FF',
|
||||
lineWidth: 3,
|
||||
},
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: 'black',
|
||||
fontSize: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
//设置edge默认属性
|
||||
defaultEdge: {
|
||||
type: 'dice-er-edge',
|
||||
style: {
|
||||
stroke: '#e2e2e2',
|
||||
lineWidth: 4,
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: ['dice-er-scroll', 'drag-node', 'drag-canvas'],
|
||||
},
|
||||
layout: {
|
||||
type: 'dagre',
|
||||
rankdir: 'LR',
|
||||
align: 'UL',
|
||||
controlPoints: true,
|
||||
nodesepFunc: () => 0.2,
|
||||
ranksepFunc: () => 0.5,
|
||||
},
|
||||
animate: true,
|
||||
})
|
||||
// 将 read 方法分解成 data() 和 render 方法,便于整个生命周期的管理
|
||||
renderMap(graph,graphData)
|
||||
return graph;
|
||||
},
|
||||
/**
|
||||
* 力导布局
|
||||
* @param G6
|
||||
* @param options
|
||||
* @returns {*}
|
||||
*/
|
||||
forceLayoutGraph: function(resolve, G6, options) {
|
||||
let graphData = options.graphData
|
||||
// let themeStyle = theme.defaultStyle // todo...先使用默认主题,后期可能增加其它风格的主体
|
||||
// 生成G6实例
|
||||
let graph = new G6.Graph({
|
||||
container: options.container,
|
||||
width: options.width,
|
||||
height: options.height,
|
||||
// nodeStateStyles: themeStyle.nodeStyle,
|
||||
// edgeStateStyles: themeStyle.edgeStyle
|
||||
})
|
||||
// 初始化力导布局
|
||||
let simulation = d3
|
||||
.forceSimulation()
|
||||
.force(
|
||||
'link',
|
||||
d3
|
||||
.forceLink()
|
||||
.id(function(d) {
|
||||
return d.id
|
||||
})
|
||||
.distance(linkDistance)
|
||||
.strength(0.5)
|
||||
)
|
||||
.force('charge', d3.forceManyBody().strength(-500).distanceMax(500).distanceMin(100))
|
||||
.force('center', d3.forceCenter(options.width / 2, options.height / 2))
|
||||
// 定义节点数据
|
||||
simulation.nodes(graphData.nodes).on('tick', ticked)
|
||||
// 定义连线数据
|
||||
let edges = []
|
||||
for (let i = 0; i < graphData.edges.length; i++) {
|
||||
edges.push({
|
||||
id: graphData.edges[i].id,
|
||||
source: graphData.edges[i].source,
|
||||
target: graphData.edges[i].target
|
||||
})
|
||||
}
|
||||
simulation.force('link').links(edges)
|
||||
graph.data(graphData)
|
||||
graph.render()
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function linkDistance(d) {
|
||||
return 150
|
||||
}
|
||||
|
||||
function ticked() {
|
||||
// protect: planA: 移除事件监听器 planB: 手动停止力模拟
|
||||
if (graph.destroyed) {
|
||||
// simulation.nodes(graphData.nodes).on('tick', null)
|
||||
simulation.stop()
|
||||
return
|
||||
}
|
||||
if (!graph.get('data')) {
|
||||
// 若是第一次渲染,定义数据,渲染
|
||||
graph.data(graphData)
|
||||
graph.render()
|
||||
} else {
|
||||
// 后续渲染,直接刷新所有点和边的位置
|
||||
graph.refreshPositions()
|
||||
}
|
||||
}
|
||||
|
||||
// 控制时间: 只布局10秒
|
||||
let t = setTimeout(function() {
|
||||
simulation.stop()
|
||||
resolve(graph)
|
||||
}, 10000)
|
||||
|
||||
// 判断force-layout结束
|
||||
simulation.on('end', () => {
|
||||
clearTimeout(t)
|
||||
resolve(graph)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default initGraph
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/15 0:16
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
|
||||
const itemHeight = 30;
|
||||
export default {
|
||||
name: 'dice-er-box',
|
||||
options: {
|
||||
draw(cfg, group) {
|
||||
const width = 250;
|
||||
const height = 316;
|
||||
const itemCount = 10;
|
||||
const boxStyle = {
|
||||
stroke: "#096DD9",
|
||||
radius: 4,
|
||||
};
|
||||
|
||||
const {
|
||||
attrs = [],
|
||||
startIndex = 0,
|
||||
selectedIndex,
|
||||
collapsed,
|
||||
icon,
|
||||
} = cfg;
|
||||
const list = attrs;
|
||||
const afterList = list.slice(
|
||||
Math.floor(startIndex),
|
||||
Math.floor(startIndex + itemCount - 1)
|
||||
);
|
||||
const offsetY = (0.5 - (startIndex % 1)) * itemHeight + 30;
|
||||
|
||||
group.addShape("rect", {
|
||||
attrs: {
|
||||
fill: boxStyle.stroke,
|
||||
height: 30,
|
||||
width,
|
||||
radius: [boxStyle.radius, boxStyle.radius, 0, 0],
|
||||
},
|
||||
draggable: true,
|
||||
});
|
||||
|
||||
let fontLeft = 12;
|
||||
|
||||
if (icon && icon.show !== false) {
|
||||
group.addShape("image", {
|
||||
attrs: {
|
||||
x: 8,
|
||||
y: 8,
|
||||
height: 16,
|
||||
width: 16,
|
||||
...icon,
|
||||
},
|
||||
});
|
||||
fontLeft += 18;
|
||||
}
|
||||
|
||||
group.addShape("text", {
|
||||
attrs: {
|
||||
y: 22,
|
||||
x: fontLeft,
|
||||
fill: "#fff",
|
||||
text: cfg.label,
|
||||
fontSize: 12,
|
||||
fontWeight: 500,
|
||||
},
|
||||
});
|
||||
|
||||
group.addShape("rect", {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: collapsed ? 30 : 300,
|
||||
height: 15,
|
||||
width,
|
||||
fill: "#eee",
|
||||
radius: [0, 0, boxStyle.radius, boxStyle.radius],
|
||||
cursor: "pointer",
|
||||
},
|
||||
name: collapsed ? "expand" : "collapse",
|
||||
});
|
||||
|
||||
group.addShape("text", {
|
||||
attrs: {
|
||||
x: width / 2 - 6,
|
||||
y: (collapsed ? 30 : 300) + 12,
|
||||
text: collapsed ? "+" : "-",
|
||||
width,
|
||||
fill: "#000",
|
||||
radius: [0, 0, boxStyle.radius, boxStyle.radius],
|
||||
cursor: "pointer",
|
||||
},
|
||||
name: collapsed ? "expand" : "collapse",
|
||||
});
|
||||
|
||||
const keyshape = group.addShape("rect", {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height: collapsed ? 45 : height,
|
||||
...boxStyle,
|
||||
},
|
||||
draggable: true,
|
||||
});
|
||||
|
||||
if (collapsed) {
|
||||
return keyshape;
|
||||
}
|
||||
|
||||
const listContainer = group.addGroup({});
|
||||
listContainer.setClip({
|
||||
type: "rect",
|
||||
attrs: {
|
||||
x: -8,
|
||||
y: 30,
|
||||
width: width + 16,
|
||||
height: 300 - 30,
|
||||
},
|
||||
});
|
||||
listContainer.addShape({
|
||||
type: "rect",
|
||||
attrs: {
|
||||
x: 1,
|
||||
y: 30,
|
||||
width: width - 2,
|
||||
height: 300 - 30,
|
||||
fill: "#fff",
|
||||
},
|
||||
draggable: true,
|
||||
});
|
||||
|
||||
if (list.length > itemCount) {
|
||||
const barStyle = {
|
||||
width: 4,
|
||||
padding: 0,
|
||||
boxStyle: {
|
||||
stroke: "#00000022",
|
||||
},
|
||||
innerStyle: {
|
||||
fill: "#00000022",
|
||||
},
|
||||
};
|
||||
|
||||
listContainer.addShape("rect", {
|
||||
attrs: {
|
||||
y: 30,
|
||||
x: width - barStyle.padding - barStyle.width,
|
||||
width: barStyle.width,
|
||||
height: height - 30,
|
||||
...barStyle.boxStyle,
|
||||
},
|
||||
});
|
||||
|
||||
const indexHeight =
|
||||
afterList.length > itemCount ?
|
||||
(afterList.length / list.length) * height :
|
||||
10;
|
||||
|
||||
listContainer.addShape("rect", {
|
||||
attrs: {
|
||||
y: 30 +
|
||||
barStyle.padding +
|
||||
(startIndex / list.length) * (height - 30),
|
||||
x: width - barStyle.padding - barStyle.width,
|
||||
width: barStyle.width,
|
||||
height: Math.min(height, indexHeight),
|
||||
...barStyle.innerStyle,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (afterList) {
|
||||
afterList.forEach((e, i) => {
|
||||
const isSelected =
|
||||
Math.floor(startIndex) + i === Number(selectedIndex);
|
||||
let {
|
||||
key = "", type
|
||||
} = e;
|
||||
if (type) {
|
||||
key += " - " + type;
|
||||
}
|
||||
const label = key.length > 26 ? key.slice(0, 24) + "..." : key;
|
||||
|
||||
listContainer.addShape("rect", {
|
||||
attrs: {
|
||||
x: 1,
|
||||
y: i * itemHeight - itemHeight / 2 + offsetY,
|
||||
width: width - 4,
|
||||
height: itemHeight,
|
||||
radius: 2,
|
||||
lineWidth: 1,
|
||||
cursor: "pointer",
|
||||
},
|
||||
name: `item-${Math.floor(startIndex) + i}-content`,
|
||||
draggable: true,
|
||||
});
|
||||
|
||||
if (!cfg.hideDot) {
|
||||
listContainer.addShape("circle", {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: i * itemHeight + offsetY,
|
||||
r: 3,
|
||||
stroke: boxStyle.stroke,
|
||||
fill: "white",
|
||||
radius: 2,
|
||||
lineWidth: 1,
|
||||
cursor: "pointer",
|
||||
},
|
||||
});
|
||||
listContainer.addShape("circle", {
|
||||
attrs: {
|
||||
x: width,
|
||||
y: i * itemHeight + offsetY,
|
||||
r: 3,
|
||||
stroke: boxStyle.stroke,
|
||||
fill: "white",
|
||||
radius: 2,
|
||||
lineWidth: 1,
|
||||
cursor: "pointer",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
listContainer.addShape("text", {
|
||||
attrs: {
|
||||
x: 12,
|
||||
y: i * itemHeight + offsetY + 6,
|
||||
text: label,
|
||||
fontSize: 12,
|
||||
fill: "#000",
|
||||
fontFamily: "Avenir,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol",
|
||||
full: e,
|
||||
fontWeight: isSelected ? 500 : 100,
|
||||
cursor: "pointer",
|
||||
},
|
||||
name: `item-${Math.floor(startIndex) + i}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// console.log(keyshape);
|
||||
return keyshape;
|
||||
},
|
||||
getAnchorPoints() {
|
||||
return [
|
||||
[0, 0],
|
||||
[1, 0],
|
||||
];
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* @author: clay
|
||||
* @data: 2021/5/15 0:18
|
||||
* @email: clay@hchyun.com
|
||||
* @description: node
|
||||
*/
|
||||
import diceErBox from './dice-er-box'
|
||||
|
||||
const node = {
|
||||
diceErBox
|
||||
}
|
||||
export default {
|
||||
node,
|
||||
register(G6) {
|
||||
Object.values(node).map(item => {
|
||||
G6.registerNode(item.name,item.options)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue