mirror of
https://gitee.com/antv/g6.git
synced 2024-12-04 12:49:04 +08:00
496 lines
13 KiB
HTML
496 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
<title>自定义卡片节点</title>
|
|
</head>
|
|
<body>
|
|
<div id="mountNode"></div>
|
|
<script src="../build/g6.js"></script>
|
|
<script>
|
|
const ERROR_COLOR = "#F5222D";
|
|
const getNodeConfig = node => {
|
|
if (node.nodeError) {
|
|
return {
|
|
basicColor: ERROR_COLOR,
|
|
fontColor: "#FFF",
|
|
borderColor: ERROR_COLOR,
|
|
bgColor: "#E66A6C"
|
|
};
|
|
}
|
|
let config = {
|
|
basicColor: "#722ED1",
|
|
fontColor: "#722ED1",
|
|
borderColor: "#722ED1",
|
|
bgColor: "#F6EDFC"
|
|
};
|
|
switch (node.type) {
|
|
case "root": {
|
|
config = {
|
|
basicColor: "#E3E6E8",
|
|
fontColor: "rgba(0,0,0,0.85)",
|
|
borderColor: "#E3E6E8",
|
|
bgColor: "#F7F9FA"
|
|
};
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return config;
|
|
};
|
|
|
|
const COLLAPSE_ICON = function COLLAPSE_ICON(x, y, r) {
|
|
return [
|
|
["M", x - r, y],
|
|
["a", r, r, 0, 1, 0, r * 2, 0],
|
|
["a", r, r, 0, 1, 0, -r * 2, 0],
|
|
["M", x - r + 4, y],
|
|
["L", x - r + 2 * r - 4, y]
|
|
];
|
|
};
|
|
const EXPAND_ICON = function EXPAND_ICON(x, y, r) {
|
|
return [
|
|
["M", x - r, y],
|
|
["a", r, r, 0, 1, 0, r * 2, 0],
|
|
["a", r, r, 0, 1, 0, -r * 2, 0],
|
|
["M", x - r + 4, y],
|
|
["L", x - r + 2 * r - 4, y],
|
|
["M", x - r + r, y - r + 4],
|
|
["L", x, y + r - 4]
|
|
];
|
|
};
|
|
|
|
const nodeBasicMethod = {
|
|
createNodeBox: (group, config, width, height, isRoot) => {
|
|
/* 最外面的大矩形 */
|
|
const container = group.addShape("rect", {
|
|
attrs: {
|
|
x: 0,
|
|
y: 0,
|
|
width,
|
|
height
|
|
}
|
|
});
|
|
if (!isRoot) {
|
|
/* 左边的小圆点 */
|
|
group.addShape("circle", {
|
|
attrs: {
|
|
x: 3,
|
|
y: height / 2,
|
|
r: 6,
|
|
fill: config.basicColor
|
|
}
|
|
});
|
|
}
|
|
/* 矩形 */
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
x: 3,
|
|
y: 0,
|
|
width: width - 19,
|
|
height,
|
|
fill: config.bgColor,
|
|
stroke: config.borderColor,
|
|
radius: 2,
|
|
cursor: "pointer"
|
|
}
|
|
});
|
|
|
|
/* 左边的粗线 */
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
x: 3,
|
|
y: 0,
|
|
width: 3,
|
|
height,
|
|
fill: config.basicColor,
|
|
radius: 1.5
|
|
}
|
|
});
|
|
return container;
|
|
},
|
|
/* 生成树上的 marker */
|
|
createNodeMarker: (group, collapsed, x, y) => {
|
|
group.addShape("circle", {
|
|
attrs: {
|
|
x,
|
|
y,
|
|
r: 13,
|
|
fill: "rgba(47, 84, 235, 0.05)",
|
|
opacity: 0,
|
|
zIndex: -2
|
|
},
|
|
className: "collapse-icon-bg"
|
|
});
|
|
group.addShape("marker", {
|
|
attrs: {
|
|
x,
|
|
y,
|
|
radius: 7,
|
|
symbol: collapsed ? EXPAND_ICON : COLLAPSE_ICON,
|
|
stroke: "rgba(0,0,0,0.25)",
|
|
fill: "rgba(0,0,0,0)",
|
|
lineWidth: 1,
|
|
cursor: "pointer"
|
|
},
|
|
className: "collapse-icon"
|
|
});
|
|
},
|
|
afterDraw: (cfg, group) => {
|
|
/* 操作 marker 的背景色显示隐藏 */
|
|
const icon = group.findByClassName("collapse-icon");
|
|
if (icon) {
|
|
const bg = group.findByClassName("collapse-icon-bg");
|
|
icon.on("mouseenter", () => {
|
|
bg.attr("opacity", 1);
|
|
graph.get("canvas").draw();
|
|
});
|
|
icon.on("mouseleave", () => {
|
|
bg.attr("opacity", 0);
|
|
graph.get("canvas").draw();
|
|
});
|
|
}
|
|
/* ip 显示 */
|
|
const ipBox = group.findByClassName("ip-box");
|
|
if (ipBox) {
|
|
/* ip 复制的几个元素 */
|
|
const ipLine = group.findByClassName("ip-cp-line");
|
|
const ipBG = group.findByClassName("ip-cp-bg");
|
|
const ipIcon = group.findByClassName("ip-cp-icon");
|
|
const ipCPBox = group.findByClassName("ip-cp-box");
|
|
|
|
const onMouseEnter = () => {
|
|
this.ipHideTimer && clearTimeout(this.ipHideTimer);
|
|
ipLine.attr("opacity", 1);
|
|
ipBG.attr("opacity", 1);
|
|
ipIcon.attr("opacity", 1);
|
|
graph.get("canvas").draw();
|
|
};
|
|
const onMouseLeave = () => {
|
|
this.ipHideTimer = setTimeout(() => {
|
|
ipLine.attr("opacity", 0);
|
|
ipBG.attr("opacity", 0);
|
|
ipIcon.attr("opacity", 0);
|
|
graph.get("canvas").draw();
|
|
}, 100);
|
|
};
|
|
ipBox.on("mouseenter", () => {
|
|
onMouseEnter();
|
|
});
|
|
ipBox.on("mouseleave", () => {
|
|
onMouseLeave();
|
|
});
|
|
ipCPBox.on("mouseenter", () => {
|
|
onMouseEnter();
|
|
});
|
|
ipCPBox.on("mouseleave", () => {
|
|
onMouseLeave();
|
|
});
|
|
ipCPBox.on("click", () => {});
|
|
}
|
|
},
|
|
setState: (name, value, item) => {
|
|
const hasOpacityClass = [
|
|
"ip-cp-line",
|
|
"ip-cp-bg",
|
|
"ip-cp-icon",
|
|
"ip-cp-box",
|
|
"ip-box",
|
|
"collapse-icon-bg"
|
|
];
|
|
const group = item.getContainer();
|
|
const childrens = group.get("children");
|
|
graph.setAutoPaint(false);
|
|
if (name === "emptiness") {
|
|
if (value) {
|
|
childrens.forEach(shape => {
|
|
if (hasOpacityClass.indexOf(shape.get("className")) > -1) {
|
|
return;
|
|
}
|
|
shape.attr("opacity", 0.4);
|
|
});
|
|
} else {
|
|
childrens.forEach(shape => {
|
|
if (hasOpacityClass.indexOf(shape.get("className")) > -1) {
|
|
return;
|
|
}
|
|
shape.attr("opacity", 1);
|
|
});
|
|
}
|
|
}
|
|
graph.setAutoPaint(true);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 计算字符串的长度
|
|
* @param {string} str 指定的字符串
|
|
*/
|
|
const calcStrLen = (str) => {
|
|
let len = 0;
|
|
for (let i = 0; i < str.length; i++) {
|
|
if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
|
|
len++;
|
|
} else {
|
|
len += 2;
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
G6.registerNode(
|
|
'card-node',
|
|
{
|
|
drawShape: (cfg, group) => {
|
|
const config = getNodeConfig(cfg);
|
|
const isRoot = cfg.type === "root";
|
|
const nodeError = cfg.nodeError;
|
|
/* 最外面的大矩形 */
|
|
const container = nodeBasicMethod.createNodeBox(
|
|
group,
|
|
config,
|
|
243,
|
|
64,
|
|
isRoot
|
|
);
|
|
|
|
if (cfg.type !== "root") {
|
|
/* 上边的 type */
|
|
group.addShape("text", {
|
|
attrs: {
|
|
text: cfg.type,
|
|
x: 3,
|
|
y: -10,
|
|
fontSize: 12,
|
|
textAlign: "left",
|
|
textBaseline: "middle",
|
|
fill: "rgba(0,0,0,0.65)"
|
|
}
|
|
});
|
|
}
|
|
|
|
let ipWidth = 0;
|
|
if (cfg.ip) {
|
|
/* ip start */
|
|
/* ipBox */
|
|
const ipRect = group.addShape("rect", {
|
|
attrs: {
|
|
fill: nodeError ? null : "#FFF",
|
|
stroke: nodeError ? "rgba(255,255,255,0.65)" : null,
|
|
radius: 2,
|
|
cursor: "pointer"
|
|
}
|
|
});
|
|
|
|
/* ip */
|
|
const ipText = group.addShape("text", {
|
|
attrs: {
|
|
text: cfg.ip,
|
|
x: 0,
|
|
y: 19,
|
|
fontSize: 12,
|
|
textAlign: "left",
|
|
textBaseline: "middle",
|
|
fill: nodeError ? "rgba(255,255,255,0.85)" : "rgba(0,0,0,0.65)",
|
|
cursor: "pointer"
|
|
}
|
|
});
|
|
|
|
const ipBBox = ipText.getBBox();
|
|
/* ip 的文字总是距离右边 12px */
|
|
ipText.attr({
|
|
x: 224 - 12 - ipBBox.width
|
|
});
|
|
/* ipBox */
|
|
ipRect.attr({
|
|
x: 224 - 12 - ipBBox.width - 4,
|
|
y: ipBBox.minY - 5,
|
|
width: ipBBox.width + 8,
|
|
height: ipBBox.height + 10
|
|
});
|
|
|
|
/* 在 IP 元素上面覆盖一层透明层,方便监听 hover 事件 */
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
stroke: "",
|
|
cursor: "pointer",
|
|
x: 224 - 12 - ipBBox.width - 4,
|
|
y: ipBBox.minY - 5,
|
|
width: ipBBox.width + 8,
|
|
height: ipBBox.height + 10,
|
|
fill: "#fff",
|
|
opacity: 0
|
|
},
|
|
className: "ip-box"
|
|
});
|
|
|
|
/* copyIpLine */
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
x: 194,
|
|
y: 7,
|
|
width: 1,
|
|
height: 24,
|
|
fill: "#E3E6E8",
|
|
opacity: 0
|
|
},
|
|
className: "ip-cp-line"
|
|
});
|
|
/* copyIpBG */
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
x: 195,
|
|
y: 8,
|
|
width: 22,
|
|
height: 22,
|
|
fill: "#FFF",
|
|
cursor: "pointer",
|
|
opacity: 0
|
|
},
|
|
className: "ip-cp-bg"
|
|
});
|
|
/* copyIpIcon */
|
|
group.addShape("image", {
|
|
attrs: {
|
|
x: 200,
|
|
y: 13,
|
|
height: 12,
|
|
width: 10,
|
|
img: "https://os.alipayobjects.com/rmsportal/DFhnQEhHyPjSGYW.png",
|
|
cursor: "pointer",
|
|
opacity: 0
|
|
},
|
|
className: "ip-cp-icon"
|
|
});
|
|
/* 放一个透明的矩形在 icon 区域上,方便监听点击 */
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
x: 195,
|
|
y: 8,
|
|
width: 22,
|
|
height: 22,
|
|
fill: "#FFF",
|
|
cursor: "pointer",
|
|
opacity: 0
|
|
},
|
|
className: "ip-cp-box",
|
|
tooltip: "复制IP"
|
|
});
|
|
|
|
const ipRectBBox = ipRect.getBBox();
|
|
ipWidth = ipRectBBox.width;
|
|
/* ip end */
|
|
}
|
|
|
|
/* name */
|
|
const nameText = group.addShape("text", {
|
|
attrs: {
|
|
text: cfg.name,
|
|
x: 19,
|
|
y: 19,
|
|
fontSize: 14,
|
|
fontWeight: 700,
|
|
textAlign: "left",
|
|
textBaseline: "middle",
|
|
fill: config.fontColor,
|
|
cursor: "pointer"
|
|
}
|
|
// tooltip: cfg.name,
|
|
});
|
|
|
|
/* 下面的文字 */
|
|
const remarkText = group.addShape("text", {
|
|
attrs: {
|
|
text: cfg.keyInfo,
|
|
x: 19,
|
|
y: 45,
|
|
fontSize: 14,
|
|
textAlign: "left",
|
|
textBaseline: "middle",
|
|
fill: config.fontColor,
|
|
cursor: "pointer"
|
|
}
|
|
});
|
|
|
|
if (nodeError) {
|
|
group.addShape("text", {
|
|
attrs: {
|
|
x: 191,
|
|
y: 62,
|
|
text: '⚠️',
|
|
fill: '#000',
|
|
fontSize: 18
|
|
}
|
|
});
|
|
}
|
|
|
|
const hasChildren = cfg.children && cfg.children.length > 0;
|
|
if (hasChildren) {
|
|
nodeBasicMethod.createNodeMarker(group, cfg.collapsed, 236, 32);
|
|
}
|
|
return container;
|
|
},
|
|
afterDraw: nodeBasicMethod.afterDraw,
|
|
setState: nodeBasicMethod.setState
|
|
},
|
|
"single-shape"
|
|
);
|
|
|
|
const data = {
|
|
nodes: [
|
|
{
|
|
name: 'cardNodeApp',
|
|
ip: '127.0.0.1',
|
|
nodeError: true,
|
|
type: 'root',
|
|
keyInfo: 'this is a card node info',
|
|
x: 100,
|
|
y: 50
|
|
},
|
|
{
|
|
name: 'cardNodeApp',
|
|
ip: '127.0.0.1',
|
|
nodeError: false,
|
|
type: 'subRoot',
|
|
keyInfo: 'this is sub root',
|
|
x: 100,
|
|
y: 150
|
|
},
|
|
{
|
|
name: 'cardNodeApp',
|
|
ip: '127.0.0.1',
|
|
nodeError: false,
|
|
type: 'subRoot',
|
|
keyInfo: 'this is sub root',
|
|
x: 100,
|
|
y: 250,
|
|
children: [
|
|
{
|
|
name: 'sub'
|
|
}
|
|
]
|
|
}
|
|
],
|
|
edges: []
|
|
}
|
|
|
|
const graph = new G6.Graph({
|
|
container: 'mountNode',
|
|
width: 800,
|
|
height: 600,
|
|
modes: {
|
|
default: ['drag-node']
|
|
},
|
|
defaultNode: {
|
|
shape: 'card-node'
|
|
}
|
|
})
|
|
|
|
graph.data(data)
|
|
graph.render()
|
|
</script>
|
|
</body>
|
|
</html> |