树形结构的勾选、取消勾选、删除、清空已选、回显、禁用

news2024/11/17 7:30:56

树形结构的勾选、取消勾选、删除、清空已选、回显、禁用

基本页面:

分为上传文件和编辑的页面

代码实现要点:

  • 上传文件页面:

    1. 点开选择范围弹窗,三个radio单选框都为可选状态,默认显示的是第一个单选框(按机构项目组)对应的内容

    2. 可以对树形数据进行勾选,勾选的数据展示在右边已选框内

    3. 可以对树形数据进行全选,取消全选

    4. 右边已选框内数据有对应的删除按钮,可以点击删除按钮或对已选数据进行删除

    5. 可以点击清空已选按钮对已选数据进行一次性全部删除

    6. 在左边树形数据取消勾选,右边已选框的数据也会对应发生变化

    7. 点击确定按钮但未确认上传时,再次打开弹框,页面中应把上一次选择的数据展示出来,且不能禁用,并同步展示在右边已选框内,同样可以对数据进行以上操作

    8. 点击确定并在上一层点击确认上传按钮,已经勾选的数据就会传给后端,同步新增到了页面中,后续可以对数据进行编辑操作来修改数据

  • 编辑页面:

    1. 点开选择范围弹窗,只有从后端返回的对应的单选框类型的单选框才能可选,其他的单选框都为禁用状态

    2. 树形数据显示的也是可选单选框对应的树形数据

    3. 从后端得到的上一次确认上传的数据,对数据进行禁用,即只能继续添加不能删除原有的

    4. 从后端得到的上一次确认上传的数据,对数据同步展示在右边已选框

    5. 从后端得到的上一次确认上传的数据,在右边已选框内不能存在删除按钮

    6. 可以继续勾选想要勾选的数据,会同步展示在右边已选框,有删除按钮,可以删除

    7. 点击全选,全选数据,点击反选,把除了从后端得到的上一次确认上传的数据都取消勾选,同时右边已选框也要同步展示

    8. 点击清空已选,把除了从后端得到的上一次确认上传的数据在右边已选框内删除,同时左边的树形结构也要同步取消勾选数据

    9. 点击确定按钮但未确认上传时,再次打开弹框,显示的勾选数据为上次点击确定前勾选的数据,禁用的数据也仅为从后端得到的上一次确认上传的数据,右边已选框也展示的是上次点击确定前勾选的数据,没有删除按钮的也仅为从后端得到的上一次确认上传的数据

    10. 点击确定并在上一层点击确认上传按钮,已经勾选的数据就会传给后端,同步更新到页面中,后续也可以对数据进行编辑操作来修改数据

代码设计:

单选框用到的是el-radio组件

<div class="person_top_nav">
    <span>选择签署范围</span>
    <el-radio-group style="margin-left: 12px" v-model="searchRange" @change="handleSearchChange">
        <el-radio style="max-width: 90px" :disabled="!(SelectedType == 'clientOrganization') && disabledArr.length > 0" :label="1">
            按机构项目组
        </el-radio>
        <el-radio style="max-width: 60px" :disabled="!(SelectedType == 'organization') && disabledArr.length > 0" :label="2">
            按事业部
        </el-radio>
        <el-radio style="max-width: 90px" :disabled="!(SelectedType == 'branchCompany') && disabledArr.length > 0" :label="3">
            按合约归属公司
        </el-radio>
    </el-radio-group>
</div>
  • 绑定的是searchRange,点击事件调用的函数是handleSearchChange()函数

  • :disabled="!(SelectedType == 'clientOrganization') && disabledArr.length > 0" 是上传文件和编辑页面对单选框是否禁用的设置,即:第一个按机构项目组被禁用的时候是:在编辑页面disabledArr.length > 0,并且得到的从后端返回的类型不等于clientOrganization(机构项目组)

左边树形数据和右边已选框用到的都是el-tree组件

左边树形数据:

