el-tree的增删改查的实现

首先上效果图:


image.png

关键代码如下:
1、首先封装成一个tree组件:



<template>
  <div class="left-tree">
    <el-tree
      id="my-tree"
      ref="tree"
      class="tree-view structure-tree"
      :data="treeData"
      highlight-current
      :default-expand-all="treeExpandAll"
      :props="defaultProps"
      check-strictly
      :node-key="treeNodeKey"
      :auto-expand-parent="false"
      :expand-on-click-node="false"
      :filter-node-method="filterNode"
      @node-click="handleNodeClick"
    >
      <span slot-scope="{ node, data }" class="custom-tree-node">
        <i class="el-icon-s-home" v-if="node.level == 1" style="color: #3D98F9;font-size: 16px;"></i>
        <i class="el-icon-folder" v-else style="color: #3D98F9;font-size: 16px;"></i>
        <span class="tooltip">
          <span class="add-f-s-14" :class="{ 'level1': node.level == 1 }">{{ data.name }}</span>
        </span>
        <!-- v-if="node.isCurrent == true" -->
        <div v-show="isHoveredNode(node)" class="hover-operations operation-view">
          <el-tooltip content="增加子级节点" placement="top" effect="dark">
            <i
              class="small-operation-btn el-icon-plus"
              @click.stop="handleAdd(data, node)"
            />
          </el-tooltip>
          <el-tooltip content="编辑当前节点" placement="top" effect="dark">
            <i
              v-show="showCurrentNode(node)"
              class="small-operation-btn el-icon-edit "
              @click.stop="handleEdit(data, node)"
            />
          </el-tooltip>
          <el-tooltip content="删除当前节点" placement="top" effect="dark">
            <i
              v-show="showCurrentNode(node)"
              class="small-operation-btn el-icon-delete"
              @click.stop="handleDelete(data, node)"
            />
          </el-tooltip>
        </div>
      </span>
    </el-tree>
  </div>
</template>

<script>
export default {
  props: {
    // 源数据
    treeData: {
      type: Array,
      default: function() {
        return []
      }
    },

    // 树节点是否默认展开
    treeExpandAll: {
      type: Boolean,
      default: true
    },

    // 树节点唯一标识
    treeNodeKey: {
      type: String,
      default: ''
    },
  },

  data() {
    return {
      defaultProps: {
        children: 'children',
        label: 'name'
      },
      selectItem: {},
      filterText: ""
    }
  },
  watch: {

  },
  methods: {
    isHoveredNode(node) {
      console.log(node, 'node')
      return true;
    },
    // 收藏夹不运行编辑和删除
    showCurrentNode(node) {
      if(node.level == 1 && node.label === '我的收藏夹') return false
      else return true;
    },
    // 添加新增按钮
    handleAdd(data) {
      console.log(data, 'dataaaa')
      this.$emit('addItem', data)
    },

    // 点击删除按钮
    handleDelete(data, node) {
      console.log(data, node, 'data and node')
      this.$emit('deleteItem', data)
    },

    // 点击编辑按钮
    handleEdit(data, node) {
      this.selectItem = data
      this.$emit('editItem', JSON.parse(JSON.stringify(data)))
    },

    // 添加新记录,树形列表回显
    treeAddItem(data) {
      this.$refs.tree.append(data, data.pid)
    },

    // 删除节点
    treeDeleteItem(val) {
      console.log(val, 'val')
      this.$refs.tree.remove(val)
    },

    // 修改记录,树形列表回显
    treeEditItem(val) {
      console.log(val, 'val'); // Object.assign修改后变成val的值  val为最新值。
      Object.assign(this.selectItem, val)
      this.selectItem = {}
    },

    // 查询
    filterNode(value, data) {
      if (!value) return true;
      return data.name.indexOf(value) !== -1;
    },

    // 点击树形节点
    handleNodeClick(data,node) {
      console.log(data, node, 'node and data')
    }

  }
}
</script>
<style lang="scss" scoped>
.structure-tree {
  .el-scrollbar .el-scrollbar__wrap {
    overflow-x: hidden;
  }
  #my-tree .el-tree > .el-tree-node {
    min-width: 100%;
    display: inline-block;
  }
  .el-tree-node__content {
    margin-bottom: 10px;
    position: relative;

    &:hover .hover-operations { // 当此元素被悬停时,显示 .hover-operations
      display: inline-block;
    }
  }
  .tooltip {
    margin-right: 5px;
    font-size: 13px;
    border-radius: 4px;
    box-sizing: border-box;
    white-space: nowrap;
    padding: 4px;
  }
  .operation-view, .hover-operations { // 将 .hover-operations 样式与 .operation-view 合并或单独定义
    display: none; // 默认隐藏
    padding: 0px 5px;
    margin-left: 5px;
    color: #777777;
  }
  .small-operation-btn {
    margin: 0px 3px;
    color: #3D98F9;
  }
}



