vue + g6 实现树级结构(compactBox 紧凑树)

news/2024/10/17 22:29:16/

G6文档
在这里插入图片描述

自定义节点

G6.registerNode("dom-node",{draw: (cfg, group) => {let str = `<div class='item-box catalog-node ${cfg.isSelected ? "is-selected" : ""} ${cfg.status}-box' οnclick='handleDetail("${cfg.id}")' id="${cfg.id}" style="width: ${cfg.size[0] - 5}px;">${cfg.status? `<span class='status ${cfg.status}'>${getLabel(ISSUE_STATUS,cfg.status)}</span>`: ""}${cfg?.manager?.name? `<p class=''><span class="title-txt avatar-img" title='负责人'><img src="${cfg?.manager?.avatar}"/></span>${cfg.manager.name}</p>`: ""}<div class='title' οnclick='handleDetail("${cfg.id}")'><span ${cfg.typeName === "Bug" ? `class='tipText'` : ""}>${cfg.title}</span></div></div>`;return group.addShape("dom", {attrs: {width: cfg.size[0],height: nodeHeight(cfg),// 传入 DOM 的 htmlhtml: str,},draggable: true,});},},"single-node"
);

在pc端,自定义的节点,绑定的点击事件起作用,但是移动端模式,不会起作用;

解决方法:
文档中也说明了,节点的选中事件,需要将Mode切换到edit模式。graph.setMode("edit");
在这里插入图片描述
如:

graph.setMode("edit");
graph.on("nodeselectchange", (e) => {// 当前操作的 itemalert("node");console.log(e.target);// 当前操作后,所有被选中的 items 集合console.log(e.selectedItems);// 当前操作时选中(true)还是取消选中(false)console.log(e.select);
});

此处,直接用自定节点的事件了。

全部代码

<template><div id="container"></div>
</template><script>
// 引入antv-G6
import G6 from "@antv/g6";
import { ISSUE_STATUS } from "@/utils/constant";
import { getLabel } from "@/utils";// G6的配置项
G6.registerNode("icon-node",{options: {size: [60, 20], // 宽高stroke: "#91d5ff", // 变颜色fill: "#fff", // 填充色},// draw是绘制后的附加操作-节点的配置项  图形分组,节点中图形对象的容器draw(cfg, group) {// 获取节点的配置const styles = this.getShapeStyle(cfg);// 解构赋值const { labelCfg = {} } = cfg;const w = styles.width;const h = styles.height;// 向分组中添加新的图形 图形 配置 rect矩形 xy 代表左上角坐标 w h是宽高const keyShape = group.addShape("rect", {attrs: {...styles,x: -w / 2,y: -h / 2,},});// 文本文字的配置if (cfg.title) {group.addShape("text", {attrs: {...labelCfg.style,text: cfg.title,x: 50 - w / 2,y: 25 - h / 2,},});}return keyShape;},// 更新节点后的操作,一般同 afterDraw 配合使用update: undefined,},"rect"
);const nodeHeight = (obj) => {// if (obj.depth == 0) {//   return 100;// }const l = ["manager", "title"];const arr = l.filter((item) => {return obj[item];});return arr.length * 25 + 50;
};
G6.registerNode("dom-node",{draw: (cfg, group) => {let str = `<div class='item-box catalog-node ${cfg.isSelected ? "is-selected" : ""} ${cfg.status}-box' οnclick='handleDetail("${cfg.id}")' id="${cfg.id}" style="width: ${cfg.size[0] - 5}px;">${cfg.status? `<span class='status ${cfg.status}'>${getLabel( ISSUE_STATUS, cfg.status )}</span>`: ""}${cfg?.manager?.name? `<p class=''><span class="title-txt avatar-img" title='负责人'> <img src="${cfg?.manager?.avatar}" /> </span>${cfg.manager.name} </p>`: ""}<div class='title' οnclick='handleDetail("${cfg.id}")'><span ${cfg.typeName === "Bug" ? `class='tipText'` : ""}>${cfg.title}</span></div></div>`;return group.addShape("dom", {attrs: {width: cfg.size[0],height: nodeHeight(cfg),// 传入 DOM 的 htmlhtml: str,},draggable: true,});},},"single-node"
);// 绘制层级之间的连接线
G6.registerEdge("flow-line", {// 绘制后的附加操作draw(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],["L", startPoint.x, (startPoint.y + endPoint.y) / 2],["L", endPoint.x, (startPoint.y + endPoint.y) / 2],["L", endPoint.x, endPoint.y],],},});return shape;},
});// 默认连接边线的颜色 末尾箭头
const defaultEdgeStyle = {stroke: "#ccc",
};// 默认布局
// compactBox 紧凑树布局
// 从根节点开始,同一深度的节点在同一层,并且布局时会将节点大小考虑进去。
const defaultLayout = {type: "compactBox", // 布局类型树direction: "TB", // TB 根节点在上,往下布局getId: function getId(d) {// 节点 id 的回调函数return d.id;},getHeight: function getHeight() {// 节点高度的回调函数return 16;},getWidth: function getWidth() {// 节点宽度的回调函数return 16;},getVGap: function getVGap(d) {// 节点纵向间距的回调函数if (d.parId === "0") return 70;return 80;},getHGap: function getHGap(d) {// 节点横向间距的回调函数if (d.parId === "0") return 100;return 150;},
};
let graph;export default {name: "Home",props: {treeListData: {type: Array,default: () => [],},options: {type: Object,default: () => {return {};},},},emits: ["handleSelected"],data() {return {listData: [],selectedId: "", // 选中的节点IdinitOptions: {isFitView: true, // 是否默认适应全局isFitCenter: true, // 是否居中isHiddenRoot: true, // 是否显示根元素},};},methods: {G6init() {if (typeof window !== "undefined") {window.onresize = () => {if (!graph || graph.get("destroyed")) return;if (!container || !container.scrollWidth || !container.scrollHeight)return;graph.changeSize(container.scrollWidth, container.scrollHeight);};}// 获取容器const container = document.getElementById("container");// 获取容器的宽高const width = container.scrollWidth;const height = container.scrollHeight - 30 || 500;// Graph 是 G6 图表的载体-实例化graph = new G6.TreeGraph({container: "container", // 图的 DOM 容器width,height,linkCenter: true, // 指定边是否连入节点的中心modes: {// default 模式中包含点击选中节点行为和拖拽画布行为;default: [{// 这个是可以展开可以收起type: "collapse-expand",onChange: function onChange(item, collapsed) {const data = item.get("model");data.collapsed = collapsed;return true;},},"drag-canvas","zoom-canvas","click-select",],edit: ["click-select"],},// 默认状态下节点的配置defaultNode: {type: "dom-node", // 'icon-node',size: [250, 60],},// 默认状态下边线的配置,defaultEdge: {type: "flow-line",style: defaultEdgeStyle,},// 布局配置项layout: defaultLayout,renderer: "svg",});graph.data([...this.listData][0]);graph.render();// 让画布内容适应视口。if (this.initOptions.isFitView) {graph.fitView();}if (this.initOptions.isFitCenter) {graph.fitCenter();}if (!this.initOptions.isHiddenRoot) {// 是否要移除根节点const item = graph.findById([...this.listData][0].id);graph.removeItem(item);}// 改变视口的缩放比例,在当前画布比例下缩放,是相对比例。graph.zoom(1);graph.setMode("edit");},async init() {let _this = this;if (graph) {// 如果原来有画布,需要先清除graph.destroy();}this.initOptions = Object.assign(this.initOptions, this.options);this.listData = [...this.treeListData];function setSelectFalse(obj) {obj.forEach((element) => {element.isSelected = false;if (element.children) {setSelectFalse(element.children);}});}window.nodeClick = function (id) {const item = graph.findById(id);setSelectFalse(_this.listData);item._cfg.model.isSelected = true;graph.changeData(_this.listData[0]);graph.refresh();graph.fitCenter();};window.handleDetail = (id) => {const item = graph.findById(id);if (item?._cfg?.parent) {_this.$emit("handleSelected", id);}};this.G6init();},},beforeDestroy() {console.log("推出");},
};
</script>
<style lang="scss" scoped>
@import "@/assets/styles/common.scss";
#container {height: 100%;width: 100%;border: 1px solid #efefef;::v-deep .title {font-size: 15px;display: block;// text-align: center;position: relative;margin: 10px 0;padding-left: 15px;color: #1199ff;cursor: pointer;}::v-deep .item-box {background-color: #fff;border-radius: 5px;padding: 5px;// height: 100%;border: 1px solid;position: relative;p {margin-bottom: 2px;display: flex;align-items: center;color: #333;}&.is-selected {border: 1px solid #1199ff;}.tipText {color: red;}.logs {height: 70px;overflow: hidden;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 3;}.title-txt {display: inline-block;width: 80px;color: rgb(169, 169, 169);}.avatar-img {width: 40px;height: 40px;margin-right: 15px;img {width: 100%;height: 100%;border-radius: 100%;}}.status {position: absolute;right: 15px;top: 15px;border: 1px solid;padding: 0 5px;font-size: 12px;border-radius: 4px;}}
}
::v-deep g g g:not(:first-child) foreignObject {font-size: 14px;
}
foreignObject {overflow: initial !important;
}
</style>

http://www.ppmy.cn/news/402304.html

相关文章

使用20分钟语音数据进行语音复刻

最近做了一些tts方面的工作&#xff0c;其中一项就是音色克隆&#xff0c;调研了许多开源项目&#xff0c;经过对比发现百度的开源项目paddlespeech的效果还不错&#xff0c;项目的活跃度也很高&#xff0c;比较适合初学者或者感兴趣的同学入门。 一、语音克隆模型介绍 目前主…

【项目】—— 语音小管家Sosuke

项目简介 借助图灵机器人和百度语音识别和合成等第三方平台和第三方工具使用C编写一个智能AI对话和语音命令执行的语音管理工具除去交流功能之外还可以执行Linux下相关命令&#xff0c;可执行的命令支持自己配置 项目技术点 C STL中主要是map和unorder_map的使用了解http第三…

lol全队消息怎么发_lol怎么发给所有人 LOL里面怎么给所有人发送消息

LOL发送所有人说话步骤&#xff1a; 1.首先进入游戏中。 2.按下回车会出现聊天界面。 3.输入/all即可实现对当前所有人发送消息。 4.如果无法成功则按键盘上的Esc键打开选项窗口。 5.切换到界面选项卡&#xff0c;勾选聊天栏目中的显示所有人频道的聊天信息并点击“确家是什么&…

机器人布里茨说什么_lol机器说的话

蒸汽机器人——布里茨。虽然布里茨会打败任何挡道的人&#xff0c;但他有一颗金子般的心&#xff0c;包裹在一具铁架内&#xff0c;一层钢壳中。喜欢他的话来看看吧!下面是学习啦小编为你搜集、整理的lol机器说的话&#xff0c;欢迎阅读! lol机器说的话精选&#xff1a; The ti…

UE4原生内置多人语音聊天

一.创建会话 1.创建 创建服务器和加入服务器的UI 2.实现创建会话和加入会话的功能 &#xff08;1&#xff09;创建会话功能 &#xff08;2&#xff09;加入会话功能 二.配置文件设置 1.DefaultEngine.ini文件配置 &#xff08;1&#xff09;在[OnlineSubsystem]下的bHasVoi…

内置语音合成芯片的智能语音扫地机器人方案推荐

扫地机器人行业背景&#xff1a; 随着现在生活水平的提高&#xff0c;越来越多的家庭开始选择智能家居产品。像智能电视、智能空调、智能冰箱等都是我们常见的智能家居产品。作为越来越多家庭清洁必备的吸尘器也逐渐向智能化转变&#xff0c;传统的老式吸尘器也逐渐被智能扫地机…

打游戏用什么蓝牙耳机好?英雄联盟手游推荐蓝牙耳机

蓝牙耳机作为手机周边产品&#xff0c;对于很多人来说&#xff0c;属于必需品。上下班的路上多得是无聊和枯燥&#xff0c;不看个视频或者听个音乐来打发时间&#xff0c;真是觉得时间会非常漫长。但公共场合如果外放音乐的话又会显得没有素质&#xff0c;所以出门带一幅耳机是…

调整计算机的设置怎么退出,英雄联盟怎么退出调电脑音量

1 回答 电脑英雄联盟怎么加好友? 首先进入到LOL英雄联盟中&#xff0c;在右侧好友列表中点击【添加好友】按钮&#xff0c;上面有两个添加好友的方式&#xff0c;点击【通过召唤师名字】这个选项&#xff0c;在里面添加好友即可。另外有和玩家一起玩过游戏的最近的玩家&#x…