el-tree 的各项骚操作

要求!!!

1. 区分两组数据,展示不同颜色

2. 给其中一组数据新增按钮 并且这个按钮可以控制下级菜单全选/反选 点击按钮时需要展开当前节点

3. 其中一组数据不采用竖型,用横行展示

实现逻辑!!

  1. 后端给到我的本身就是两组不同的数据,我需要把这两组数据合并成一组,才能在一个el-tree中展示,一组数据是菜单一组数据是组件,组件属于菜单的子集,通过组件里面的menuId和当前的菜单id做比较,相同则合并到对应菜单的children中

  2. 同时在这两组数据合并之前,我需要遍历递归,给每组数据新增一个属性type,菜单的新增type=menu,组件新增type为comp,方便在合并之后区分谁是谁

  3. 这里展示不同的颜色,我则只需要判断当前这个type为什么,

  4. 新增按钮,这里的按钮是只要是菜单就都有按钮,那么同样的,我在渲染的时候,button中做一个判断,v-if 是否包含type为menu

  5. 按钮控制下级菜单全选和反选就复杂很多this.$refs.tree.setChecked(id, this.checked, true),我用的它自身包含的属性,设置当前节点是否选中,this.checked 是布尔值,如果为true表示选中,false 表示不选中,但是这里会遇到问题?? 没有及时更新

  6. 针对没有及时更新的问题,我再次查找了文档,getCheckedNodes,getHalfCheckedNodes 找到了这两个api,是获取到当前选中的id的,在点击全选反选按钮之后,再次调这个函数,就可以解决问题了

  7. 其中组件这组数据,它不要按照原版的方式处理,要改成横行展示,这里需要获取到节点的dom,然后找到父元素去修改样式,会遇到一个问题,无法及时更新获取到dom,这个问题我是采用的api 展开当前节点,handleNodeClick 然后再this.$nextTick 之后再调用方法,这样就解决这个问题了

  8. 点击按钮的时候需要展开当前节点,这个我找了api,没有可以直接用的,于是百度, this.$refs.tree.store.nodesMap[id].expanded = true,这个方法可行

初版

el-tree 的各项骚操作

完善之后

el-tree 的各项骚操作

el-tree 的各项骚操作

具体代码如下

分类介绍:

1. 这个是具体的树结构

data=”processedMenuOptions” 是整体的数据

:props=”defaultMenuProps” 展示对应的字段

@check=”handleCheck” 选中的事件

@node-expand=”handleNodeClick” 点击点击的事件,展开收起节点

<el-form-item label="菜单组件权限" prop="components">
  <el-tree class="tree-border" :data="processedMenuOptions" show-checkbox ref="tree" node-key="id"
    empty-text="加载中,请稍候" :props="defaultMenuProps" @check="handleCheck" @node-expand="handleNodeClick">
    <template v-slot="{ node, data }">
      <span :style="getNodeStyle(node, data)" :class="data.type == 'comp' ? 'levelname' : ''">
        {{ data.menuTitle }}
        <el-button type="primary" size="mini"
          style="position: absolute; right:12px;margin-top: 8px;height: 26px;"
          v-if="data.type == 'menu'" @click.stop="toggleSubMenuSelection(data)"
          :disabled="!data.isButton">
          全选/反选</el-button>
      </span>
    </template>
  </el-tree>
</el-form-item>

2.事件实现具体代码

