Element-ui 中树形控件 el-tree 结合 el-input 或 el- cascader实现增删改功能

实现功能主要有双击修改节点、增加父节点、增加子节点删除节点以及保存时的非空校验。

注意:图中仅支持四级,且el-cascader的指标和名称不能在同一级。写本文仅为记录,仅供参考,如有不对的地方,欢迎指点。

      增加节点和删除节点可以用el-tree现有的方法,但是需要指定node-key

// 删除节点,需要指定node-key
this.$refs.tree.remove(node) 

// data表示要追加的子节点的数据  
// parentNode表示子节点的 parent 的 data、key 或者 node,指定node-key时三者都可以,没有指定只能使用node
this.$refs.tree.append(data, parentNode) 

实现代码:

<template>
 <div>
     <el-form :model="form" ref="form" :rules="rules">
      <div class="overflow-auto pe-4" style="height:300px;">
        <el-tree ref="tree" :data="rootNodes" :props="{ label: 'name' }" node-key="id" :expand-on-click-node="false" default-expand-all>
          <template slot-scope="{node,data}">
            <div class="w-100 d-flex align-items-center">
              <el-form-item v-if="data.isEdit" :prop="`name_${data.id}`"
                :rules="{ required: true, validator: validateOrg(data.name, ''), trigger: 'folder' }" class="flex-1">
                <el-input v-model="data.name" :ref="data.id" placeholder="请输入名称"
                  @blur="handleInputBlur(node, data)"></el-input>
              </el-form-item>
              <el-form-item v-else-if="data.type === 'indicator'" :prop="`targetIds_${data.id}`"
                :rules="{ type: 'array', required: true, validator: validateOrg(data.targetIds, 'indicator'), trigger: ['blur', 'change'] }"
                class="d-flex flex-1">
                <el-cascader :ref="data.id" v-model="data.targetIds" placeholder="请输入或选择指标(支持模糊查询)"
                  :options="indexOption"
                  :props="{ multiple: true, children: 'targetList', label: 'targetChineseName', value: 'targetId', emitPath: false }"
                  :show-all-levels="false" filterable clearable class="flex-1"></el-cascader>
              </el-form-item>
              <div v-else @dblclick="headerDbClick(data)">{{ node.label }}</div>
              <el-button v-show="!data.isEdit && data.type === 'folder' && node.level < 4" type="text"
                icon="el-icon-plus" :disabled="!!(data.children?.filter(({ type }) => type === 'indicator').length)"
                class="ms-2 font-weight-bold" @click="appendSub(node, data)"></el-button>
              <svg-icon
                v-show="!data.isEdit && data.type === 'folder' && node.level < 4 && !(data.children?.filter(({ type }) => !!type).length)"
                icon-class="gather" class="text-primary ms-2 fs-14 flex-auto" @click="addIndex(node, data)"></svg-icon>
              <i :class="['el-icon-delete-solid text-primary ms-2', data.isEdit || data.type === 'indicator' ? 'margin-15' : '']"
                @click="remove(node, data)"></i>
            </div>
          </template>
        </el-tree>
      </div>
    </el-form>
    <div slot="footer">
      <div style="text-align:left;">
        <el-button type="warning" class="my-2" size="mini" @click="append">添加新节点</el-button>
      </div>
      <div class="text-center">
        <el-button type="primary" @click="confirm"> 保存</el-button>
        <el-button>返回</el-button>
      </div>
    </div>
 </div>
</template>


<script>

