Files
fe.ems.vue3/src/components/ChartGraphG6/index.vue
2023-12-07 20:15:18 +08:00

514 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div>
<div ref="g6Dom" :style="{ height: height, width: width }"></div>
<button @click="actions['Enable/Disable Node States'].Breathing()">
Breathing()
</button>
<button @click="actions['Enable/Disable Node States'].Scaling()">
Scaling()
</button>
<button @click="actions['Enable/Disable Edge States'].Growing()">
Growing()
</button>
<button @click="actions['Enable/Disable Edge States'].Running()">
Running()
</button>
</div>
</template>
<script lang="ts" setup>
import { nextTick, watch, onMounted, onBeforeUnmount, ref } from 'vue';
import { Graph, extend, Extensions } from '@antv/g6';
const props = defineProps({
/**
* 图表主题
*
* 'dark' | 'light'
*/
theme: {
type: String,
default: 'light', // 'dark' | 'light'
},
/**宽度默认100% */
width: {
type: String,
default: '100%',
},
/**高度 */
height: {
type: String,
default: '500px',
},
});
const g6Dom = ref<HTMLElement | undefined>(undefined);
const ExtGraph = extend(Graph, {
// layouts: {
// dagre: Extensions.DagreLayout,
// },
edges: {
'polyline-edge': Extensions.PolylineEdge,
'custom-edge': Extensions.CubicEdge,
},
});
const data = {
nodes: [
{
id: '0',
data: {
label: 'A0',
x: 110,
y: 110,
},
},
// B
{
id: '1',
data: {
label: 'B1',
parentId: 'B',
x: 110,
y: 350,
},
},
{
id: '2',
data: {
label: 'B2',
parentId: 'B',
x: 220,
y: 350,
},
},
{
id: '3',
data: {
label: 'B3',
parentId: 'A',
x: 360,
y: 350,
},
},
// C
{
id: '4',
data: {
label: 'C1',
parentId: 'C',
x: 360,
y: 150,
},
},
{
id: '5',
data: {
label: 'C2',
parentId: 'C',
x: 460,
y: 150,
},
},
],
edges: [
{
id: 'edge-102',
source: '0',
target: '1',
data: {
type: 'custom-edge',
animates: {
update: [
{
// 在 selected 和 active 状态变化导致的 haloShape opacity 变化时,使 opacity 带动画地更新
fields: ['opacity'],
shapeId: 'haloShape',
states: ['selected', 'active'],
duration: 500,
},
],
},
},
},
{
id: 'edge-161',
source: '0',
target: '2',
data: {},
},
{
id: 'edge-253',
source: '0',
target: 'A',
data: {
keyShape: {
endArrow: true,
},
labelShape: {
text: 'asdf-arrow',
},
},
},
{
id: 'edge-237',
source: '1',
target: '4',
data: {
type: 'polyline-edge',
keyShape: {
endArrow: true,
routeCfg: {
/**
* 目前支持正交路由 'orth' 和地铁路由 'er'
*/
// name: 'er',
/**
* 是否开启自动避障,默认为 false
* Whether to enable automatic obstacle avoidance, default is false
*/
enableObstacleAvoidance: true,
},
/**
* 拐弯处的圆角弧度,默认为直角,值为 0
* The radius of the corner rounding, defaults to a right angle
*/
// radius: 20,
/**
* 拐弯处距离节点最小距离, 默认为 2
* Minimum distance from the node at the corner, default is 5.
*/
// offset: 0,
/**
* 控制点数组,不指定时根据 A* 算法自动生成折线。若指定了,则按照 controlPoints 指定的位置进行弯折
* An array of control points that, when not specified, automatically generates the bends according to the A* algorithm. If specified, bends are made at the position specified by controlPoints.
*/
// controlPoints: [],
},
},
},
{
id: 'edge-133',
source: '2',
target: '4',
data: {
type: 'polyline-edge',
keyShape: {
/**
* 拐弯处的圆角弧度,默认为直角
*/
radius: 20,
/**
* 拐弯处距离节点最小距离, 默认为 5
*/
// offset: 0,
/**
* 控制点数组,不指定时根据 A* 算法自动生成折线。若指定了,则按照 controlPoints 指定的位置进行弯折
*/
controlPoints: [
{ x: 220, y: 220 },
{ x: 300, y: 130 },
],
},
},
},
{
id: 'edge-320',
source: '2',
target: '5',
data: {
keyShape: {
endArrow: true,
},
labelShape: {
text: 'edge-arrow',
},
},
},
{
id: 'edge-355',
source: '3',
target: '4',
data: {
keyShape: {
endArrow: true,
},
labelShape: {
text: 'asdf-arrow',
},
},
},
],
combos: [
{
id: 'A',
data: {
text: 'combo A',
},
},
{
id: 'B',
data: {
label: 'combo B',
},
},
{
id: 'C',
data: {
label: 'combo C',
},
},
],
};
let graph: any = null;
/**初始化渲染图表 */
function initChart() {
if (!g6Dom.value) return;
graph = new ExtGraph({
container: g6Dom.value,
height: 500,
plugins: [
{
// lod-controller will be automatically assigned to graph with `disableLod: false` to graph if it is not configured as following
type: 'lod-controller',
disableLod: true,
},
],
modes: {
default: [
{
type: 'drag-node',
enableTransient: false,
updateComboStructure: false,
},
{
type: 'click-select',
itemTypes: ['node', 'edge', 'combo'],
},
{
type: 'drag-combo',
enableTransient: true,
updateComboStructure: true,
},
// 'drag-combo',
'drag-canvas',
// 'click-select',
'zoom-canvas',
],
},
theme: {
type: 'spec',
base: 'light',
specification: {
node: {
dataTypeField: 'parentId',
},
},
},
node: model => {
const { id, data } = model;
return {
id,
data: {
...data,
type: 'rect-node',
keyShape: {
width: 60,
height: 30,
radius: 8,
},
labelShape: {
position: 'center',
text: data.label,
},
animates: {
update: [
{
fields: ['opacity'],
shapeId: 'haloShape',
states: ['breathing'],
iterations: Infinity,
direction: 'alternate',
duration: 500,
},
{
fields: ['lineWidth'],
shapeId: 'keyShape',
states: ['breathing'],
iterations: Infinity,
direction: 'alternate',
duration: 500,
},
{
fields: ['height'],
shapeId: 'keyShape',
states: ['scaling'],
iterations: Infinity,
direction: 'alternate',
duration: 500,
},
],
},
},
};
},
edge: {
type: 'custom-edge',
animates: {
update: [
{
fields: ['lineDash'],
shapeId: 'keyShape',
states: ['growing', 'running'],
iterations: Infinity,
duration: 2000,
},
{
fields: ['offsetDistance'],
shapeId: 'buShape',
states: ['circleRunning'],
iterations: Infinity,
duration: 2000,
},
],
},
},
combo: model => {
const { id, data } = model;
return {
id,
data: {
...data,
type: 'rect-combo',
keyShape: {
opacity: 0.8,
padding: [20, 20, 20, 20],
radius: 8,
},
labelShape: {
text: data.label,
offsetY: 8,
},
labelBackgroundShape: {},
animates: {
update: [
{
fields: ['width', 'height', 'x', 'y'],
shapeId: 'keyShape',
},
],
},
},
};
},
nodeState: {
breathing: {
haloShape: {
opacity: 0.25,
lineWidth: 20,
visible: true,
},
keyShape: {
stroke: '#000',
lineWidth: 4,
},
},
scaling: {
keyShape: {
r: 24,
width: 48,
height: 48,
},
},
},
edgeState: {
growing: {
keyShape: {
lineWidth: 2,
lineDash: ['100%', 0],
},
},
running: {
keyShape: {
lineWidth: 2,
lineDash: [2, 2],
// TODO: lineDashOffset
},
},
},
data,
});
}
const actions = {
'Enable/Disable Node States': {
Breathing: () => {
graph.getAllNodesData().forEach(node => {
if (graph.getItemState(node.id, 'breathing')) {
graph.setItemState(node.id, 'breathing', false);
} else {
graph.setItemState(node.id, 'scaling', false);
graph.setItemState(node.id, 'breathing', true);
}
});
},
Scaling: () => {
graph.getAllNodesData().forEach(node => {
if (graph.getItemState(node.id, 'scaling')) {
graph.setItemState(node.id, 'scaling', false);
} else {
graph.setItemState(node.id, 'breathing', false);
graph.setItemState(node.id, 'scaling', true);
}
});
},
},
'Enable/Disable Edge States': {
Growing: () => {
graph.getAllEdgesData().forEach(edge => {
if (graph.getItemState(edge.id, 'growing')) {
graph.setItemState(edge.id, 'growing', false);
} else {
graph.setItemState(edge.id, 'running', false);
graph.setItemState(edge.id, 'growing', true);
}
});
},
Running: () => {
graph.getAllEdgesData().forEach(edge => {
if (graph.getItemState(edge.id, 'running')) {
graph.setItemState(edge.id, 'running', false);
} else {
graph.setItemState(edge.id, 'growing', false);
graph.setItemState(edge.id, 'running', true);
}
});
},
},
};
watch(
() => props.option,
val => {
// if (val) {
// nextTick(() => {
// initChart();
// });
// }
}
);
onMounted(() => {
nextTick(() => {
initChart();
});
});
onBeforeUnmount(() => {});
</script>
<style lang="less" scoped></style>