.left-tree {
  width: calc(20vw - 10px);
  max-width: 700px;
  padding:0;
  height: 100%;
  background: #fff;
  border:1px solid #d2dce5;
  .add-f-s-14 {
    font-size: 14px;
  }
  .level1 {
    font-weight: bold;
  }
  ::v-deep {
    background: none;
    line-height: 32px;
    padding: 0 5px;
    font-size: 14px;
    height: calc(100% - 40px);
    overflow: auto;
    .el-tree-node__expand-icon.expanded {
            transform: rotate(0deg);
            -webkit-transform: rotate(0deg);
            -moz-transform: rotate(0deg);
            -ms-transform: rotate(0deg);
            -o-transform: rotate(0deg);
          }
    .el-tree-node__expand-icon:not(.is-leaf) {
            background: #fafbff;
            position: relative;
            z-index: 99;
          }

          .el-tree-node__expand-icon.is-leaf {
            &:before {
              content: "";
              width: 20px;
              display: inline-block;
            }

            // &:after {
            //   content: "\e785";
            //   color: #ccc;
            // }
          }
    .el-tree-node__content {
      height: 28px;
      -webkit-box-align: start;
      -ms-flex-align: start;
      align-items: flex-start;
      position: relative;
      padding-left: 0 !important;

      span.el-tree-node__expand-icon {
        white-space: nowrap;
        color: #000;
        font-size: 16px;
      }
    }

    .el-tree-node {
      position: relative;
      padding-left: 16px;
      &:before {
        content: "";
        height: 100%;
        width: 1px;
        position: absolute;
        border-width: 1px;
        border-left: 1px dashed #c1c1c1;
        left: 0px;
        top: 0;
      }

      &:after {
        content: "";
        width: 34px;
        height: 20px;
        position: absolute;
        border-width: 1px;
        border-top: 1px dashed #c1c1c1;

        top: 13px;
        left: 0;
      }
    }

    .el-icon-caret-right {
      padding-right: 0;
      &:before {
        content: "\e723";
        margin-right: 5px;
        color: #3D98F9;
      }
    }

    .expanded.el-icon-caret-right {
      &:before {
        content: "\e722";
        margin-right: 5px;
        color: #3D98F9;
      }
    }
  }
}
</style>
2、封装dialog,用做是curd
<template>
  <div>
    <el-dialog
      :title="title"
      :visible.sync="dialogVisible"
      width="50%"
    >
      <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="demo-form">
        <el-form-item label="名称" prop="name">
          <el-input v-model="form.name" />
        </el-form-item>
        <el-form-item label="描述" prop="desc">
          <el-input v-model="form.desc" resize="none" type="textarea" />
        </el-form-item>
        <el-form-item>
          <el-button class="pull-right" type="primary" @click="submitForm()">确定</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>
</template>
<script>
export default {
  data() {
    return {
      dialogVisible: false,
      title: '',
      form: {
        name: '',
        desc: ''
      },
      rules: {
        name: [
          { required: true, message: '请输入名称', trigger: 'blur' }
        ],
        desc: [
          { required: false, message: '请输入描述', trigger: 'blur' }
        ]
      },
      isEdit: false,
      pid: '',
      data: {}
    }
  },
  methods: {
    /**
     * @description 打开弹窗
     * @param { Boolean } isEdit 是否是修改状态 true 修改 / false 新增
     * @param { String } pid 父级id,新增时默认选中父级时使用
     * @param { Object } data 表单数据,编辑时使用
     */
    openDialog(isEdit, pid, data) {
      console.log(isEdit, pid, data, 'isEdit, pid, data')
      this.isEdit = isEdit
      this.pid = pid
      this.data = data
      this.title = isEdit ? '编辑' : '新增'
      this.initFormData()
      this.dialogVisible = true
      if (this.isEdit) {
        this.$nextTick(() => {
          this.form = data; //编辑时赋值
        })
      }
    },

    // 初始化表单数据
    initFormData() {
      this.form = {
        name: '',
        desc: ''
      }
    },

    // 提交表单
    submitForm() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          console.log(this.form, 'this.form')
          const formData = JSON.parse(JSON.stringify(this.form))
          console.log(formData, 'formData')
          if (this.isEdit) {
            this.$emit('editData', formData)
          } else {
            // 设置新创建节点的父级编号
            formData.pid = this.pid ? this.pid : ''
            // 随机生成id(仅为前端模拟使用,正常场景应为后台生成)
            formData.id = Math.random()
            this.$emit('addData', formData)
          }
          this.closeDialog()
        }
      })
    },

    // 关闭当前弹窗
    closeDialog() {
      this.$refs.form.resetFields()
      this.dialogVisible = false
    }
  }
}
</script>
<style scoped>
.pull-right {
  float: right
}
</style>
3、然后做引用:
<template>
  <div>
    <Tree ref="customTree"
      :tree-data="treeData"
      :tree-expand-all="treeExpandAll"
      :tree-node-key="treeNodeKey"
      @addItem="addTreeItem"
      @deleteItem="deleteTreeItem"
      @editItem="editTreeItem"
    />
    <PlaceDialog
      ref="placeDialog"
      @addData="addData"
      @editData="editData"
    />
  </div>
