二次封装elementUI级联选择器组件(父关联子,子不关联父)

现在有这样的一个需求,需要选中父,子全部勾选,且显示的是勾选中的所有值;如果只勾选子,父不关联在内;如果勾选了所有的子节点,且父节点不选中,默认每个节点下都有人,所以,勾选所有的子,不代表就是选中了父下的直接人员。实现的效果动图如下:

1、创建一个vue文件(包含新增,修改是的回显),如myCascader.vue

<!--
 改装后的级联选择器,父关联子,子不关联父
-->
<template>
    <div>
      <el-cascader
        ref="myCascader"
        :options="options"
        v-model="vop"
        :show-all-levels="false"
        @change="handleChange"
        :props="{children: 'children',label: 'deptname', 
                    value:'id', multiple: true, checkStrictly: true,
                     emitPath: false, expandTrigger:'click' 
                }"
        clearable></el-cascader>
<!--      <input type="text" v-model="vop" size="100" name="rex" style="margin-top:200px">-->
    </div>
</template>
<script>

  export default {
    props: {
      // 级联树数据定义
      options: {
        type: Array,
        required: false,
        default: false
      },
      // 级联选择器一开始绑定值,需回显
      updateValue:{
        type: Array,
        required: false,
        default: false
      }
    },
    data () {
      return {
        // 选中的值
        vop: [],
        // 临时存放的当前选中值,用于后期的点击对比,获得当前节点值
        tmpVop: [],
        // 临时存放的值,用于递归函数给选中值
        tmp: '',
      }
    },
    watch:{
      /** v-model绑定得值 */
      vop: {
        handler(n) {
          // 如果数据发生变化
          if(n){
            this.$emit('childByValue', this.vop)
          }
        },
        deep: true,
        immediate: true
      },
      /**  如果一开始就给级联选择器赋值了 */
      updateValue:{
        handler(n) {
          if(n){
            // 拿到父级节点的值,进行回显
            this.vop = this.updateValue
            this.tmpVop = this.vop
          }
        },
        deep: true,
        immediate: true
      }
    },
    methods: {
      // 选中或取消选中后的赋值
      checkArr (value, options, operation) {
        options.map((x) => {
          if (value === x.id) {
            // 选中value项,并循环该节点下的其他所有子节点选中
            if (x.children) {
              this.checkArrNull(x.children, operation)
            }
          } else if (x.children) {
            this.checkArr(value, x.children, operation)
          }
        })
      },
      checkArrNull (options, operation) {
        options.map((x) => {
          // 如果有子项,则递归,没有则选中
          // 选中当前节点2判断子节点,有则继续递归
          if (operation === 'add') {
            this.tmp = this.tmp + ',' + x.id
          } else if (operation === 'sub') {
            this.tmp = this.tmp.split(',')
            // shanchu zhi
            this.tmp = this.removeValue(x.id, this.tmp)
            this.tmp = this.tmp.join(',')
          }
          if (x.children) {
            this.checkArrNull(x.children, operation)
          }
        })
      },
      // 获得点击change事件时点击节点的值
      valueChange (tmp1, tmp2) {
        for (var i = 0; i < tmp2.length; i++) {
          var obj = tmp2[i]
          var isExist = false
          for (var j = 0; j < tmp1.length; j++) {
            var aj = tmp1[j]
            if (obj === aj) {
              isExist = true
              break
            }
          }
          if (!isExist) {
            return obj
          }
        }
      },
      // 删除数组指定的值的元素
      removeValue (v, arr) {
        let index = arr.indexOf(v)
        if (index !== -1) {
          arr.splice(index, 1)
        }
        return arr
      },
      // 数组去重
      unique (arr) {
        var arr2 = arr.sort()
        var res = [arr2[0]]
        for (var i = 1; i < arr2.length; i++) {
          if (arr2[i] !== res[res.length - 1]) {
            res.push(arr2[i])
          }
        }
        return res
      },
      // 将options的value值按照value生成一组数组
      optionsToarr (options) {
        this.tmp = ''
        options.map((x) => {
          this.tmp = this.tmp + x.id + ','
          if (x.children) {
            this.optionsToarrChild(x.children)
          }
        })
      },
      optionsToarrChild (options) {
        console.log(options,'options')
        options.map((x) => {
          this.tmp = this.tmp + x.id + ','
          if (x.children) {
            this.optionsToarrChild(x.children)
          }
        })
      },
      // change事件
      handleChange (value) {
        // 获得点击变化时的值,然后判断是加值还是减值。根据值去递归
        let valueCh = ''
        // 操作是选中还是取消
        let action = ''
        // 对比获得是选中还是取消操作
        if ((this.vop).length > 0) {
          if ((this.tmpVop).length > (this.vop).length) {
            valueCh = this.valueChange(this.vop, this.tmpVop)
            action = 'sub'
          } else {
            valueCh = this.valueChange(this.tmpVop, this.vop)
            action = 'add'
          }
        }
        if (valueCh) {
          this.tmp = this.vop.join(',')
          this.checkArr(valueCh, this.options, action)
          // 去重
          this.vop = this.unique(this.tmp.split(','))
        }
        // 获得options的value值一维数组,用于排序对照
        this.optionsToarr(this.options)
        if (this.tmp.substring(this.tmp.length - 1) === ',') {
          this.tmp = this.tmp.substring(0, this.tmp.length - 1)
        }
        this.tmp = this.tmp.split(',')
        // 排序
        this.vop.sort((prev, next) => {
          return this.tmp.indexOf(prev) - this.tmp.indexOf(next)
        })
        this.tmpVop = this.vop
      }
    },
    created () {
      // 创建时默认给tmpVop赋值
      this.tmpVop = this.vop
    }
  }