<el-tree
    :data="dataTree" // 展示数据
    :default-checked-keys="checkboxId" // 默认勾选节点
    show-checkbox // 节点可被选择
    v-loading="loading"
    class="filter-tree" // 绑定类
    node-key="value" // 树节点唯一标识
    ref="tree" // 绑定树,可后续调用方法
    :props="defaultProps" // 配置选项 defaultProps: { children: 'children', label: 'label'}
    @check-change="getCheckedNodes" // 节点选中状态发生变化时的回调
>
</el-tree>
  • data属性是绑定展示的数据,这里绑定的是dataTree

  • default-expand-all属性是绑定默认勾选的节点的 key 的数组,绑定的数据为checkboxId,是默认勾选的数据value数组

  • node-key属性是每个树节点用来作为唯一标识的属性,整棵树应该是唯一的,这里绑定的是节点的value

  • check-change事件是节点选中状态发生变化时的回调,绑定着函数getCheckedNodes()

右边已选框数据:

<el-tree :data="isSelectedData" :props="defaultProps" :default-expand-all="true">
    <span class="custom-tree-node" slot-scope="{ node, data }">
        <span class="tree_label">{{ node.label }}</span>
        <el-button
            class="remove_button"
            v-if="!(node.disabled || data.ifShow)"
            type="text"
            size="medium"
            @click="() => remove(node, data)"
        >
            ×
        </el-button>
    </span>
</el-tree>

绑定的数据为isSelectedData,default-expand-all默认展开所有节点

重要数据:

dataTree:调用选择radio的接口数据,也就是一棵树

checkboxId:左边树的默认勾选节点,是一个value数组

isSelectedData:右边已选框的展示数据,也就是左边树的数据

searchRange:radio单选框绑定的数据

disabledArr:原始数据,应被禁用和默认勾选,展示【从父组件传入】

peList:上一次勾选提交的数据,应被默认勾选和展示(每一次点击确定后都会被改变)【从父组件传入】

oldDisabledArr:disabledArr得来

oldDisabledArrId:disabledArr得来

SelectedType:从父组件传入的选择范围类型

用到的el-tree方法:

setCheckedNodes():设置目前勾选的节点,使用此方法必须设置 node-key 属性,(nodes) 接收勾选节点数据的数组

getCheckedNodes():若节点可被选择(即 show-checkboxtrue),则返回目前被选中的节点所组成的数组,(leafOnly, includeHalfChecked) 接收两个 boolean 类型的参数,1. 是否只是叶子节点,默认值为 false 2. 是否包含半选节点,默认值为 false

setChecked():通过 key / data 设置某个节点的勾选状态,使用此方法必须设置 node-key 属性,(key/data, checked, deep) 接收三个参数,1. 勾选节点的 key 或者 data 2. boolean 类型,节点是否选中 3. boolean 类型,是否设置子节点 ,默认为 false

代码执行流程:

