ER图测试,域账号登录测试,修改数据查询的展示问题
This commit is contained in:
26
zyplayer-doc-ui/db-ui/src/components/er/ERGraph/index.less
Normal file
26
zyplayer-doc-ui/db-ui/src/components/er/ERGraph/index.less
Normal file
@@ -0,0 +1,26 @@
|
||||
.er-editor-demo-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fafafa;
|
||||
|
||||
.minimap-container {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
|
||||
/** X6推荐覆写样式 */
|
||||
.x6-widget-minimap {
|
||||
.x6-graph {
|
||||
box-shadow: 0 0 0 0 #ffffff;
|
||||
}
|
||||
|
||||
.x6-widget-minimap-viewport {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
|
||||
.x6-widget-minimap-viewport-zoom {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
zyplayer-doc-ui/db-ui/src/components/er/ERGraph/index.vue
Normal file
88
zyplayer-doc-ui/db-ui/src/components/er/ERGraph/index.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="er-editor-demo-container">
|
||||
<div
|
||||
id="refContainer"
|
||||
ref="refContainer"
|
||||
style="width: 100%; height: 100%"/>
|
||||
<div id="refMinimapContainer" ref="refMinimapContainer" class="minimap-container" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import './index.less'
|
||||
import { BaseGraph } from '../xflow/index' // GraphData, GraphOptions
|
||||
|
||||
export default {
|
||||
name: 'index',
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
graphOptions: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: false
|
||||
},
|
||||
graphData: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// graphContainer: document.getElementById('refContainer'),
|
||||
// minimapContainer: document.getElementById('refMinimapContainer'),
|
||||
baseGraph: null
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
const that = this
|
||||
|
||||
setTimeout(() => {
|
||||
/** 初始化画布 */
|
||||
this.baseGraph = new BaseGraph({
|
||||
...this.graphOptions,
|
||||
container: document.getElementById('refContainer'),
|
||||
grid: {
|
||||
visible: false
|
||||
},
|
||||
minimap: {
|
||||
enabled: true,
|
||||
container: document.getElementById('refMinimapContainer'),
|
||||
minScale: 0.5,
|
||||
maxScale: 2
|
||||
}
|
||||
})
|
||||
/** 渲染画布内容 */
|
||||
this.baseGraph.updateGraph(that.graphData)
|
||||
}, 100)
|
||||
},
|
||||
methods: {
|
||||
onHandleToolbar (action) {
|
||||
switch (action) {
|
||||
case 'in':
|
||||
console.log('in')
|
||||
this.baseGraph.zoomGraph(0.1)
|
||||
break
|
||||
case 'out':
|
||||
console.log('out')
|
||||
this.baseGraph.zoomGraph(-0.1)
|
||||
break
|
||||
case 'fit':
|
||||
console.log('fit')
|
||||
this.baseGraph.zoomGraph('fit')
|
||||
break
|
||||
case 'real':
|
||||
console.log('real')
|
||||
this.baseGraph.zoomGraph('real')
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
113
zyplayer-doc-ui/db-ui/src/components/er/ERGraphDemo/Entity.less
Normal file
113
zyplayer-doc-ui/db-ui/src/components/er/ERGraphDemo/Entity.less
Normal file
@@ -0,0 +1,113 @@
|
||||
.entity-container {
|
||||
width: calc(100% - 2px);
|
||||
height: calc(100% - 2px);
|
||||
border-radius: 2px;
|
||||
background-color: white;
|
||||
|
||||
&.fact {
|
||||
border: 1px solid #ced4de;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
&.dim {
|
||||
border: 1px solid #cdddfd;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
&.other {
|
||||
border: 1px solid #decfea;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 1px 1px;
|
||||
width: calc(100% - 2px);
|
||||
height: calc(100% - 2px);
|
||||
display:block;
|
||||
|
||||
&.fact {
|
||||
background-color: #ced4de;
|
||||
}
|
||||
|
||||
&.dim {
|
||||
background-color: #cdddfd;
|
||||
}
|
||||
|
||||
&.other {
|
||||
background-color: #decfea;
|
||||
}
|
||||
|
||||
.head {
|
||||
width: calc(100% - 12px);
|
||||
height: 38px;
|
||||
margin-left: 6px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.type {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.more {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
width: calc(100% - 12px);
|
||||
height: calc(100% - 36px - 6px);
|
||||
margin-left: 6px;
|
||||
margin-bottom: 6px;
|
||||
background-color: white;
|
||||
overflow: auto;
|
||||
cursor: pointer;
|
||||
|
||||
//float:clear;
|
||||
|
||||
.body-item {
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
color: #595959;
|
||||
border-bottom: 1px solid rgba(206, 212, 222, 0.2);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
margin-left: 6px;
|
||||
|
||||
.pk,
|
||||
.fk {
|
||||
width: 12px;
|
||||
font-family: HelveticaNeue-CondensedBold;
|
||||
color: #ffd666;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.type {
|
||||
color: #bfbfbf;
|
||||
font-size: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="entity-container fact">
|
||||
<div class="content other">
|
||||
<div class="head">
|
||||
<div>
|
||||
<a-icon type="bars" class="type"/>
|
||||
<span>{{ entity.name }}</span>
|
||||
</div>
|
||||
<a-icon type="ellipsis" class="more" />
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
|
||||
<div class="body-item" v-for="(item,index) in entity.properties" :key="index">
|
||||
<div class="name">
|
||||
<!-- {{ value.isPK }} && <span class="pk">PK</span>-->
|
||||
<!-- {{value.isFK }} && <span class="fk">FK</span>-->
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div class="type">{{ item.propertyType }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import './Entity.less'
|
||||
export default {
|
||||
name: 'Entity',
|
||||
props: {
|
||||
entity: {
|
||||
type: Object,
|
||||
default: null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
console.log(this.entity, 'this.entity')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: 650px">
|
||||
<ERGraph :graphData="graphData"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import ERGraph from '../ERGraph'
|
||||
import { mockEntityData, mockRelationData } from './mock'
|
||||
import Entity from './Entity'
|
||||
export default {
|
||||
name: 'index',
|
||||
components: {
|
||||
ERGraph
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
graphData: {
|
||||
nodes: '',
|
||||
edges: ''
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.calRenderData()
|
||||
},
|
||||
methods: {
|
||||
calRenderData () {
|
||||
this.graphData.nodes = mockEntityData.map((entity) => {
|
||||
const { entityId, x, y, width, height } = entity
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
id: entityId,
|
||||
component: {
|
||||
template: '<Entity :entity="entity"/>',
|
||||
data () {
|
||||
return {
|
||||
entity: entity
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Entity
|
||||
}
|
||||
}
|
||||
// data: entity
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.graphData.edges = mockRelationData.map((relation) => {
|
||||
const { relationId, sourceEntityId, targetEntityId } = relation
|
||||
return {
|
||||
id: relationId,
|
||||
source: sourceEntityId,
|
||||
target: targetEntityId,
|
||||
label: '1:N',
|
||||
// render: (data: RelationCanvasModel) => {
|
||||
// return null;
|
||||
// },
|
||||
data: relation
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
193
zyplayer-doc-ui/db-ui/src/components/er/ERGraphDemo/mock.js
Normal file
193
zyplayer-doc-ui/db-ui/src/components/er/ERGraphDemo/mock.js
Normal file
@@ -0,0 +1,193 @@
|
||||
// import {
|
||||
// EntityProperty,
|
||||
// EntityCanvasModel,
|
||||
// RelationCanvasModel
|
||||
// } from './interface'
|
||||
// : EntityProperty[]
|
||||
export const mockProperties = [
|
||||
{
|
||||
propertyId: 'propertyId1',
|
||||
name: '业务日期',
|
||||
propertyType: 'string',
|
||||
isPK: true
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId2',
|
||||
name: '交易号1',
|
||||
propertyType: 'bigint',
|
||||
isFK: true
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId3',
|
||||
name: '最长显示的表单名最长显示的表单名',
|
||||
propertyType: 'string'
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId4',
|
||||
name: '交易支付外键',
|
||||
propertyType: 'string'
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId5',
|
||||
name: '卖家支付日期',
|
||||
propertyType: 'string'
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId6',
|
||||
name: '网商银行',
|
||||
propertyType: 'string'
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId7',
|
||||
name: '业务日期',
|
||||
propertyType: 'string'
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId8',
|
||||
name: '业务日期111',
|
||||
propertyType: 'string'
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId9',
|
||||
name: '业务日期222',
|
||||
propertyType: 'string'
|
||||
},
|
||||
{
|
||||
propertyId: 'propertyId10',
|
||||
name: '业务日期333',
|
||||
propertyType: 'string'
|
||||
}
|
||||
]
|
||||
// : EntityCanvasModel[]
|
||||
export const mockEntityData = [
|
||||
{
|
||||
entityId: 'fact_1',
|
||||
name: '事实表',
|
||||
entityType: 'FACT',
|
||||
properties: mockProperties,
|
||||
x: 550,
|
||||
y: 400,
|
||||
width: 214,
|
||||
height: 248
|
||||
},
|
||||
{
|
||||
entityId: 'fact_up',
|
||||
name: '事实表',
|
||||
entityType: 'FACT',
|
||||
properties: mockProperties,
|
||||
x: 100,
|
||||
y: 100,
|
||||
width: 214,
|
||||
height: 248
|
||||
},
|
||||
{
|
||||
entityId: 'dim_up',
|
||||
name: '维度表',
|
||||
entityType: 'DIM',
|
||||
properties: mockProperties,
|
||||
x: 100,
|
||||
y: 400,
|
||||
width: 214,
|
||||
height: 248
|
||||
},
|
||||
{
|
||||
entityId: 'other_up',
|
||||
name: '其他表',
|
||||
entityType: 'OTHER',
|
||||
properties: mockProperties,
|
||||
x: 100,
|
||||
y: 700,
|
||||
width: 214,
|
||||
height: 248
|
||||
},
|
||||
{
|
||||
entityId: 'other_down',
|
||||
name: '其他表',
|
||||
entityType: 'OTHER',
|
||||
properties: mockProperties,
|
||||
x: 900,
|
||||
y: 0,
|
||||
width: 214,
|
||||
height: 248
|
||||
},
|
||||
{
|
||||
entityId: 'fact_down1',
|
||||
name: '事实表',
|
||||
entityType: 'FACT',
|
||||
properties: mockProperties,
|
||||
x: 900,
|
||||
y: 280,
|
||||
width: 214,
|
||||
height: 248
|
||||
},
|
||||
{
|
||||
entityId: 'dim_down',
|
||||
name: '维度表',
|
||||
entityType: 'DIM',
|
||||
properties: mockProperties,
|
||||
x: 900,
|
||||
y: 580,
|
||||
width: 214,
|
||||
height: 248
|
||||
},
|
||||
{
|
||||
entityId: 'fact_down2',
|
||||
name: '事实表',
|
||||
entityType: 'FACT',
|
||||
properties: mockProperties,
|
||||
x: 900,
|
||||
y: 860,
|
||||
width: 214,
|
||||
height: 248
|
||||
}
|
||||
]
|
||||
|
||||
// : RelationCanvasModel[]
|
||||
|
||||
export const mockRelationData = [
|
||||
{
|
||||
relationId: 'relationId_1',
|
||||
sourceEntityId: 'fact_up',
|
||||
targetEntityId: 'fact_1'
|
||||
},
|
||||
{
|
||||
relationId: 'relationId_2',
|
||||
sourceEntityId: 'fact_1',
|
||||
targetEntityId: 'fact_up'
|
||||
},
|
||||
{
|
||||
relationId: 'relationId_1_loop',
|
||||
sourceEntityId: 'fact_1',
|
||||
targetEntityId: 'fact_1'
|
||||
},
|
||||
{
|
||||
relationId: 'relationId_2',
|
||||
sourceEntityId: 'dim_up',
|
||||
targetEntityId: 'fact_1'
|
||||
},
|
||||
{
|
||||
relationId: 'relationId_3',
|
||||
sourceEntityId: 'other_up',
|
||||
targetEntityId: 'fact_1'
|
||||
},
|
||||
{
|
||||
relationId: 'relationId_4',
|
||||
sourceEntityId: 'fact_1',
|
||||
targetEntityId: 'other_down'
|
||||
},
|
||||
{
|
||||
relationId: 'relationId_5',
|
||||
sourceEntityId: 'fact_1',
|
||||
targetEntityId: 'fact_down1'
|
||||
},
|
||||
{
|
||||
relationId: 'relationId_6',
|
||||
sourceEntityId: 'fact_1',
|
||||
targetEntityId: 'dim_down'
|
||||
},
|
||||
{
|
||||
relationId: 'relationId_7',
|
||||
sourceEntityId: 'fact_1',
|
||||
targetEntityId: 'fact_down2'
|
||||
}
|
||||
]
|
||||
5
zyplayer-doc-ui/db-ui/src/components/er/index.less
Normal file
5
zyplayer-doc-ui/db-ui/src/components/er/index.less
Normal file
@@ -0,0 +1,5 @@
|
||||
.erGraphDemo {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
46
zyplayer-doc-ui/db-ui/src/components/er/index.vue
Normal file
46
zyplayer-doc-ui/db-ui/src/components/er/index.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="styles.erGraphDemo">
|
||||
<ERGraphDemo />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import styles from './index.less'
|
||||
import ERGraphDemo from './ERGraphDemo'
|
||||
export default {
|
||||
name: 'index',
|
||||
components: {
|
||||
ERGraphDemo
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
visible: false,
|
||||
graph: '',
|
||||
styles: styles
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
showDrawer () {
|
||||
// setTimeout(() => {
|
||||
// this.initGraph()
|
||||
// }, 100)
|
||||
this.visible = true
|
||||
},
|
||||
|
||||
onClose () {
|
||||
// this.graph.dispose()
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
87
zyplayer-doc-ui/db-ui/src/components/er/index2.vue
Normal file
87
zyplayer-doc-ui/db-ui/src/components/er/index2.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div style="padding: 10px;">
|
||||
<div ref="graphContainer"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Graph} from '@antv/x6';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
graphData: {
|
||||
// 节点
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1', // String,可选,节点的唯一标识
|
||||
x: 40, // Number,必选,节点位置的 x 值
|
||||
y: 40, // Number,必选,节点位置的 y 值
|
||||
width: 80, // Number,可选,节点大小的 width 值
|
||||
height: 40, // Number,可选,节点大小的 height 值
|
||||
label: 'hello', // String,节点标签
|
||||
},
|
||||
{
|
||||
id: 'node2', // String,节点的唯一标识
|
||||
x: 160, // Number,必选,节点位置的 x 值
|
||||
y: 180, // Number,必选,节点位置的 y 值
|
||||
width: 80, // Number,可选,节点大小的 width 值
|
||||
height: 40, // Number,可选,节点大小的 height 值
|
||||
label: 'world', // String,节点标签
|
||||
},
|
||||
],
|
||||
// 边
|
||||
edges: [
|
||||
{
|
||||
source: 'node1', // String,必须,起始节点 id
|
||||
target: 'node2', // String,必须,目标节点 id
|
||||
},
|
||||
],
|
||||
},
|
||||
graph: {},
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.graph = new Graph({
|
||||
container: this.$refs.graphContainer,
|
||||
height: 600,
|
||||
scroller: {
|
||||
enabled: true,
|
||||
pannable: true,
|
||||
pageBreak: false,
|
||||
},
|
||||
grid: {
|
||||
size: 10,
|
||||
visible: true
|
||||
}
|
||||
});
|
||||
this.graph.fromJSON(this.graphData);
|
||||
this.graph.centerContent();
|
||||
},
|
||||
methods: {
|
||||
addNode(){
|
||||
this.graph.createNode({
|
||||
shape: 'org-node',
|
||||
attrs: {
|
||||
'.card': { fill: background },
|
||||
'.image': { xlinkHref: image },
|
||||
'.rank': {
|
||||
fill: textColor,
|
||||
text: Dom.breakText(rank, { width: 160, height: 45 }),
|
||||
},
|
||||
'.name': {
|
||||
fill: textColor,
|
||||
text: Dom.breakText(name, { width: 160, height: 45 }),
|
||||
},
|
||||
'.btn > circle': { stroke: textColor },
|
||||
'.btn > text': { fill: textColor, stroke: textColor },
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
// import { Graph } from '@antv/x6'
|
||||
import { Edge } from '@antv/x6'
|
||||
// !!!!!!!!!!!!!!!
|
||||
// import '@antv/x6-react-shape'
|
||||
// import X6BaseGraph from '../graph/baseGraph'
|
||||
// import Node from '../graph/node'
|
||||
// import Edge from '../graph/edge'
|
||||
// import { NodeConfig, EdgeConfig } from '../interface/graph-core'
|
||||
import _ from 'lodash'
|
||||
import '@antv/x6-vue-shape'
|
||||
const x6VueShape = 'vue-shape'
|
||||
|
||||
export default class CellController {
|
||||
// private graph: Graph;
|
||||
//
|
||||
// /** 记录画布中的节点 */
|
||||
// public nodes: Node[] = [];
|
||||
// /** 记录画布中的连线 */
|
||||
// public edges: Edge[] = [];
|
||||
|
||||
constructor (x6BaseGraph) {
|
||||
this.graph = x6BaseGraph.graph
|
||||
/** 记录画布中的节点 */
|
||||
this.nodes = []
|
||||
/** 记录画布中的连线 */
|
||||
this.edges = []
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加节点
|
||||
* @template T
|
||||
* @param {T[]} nodesData 节点数据集合
|
||||
* @memberof CellController
|
||||
*/
|
||||
addNodes (nodesData) {
|
||||
nodesData.forEach((nodeData) => {
|
||||
this.addNode(nodeData)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单个节点
|
||||
* @template T
|
||||
* @param {T} nodeData 节点数据
|
||||
* @memberof CellController
|
||||
*/
|
||||
addNode (nodeData) {
|
||||
console.log(nodeData, 'nodeData')
|
||||
const { id, x, y, width, height, component, data, ...rest } = nodeData
|
||||
const newNode = this.graph.addNode({
|
||||
id,
|
||||
x: x || 0,
|
||||
y: y || 0,
|
||||
width: width || 100,
|
||||
height: height || 60,
|
||||
// data: data || undefined,
|
||||
shape: x6VueShape,
|
||||
component: component,
|
||||
...rest
|
||||
})
|
||||
this.nodes.push(newNode)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
* @param {Node} node 节点实例
|
||||
* @param {NodeConfig} newNodeData 节点最新数据
|
||||
* @memberof CellController
|
||||
*/
|
||||
updateNode (node, newNodeData) {
|
||||
// !!! 现在仅支持data数据不一致才认为更新
|
||||
if (!_.isEqual(node.data, newNodeData.data)) {
|
||||
// 重设节点数据, 触发x6更新节点逻辑
|
||||
node.setData(newNodeData.data)
|
||||
// node.setPosition(newNodeData.x, newNodeData.y)
|
||||
// node.setSize(newNodeData.width, newNodeData.height)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除节点
|
||||
* @param {Node[]} removeNodes
|
||||
* @memberof CellController
|
||||
*/
|
||||
removeNodes (removeNodes) {
|
||||
if (_.size(removeNodes) > 0) {
|
||||
this.graph.removeCells(removeNodes)
|
||||
this.nodes = _.pullAll(this.nodes, removeNodes)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个删除节点
|
||||
* @param {(Node | string)} node
|
||||
* @memberof CellController
|
||||
*/
|
||||
removeNode (removeNode) {
|
||||
if (!removeNode) {
|
||||
return
|
||||
}
|
||||
if (removeNode instanceof Node) {
|
||||
this.removeNodes([removeNode])
|
||||
} else {
|
||||
const findNode = this.findNodeById(removeNode)
|
||||
if (findNode) {
|
||||
this.removeNodes([findNode])
|
||||
}
|
||||
}
|
||||
// !!! 待补充: 删除节点时是否需要将与之关联的连线都删除, 这里可以加一个标记
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加连线
|
||||
* @template T
|
||||
* @param {T[]} edgesData 边数据集合
|
||||
* @memberof CellController
|
||||
*/
|
||||
addEdges (edgesData) {
|
||||
edgesData.forEach((edgeData) => {
|
||||
this.addEdge(edgeData)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单个连线
|
||||
* @template T
|
||||
* @param {T} edgeData 边数据
|
||||
* @memberof CellController
|
||||
*/
|
||||
addEdge (edgeData) {
|
||||
const { id, source, target, render, data, ...rest } = edgeData
|
||||
|
||||
const sourceNode = _.find(this.nodes, (node) => node.id === source)
|
||||
const targetNode = _.find(this.nodes, (node) => node.id === target)
|
||||
if (!source || !target) {
|
||||
throw new Error('edge must has source and target!')
|
||||
}
|
||||
|
||||
const newEdge = this.graph.addEdge({
|
||||
id: id || `${source}-${target}`,
|
||||
data: data || undefined,
|
||||
source: sourceNode,
|
||||
target: targetNode,
|
||||
// label: (edge: Edge) => {
|
||||
// return render && render(edge && edge.data)
|
||||
// },
|
||||
attrs: {
|
||||
line: {
|
||||
stroke: '#B4BDCF',
|
||||
strokeWidth: 1
|
||||
}
|
||||
},
|
||||
...rest
|
||||
})
|
||||
this.edges.push(newEdge)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新边
|
||||
* @param {Edge} edge 边实例
|
||||
* @param {EdgeConfig} newEdgeData 边最新数据
|
||||
* @memberof CellController
|
||||
*/
|
||||
updateEdge (edge, newEdgeData) {
|
||||
// !!! 现在仅支持data数据不一致才认为更新, 需要扩展
|
||||
if (!_.isEqual(edge.data, newEdgeData.data)) {
|
||||
edge.setData(newEdgeData.data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除边
|
||||
* @param {Edge[]} removeEdges
|
||||
* @memberof CellController
|
||||
*/
|
||||
removeEdges (removeEdges) {
|
||||
this.graph.removeCells(removeEdges)
|
||||
this.edges = _.pullAll(this.edges, removeEdges)
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个删除边
|
||||
* @param {(Edge | string)} removeEdge
|
||||
* @memberof CellController
|
||||
*/
|
||||
removeEdge (removeEdge) {
|
||||
if (!removeEdge) {
|
||||
return
|
||||
}
|
||||
if (removeEdge instanceof Edge) {
|
||||
this.graph.removeCells([removeEdge])
|
||||
this.removeEdges([removeEdge])
|
||||
} else {
|
||||
const findEdge = this.findEdgeById(removeEdge)
|
||||
if (findEdge) {
|
||||
this.removeEdges([findEdge])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点id获取节点
|
||||
* @param {string} id 节点id
|
||||
* @returns {(Node | undefined)}
|
||||
* @memberof CellController
|
||||
*/
|
||||
findNodeById (id) {
|
||||
return this.nodes.find((node) => node.id === id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据连线id获取连线
|
||||
* @param {string} id 边id
|
||||
* @returns {(Edge | undefined)}
|
||||
* @memberof CellController
|
||||
*/
|
||||
findEdgeById (id) {
|
||||
return this.edges.find((edge) => edge.id === id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// import { Graph } from '@antv/x6'
|
||||
// import X6BaseGraph from '../graph/baseGraph'
|
||||
// import { EventArg } from '../interface/graph-core'
|
||||
|
||||
export default class EventController {
|
||||
// private x6BaseGraph: X6BaseGraph;
|
||||
// private graph: Graph;
|
||||
//
|
||||
// // 是否删除X6自动生成的无实际业务含义的连线(用户在画布上拖拽生成连线, 这条连线是没有业务含义的)
|
||||
// private isDeleteX6DefaultEdge!: boolean;
|
||||
|
||||
constructor (x6BaseGraph) {
|
||||
this.x6BaseGraph = x6BaseGraph
|
||||
this.graph = x6BaseGraph.graph
|
||||
}
|
||||
|
||||
registerEvent = (events) => {
|
||||
events &&
|
||||
events.forEach((event) => {
|
||||
switch (event.eventName) {
|
||||
case 'scale': {
|
||||
this.graph.on('scale', ({ sx, sy, ox, oy }) => {
|
||||
const scale = sx
|
||||
event.handler && event.handler({ scale })
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'graph:mouseenter': {
|
||||
this.graph.on('graph:mouseenter', ({ e }) => {
|
||||
event.handler && event.handler()
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'graph:mouseleave': {
|
||||
this.graph.on('graph:mouseleave', ({ e }) => {
|
||||
event.handler && event.handler()
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'blank:mouseDown': {
|
||||
this.graph.on('blank:mousedown', ({ e, x, y }) => {
|
||||
event.handler && event.handler({ x, y })
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'blank:mouseUp': {
|
||||
this.graph.on('blank:mouseup', ({ e, x, y }) => {
|
||||
event.handler && event.handler({ x, y })
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'node:added': {
|
||||
this.graph.on('node:added', ({ node }) => {
|
||||
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
|
||||
// event.handler && event.handler({ node });
|
||||
// }
|
||||
this.x6BaseGraph.bringNodesToFront([node])
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'node:removed': {
|
||||
this.graph.on('node:removed', ({ node }) => {
|
||||
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
|
||||
// event.handler && event.handler({ node })
|
||||
// }
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'edge:added': {
|
||||
this.graph.on('edge:added', ({ edge }) => {
|
||||
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
|
||||
// event.handler && event.handler({ edge })
|
||||
// }
|
||||
// this.x6BaseGraph.bringCellsToBack([edge])
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'edge:removed': {
|
||||
this.graph.on('edge:removed', ({ edge }) => {
|
||||
if (this.isDeleteX6DefaultEdge) {
|
||||
|
||||
}
|
||||
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
|
||||
// event.handler && event.handler({ edge })
|
||||
// }
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'node:mousedown': {
|
||||
this.graph.on('node:mousedown', ({ e, view, x, y }) => {
|
||||
event.handler && event.handler({ node: view.cell, x, y })
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'node:mousemove': {
|
||||
this.graph.on('node:mousemove', ({ e, view, x, y }) => {
|
||||
event.handler && event.handler({ node: view.cell, x, y })
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'node:mouseup': {
|
||||
this.graph.on('node:mouseup', ({ e, view, x, y }) => {
|
||||
event.handler && event.handler({ node: view.cell, x, y })
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'node:click': {
|
||||
this.graph.on('node:click', ({ view }) => {
|
||||
event.handler && event.handler({ node: view.cell })
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'node:dbclick': {
|
||||
this.graph.on('node:dblclick', ({ view }) => {
|
||||
event.handler && event.handler({ node: view.cell })
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'edge:connected': {
|
||||
this.graph.on('edge:connected', ({ edge }) => {
|
||||
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
|
||||
// // 拖拽连线过程中画布会出现一条连线, 该连线无实际业务含义, 需要删除
|
||||
// if (!edge.data && edge.id.toString().startsWith('cell-', 0)) {
|
||||
// this.isDeleteX6DefaultEdge = true;
|
||||
// this.graph.removeCells([edge]);
|
||||
// this.isDeleteX6DefaultEdge = false;
|
||||
// }
|
||||
// event.handler && event.handler({ edge });
|
||||
// }
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'selection:changed': {
|
||||
this.graph.on(
|
||||
'selection:changed',
|
||||
({ selected, removed, added }) => {
|
||||
event.handler && event.handler({ selected, removed, added })
|
||||
this.x6BaseGraph.bringCellsToFront(selected)
|
||||
}
|
||||
)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export { default as CellController } from './cellController'
|
||||
export { default as EventController } from './eventController'
|
||||
|
||||
/** 更多独立功能控制器, 比如快捷方式、布局等 */
|
||||
267
zyplayer-doc-ui/db-ui/src/components/er/xflow/graph/baseGraph.js
Normal file
267
zyplayer-doc-ui/db-ui/src/components/er/xflow/graph/baseGraph.js
Normal file
@@ -0,0 +1,267 @@
|
||||
import { Graph } from '@antv/x6'
|
||||
// import Node from '../graph/node'
|
||||
// import Edge from '../graph/edge'
|
||||
import { CellController, EventController } from '../controller/index'
|
||||
import _ from 'lodash'
|
||||
|
||||
export default class X6BaseGraph {
|
||||
// public graph!: Graph;
|
||||
// public cellController!: CellController;
|
||||
// public eventController!: EventController;
|
||||
|
||||
constructor (graphOptions) {
|
||||
/** 创建X6画布实例 */
|
||||
const defaultCfg = this.getDefaultCfg()
|
||||
this.graph = new Graph({
|
||||
...defaultCfg,
|
||||
...graphOptions
|
||||
})
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
init () {
|
||||
this.cellController = new CellController(this)
|
||||
this.eventController = new EventController(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取画布默认配置项
|
||||
* @returns {Partial<GraphOptions>}
|
||||
* @memberof X6BaseGraph
|
||||
*/
|
||||
getDefaultCfg () {
|
||||
const defaultCfg = {
|
||||
/** 无限画布设置 */
|
||||
scroller: {
|
||||
enabled: true,
|
||||
pageVisible: false,
|
||||
pageBreak: false,
|
||||
pannable: true
|
||||
},
|
||||
/** 画布网格 */
|
||||
grid: {
|
||||
visible: true,
|
||||
size: 20,
|
||||
type: 'doubleMesh',
|
||||
args: [
|
||||
{
|
||||
color: '#888',
|
||||
thickness: 1
|
||||
}
|
||||
// {
|
||||
// color: '#ddd',
|
||||
// thickness: 1,
|
||||
// factor: 4,
|
||||
// },
|
||||
]
|
||||
},
|
||||
/** 全局连线配置 */
|
||||
// connecting: {
|
||||
// anchor: 'midSide',
|
||||
// router: {
|
||||
// name: 'er',
|
||||
// },
|
||||
// // connector: {
|
||||
// // name: 'smooth',
|
||||
// // },
|
||||
// },
|
||||
connecting: {
|
||||
connector: {
|
||||
name: 'rounded'
|
||||
},
|
||||
router: {
|
||||
name: 'er',
|
||||
args: {
|
||||
direction: 'H'
|
||||
}
|
||||
}
|
||||
},
|
||||
/** 对齐线 */
|
||||
snapline: {
|
||||
enabled: true
|
||||
},
|
||||
/** 滚轮缩放 */
|
||||
// mousewheel: {
|
||||
// enabled: true,
|
||||
// zoomAtMousePosition: false,
|
||||
// factor: 1.1,
|
||||
// },
|
||||
keyboard: {
|
||||
enabled: true
|
||||
},
|
||||
clipboard: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
return defaultCfg
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新画布内容
|
||||
* @param {GraphData} graphData 画布元素数据
|
||||
* @memberof X6BaseGraph
|
||||
*/
|
||||
updateGraph (graphData) {
|
||||
if (!graphData) {
|
||||
throw new Error('graphData must be defined first!')
|
||||
}
|
||||
const { addNodesData, addEdgesData } = this.graphContentDiff(graphData)
|
||||
|
||||
this.graph.batchUpdate('updateGraph', () => {
|
||||
if (addNodesData && addNodesData.length > 0) {
|
||||
this.cellController.addNodes(addNodesData)
|
||||
}
|
||||
if (addEdgesData && addEdgesData.length > 0) {
|
||||
this.cellController.addEdges(addEdgesData)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 画布缩放
|
||||
* @param {number} factor 缩放比例尺
|
||||
* @memberof X6BaseGraph
|
||||
*/
|
||||
zoomGraph (factor) {
|
||||
if (typeof factor === 'number') {
|
||||
this.graph.zoom(factor)
|
||||
} else if (factor === 'fit') {
|
||||
this.graph.zoomToFit({ padding: 12 })
|
||||
} else if (factor) {
|
||||
this.graph.scale(1)
|
||||
this.graph.centerContent()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动节点到画布中央
|
||||
* @param {(Node | string)} node
|
||||
* @memberof X6BaseGraph
|
||||
*/
|
||||
focusNodeToGraphCenter (node) {
|
||||
if (node instanceof Node) {
|
||||
this.graph.centerCell(node)
|
||||
} else {
|
||||
const temp = this.cellController.findNodeById(node)
|
||||
if (temp) {
|
||||
this.graph.centerCell(temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Nodes置于画布最前方
|
||||
* @param {Node[]} nodes
|
||||
* @memberof X6BaseGraph
|
||||
*/
|
||||
bringNodesToFront (nodes) {
|
||||
nodes.forEach((node) => {
|
||||
node.toBack()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Nodes置于画布最后方
|
||||
* @param {Node[]} nodess
|
||||
* @memberof X6BaseGraph
|
||||
*/
|
||||
bringNodesToBack (nodes) {
|
||||
nodes.forEach((node) => {
|
||||
node.toBack()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空画布内容
|
||||
* @memberof X6BaseGraph
|
||||
*/
|
||||
clearGraph () {
|
||||
// todo
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册监听事件
|
||||
* @param {EventArg[]} events
|
||||
* @memberof X6BaseGraph
|
||||
*/
|
||||
registerEvent (events) {
|
||||
this.eventController.registerEvent(events)
|
||||
}
|
||||
|
||||
/** 画布内容Diff */
|
||||
graphContentDiff (graphData) {
|
||||
const { nodes: nodesData, edges: edgesData } = graphData
|
||||
|
||||
// 新增节点数据
|
||||
const addNodesData = []
|
||||
nodesData.forEach((nodeData) => {
|
||||
const findNode = this.cellController.findNodeById(nodeData.id)
|
||||
if (!findNode) {
|
||||
addNodesData.push(nodeData)
|
||||
}
|
||||
})
|
||||
|
||||
// 保持、更新、移除的节点
|
||||
const retainNodes = []
|
||||
const updateNodes = []
|
||||
const removeNodes = []
|
||||
this.cellController.nodes.forEach((node) => {
|
||||
const findNodeData = nodesData.find(
|
||||
(nodeData) => nodeData.id === node.id
|
||||
)
|
||||
if (!findNodeData) {
|
||||
removeNodes.push(node)
|
||||
} else {
|
||||
// !!! 目前仅支持节点data数据变化, 才认为更新
|
||||
if (_.isEqual(node.data, findNodeData.data)) {
|
||||
retainNodes.push(node)
|
||||
} else {
|
||||
updateNodes.push(node)
|
||||
this.cellController.updateNode(node, findNodeData)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 新增连线数据
|
||||
const addEdgesData = []
|
||||
edgesData.forEach((edgeData) => {
|
||||
if (!edgeData.id) {
|
||||
return
|
||||
}
|
||||
const findEdge = this.cellController.findEdgeById(edgeData.id)
|
||||
if (!findEdge) {
|
||||
addEdgesData.push(edgeData)
|
||||
}
|
||||
})
|
||||
|
||||
// 保持、更新、移除的连线
|
||||
const retainEdges = []
|
||||
const updateEdges = []
|
||||
const removeEdges = []
|
||||
this.cellController.edges.forEach((edge) => {
|
||||
const findEdgeData = edgesData.find(
|
||||
(edgeData) => edgeData.id === edge.id
|
||||
)
|
||||
if (!findEdgeData) {
|
||||
removeEdges.push(edge)
|
||||
} else {
|
||||
// !!! 目前仅支持连线data数据变化, 才认为更新
|
||||
if (_.isEqual(edge.data, findEdgeData.data)) {
|
||||
retainEdges.push(edge)
|
||||
} else {
|
||||
updateEdges.push(edge)
|
||||
this.cellController.updateEdge(edge, findEdgeData)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.cellController.removeNodes(removeNodes)
|
||||
this.cellController.removeEdges(removeEdges)
|
||||
|
||||
return {
|
||||
addNodesData,
|
||||
addEdgesData
|
||||
}
|
||||
}
|
||||
}
|
||||
7
zyplayer-doc-ui/db-ui/src/components/er/xflow/index.js
Normal file
7
zyplayer-doc-ui/db-ui/src/components/er/xflow/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
/** 类型定义 */
|
||||
// export * from './interface/graph-core'
|
||||
|
||||
/** BaseGraph、Node、Edge */
|
||||
export { default as BaseGraph } from './graph/baseGraph'
|
||||
// export { default as Node } from './graph/node'
|
||||
// export { default as Edge } from './graph/edge'
|
||||
Reference in New Issue
Block a user