<template>
<div>
<div class="login" :style="{ height: scrollerHeight }" style="width: 100%;">
<div style="width: 100%;height: 100%" ref="graphWrap" id="graphWrap"></div>
<el-dialog class="link_graph_fullscreen" :fullscreen="true" title="数据链路" :visible.sync="open" :destroy-on-close="true" width="100%" append-to-body>
<dataLinkedFull ref='apidatalinked' :flowList="flowList" chart-id="linkedChart"></dataLinkedFull>
</el-dialog>
</div>
</div>
</template>
<script>
import G6 from '@antv/g6';
// import { linkdata } from '@/mock/link1';
import insertCss from 'insert-css';
import dataLinkedFull from "@/components/newLinkedChart/linkFull";
import full from "@/assets/images/app/full.png";
insertCss(`
.link_graph_fullscreen .g6-component-toolbar {
background-color: rgba(200, 200, 200, 0.3);
}
.link_graph_fullscreen .g6-component-toolbar li {
margin-left: 0px;
margin-top: 0px;
}
.g6-component-toolbar {
position: absolute;
list-style-type: none;
padding: 6px;
left: 6px !important;
top: 6px !important;
background-color: #FFFFFF;
border: 0px solid #e2e2e2;
border-radius: 12px;
font-size: 12px;
color: #545454;
margin: 0;
width: 46px;
}
.g6-component-toolbar li {
float: left;
text-align: center;
width: 35px;
height: 35px;
cursor: pointer;
list-style-type: none;
list-style: none;
margin-left: -15px;
margin-top: -15px;
line-height: 55px;
}
.g6-component-tooltip {
background-color: rgba(0,0,0, 0.65);
padding: 10px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
width: fit-content;
color: #fff;
border-radius = 4px;
}
.g6-tooltip {
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #545454;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 8px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
`);
// const data = {nodes:[], edges:[], combos: []};
export default {
name: 'dataLinkeVue',
components: { dataLinkedFull },
props: {
// flowList: {
// type: Array,
// default: [],
// require: true
// },
// titles: {
// type: Array,
// default: [],
// require: true
// },
// tags: {
// type: Array,
// default: [],
// require: true
// },
isFull: {
type: Boolean,
default: false
},
appName: { type: String },
appCode: { type: String },
chartId: {
type: String,
default: () => {
return 'linkedChart'
}
}
},
data() {
return {
flowList:[{
"detailInfo": {},
"id": "xq1",
"isFmqd": "0",
"nodeName": "数据分析应用建设需求",
"nodeType": "XQ",
"preNodes": []
},{
"detailInfo": {},
"id": "xq2",
"isFmqd": "0",
"nodeName": "资产数据分析需求",
"nodeType": "XQ",
"preNodes": []
}, {
"detailInfo": {},
"id": "yd1",
"isFmqd": "0",
"nodeName": "yd_db1_inst_elec_cons_tmp_today",
"nodeType": "YD",
"preNodes": ["xq1"]
}, {
"detailInfo": {},
"id": "yd2",
"isFmqd": "0",
"nodeName": "yd_db2_inst_elec_cons_tmp_today2",
"nodeType": "YD",
"preNodes": ["xq1"],
"projectName": "js_ods_prod"
}, {
"detailInfo": {
"tbDatabase": "js_ods_prod",
"nodeCnName": "贴源表1",
"projectEnv": "生产环境",
"isDateSet": false
},
"id": "ty1",
"isFmqd": "0",
"accreditStatus": "1",
"nodeName": "ods_db1_inst_elec_cons_tmp_today",
"nodeType": "TY",
"preNodes": ["yd1"]
}, {
"detailInfo": {
"tbDatabase": "js_ods_prod",
"nodeCnName": "贴源表2",
"projectEnv": "生产环境",
"isDateSet": false
},
"id": "ty2",
"isFmqd": "0",
"accreditStatus": "3",
"nodeName": "ods_db2_inst_elec_cons_tmp_today2",
"nodeType": "TY",
"preNodes": ["yd2"]
}, {
"detailInfo": {
"tbDatabase": "js_dwd_prod",
"nodeCnName": "共享层表1",
"projectEnv": "测试环境",
"isDateSet": false
},
"id": "gx1",
"isFmqd": "0",
"accreditStatus": "4",
"nodeName": "dwd_db1_inst_elec_cons_tmp_today",
"nodeType": "GX",
"preNodes": ["ty1"]
}, {
"detailInfo": {
"tbDatabase": "js_dwd_prod",
"nodeCnName": "共享层表2",
"projectEnv": "测试环境",
"isDateSet": false
},
"id": "gx2",
"isFmqd": "0",
"accreditStatus": "2",
"nodeName": "dwd_db2_inst_elec_cons_tmp_today2",
"nodeType": "GX",
"preNodes": ["ty2"]
}, {
"detailInfo": {
"tbDatabase": "js_js_dws_proj_prod",
"nodeCnName": "分析层表1",
"projectEnv": "测试环境",
"isDateSet": false
},
"id": "fx1",
"isFmqd": "0",
"accreditStatus": "4",
"nodeName": "asd_db1_inst_elec_cons_tmp_today",
"nodeType": "FX",
"preNodes": ["gx1"]
}, {
"detailInfo": {
"tbDatabase": "js_js_dws_proj_prod",
"nodeCnName": "分析层表2",
"projectEnv": "生产环境",
"isDateSet": true
},
// 模拟虚拟节点
"id": "fx2",
"isFmqd": "0",
"accreditStatus": "2",
"nodeName": "asd_db2_inst_elec_cons_tmp_today2",
"nodeType": "EMPTY",
"preNodes": ["gx2"]
}, {
"detailInfo": {},
"id": "rds1",
"isFmqd": "0",
"nodeName": "rds_db1_inst_elec_cons_tmp_today",
"nodeType": "RDS",
"preNodes": ["fx1"]
}, {
"detailInfo": {},
"id": "rds2",
"isFmqd": "0",
"nodeName": "rds_db2_inst_elec_cons_tmp_today2",
"nodeType": "RDS",
"preNodes": ["fx2"]
}, {
"detailInfo": {},
"id": "yd3",
"isFmqd": "0",
"nodeName": "yd_mgja",
"nodeType": "YD",
"preNodes": ["xq2"]
}, {
"detailInfo": {},
"id": "yd4",
"isFmqd": "0",
"nodeName": "yd_summary_day_total",
"nodeType": "YD",
"preNodes": ["xq2"]
}, {
"detailInfo": {
"tbDatabase": "js_ods_prod",
"nodeCnName": "贴源3",
"projectEnv": "生产环境",
"isDateSet": false
},
"id": "ty3",
"isFmqd": "0",
"accreditStatus": "2",
"nodeName": "ods_mgjs_palw",
"nodeType": "TY",
"preNodes": ["yd3"]
}, {
"detailInfo": {
"tbDatabase": "js_js_dws_proj_prod",
"nodeCnName": "分析层表3",
"projectEnv": "生产环境",
"isDateSet": false
},
"id": "fx3",
"isFmqd": "0",
"accreditStatus": "2",
"nodeName": "dws_grid_grid_abn_stats_day_ts_dj",
"nodeType": "FX",
"preNodes": ["ty3"]
}, {
"detailInfo": {
"tbDatabase": "js_js_dws_proj_prod",
"nodeCnName": "分析层表4",
"projectEnv": "生产环境",
"isDateSet": false
},
"id": "fx4",
"isFmqd": "0",
"accreditStatus": "2",
"nodeName": "dws_cst_bus_app_form_scale_yc_dqgds",
"nodeType": "FX",
"preNodes": ["ty3"]
}, {
"detailInfo": {
"tbDatabase": "js_js_dws_proj_prod",
"nodeCnName": "分析层表5",
"projectEnv": "生产环境",
"isDateSet": false
},
"id": "fx5",
"isFmqd": "0",
"accreditStatus": "2",
"nodeName": "dws_cst_bus_app_form_scale_sz_linhugds",
"nodeType": "FX",
"preNodes": ["ty3"]
}, {
"detailInfo": {
"tbDatabase": "js_ods_prod",
"nodeCnName": "贴源4",
"projectEnv": "生产环境",
"isDateSet": false
},
"id": "ty4",
"isFmqd": "0",
"accreditStatus": "2",
"nodeName": "ods_grid_grid_abn_stats_day_sz_xukougds",
"nodeType": "TY",
"preNodes": ["yd4"]
}, {
"detailInfo": {
"tbDatabase": "js_js_dws_proj_prod",
"nodeCnName": "分析层表6",
"projectEnv": "生产环境",
"isDateSet": false
},
"id": "fx6",
"isFmqd": "0",
"accreditStatus": "2",
"nodeName": "dws_grid_grid_abn_stats_day_sz_xukougds",
"nodeType": "FX",
"preNodes": ["ty4"]
}, {
"detailInfo": {},
"id": "rds3",
"isFmqd": "0",
"nodeName": "ava_cap_mon_sz_fengqiaogds",
"nodeType": "RDS",
"preNodes": ["fx3","fx4","fx5"]
}, {
"detailInfo": {},
"id": "rds4",
"isFmqd": "0",
"nodeName": "cst_bus_app_form_scale_ts_mp",
"nodeType": "RDS",
"preNodes": ["fx6"]
}],
scrollerHeight: '520px',
zoomRate: 1,
graph: null,
toolbar: null,
combos: {},
lazy: false,
data: { nodes: [], edges: [], combos: [] },
animateCfg: { duration: 200, easing: 'easeCubic' },
nodeExtraAttrs: { //
XQ: { fill: "#00a096", font: "#144b32", title: '需求', layer: 1 },
YD: { fill: "#eefaf9", font: "#144b32", title: '源端', layer: 2 },
TY: { fill: "#78BEB3", font: "#144b32", title: '贴源层', layer: 3 },
GX: { fill: "#81E380", font: "#144b32", title: '共享层', layer: 4 },
FX: { fill: "#92A4EB ", font: "#144b32", title: '分析层', layer: 5 },
RDS: { fill: "#ebf5ff", font: "#144b32", title: 'RDS', layer: 9 },
SERVICE: { fill: "#FFD496", font: "#144b32", title: '服务', layer: 35 },
API: { fill: "#FFD496", font: "#144b32", title: '接口', layer: 36 },
APP: { fill: "#EDBBAB", font: "#144b32", title: 'API聚合', layer: 40 },
EMPTY:{fill:"pink",font:"#1b32",title:"过渡点",layer:7}
},
nodeHeight: 25,
nodeWidth: 220,
padding: 7,
open: false
};
},
created() {
// this.scrollerHeight=(window.innerHeight - 110)+"px";
},
mounted() {
let _this = this;
//初始化链路图
this.initDraw();
if (this.graph) {
if (_this.flowList.length > 1000) { //大于100个节点就分批加载
this.lazy = true
this.$modal.notifyWarning("链路数据过大,请点击具体节点展开!");
//只加载服务节点,点击服务展开
_this.combos["APP"] = { id: "APP", label: "应用" }
// _this.combos["SERVICE"] = {id:"SERVICE", label: "服务"}
_this.flowList
.forEach((item, idx) => {
switch (item.nodeType) {
case 'XQ':
item.icon = 'a1.png';
item.fill = "#00a096";
item.stroke = "#00a096";
item.labelfill = "white";
break;
case 'YD':
item.icon = 'a2.png';
item.fill = "#eefaf9";
item.stroke = "#00a096";
item.labelfill = "black";
break;
case 'TY':
case 'GX':
case 'FX':
// 根据 accreditStatus 的值判断
switch (item.accreditStatus) {
case '1':
item.icon = 'a3_g.png';
item.fill = "#e4e4e4";
item.stroke = "#929292";
item.labelfill = "black";
break;
case '2':
item.icon = 'a3_b.png';
item.fill = "l(0) 0:#B5C1FD 0.4:#eefaf9";
item.stroke = "#B5C1FD";
item.labelfill = "black";
break;
case '3':
item.icon = 'a3.png';
item.fill = "#ebf5ff";
item.stroke = "#0a84fc";
item.labelfill = "black";
break;
case '4':
item.icon = 'a3_g.png';
item.fill = "#fff";
item.stroke = "#7C7C7C";
item.labelfill = "black";
break;
}
break;
case 'RDS':
item.icon = 'a4.png';
item.fill = "#ebf5ff";
item.stroke = "#0a84fc";
item.labelfill = "black";
break;
}
//节点
let _node = {
id: item.id,
label: item.nodeName,
name: item.nodeName,
comboId: item.nodeType,
nodeType: item.nodeType,
icon: item.icon,
fill: item.fill,
stroke: item.stroke,
labelfill: item.labelfill,
accreditStatus: item.accreditStatus ? item.accreditStatus : "0",
isFmqd: item.isFmqd,
preNodes: item.preNodes,
detailInfo: { ...item.detailInfo },
layer: _this.getLayer(item)
}
_this.data.nodes.push(_node)
//处理第一个节点的子父节点
if (idx === 0 && item.preNodes) {
_node.loaded = true
_this.flowList.filter(n => item.preNodes.includes(n.id))
.forEach((pNode, i) => {
//设置分组
if (!_this.combos[pNode.nodeType]) {
let _attr = this.nodeExtraAttrs[pNode.nodeType]
_this.combos[pNode.nodeType] = { id: pNode.nodeType, label: _attr.title, labelCfg: { position: 'top', refX: 60, refY: 40 } }
}
_this.data.nodes.push({
id: pNode.id,
label: pNode.nodeName,
name: item.nodeName,
comboId: pNode.nodeType,
// type: pNode.nodeType,
nodeType: pNode.nodeType,
accreditStatus: item.accreditStatus ? item.accreditStatus : "0",
preNodes: pNode.preNodes,
detailInfo: { ...item.detailInfo },
isFmqd: item.isFmqd,
layer: _this.getLayer(pNode)
})
//处理连线
_this.data.edges.push({ //父节点到当前节点的连线
source: pNode.id,
type: 'cubic-horizontal',
target: item.id
})
})
}
//连线,此处不连线,放入实际环境,需要添加到应用的连线,并且需要动态控制combo的加载
})
} else {
this.lazy = false;
_this.flowList.forEach((item, idx) => {
switch (item.nodeType) {
case 'XQ':
item.icon = 'a1.png';
item.fill = "#00a096";
item.stroke = "#00a096";
item.labelfill = "white";
break;
case 'YD':
item.icon = 'a2.png';
item.fill = "#eefaf9";
item.stroke = "#00a096";
item.labelfill = "black";
break;
case 'TY':
case 'GX':
case 'FX':
// 根据 accreditStatus 的值判断
switch (item.accreditStatus) {
case '1':
item.icon = 'a3_g.png';
item.fill = "#e4e4e4";
item.stroke = "#929292";
item.labelfill = "black";
break;
case '2':
item.icon = 'a3_b.png';
item.fill = "l(0) 0:#B5C1FD 0.4:#eefaf9";
item.stroke = "#B5C1FD";
item.labelfill = "black";
break;
case '3':
item.icon = 'a3.png';
item.fill = "#ebf5ff";
item.stroke = "#0a84fc";
item.labelfill = "black";
break;
case '4':
item.icon = 'a3_g.png';
item.fill = "#fff";
item.stroke = "#7C7C7C";
item.labelfill = "black";
break;
}
break;
case 'RDS':
item.icon = 'a4.png';
item.fill = "#ebf5ff";
item.stroke = "#0a84fc";
item.labelfill = "black";
break;
}
//设置分组
if (!_this.combos[item.nodeType]) {//&& item.nodeType !== 'SERVICE'
let _attr = this.nodeExtraAttrs[item.nodeType]
_this.combos[item.nodeType] = { id: item.nodeType, label:item.nodeType == "EMPTY" ? "" : _attr.title, labelCfg: { position: 'top', refX: 145, refY: item.nodeType==="XQ" ? -69 : -40 } }
}
_this.data.nodes.push({
id: item.id,
label: item.nodeName,
name: item.nodeName,
comboId: item.nodeType,
isFmqd: item.isFmqd,
icon: item.icon,
fill: item.fill,
stroke: item.stroke,
labelfill: item.labelfill,
// groupId:item.nodeType,
// type: item.nodeType,
nodeType: item.nodeType,
accreditStatus: item.accreditStatus ? item.accreditStatus : "0",
preNodes: item.preNodes,
detailInfo: { ...item.detailInfo },
layer: _this.getLayer(item)
})
if (item.preNodes && item.preNodes.length > 0) {
item.preNodes.forEach((p, i) => {
_this.data.edges.push(
{
source: p,
type: 'cubic-horizontal',
target: item.id
}
)
})
}
})
}
//设置分组
_this.data.combos = Object.values(_this.combos)
this.procLayer(_this.data)
this.graph.data(_this.data);
this.graph.node((node) => {
const styleAttrs = this.nodeExtraAttrs[node.nodeType] || {};
const style = Object.assign({
fillOpacity: 1,
radius: 5,
fontSize: 14
}, styleAttrs);
switch (node.nodeType) {
case 'XQ':
style.fill = "#00a096";
style.stroke = "#00a096";
// labelCfg.style.fill = "#fff";
// 添加图标
// style.icon = "your_xq_icon_url";
break;
case 'YD':
style.fill = "#eefaf9";
style.stroke = "#00a096";
// labelCfg.style.fill = "black";
// 添加图标
// style.icon = "your_yd_icon_url";
break;
case 'TY':
case 'GX':
case 'FX':
// 根据 accreditStatus 的值判断
switch (node.accreditStatus) {
case '1':
style.fill = "#e4e4e4";
style.stroke = "#acacac";
// labelCfg.style.fill = "black";
break;
case '2':
style.fill = "l(0) 0:#c1cbfd 0.4:#eefaf9";
style.stroke = "#4a67fa";
// labelCfg.style.fill = "black";
break;
case '3':
style.fill = "#ebf5ff";
style.stroke = "#0a84fc";
// labelCfg.style.fill = "black"
break;
case '4':
style.fill = "#fff";
style.stroke = "#0a84fc";
style.lineDash = [2];
// labelCfg.style.fill = "black";
break;
}
break;
case 'RDS':
style.fill = "#ebf5ff";
style.stroke = "#0a84fc";
// labelCfg.style.fill = "black";
break;
}
let _style = Object.assign({
fillOpacity: 1,
radius: 15,
fontSize: 14
}, style)
return {
id: node.id,
label: _this.fittingString(node.label, _this.nodeWidth - _this.padding * 2 - 10, 14),
name: node.label,
style: _style,
type: 'service',
// type: 'service',
// type:'modelRect',
size: [280, 40],
// labelCfg: {
// style: {
// fill: 'black',
// fontSize: 14,
// },
// offset: 30,
// },
};
});
//更新数据
this.graph.render();
if (typeof window !== 'undefined') {
window.onresize = () => {
const container = this.$refs.graphWrap;
if (!this.graph || this.graph.get('destroyed')) return;
if (!container || !container.scrollWidth || !container.scrollHeight) return;
this.graph.changeSize(container.scrollWidth, container.scrollHeight);
};
}
// this.graph.zoom(this.zoomRate);
}
},
methods: {
getLayer(target) {
return this.nodeExtraAttrs[target.nodeType].layer
},
procLayer(data) {
data.nodes.forEach(node => {
if (node.preNodes && node.preNodes.length > 0) {
// 查找同一层级父节点
// let groupNodes = data.nodes.filter(n => n.nodeType === node.nodeType);
// let pNodes = groupNodes.filter(n => node.preNodes.includes(n.id));
let pNodes = data.nodes.filter(n => (n.nodeType === node.nodeType && node.preNodes.includes(n.id)));
console.log(pNodes,"pNodes");
//如果同组有多个节点,取最深一个节点的layer数加1,否则如果没有同组的节点,则直接用组的layer,即不修改
if (pNodes && pNodes.length > 0) {
let maxLevel = 1;
pNodes.forEach(_pNode => {
maxLevel = Math.max(maxLevel, this.getGroupParentLevel(_pNode, maxLevel))//groupNodes,
console.log(maxLevel,"maxLevel");
})
node.layer += maxLevel; //如果没变
}
}
})
},
getGroupParentLevel(node, level) {//groupNodes,
//继续找该节点的父级
if (!node.preNodes) {
return level;
}
let max = level;
if (node.preNodes.length === 1) {
if (node.id == node.preNodes[0]) {
return max;
}
let _node = this.data.nodes.find(n => (n.nodeType === node.nodeType && n.id === node.preNodes[0]));
// let _node = groupNodes.find(n => n.id === node.preNodes[0]);
if (_node) {
max = Math.max(max, this.getGroupParentLevel(_node, max + 1))//groupNodes,
}
return max;
}
if (node.preNodes.length > 1) {
for (let _node in this.data.nodes) {
if (_node.nodeType === node.nodeType && node.preNodes.includes(_node.id)) {
max = Math.max(max, this.getGroupParentLevel(_node, max + 1))//groupNodes,
}
}
return max;
}
},
initDraw() {
let _this = this;
const container = this.$refs.graphWrap;
G6.registerNode(
"service", //第一个参数自定义节点的名字
// 第二个参数是这个节点的图形分组
{
draw: function (cfg, group) {
console.log(cfg,"cfg内容");
//字符串截取,中英文都适用
function beautySub(str, len) {
var reg = /[\u4e00-\u9fa5]/g, slice = str.substring(0, len),
cCharNum = (~~(slice.match(reg) && slice.match(reg).length)),
realen = slice.length * 2 - cCharNum - 1;
return str.substr(0, realen) + (realen < str.length ? "..." : "");
}
if(cfg.nodeType != "EMPTY"){
var keyShape = group.addShape('rect', {
// 代表矩形的一些属性
attrs: {
// 相对定位
x: 0,
y: 0,
width: 186,
height: 41,
stroke: cfg.stroke, //描边色
fill: cfg.fill,
radius: 12,
lineWidth: 1,
},
name: "card-node-keyShape" //起个唯一名字便于识别
});
// 节点
group.addShape("image", {
attrs: {
x: 8,
y: 6,
width: 32,
height: 32,
img: require(`../../assets/images/appDetail/${cfg.icon}`),
},
name: "card-node-yewuico"
});
// 文字
group.addShape("text", {
attrs: {
text: cfg.name.length > 8 ? beautySub(cfg.name,8) : cfg.name,
x: 50,
y: 30,
fontSize: 14,
fill: cfg.labelfill,
},
name: "card-node-text1",
});
}else{
var keyShape = group.addShape('circle', {
// 代表矩形的一些属性
attrs: {
// 相对定位
x: 0,
y: 0,
r:1,
fill: "#00a096",
},
name: "card-node-keyShape1" //起个唯一名字便于识别
});
}
return keyShape;
},
}, 'rect');
//图例初始化
this.initToolbar();
// const width = container.scrollWidth;
// const height = container.scrollHeight || 500;
this.graph = new G6.Graph({
// width:width,
// height: 520,
container: container,
plugins: [_this.toolbar],
fitView: true,
// fitCenter: true,
fitViewPadding: 5,
animate: true,
animateCfg: {
duration: 500, // Number,一次动画的时长
easing: 'easeCubic', // String,动画函数
},
layout: {
type: 'dagre',
rankdir: 'LR', // 可选,默认为图的中心
// align: 'DL', // 可选
nodesep: 8, // 可选, 纵向间距
ranksep: 20, // 可选,横向间距
gpuEnabled: true,
preventOverlap: true, // 防止节点重叠
workerEnabled: false, // 开启 Web-Worker
controlPoints: true, // 可选
// sortByCombo: true,
},
modes: {
// default: ['drag-combo',"zoom-canvas", 'drag-canvas',"drag-node",
default: [
// default: [
// { type: "activate-relations", activeState: 'active', inactiveState: 'inactive' },
{
type: 'tooltip',
offset: 10,
formatText(model) {
let accreditStatusName = '';
if(model.accreditStatus=='1'){
accreditStatusName = "未接入";
} else if(model.accreditStatus=='2'){
accreditStatusName = "未授权";
} else if(model.accreditStatus=='3'){
accreditStatusName = "已授权";
} else if(model.accreditStatus=='4'){
accreditStatusName = "未申请";
} else if(model.nodeType=='RDS'){
accreditStatusName = "已授权";
}
let text = '<span style=\'margin-top:10px;font-size:16px\'>表名: ' + model.name + '</span>';
if(model.nodeType == 'XQ'){
text = '<span style=\'margin-top:10px;font-size:16px\'>需求: ' + model.name + '</span>';
}
// if (model.nodeType != 'APP' && model.nodeType != 'SERVICE' && model.nodeType != 'RDS' && model.nodeType != 'API' && model.nodeType != 'TY') {
// if (model.nodeType == 'YD') {
// text += '<br/><span style="margin-top:10px;font-size:16px">数据库: ' + model.detailInfo.tbDatabase;
// }if ( model.nodeType == 'GX'|| model.nodeType == 'FX') {
// text += '<br/><span style="margin-top:10px;font-size:16px;color:#4a67fa;line-height:16px;"> <span style="display:block;width:10px;height:10px;border-radius:50%;margin-right:20px;background-color:#4a67fa;float:left"></span><span>'+ model.accreditStatus+'</span> ' ;
// }
// else {
// text += '<br/><span style=\'margin-top:10px;font-size:16px\'>项目空间: ' + model.detailInfo.tbDatabase;
// }
// }
if (model.nodeType == 'GX' || model.nodeType == 'FX' || model.nodeType == 'TY') {
text += '<br/><span style=\'margin-top:10px;font-size:16px\'>' + model.detailInfo.nodeCnName + '</span>'
+ '<br/><span style=\'margin-top:10px;font-size:16px\'>项目空间: ' + model.detailInfo.tbDatabase + '</span>'
+ '<br/><span style=\'width:65px;text-align:center;display:block;border:1px solid #85e4d3;border-radius:5px;margin-top:10px;font-size:14px;color:#85e4d3;\'>' + model.detailInfo.projectEnv + '</span>'
+ '<br/><span style="float:left;display:block;margin-top:-10px;font-size:14px;color:#4a67fa;line-height:16px;"> <span style="display:block;margin-top:3px;width:10px;height:10px;border-radius:50%;margin-right:5px;background-color:#4a67fa;float:left"></span>'
+ accreditStatusName + '</span> ' ;
if(model.detailInfo.isDateSet){
text += '<span style=\'display:block;margin-top:-13px;float:left;width:60px;height:20px;line-height:20px;text-align:center;;border-radius:5px;background-color: #ffead3;font-size:14px;color:#dc7e17;margin-left:5px;\'>' + '数据集'+ '</span>';
}
}
text += '</span>';
// text += '<br/><span style="margin-top:10px;font-size:16px">负面清单: ' + (model.isFmqd == '1' ? '是' : '否') + '</span>';
return text;
}
}
]//'click-select','collapse-expand-combo',
},
nodeStateStyles: {
hover: {
lineWidth: 2,
stroke: '#6cc9f5',
fill: '#e6f7ff',
shadowColor: "#ddd",
cursor: "pointer"
},
highlight: {
fill: "#db4437",
shadowColor: '#fff',
stroke: "#db4437",
cursor: "pointer",
'text-shape': {
lineWidth: 1,
fill: "#db4437",
stroke: "#db4437",
},
},
// click: {
// stroke: '#000',
// lineWidth: 3,
// }
},
edgeStateStyles: {
// 鼠标点击边,即 click 状态为 true 时的样式
// click: {
// stroke: 'steelblue',
// },
hover: {
lineWidth: 3,
stroke: '#e6f7ff',
// fill: '#e6f7ff',
cursor: "pointer"
},
},
comboStateStyles: {
highlight: {
fill: "#f6cd6b",
opacity: 0.7,
cursor: "pointer",
'text-shape': {
fill: "#A5E4F0",
stroke: "#A5E4F0",
lineWidth: 1,
},
},
inactive: {
stroke: '#eee',
lineWidth: 1,
'text-shape': {
fill: "#eee",
stroke: "#eee",
},
},
hover:{
stroke: '#00a096',
}
},
groupByTypes: false,
defaultCombo: {
type: 'rect',
label: '',
// collapsed:true,
// collapsedSubstituteIcon:{show:true}, //分组收缩(双击)
style: {
fill: '#fff',
stroke: 'transparent',
cursor: "grab",
// radius: 4
},
// size: [300, 50],
padding: [90, 100, 200, 100],
labelCfg: {
position: 'top',
style: {
fill: '#00a096',
fontSize: 30,
fontWeight: 400,
shadowOffsetX: 10,
shadowOffsetY: 10,
// shadowColor: '#00a096',
// shadowBlur: 10,
},
},
},
defaultNode: {
type: 'service',
// size: [220, 25],
// style: {
// radius: 20,
// stroke: '#fff',
// fill: '#cc0'
// },
// labelCfg: {
// style: {
// fill: "#144b32",
// fontSize: 14
// }
// }
},
defaultEdge: {
// shape: "line-with-arrow",
style: {
endArrow: {
path: 'M 0,0 L 8,4 L 8,-4 Z',
fill: '#00a096'
},
lineWidth: 1,
stroke: "#00a096",
}
},
})
// 关闭局部刷新,解决残影问题
this.graph.get('canvas').set('localRefresh', false);
this.graph.on('node:click', ({ item }) => {
let _this = this;
const id = item.getID();
const model = item.getModel();
if (this.lazy && !model.loaded) {
if (model.preNodes) {
_this.flowList.filter(node => model.preNodes.includes(node.id))
.forEach((pNode, i) => {
//设置分组
if (!_this.combos[pNode.nodeType]) {
let _attr = _this.nodeExtraAttrs[pNode.nodeType]
_this.combos[pNode.nodeType] = { id: pNode.nodeType, label: _attr.title, labelCfg: { position: 'top', refX: 60, refY: 40 } }
}
//添加节点
_this.data.nodes.push({
id: pNode.id,
label: pNode.nodeName,
name: item.nodeName,
comboId: pNode.nodeType,
type: pNode.nodeType,
nodeType: pNode.nodeType,
accreditStatus: item.accreditStatus ? item.accreditStatus : "0",
preNodes: pNode.preNodes,
detailInfo: { ...item.detailInfo },
layer: _this.getLayer(pNode)
})
// this.graph.addItem('node', {
// id: pNode.id,
// label: pNode.nodeName,
// comboId:pNode.nodeType,
// type: pNode.nodeType,
// nodeType: pNode.nodeType,
// preNodes: pNode.preNodes,
// layer: _this.getLayer(pNode)
// });
//添加线条
_this.data.edges.push({ //父节点到当前节点的连线
source: pNode.id,
type: 'cubic-horizontal',
target: id
})
// this.graph.addItem('edge', {
// source: pNode.id,
// type: 'cubic-horizontal',
// target: id
// });
})
//重新计算布局
// this.graph.layout();
_this.data.combos = Object.values(_this.combos)
this.procLayer(_this.data);
this.graph.changeData(_this.data)
// this.graph.render();
} else {
this.$modal.notifyWarning("没有下级节点");
}
model.loaded = true
}
else if (this.lazy && model.loaded) { //已加载的情况下直接移除所有子节点
}
});
},
initToolbar() {
let fullDiv = ``;
if (this.isFull) {
fullDiv = `<li code='full'>
<img src=`+full+`
style="position: relative; margin-right: 10px; width: 46px; height: 46px; top: 2px;" />
</li>`;
}
this.toolbar = new G6.ToolBar({
position: { x: 0, y: 0 },
getContent: () => `
<ul class='g6-component-toolbar'>`
+ fullDiv +
`</ul>`,
handleClick: (code, graph) => {
switch (code) {
case 'full':
this.open = true;
break;
}
}
})
},
/**
* format the string
* @param {string} str The origin string
* @param {number} maxWidth max width
* @param {number} fontSize font size
* @return {string} the processed result
*/
fittingString(str, maxWidth, fontSize) {
if (typeof (str) == 'undefined') {
str = "";
}
const ellipsis = "...";
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
let currentWidth = 0;
let res = str;
const pattern = new RegExp("[\u4E00-\u9FA5]+"); // distinguish the Chinese charactors and letters
str.split("").forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`;
}
});
return res;
},
copyStr(str) {
const input = document.createElement("textarea");
input.value = str;
document.body.appendChild(input);
input.select();
document.execCommand("Copy");
document.body.removeChild(input);
alert("Copy Success!");
},
}
};
</script>
注意的点:
- 根据不同节点类型添加不同节点样式属性,绘制不同的节点的样式,分组
- 分组函数定义,设置的是每个分组的起始节点的位置
- 要是一个节点内有多个图形,使用addShape()多次累加,设置定位,组成一个大的节点
- 如果要在边上面添加自定义标签或者图标
-
G6.registerEdge('flow-line-resource', { draw: function (cfg, group) { const startPoint = cfg.startPoint; const endPoint = cfg.endPoint; const { style } = cfg; const shape = group.addShape('path', { attrs: { stroke: style.stroke, endArrow: style.endArrow, path: [ ['M', startPoint.x, startPoint.y], ['C', startPoint.x + (endPoint.x - startPoint.x) / 2, startPoint.y, startPoint.x + (endPoint.x - startPoint.x) / 2, endPoint.y, endPoint.x, endPoint.y], ], }, }); // 添加文本背景 group.addShape('rect', { attrs: { x: (startPoint.x + endPoint.x) / 2 - 45, y: (startPoint.y + endPoint.y) / 2 - 12, width: 90, height: 24, fill: '#ffffff', stroke: cfg.call_num ? '#5bc2ba' : '#a5a5a5', radius: 5, }, }); // 添加文本 group.addShape('text', { attrs: { x: (startPoint.x + endPoint.x) / 2, y: (startPoint.y + endPoint.y) / 2, text: cfg.call_num ? "调度次数:" + cfg.call_num : "无调度", fill:cfg.call_num ? '#5bc2ba' : '#a5a5a5', textAlign: 'center', textBaseline: 'middle', }, }); return shape; }, });
自定义边样式效果如图:
- 当需要拖动画布的时候,出现分组的文字显示残影,请关闭局部刷新来解决残影问题
// 关闭局部刷新,解决残影问题
this.graph.get('canvas').set('localRefresh', false);
- 如果后台返回一个无数据的虚拟节点,添加empty空类型节点,动态判断节点类型,添加模拟节点类型样式(小圆点)
链路图效果如下:
添加了虚拟节点样式
此时还有个小箭头,然后在设置边上的属性里面添加nodeType,将边设置成自定义的边,根据nodeType来设置不同的边的类型
G6.registerEdge('flow-line-resource', {
draw: function (cfg, group) {
console.log(cfg,"cfg的11111");
const startPoint = cfg.startPoint;
const endPoint = cfg.endPoint;
const { style } = cfg;
if(cfg.nodeType != "EMPTY"){
var shape = group.addShape('path', {
attrs: {
stroke: style.stroke,
endArrow: style.endArrow,
path: [
['M', startPoint.x, startPoint.y],
['C', startPoint.x + (endPoint.x - startPoint.x) / 2, startPoint.y, startPoint.x + (endPoint.x - startPoint.x) / 2, endPoint.y, endPoint.x, endPoint.y],
],
},
});
}else{
var shape = group.addShape('path', {
attrs: {
stroke: style.stroke,
endArrow: false,
path: [
['M', startPoint.x, startPoint.y],
['C', startPoint.x + (endPoint.x - startPoint.x) / 2, startPoint.y, startPoint.x + (endPoint.x - startPoint.x) / 2, endPoint.y, endPoint.x, endPoint.y],
],
},
});
}
return shape;
},
});
完成!!!