<script>
import { deepClone } from '@/util/validate'
export default {
name: "Role",
data() {
return {
// 表单参数
form: {},
defaultMenuProps: {
children: "children",
label: "menuTitle"
},
//是否选中
checked: false
};
},
created() {
this.getList();
this.getMenuTreeselect()
this.getDeptTreeselect()
},
mounted() {
var levelName = document.getElementsByClassName('levelname');
if (levelName.length > 0) {
this.changeCss(); // 初次加载时调用一次
}
},
methods: {
// 默认选中情况
handleCheck(data, { checkedKeys }) {
this.$refs.tree.getCheckedKeys()
this.getMenuAllCheckedKeys()
},
// 展开当前节点
handleNodeClick() {
this.$nextTick(() => {
this.changeCss()
})
},
// 全选反选
toggleSubMenuSelection(data) {
this.handleNodeClick()
let id = data.id
let node = this.$refs.tree.getNode(id);
if (node) {
this.checked = !this.hasCheckedDescendants(node);
// 展开节点
// 设置当前节点
this.$refs.tree.setCurrentKey(id);
// 获取当前节点属性
this.$refs.tree.store.nodesMap[id]
this.$refs.tree.store.nodesMap[id].expanded = true
this.handleNodeClick()
}
this.$refs.tree.setChecked(id, this.checked, true);
this.$refs.tree.setChecked(id, true);
// 反转选中状态并设置节点的选中状态(仅需调用一次)
this.checked = !this.checked
this.getMenuAllCheckedKeys();
},
// 检查节点及其子孙节点是否有选中的菜单或组件
hasCheckedDescendants(node) {
if (node.childNodes && node.childNodes.length > 0) {
for (let childNode of node.childNodes) {
if (childNode.checked) {
return true;
}
if (this.hasCheckedDescendants(childNode)) {
return true;
}
}
}
return false;
},
// 区分菜单和组件颜色
getNodeStyle(node, data) {
const nodeStyle = {
color: data.type === 'comp' ? '#666' : '#333'
};
return nodeStyle;
},
changeCss() {
// levelname是上面的最底层节点的名字
var levelName = document.getElementsByClassName('levelname');
// console.log(levelName, 'levelName');
if (levelName.length > 0) {
for (var i = 0; i < levelName.length; i++) {
levelName[i].parentNode.style.cssFloat = 'left';
levelName[i].parentNode.style.styleFloat = 'left';
levelName[i].parentNode.style.paddingLeft = '0px'
levelName[i].parentNode.style.paddingRight = '10px'
levelName[i].parentNode.style.marginLeft = '54px'
}
}
},
/** 查询菜单树和组件树结构 */
getMenuTreeselect() {
listComponent().then(res => {
this.menuOptions = res.data.menus.map(menu => ({
...menu,
type: 'menu'
}));;
this.componentsOptions = res.data.components.map(component => ({
...component,
menuTitle: component.componentName,
type: 'comp',
id: 'comp_' + component.id, // 添加前缀
}))
const menu = { id: 'root', menuTitle: '主类目', children: [...deepClone(this.menuOptions)], type: 'menu' };
// 创建新的数组来存储处理后的菜单数据,不修改原始数据
this.processedMenuOptions = deepClone([menu])
// 递归遍历菜单树,将匹配的组件数据作为子集添加到菜单中
for (let menu of this.processedMenuOptions) {
this.updateComponents(menu);
}
console.log(this.processedMenuOptions, '处理后的菜单数据');
})
},
// 递归函数,用于处理多层嵌套的菜单树
updateComponents(menu) {
const children = menu.children;
menu.type = 'menu'; // 设置当前节点的类型为菜单
if (children && children.length > 0) {
for (let child of children) {
this.updateComponents(child);
}
} else {
const menuId = menu.id;
const matchedComponents = this.componentsOptions.filter(component => component.menuId === menuId);
menu.children = matchedComponents;
}
},
// 所有节点数据
getMenuAllCheckedKeys() {
let checkedMenuKeys = [];
let checkedCompKeys = [];
for (let node of this.$refs.tree.getCheckedNodes()) {
if (node.type === 'menu') {
checkedMenuKeys.push(node.id);
} else if (node.type === 'comp') {
checkedCompKeys.push(node.id);
}
}
for (let node of this.$refs.tree.getHalfCheckedNodes()) {
if (node.type === 'menu') {
checkedMenuKeys.push(node.id);
}
}
// 递归更新 isButton 属性
const updateIsButton = (node) => {
if (checkedMenuKeys.includes(node.id)) {
this.$set(node, 'isButton', true);
} else {
this.$delete(node, 'isButton');
}
if (node.children) {
node.children.forEach(child => {
updateIsButton(child);
});
}
};
this.processedMenuOptions.forEach(node => {
updateIsButton(node);
});
return {
menuKeys: checkedMenuKeys,
compKeys: checkedCompKeys
};
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 取消按钮(数据权限)
cancelDataScope() {
this.reset();
},
// 表单重置
reset() {
if (this.$refs.tree != undefined) {
this.$refs.tree.setCheckedKeys([]);
}
this.form = {
id: undefined,
roleName: undefined,
roleType: undefined,
deptId: undefined,
allowEdit: 0,
menus: [],
components: [],
remark: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.getMenuTreeselect();
this.open = true;
this.title = "添加角色";
},
/** 编辑按钮操作 */
handleUpdate(row) {
this.reset();
this.getMenuTreeselect();
const roleId = row.id;
getRole({ id: roleId }).then(res => {
this.open = true;
this.form = res.data;
this.title = "修改角色";
// 在数据加载完成后再设置选中节点
this.$nextTick(() => {
if (this.form.menus && this.form.components) {
this.setCheckedNodes(this.$refs.tree, this.form.menus, 'menu');
// 对 components 数据中的 id 添加 'comp_' 前缀
const processedComponents = this.form.components.map(componentId => 'comp_' + componentId);
this.setCheckedNodes(this.$refs.tree, processedComponents, 'comp');
}
});
});
},
setCheckedNodes(ref, nodeIds, nodeType) {
if (ref && nodeIds && nodeIds.length > 0) {
this.$nextTick(() => {
nodeIds.forEach(nodeId => {
this.setCheckedNode(ref, nodeId, nodeType);
});
});
}
},
setCheckedNode(ref, nodeId, nodeType) {
this.$nextTick(() => {
const node = ref.getNode(nodeId, nodeType);
if (node) {
// 回显
// 修改节点数据对象中的 isButton 字段
ref.setChecked(nodeId, true, false, nodeType);
this.getMenuAllCheckedKeys()  //要放在最后,否则容易出现数据未加载就执行问题
}
});
},
/** 提交按钮 */
submitForm: function () {
this.$refs["form"].validate(valid => {
if (valid) {
let menuData = this.getMenuAllCheckedKeys();
// 过滤掉名为 "root" 的菜单项
this.form.menus = menuData.menuKeys.filter(menuKey => menuKey !== 'root');
this.form.components = menuData.compKeys.map(item => {
return parseInt(item.split('_')[1]);
});
if (this.form.id != undefined) {
updateRole(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addRole(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const roleIds = row.id
this.$modal.confirm('是否确认删除角色编号为"' + roleIds + '"的数据项?').then(function () {
return delRole({ id: roleIds });
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
}
};
</script>

原文链接:https://juejin.cn/post/7345300528703668261 作者:用户5327161192068

(0)
上一篇 2024年3月13日 上午10:57
下一篇 2024年3月13日 上午11:08

相关推荐

发表回复

登录后才能评论