编辑页面:

  1. 点击选择范围弹框,调用open()方法,打开弹框,根据是否有从父组件传入的peList(上一次选择的数据)来判断是上传文件还是编辑页面

    • 上传文件:默认调用机构项目组接口和展示机构项目组树形数据

    • 编辑:调用从父组件传入的SelectedType(选择范围类型),在handleSearchChange()函数内调用对应的接口和展示对应的树形数据

    async opened() {
        // 存在已选数据
        this.disabledArr.forEach((item, index) => {
            this.$set(this.oldDisabledArr, index, item);
        });
        this.disabledArr.forEach((item, index) => {
            this.$set(this.oldDisabledArrId, index, item.value);
        });
    ​
        // 打开选择范围弹框
        this.dialogVisible = true;
        // clientOrganization 机构项目组   organization 事业部   branchCompany 合约归属公司
    ​
        if (!this.peList) {
            // 上传文件 默认选中机构项目组
            await this.getInstitutionIssueList();
        } else {
            // 编辑
            if (this.SelectedType === 'clientOrganization') {
                this.searchRange = 1;
            } else if (this.SelectedType === 'organization') {
                this.searchRange = 2;
            } else if (this.SelectedType === 'branchCompany') {
                this.searchRange = 3;
            }
            // 调用对应接口
            await this.handleSearchChange();
            // 初始化
            this.init();
        }
    }
  2. 调用init()函数,执行的逻辑为

    init() {
        this.$nextTick(() => {
            // 给checkboxId赋值
            this.checkboxId = [];
            this.peList.map((item, index) => this.$set(this.checkboxId, index, item.value));
            this.$refs.tree.setCheckedNodes(this.peList);
            this.$forceUpdate();
    ​
            // 把禁用节点显示在右边
            this.getCheckedNodes();
    ​
            // 禁用已选节点
            this.reShowDisabled(this.dataTree, this.oldDisabledArrId);
    ​
            // 去掉父节点的删除按钮
            this.getParentValueList();
        });
    }
    • 初始化checkboxId,从peList中得到(上一次选择的数据)【注意:不是从disabledArr中得到,因为如果第一次勾选数据后点击确认,以后在确认上传前再次点击选择范围,页面不会回显上一次勾选的数据,但是确认上传后,提交给后端的又是上一次勾选的数据,不符合逻辑】

    • 通过el-tree的方法setCheckedNodes()设置目前勾选的节点【注意:一定要执行这一步,否则后续指定删除、清空已选、取消勾选并确定后,在确认上传前的再次点击选择范围,被删除的数据节点还是会被勾选和展示在已选框,猜测的原因是没有取消勾选删除的节点数据,即再次设置勾选节点,而不是通过default-expand-all属性来自动勾选】

    • 调用getCheckedNodes()函数,把上一次勾选提交的数据节点展示在右边已选框

      • getCheckedNodes()函数,【这个函数也绑定着左边树的change回调】,里面的逻辑为:调用el-tree的getCheckedNodes()方法,获取目前被选中的节点的数组checkedList,然后用checkedList作为参数调用depthTraversal()函数,递归来返回选中的树形节点,并赋值给isSelectedData,就同步展示在右边已选框了

      // 选择数据 -> 展示 el-tree回调
      getCheckedNodes() {
          const checkedList = this.$refs.tree.getCheckedNodes(false, true);
          this.isSelectedData = this.depthTraversal(JSON.parse(JSON.stringify(this.dataTree)), checkedList);
      }
      // 返回选中的数据列表
      depthTraversal(list, filterList) {
          const addObj = (list) => {
              return list.filter((v) => {
                  if (v.children && filterList.findIndex((val) => val.value === v.value) !== -1) {
                      v.children = addObj(v.children);
                      return v;
                  }
                  return filterList.findIndex((val) => val.value === v.value) !== -1;
              });
          };
          return addObj(list);
      }
    • 调用reShowDisabled()函数,把禁用节点进行禁用

      • 传入的参数为oldDisabledArrId(感觉等同于checkboxId),利用闭包写一个递归函数并调用,找到value在oldDisabledArrId数组中的节点:

        • 给它设置disabled为true的属性,实现禁用

        • 调用setdisabledParentList()函数,把它的parentValue放入disabledParentList中,便于后续删除掉父节点的删除按钮【存在逻辑是SelectedType是branchCompany时取反parentValue,树形节点的唯一性问题】

      // 回显禁用节点 checkboxId 禁用节点value数组
      reShowDisabled(list, checkboxId) {
          this.disabledParentList = [];
          const disabledObj = (list) => {
              list.forEach((v) => {
                  if (checkboxId.findIndex((val) => val === v.value) !== -1) {
                      this.$set(v, 'disabled', true);
                      this.setdisabledParentList(v);
                      // console.log('禁用', v);
                  }
                  if (v.children) {
                      disabledObj(v.children);
                  }
              });
          };
          disabledObj(list);
      }
    • 调用getParentValueList()函数,去掉父节点的删除按钮

      • 利用闭包写递归函数,在树节点中找到value在disabledParentList中的节点,为它设置ifShow为true的属性,实现父节点删除按钮的去掉

      // 获取需要隐藏删除按钮的所有父节点,设置ifShow属性
      getParentValueList() {
          const ifShowObj = (list) => {
              list.forEach((v) => {
                  if (this.disabledParentList.findIndex((val) => val === v.value) !== -1) {
                      this.$set(v, 'ifShow', true);
                  }
                  if (v.children) {
                      ifShowObj(v.children);
                  }
              });
          };
          ifShowObj(this.dataTree);
      }
    • 初始化完成

  3. 对左边树形数据进行勾选和取消勾选,都会触发树的change事件,调用getCheckedNodes()函数,逻辑如上

    // 选择数据 -> 展示 el-tree回调
    getCheckedNodes() {
        const checkedList = this.$refs.tree.getCheckedNodes(false, true);
        this.isSelectedData = this.depthTraversal(JSON.parse(JSON.stringify(this.dataTree)), checkedList);
    }
  4. 对右边已选框的数据点击删除按钮进行删除,调用remove()函数,调用el-tree的setChecked()方法,取消对对应节点的勾选,然后再次调用getCheckedNodes()函数

    // delete 右边移除选中的数据
    remove(node, data) {
        this.$refs.tree.setChecked(data.value, false, true);
        this.getCheckedNodes();
    }
  5. 点击清空已选按钮,调用empty()函数,在dataTree中过滤出除了禁用节点(oldDisabledArr/disabledArr)以外的节点,通过el-tree的setChecked()方法设置节点为取消勾选

    //点击清空时清空所有人员跟人员id
    empty() {
        // 过滤出除了所有外的数据,取消勾选
        const aa = this.dataTree.filter((item) => this.oldDisabledArr.findIndex((val) => val.value === item.value) === -1);
        aa.map((item) => this.$refs.tree.setChecked(item.value, false, true));
        this.checkAll = false;
    }
  6. 点击全选或反选按钮,调用handleCheckAllChange()函数,也是在dataTree中过滤出除了禁用节点(oldDisabledArr/disabledArr)以外的节点:

    • 若是全选,则用setChecked()方法设置节点为勾选

    • 若是反选,则用setChecked()方法设置节点为取消勾选

    // 全选反选
    handleCheckAllChange() {
        console.log('checkAll', this.checkAll);
        const aa = this.dataTree.filter((item) => this.oldDisabledArr.findIndex((val) => val.value === item.value) === -1);
        if (this.checkAll) {
            console.log('全选');
            aa.map((item) => this.$refs.tree.setChecked(item.value, true, true));
        } else {
            console.log('反选');
            console.log(this.disabledArr);
            aa.map((item) => this.$refs.tree.setChecked(item.value, false, true));
        }
    }
  7. 点击确定按钮,调用surePromoter()方法,校验通过后,通过getCheckedNodes()方法得到目前所有被选择的节点赋值给resultSelectedData:

    • 若是上传文件,则通过$emit传给父组件对应的radio类型和resultSelectedData

    • 若是编辑,则通过$emit传给父组件SelectedType和resultSelectedData

    然后关闭选择范围弹框

    surePromoter() {
        if (this.isSelectedData.length === 0) {
            this.$message({
                message: '请选择成员!',
                type: 'warning'
            });
            return;
        }
    ​
        // 提交的list
        const resultSelectedData = this.$refs.tree.getCheckedNodes(false, false);
        // clientOrganization 机构项目组   organization 事业部   branchCompany 合约归属公司
    ​
        if (this.oldDisabledArr.length === 0) {
            // 上传文件
            if (this.searchRange === 1) {
                this.$emit('getOrgList', 'clientOrganization', resultSelectedData, this.checkAll);
            } else if (this.searchRange === 2) {
                this.$emit('getOrgList', 'organization', resultSelectedData, this.checkAll);
            } else if (this.searchRange === 3) {
                this.$emit('getOrgList', 'branchCompany', resultSelectedData, this.checkAll);
            }
        } else {
            //编辑
            this.$emit('getOrgList', this.SelectedType, resultSelectedData, this.checkAll);
        }
    ​
        this.dialogVisible = false;
    }