</template>

<script>
import Tree from '@/apps/xzbg/views/gwsc/tree.vue'
import PlaceDialog from '@/apps/xzbg/views/gwsc/placeDialog.vue'
export default {
  components: {
    Tree,
    PlaceDialog,
  },
  data() {
    return {
      treeData: [],
      treeExpandAll: true,
      treeNodeKey: 'id'
    };
  },
  created() {
    this.initTreeData()
  },
  methods: {
    // 初始化列表
    initTreeData() {
      // this.treeData = [
      //   {
      //     children: [
      //       {
      //         children: [],
      //         name: '1楼',
      //         desc: '这是教学楼1楼',
      //         parentId: '1',
      //         id: '2'
      //       },
      //       {
      //         children: [],
      //         name: '2楼',
      //         desc: '这是教学楼1楼',
      //         parentId: '1',
      //         id: '3'
      //       },
      //       {
      //         children: [],
      //         name: '3楼',
      //         desc: '这是教学楼3楼',
      //         parentId: '1',
      //         id: '4'
      //       }
      //     ],
      //     name: '教学楼',
      //     parentId: '',
      //     id: '1'
      //   },
      //   {
      //     children: [
      //       {
      //         children: [],
      //         name: '办公1楼',
      //         desc: '这是办公楼',
      //         parentId: '5',
      //         id: '6'
      //       }
      //     ],
      //     name: '办公楼',
      //     parentId: '',
      //     id: '5'
      //   }
      // ]
      this.treeData = [
          {
          "children": [
              {
                  "children": [
                      {
                          "children": [],
                          "gwCount": "0",
                          "id": "88f62562122d4efab64273313090773d",
                          "userId": "2b9689b34dbd405b88debcbb00664327",
                          "pid": "82ead47871ad4fb1bc313402fc29e52d",
                          "name": "制度方法"
                      }
                  ],
                  "gwCount": "1",
                  "id": "82ead47871ad4fb1bc313402fc29e52d",
                  "userId": "2b9689b34dbd405b88debcbb00664327",
                  "pid": "0",
                  "name": "数据管理"
              },
              {
                  "children": [],
                  "gwCount": "0",
                  "id": "d956dba0871441f49004ff12a768cb99",
                  "userId": "2b9689b34dbd405b88debcbb00664327",
                  "pid": "0",
                  "name": "总局来文"
              }
          ],
          "name": "我的收藏夹",
          "pid": "",
          "id": "0"
      }
      ]
    },

    // 添加新记录
    addNewRecord() {
      this.$refs.placeDialog.openDialog(false)
    },

    // 新增表单数据
    addData(data) {
      // 新增树节点
      this.$refs.customTree.treeAddItem(data)
    },

    // 修改表单数据
    editData(data) {
      // 修改树节点
      this.$refs.customTree.treeEditItem(data)
    },

    // 增加树节点
    addTreeItem(data) {
      console.log(data, 'data')
      // 打开地点弹窗,设置默认父级节点
      this.$refs.placeDialog.openDialog(false, data.id)
    },

    // 删除树节点
    deleteTreeItem(data) {
      this.$confirm('确定删除吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 删除树节点
        this.$refs.customTree.treeDeleteItem(data)
        // 提示
        this.$message({
          type: 'success',
          message: '删除成功!'
        })
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        })
      })
    },

    // 修改树节点
    editTreeItem(data) {
      console.log(data, 'editTreeItem')
      // 打开地点编辑弹窗
      this.$refs.placeDialog.openDialog(true, data.pid, data)
    }
  },
};
</script>

<style scoped>
</style>
所用到的数据格式如下:
this.treeData = [
          {
          "children": [
              {
                  "children": [
                      {
                          "children": [],
                          "gwCount": "0",
                          "id": "88f62562122d4efab64273313090773d",
                          "userId": "2b9689b34dbd405b88debcbb00664327",
                          "pid": "82ead47871ad4fb1bc313402fc29e52d",
                          "name": "制度方法"
                      }
                  ],
                  "gwCount": "1",
                  "id": "82ead47871ad4fb1bc313402fc29e52d",
                  "userId": "2b9689b34dbd405b88debcbb00664327",
                  "pid": "0",
                  "name": "数据管理"
              },
              {
                  "children": [],
                  "gwCount": "0",
                  "id": "d956dba0871441f49004ff12a768cb99",
                  "userId": "2b9689b34dbd405b88debcbb00664327",
                  "pid": "0",
                  "name": "总局来文"
              }
          ],
          "name": "我的收藏夹",
          "pid": "",
          "id": "0"
      }
      ]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容