zTree api 文档:https://www.treejs.cn/v3/api.php
1. 初始化树的配置项
const initZtreeSetting = () => {const setting = {view: {addHoverDom: addHoverDom, // 显示用户自定义控件selectedMulti: false,// 是否允许同时选中多个节点,默认为trueshowIcon: true,//是否显示节点的图标,默认为true},data: {simpleData: {// 使用用简单数据模式enable: true,idKey: 'id',pIdKey: 'parentId',rootPId: 0,},},callback: {onClick: handleNodeClick,// 节点被点击的回调函数beforeClick: handleNodeBeforeClick,//节点被点击前的回调函数,设置节点是否可以点击},};return setting;
};
2. 自定义操作按钮
const addHoverDom = (treeId, treeNode) => {let addStr = null;const add = `<span class='button add' id='btn_add_${treeNode.tId}' title='添加文件' οnfοcus='this.blur();'></span>`;const addSm = `<span class='button addSm' id='btn_addSm_${treeNode.tId}' title='添加节点' οnfοcus='this.blur();'></span>`;const edit = `<span class='button edit' id='btn_edit_${treeNode.tId}' title='编辑' οnfοcus='this.blur();'></span>`;const del = `<span class='button remove' id='btn_remove_${treeNode.tId}' title='删除' οnfοcus='this.blur();'></span>`;if (treeNode.id === '自定义') {// 自定义addStr = addSm; // 添加节点} else if (treeNode.code === 'custom') {// 自定义下的分类节点(二层)if (treeNode.pid === '自定义') {addStr = add + edit;if (_.isEmpty(treeNode.children)) {addStr += del; // 分类下子节点时,可删除该分类}} else {// 三层addStr = edit + del;}if (addStr) {const _id = `#${treeNode.tId}_span`;tippy(_id, {content: addStr,// 提示的内容trigger: 'mouseenter click',// 触发方式placement: 'right',// 出现位置interactive: true,// 是否可以交互theme: 'material',// 主题onMount: function () {// 挂载后执行$(`#btn_edit_${treeNode.tId}`)?.off('click')?.on('click', (e) => {editNode(treeId, treeNode);e.stopPropagation();});$(`#btn_add_${treeNode.tId}`)?.off('click')?.on('click', (e) => {addNode(treeId, treeNode);e.stopPropagation();});$(`#btn_addSm_${treeNode.tId}`)?.off('click')?.on('click', (e) => {saveSmNode('add', treeId, treeNode);e.stopPropagation();});$(`#btn_remove_${treeNode.tId}`)?.off('click')?.on('click', (e) => {removeNode(treeId, treeNode);e.stopPropagation();});},});}
};
tippy api文档:https://atomiks.github.io/tippyjs/v6/all-props
3. 按钮绑定事件
// 添加、编辑节点(分类)
async function saveSmNode(type: 'add' | 'mod', treeId, treeNode) {const title = type === 'add' ? '新增节点' : '编辑节点';let { value } = await ElMessageBox.prompt(title, '', {confirmButtonText: '确认',cancelButtonText:'取消',inputPattern: /\S+/,// 输入框校验规则inputPlaceholder: type === 'add' ? '请输入节点名称' : null,// 占位符inputErrorMessage: '节点名不能为空!',// 校验不通过的信息inputValue: type === 'add' ? null : treeNode.name,// 输入框默认值});try {//case1: 在该节点下新增节点if (type === 'add') {const res = await $.ajax({url: `${window.__ctx}/report/module/addNode`,type: 'POST',dataType: 'json',data: {parentId:treeNode.id,name: value,// 获取到输入框的值},});if (!res.result) throw new Error(res?.message);ElMessage.success(res?.message || '提交成功');const zTreeObj = $.fn.zTree.getZTreeObj(treeId);zTreeObj.addNodes(treeNode , res?.object);// 更新树节点的显示//case2:对该节点进行编辑} else {const res = await $.ajax({url: `${window.__ctx}/report/module/rename`,type: 'POST',dataType: 'json',data: {id: treeNode.id,reName: value,},});if (!res.result) throw new Error(res?.message);ElMessage.success(res?.message || '提交成功');const zTreeObj = $.fn.zTree.getZTreeObj(treeId);treeNode.name = value;zTreeObj.updateNode(treeNode);}} catch (error) {if (error === 'cancel') return;ElMessage.error(error?.message ||'提交失败');console.log(`addSmNode - error`, error);}
}// 删除
async function removeNode(treeId, treeNode) {try {const modId = treeNode.id;await ElMessageBox.confirm('是否要删除该节点?', '提示', {confirmButtonText: '确认',cancelButtonText:'取消',type: 'warning',});const res = await $.ajax({url: `${window.__ctx}/report/module/delete/${modId}`,type: 'GET',dataType: 'json',});if (!res.result) throw new Error(res?.message);ElMessage.success(res?.message || '提交成功');$.fn.zTree.getZTreeObj(treeId).removeNode(treeNode);//移除该节点} catch (error) {if (error === 'cancel') return;ElMessage.error(error?.message || '提交失败');console.log(`removeNode - error`, error);}
}
// 添加文件
function addNode(treeId, treeNode) {...
}
// 编辑文件
function editNode(treeId, treeNode) {...
}
4. 接口请求数据, 初始化zTree
async function initZtree() {const setting = initZtreeSetting ();try {isTreeLoading.value = true;const res = await $.ajax({url: `${window.__ctx}/report/tree`,type: 'POST',dataType: 'json',});if (!res?.result) return;const treeObj = $.fn.zTree.init($('#treeId'), setting, res?.object);if (treeObj.getNodes().length >= 1) {treeObj.expandNode(treeObj.getNodes()[0], true);// 展开第一个节点}} catch (err) {console.log(`[log] - initZtree- err`, err);} finally {isTreeLoading.value = false;}
}
5. 全部代码
<template><div v-loading="isTreeLoading"><ul class="ztree" id="treeId"></ul></div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const isTreeLoading = ref(false);// 初始化树的配置项
const initZtreeSetting = () => {const setting = {view: {addHoverDom: addHoverDom, // 显示用户自定义控件selectedMulti: false,// 是否允许同时选中多个节点,默认为trueshowIcon: true,//是否显示节点的图标,默认为true},data: {simpleData: {// 使用用简单数据模式enable: true,idKey: 'id',pIdKey: 'parentId',rootPId: 0,},},callback: {onClick: handleNodeClick,// 节点被点击的回调函数beforeClick: handleNodeBeforeClick,//节点被点击前的回调函数,设置节点是否可以点击},};return setting;
};
// 节点前点击事件
const handleNodeBeforeClick = (treeId, treeNode, clickFlag) => {return treeNode.reportType !== 'Node';// 返回 false,无法点击
};
// 节点点击事件
const handleNodeClick = (event, treeId, treeNode) => {console.log(`treeNode->`, treeNode);
};
// 初始化树
async function initZtree() {const setting = initZtreeSetting ();try {isTreeLoading.value = true;const res = await $.ajax({url: `${window.__ctx}/report/tree`,type: 'POST',dataType: 'json',});if (!res?.result) return;const treeObj = $.fn.zTree.init($('#treeId'), setting, res?.object);if (treeObj.getNodes().length >= 1) {treeObj.expandNode(treeObj.getNodes()[0], true);// 展开第一个节点}} catch (err) {console.log(`[log] - initZtree- err`, err);} finally {isTreeLoading.value = false;}
}
onMounted(() => {initZtree();
});//自定义操作按钮
const addHoverDom = (treeId, treeNode) => {let addStr = null;const add = `<span class='button add' id='btn_add_${treeNode.tId}' title='添加文件' οnfοcus='this.blur();'></span>`;const addSm = `<span class='button addSm' id='btn_addSm_${treeNode.tId}' title='添加节点' οnfοcus='this.blur();'></span>`;const edit = `<span class='button edit' id='btn_edit_${treeNode.tId}' title='编辑' οnfοcus='this.blur();'></span>`;const del = `<span class='button remove' id='btn_remove_${treeNode.tId}' title='删除' οnfοcus='this.blur();'></span>`;if (treeNode.id === '自定义') {// 自定义addStr = addSm; // 添加节点} else if (treeNode.code === 'custom') {// 自定义下的分类节点(二层)if (treeNode.pid === '自定义') {addStr = add + edit;if (_.isEmpty(treeNode.children)) {addStr += del; // 分类下子节点时,可删除该分类}} else {// 三层addStr = edit + del;}if (addStr) {const _id = `#${treeNode.tId}_span`;tippy(_id, {content: addStr,// 提示的内容trigger: 'mouseenter click',// 触发方式placement: 'right',// 出现位置interactive: true,// 是否可以交互theme: 'material',// 主题onMount: function () {// 挂载后执行$(`#btn_edit_${treeNode.tId}`)?.off('click')?.on('click', (e) => {editNode(treeId, treeNode);e.stopPropagation();});$(`#btn_add_${treeNode.tId}`)?.off('click')?.on('click', (e) => {addNode(treeId, treeNode);e.stopPropagation();});$(`#btn_addSm_${treeNode.tId}`)?.off('click')?.on('click', (e) => {saveSmNode('add', treeId, treeNode);e.stopPropagation();});$(`#btn_remove_${treeNode.tId}`)?.off('click')?.on('click', (e) => {removeNode(treeId, treeNode);e.stopPropagation();});},});}
};// 添加、编辑节点(分类)
async function saveSmNode(type: 'add' | 'mod', treeId, treeNode) {const title = type === 'add' ? '新增节点' : '编辑节点';let { value } = await ElMessageBox.prompt(title, '', {confirmButtonText: '确认',cancelButtonText:'取消',inputPattern: /\S+/,// 输入框校验规则inputPlaceholder: type === 'add' ? '请输入节点名称' : null,// 占位符inputErrorMessage: '节点名不能为空!',// 校验不通过的信息inputValue: type === 'add' ? null : treeNode.name,// 输入框默认值});try {//case1: 在该节点下新增节点if (type === 'add') {const res = await $.ajax({url: `${window.__ctx}/report/module/addNode`,type: 'POST',dataType: 'json',data: {parentId:treeNode.id,name: value,// 获取到输入框的值},});if (!res.result) throw new Error(res?.message);ElMessage.success(res?.message || '提交成功');const zTreeObj = $.fn.zTree.getZTreeObj(treeId);zTreeObj.addNodes(treeNode , res?.object);// 更新树节点的显示//case2:对该节点进行编辑} else {const res = await $.ajax({url: `${window.__ctx}/report/module/rename`,type: 'POST',dataType: 'json',data: {id: treeNode.id,reName: value,},});if (!res.result) throw new Error(res?.message);ElMessage.success(res?.message || '提交成功');const zTreeObj = $.fn.zTree.getZTreeObj(treeId);treeNode.name = value;zTreeObj.updateNode(treeNode);}} catch (error) {if (error === 'cancel') return;ElMessage.error(error?.message ||'提交失败');console.log(`addSmNode - error`, error);}
}// 删除
async function removeNode(treeId, treeNode) {try {const modId = treeNode.id;await ElMessageBox.confirm('是否要删除该节点?', '提示', {confirmButtonText: '确认',cancelButtonText:'取消',type: 'warning',});const res = await $.ajax({url: `${window.__ctx}/report/module/delete/${modId}`,type: 'GET',dataType: 'json',});if (!res.result) throw new Error(res?.message);ElMessage.success(res?.message || '提交成功');$.fn.zTree.getZTreeObj(treeId).removeNode(treeNode);//移除该节点} catch (error) {if (error === 'cancel') return;ElMessage.error(error?.message || '提交失败');console.log(`removeNode - error`, error);}
}
// 添加文件
function addNode(treeId, treeNode) {...
}
// 编辑文件
function editNode(treeId, treeNode) {...
}</script >