还存在的问题:

1. 全选反选按钮是否勾选问题,已解决

2. 树形节点全选反选的速度较慢,待改进 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1864582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

邮件推送怎么设计吸引邮件内容?有何策略?

邮件推送在营销策略中的作用&#xff1f;如何评估邮件推送效果&#xff1f; 设计吸引人的邮件内容不仅可以提高打开率&#xff0c;还能促进用户的积极参与和转换。AokSend将探讨如何设计吸引人的邮件推送内容&#xff0c;以确保您的邮件不被忽略&#xff0c;并能够有效传达您的…

MES的实施难点与解决方案

随着智能制造的发展&#xff0c;制造执行系统&#xff08;MES&#xff09;已成为诸多制造企业争相引入的宝贵资产&#xff0c;它能有效衔接上层ERP系统与底层自动化设备&#xff0c;实现生产过程的实时监控与优化。然而&#xff0c;MES系统的成功实施并非易事&#xff0c;不当的…

Java毕设服务工作室

Java毕设服务工作室&#xff1a;专注提供高质量Java代码解决方案 在Java编程领域&#xff0c;毕业设计&#xff08;毕设&#xff09;项目往往需要大量的代码编写和调试。为了让同学们能够更专注于项目的核心逻辑和技术实现&#xff0c;Java毕设服务工作室应运而生&#xff0c;…