export default {
  data () {
    return {
      form: {},
      rules: {},
      rootNodes: [{
        id: 1,
        name: '请输入名称',
        isEdit: false, // 代表是否 编辑
        targetIds: [],
        type: 'folder', // 代表是否是指标 folder名称  indicator-指标
        children: [{
          id: 2,
          name: '请输入名称',
          isEdit: false,
          targetIds: [],
          type: 'folder',
          children: []
        }]
      }],
      id: 3,  // 给el-tree每个节点添加一个不重复的id标识
      indexOption:  [{
          value: 1,
          label: '东南',
          children: [{
            value: 2,
            label: '上海',
            children: [
              { value: 3, label: '普陀' },
              { value: 4, label: '黄埔' },
              { value: 5, label: '徐汇' }
            ]
          }, {
            value: 7,
            label: '江苏',
            children: [
              { value: 8, label: '南京' },
              { value: 9, label: '苏州' },
              { value: 10, label: '无锡' }
            ]
          }, {
            value: 12,
            label: '浙江',
            children: [
              { value: 13, label: '杭州' },
              { value: 14, label: '宁波' },
              { value: 15, label: '嘉兴' }
            ]
          }]
        }, {
          value: 17,
          label: '西北',
          children: [{
            value: 18,
            label: '陕西',
            children: [
              { value: 19, label: '西安' },
              { value: 20, label: '延安' }
            ]
          }, {
            value: 21,
            label: '新疆维吾尔族自治区',
            children: [
              { value: 22, label: '乌鲁木齐' },
              { value: 23, label: '克拉玛依' }
            ]
          }]
        }],
    }
  },
  methods: {
    validateOrg (data, type) {
      // 不知道为什么value为undefined
      return (rule, value, callback) => {
        if (!data?.length) {
          callback(new Error(`${type === 'indicator' ? '请选择指标' : '请输入名称'}`))
        } else {
          callback()
        }
      }
    },

    // 双击编辑
    headerDbClick (data) {
      this.$set(data, 'isEdit', true) // 使用该方法原因防止调接口获取的节点没有isEdit属性
      this.$nextTick(() => {
        this.$refs[data.id] && this.$refs[data.id].focus()
      })
    },

    // 输入框失去焦点时
    handleInputBlur (node, data) {
      if (data.name === '') return
      data.isEdit = false
    },

    // 添加父节点
    append () {
      // 判断当前是否有节点,添加在最后一个节点上
      if (this.rootNodes?.length) {
        // 如果当前节点已经添加了一个空的子节点,就添加不了了
        if (!this.rootNodes[this.rootNodes.length - 1].isEdit) {
          const id = this.id++
          this.$refs.tree.insertAfter({ name: '', isEdit: true, id, targetIds: [], type: 'folder', children: [] }, this.rootNodes[this.rootNodes.length - 1].id)
          this.$nextTick(() => {
            this.$refs[id] && this.$refs[id].focus()
          })
        }
      } else {
        const id = this.id++
        this.rootNodes = [{ name: '', isEdit: true, id, targetIds: [], type: 'folder', children: [] }]
        this.$nextTick(() => {
          this.$refs[id] && this.$refs[id].focus()
        })
      }
    },
    
    // 添加子节点
    appendSub (node, data) {
      const id = this.id++
      this.$refs.tree.append({ name: '', isEdit: true, id, targetIds: [], type: 'folder', children: [] }, node)
      this.$nextTick(() => {
        this.$refs[id] && this.$refs[id].focus()
      })
    },
    
    // 删除
    remove (node, data) {
      this.$confirm(`当前您正在执行${data.children?.length ? '级联' : ''}删除操作!`, '提示', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$refs.tree.remove(node)
      }).catch(() => null)
    },

    // 添加指标,使用级联组件el-cascader
    addIndex (node, data) {
      const id = this.id++
      this.$refs.tree.append({ name: '', isEdit: false, id, targetIds: [], type: 'indicator' }, node)
    },
    confirm () {
        this.$refs.form.validate((valid) => {
          if (valid) {
             this.$message.success('成功')
          } else {
            this.$message.warning('失败')
          }
        })  
    },
  },
}
</script>

<style lang="scss" scoped>

::v-deep .el-tree-node__content {
  height: auto;
  padding: 5px 0;
}

::v-deep .el-form-item__content {
  display: flex;
  flex: 1 0 0px !important;
}

.margin-15 {
  margin-top: -15px
}
</style>

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值