</script>

2、去相应界面引入组件,使用

import myCascader from '@/components/myCascader'
export default {
  components: {userTree,myCascader },
},

新增的时候,不需要回显,如下使用:

<myCascader
  v-model="checkerForm.dept"
  :updateValue="checkerForm.dept"
  v-on:childByValue="childByValue"
  :options="deptList1">
</myCascader>
methods: {
  /** 新增时候级联选择器子传值给父 */
  childByValue(childValue){
      this.checkerForm.dept = childValue
  }, 
},

注:checkerForm.dept为自定义新增的时候,级联选择器选中的值;

       deptList1为级联选择的数据

修改的时候,会有回显的问题,这时候会多一步

<myCascader
  v-model="updateCheckerForm.dept"
  :updateValue="updateCheckerForm.dept"                
  v-on:childByValue="childByValueUpdate"
  :options="deptList1">
</myCascader>
/** 修改时候级联选择子传值给父 */
childByValueUpdate(childValue){
  this.updateCheckerForm.dept = childValue
},

注::updateValue="updateCheckerForm.dept" ,修改时的默认数据,进行回显

新增和修改也可以公用一个组件,加一个三目判断即可。这里为了方便理解,故而分开了。

补充:

// 数据定义

data () {
  return {
    deptList1:[{
      id: 'zhinan',
      deptname: '指南',
      children: [{
        id: 'shejiyuanze',
        deptname: '设计原则',
        checked: true,
        children: [{
          id: 'yizhi',
          deptname: '一致'
        }, {
          id: 'fankui',
          deptname: '反馈'
        }, {
          id: 'xiaolv',
          deptname: '效率'
        }, {
          id: 'kekong',
          deptname: '可控'
        }]
      }, {
        id: 'daohang',
        deptname: '导航',
        children: [{
          id: 'cexiangdaohang',
          deptname: '侧向导航'
        }, {
          id: 'dingbudaohang',
          deptname: '顶部导航'
        }]
      }]
    }, {
      id: 'zujian',
      deptname: '组件',
      children: [{
        id: 'basic',
        deptname: 'Basic',
        children: [{
          id: 'layout',
          deptname: 'Layout 布局'
        }, {
          id: 'color',
          deptname: 'Color 色彩'
        }, {
          id: 'typography',
          deptname: 'Typography 字体'
        }, {
          id: 'icon',
          deptname: 'Icon 图标'
        }, {
          id: 'button',
          deptname: 'Button 按钮'
        }]
      }, {
        id: 'form',
        deptname: 'Form',
        children: [{
          id: 'radio',
          deptname: 'Radio 单选框'
        }, {
          id: 'checkbox',
          deptname: 'Checkbox 多选框'
        }, {
          id: 'input',
          deptname: 'Input 输入框'
        }, {
          id: 'input-number',
          deptname: 'InputNumber 计数器'
        }, {
          id: 'select',
          deptname: 'Select 选择器'
        }, {
          id: 'cascader',
          deptname: 'Cascader 级联选择器'
        }, {
          id: 'switch',
          deptname: 'Switch 开关'
        }, {
          id: 'slider',
          deptname: 'Slider 滑块'
        }, {
          id: 'time-picker',
          deptname: 'TimePicker 时间选择器'
        }, {
          id: 'date-picker',
          deptname: 'DatePicker 日期选择器'
        }, {
          id: 'datetime-picker',
          deptname: 'DateTimePicker 日期时间选择器'
        }, {
          id: 'upload',
          deptname: 'Upload 上传'
        }, {
          id: 'rate',
          deptname: 'Rate 评分'
        }, {
          id: 'form1',
          deptname: 'Form 表单'
        }]
      }, {
        id: 'data',
        label: 'Data',
        children: [{
          id: 'table',
          deptname: 'Table 表格'
        }, {
          id: 'tag',
          deptname: 'Tag 标签'
        }, {
          id: 'progress',
          deptname: 'Progress 进度条'
        }, {
          id: 'tree',
          deptname: 'Tree 树形控件'
        }, {
          id: 'pagination',
          deptname: 'Pagination 分页'
        }, {
          id: 'badge',
          deptname: 'Badge 标记'
        }]
      }, {
        id: 'notice',
        deptname: 'Notice',
        children: [{
          id: 'alert',
          deptname: 'Alert 警告'
        }, {
          id: 'loading',
          deptname: 'Loading 加载'
        }, {
          id: 'message',
          deptname: 'Message 消息提示'
        }, {
          id: 'message-box',
          deptname: 'MessageBox 弹框'
        }, {
          id: 'notification',
          deptname: 'Notification 通知'
        }]
      }, {
        id: 'navigation',
        deptname: 'Navigation',
        children: [{
          id: 'menu',
          deptname: 'NavMenu 导航菜单'
        }, {
          id: 'tabs',
          deptname: 'Tabs 标签页'
        }, {
          id: 'breadcrumb',
          deptname: 'Breadcrumb 面包屑'
        }, {
          id: 'dropdown',
          deptname: 'Dropdown 下拉菜单'
        }, {
          id: 'steps',
          deptname: 'Steps 步骤条'
        }]
      }, {
        id: 'others',
        deptname: 'Others',
        children: [{
          id: 'dialog',
          deptname: 'Dialog 对话框'
        }, {
          id: 'tooltip',
          deptname: 'Tooltip 文字提示'
        }, {
          id: 'popover',
          deptname: 'Popover 弹出框'
        }, {
          id: 'card',
          deptname: 'Card 卡片'
        }, {
          id: 'carousel',
          deptname: 'Carousel 走马灯'
        }, {
          id: 'collapse',
          deptname: 'Collapse 折叠面板'
        }]
      }]
    }, {
      id: 'ziyuan',
      deptname: '资源',
      children: [{
        id: 'axure',
        deptname: 'Axure Components'
      }, {
        id: 'sketch',
        deptname: 'Sketch Templates'
      }, {
        id: 'jiaohu',
        deptname: '组件交互文档'
      }]
    }],
      checkerForm: {
        dept:[],
      },
      updateCheckerForm:{
        account:[]
      },
    }
  }
},
element ui二次封装是指对element ui框架进行进一步封装和定制,以适应具体项目的需求和开发规范。这样可以提高开发效率和代码的可维护性。 element ui二次封装的优点包括: 1. 提高开发效率:通过封装常用的组件和功能,可以减少开发人员的重复工作,简化开发流程。 2. 统一规范:通过封装和定制,可以使项目中的UI组件保持一致的样式和行为,提升用户体验。 3. 提高代码可维护性:通过封装和抽象,可以隐藏底层细节,使代码更易读、易维护。 然而,element ui二次封装也存在一些缺点,包括: 1. 封装过程较难:封装一个复杂的组件需要对element ui的源码和API有一定的了解,需要花费一定的时间和精力。 2. 封装后的页面较为单一:封装组件往往只适用于特定的场景,不太适合用于具有变化多、逻辑复杂的页面的系统。 3. 代码可读性较差:如果在封装过程中不写注释或者命名不规范,封装后的代码可读性会下降。 关于element ui二次封装的过程和原理,一般可以按照以下步骤进行: 1. 定义需求:明确项目需要封装哪些组件或功能,并进行规划。 2. 学习和研究element ui:熟悉element ui框架的源码和API,了解其组件的结构和使用方法。 3. 封装组件:根据需求,将element ui的组件进行二次封装,可以通过继承、混入或者自定义指令等方式进行封装。 4. 添加额外功能:根据项目需求,可以添加一些额外的功能或者样式,以满足具体的项目要求。 5. 测试和调试:进行组件的测试和调试,确保封装后的组件能够正常运行和符合预期效果。 6. 文档和示例:编写组件的使用文档和示例代码,方便其他开发人员使用和理解。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值