OpenAI停止对中国服务:背后原因与影响深度解析

引言 近日&#xff0c;OpenAI宣布停止对中国地区的服务&#xff0c;引发了广泛关注与讨论。许多开发者和企业用户收到了OpenAI的官方邮件&#xff0c;通知他们从7月9日开始&#xff0c;OpenAI将采取额外措施阻止来自不支持地区的API流量。这一决定对中国地区的开发者和企业意味…

GO sync包——读写锁

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

OpenCV-Python——环境配置及图像基本操作

一、环境配置 1、在虚拟环境中安装以下库&#xff1a; opencv-python3.4.1.15 opencv-contrib-python3.4.1.15 建议安装3.4.1的版本&#xff0c;3.4.2之后的版本做了专利保护&#xff0c;有的功能不能实现。 虚拟环境的安装以及安装库的问题见PyQt5专栏中的第一节。 2、安…

Pytest+Allure+Yaml+Jenkins+Gitlab接口自动化中Jenkins配置

一、背景 Jenkins&#xff08;本地宿主机搭建&#xff09; 拉取GitLab(服务器)代码到在Jenkins工作空间本地运行并生成Allure测试报告 二、框架改动点 框架主运行程序需要先注释掉运行代码&#xff08;可不改&#xff0c;如果运行报allure找不到就直接注释掉&#xff09; …

使用命令行创建uniapp+TS项目,使用vscode编辑器

一:如果没有pnpm,先安装pnpm 二:使用npx工具和degit工具从 GitHub 上的 dcloudio/uni-preset-vue 仓库克隆一个名为 vite-ts 的分支,到项目中. 执行完上面命令后,去manifest.json添加appid(自己微信小程序的Id),也可不执行直接下一步,执行pnpm install ,再执行pnpm:dev:mp-weix…

BW:CP里添加信息对象小问题记录

之前做视图直接添加进CP里&#xff0c;以为不能直接往CP里加信息对象&#xff0c;还专门建了一个带信息对象的模型&#xff0c;把信息对象拖到CP里&#xff0c;然后再链接视图的字段 今天发现原来不用这样&#xff0c;直接加就可以&#xff0c;小记一下 如图直接诶创建&#x…

虚拟化 之八 详解构造带有 jailhouse 的 openEuler 发行版(ARM 飞腾派)

基本环境 嵌入式平台下,由于资源的限制,通常不具备通用性的 Linux 发行版,各大主流厂商都会提供自己的 Linux 发行版。这个发行版通常是基于某个 Linux 发行版构建系统来构建的,而不是全部手动构建,目前主流的 Linux 发行版构建系统是 Linux 基金会开发的 Yocto 构建系统。…

Revit插件|建模助手「综合模块」30+个功能全面免费

噢~我的伙计们&#xff0c;快来猜猜是谁快满三岁了&#xff1f;没错&#xff0c;咱们的得力干将——Revit插件「建模助手」&#xff01;这三年里&#xff0c;【综合】模块就像那个超受欢迎的小兄弟&#xff0c;帮了大家大忙。说起来都激动&#xff0c;到今年5月底&#xff0c;它…

高考填报志愿的纠结,钱途重要还是兴趣爱好重要?

不管考得好不好&#xff0c;都会纠结怎么填报学校怎么填报专业&#xff0c;毕竟这与未来息息相关&#xff0c;这决定了未来的就业方向。为了好好规划未来&#xff0c;很多人会在此时犹豫不决&#xff0c;不知道到底是结合就业前景来安排好&#xff0c;还是根据个人的喜好来安排…

告别质量烦恼,精益生产培训帮你打造品质传奇!

在企业运营的复杂环境中&#xff0c;我们常常会观察到一种现象&#xff1a;尽管团队全力以赴&#xff0c;但效率和效果却难以达到预期。这时&#xff0c;精益生产&#xff08;Lean Production&#xff09;的理念和方法就显得尤为重要。 精益生产简介 精益生产并非只适用于大…

探索网络爬虫技术:原理、实践与挑战

一、引言 在数字化时代&#xff0c;信息如同潮水般汹涌而来。过去&#xff0c;我们可能依赖书籍、报纸或电视来获取信息&#xff0c;但这些渠道的信息量有限&#xff0c;而且筛选过的信息未必能满足我们的需求。如今&#xff0c;互联网为我们提供了海量的信息&#xff0c;但同…

Linux网络-网络层IP协议、数据链路层以太网协议、ARP协议、ICMP协议和NAT技术

文章目录 前言一、IP协议IP报头解析4位版本4位首部长度16位总长度8位生存时间8位协议16位首部校验和32位源IP地址和32位目标IP地址 网段划分子网-局域网子网掩码特殊的IP地址 公网IP地址与私网IP地址运营商路由路由表 数据链路层MAC帧格式局域网通信原理局域网数据碰撞 MTU分片…

AWS云中的VPC启用流日志保存S3(AWS中国云)

问题 需要在AWS中国云中对VPC启用流日志操作。 步骤 创建s3桶 这里设置一个s3桶名&#xff0c;创建即可。如果出现已存在具有相同名称的存储桶错误&#xff0c;就换个桶名再试一试吧。 启用vpc流日志 找到vpc流日志入口操作&#xff0c;如下图&#xff1a; 设置vpc流日志…

基于requests模块爬取网易云歌曲评论并制作热词云图

本实践大作业要求 本次实践大作业主要要求主要包括&#xff1a; 1、选择一个热点或者你感兴趣的主题作为本次爬虫实践作业要完成的任务。 2、为了完成本次任务&#xff0c;需要确定从网上爬取的数据对象与范围。 3、利用python及网络爬虫相关技术实现从网上爬取相应内容数据。 …

JJ-20H型水泥胶砂搅拌机

一、 用途和适用范围 水泥胶砂搅拌机是根据中国建材院水泥所的统一图纸制造&#xff0c;符合我国执行国际强度试验方法&#xff08;ISO679&#xff09;的标准设备。也可代替 JC/T722 用作GB/T17671 水泥胶砂试验方法的搅拌机。并可用作美国标准、欧洲标准、日本标准水泥试验的净…

如何从0构建一款类jest工具

Jest工作原理 Jest 是一个流行的 JavaScript 测试框架&#xff0c;特别适用于 React 项目&#xff0c;但它也可以用来测试任何 JavaScript 代码。Jest 能够执行用 JavaScript 编写的测试文件的原因在于其设计和内部工作原理。下面是 Jest 的工作原理及其内部机制的详细解释&…