element树形结构新建编辑节点组件

el-tree 只要数据结构对就能直接用的组件 

功能:

1.可新建最外层一级节点

2.可编辑删除每一级节点

3.可在每一级节点下新增子节点(添加下级)

4.可拖动排序,但只能平级拖动(为了防止拖动乱了的情况)

5.可搜索

6.初始化折叠状态,逐个打开编辑后,保持当前节点打开状态

效果如图:

(附赠数据格式)

代码如下:

父组件引用:

<template>
    <div class="view-tree">
      <!-- 树组件 -->
      <leftTree
        :treeData="treeData"
        @handleTreeClick="handleTreeClick"
      ></leftTree>
    </div>

</template>

<script>
import { treeListApi } from '@/http/thematicLibrary';
import leftTree from './components/leftTree';
export default {
  components: {
    leftTree,
  },
  data() {
    return {
      treeData: [], // 左侧菜单树信息
      orgId: '', //组织机构id
      departTitle: '' //左侧状态选择的值
    };
  },
  created() {
    this.getlistList(); //获取目录
  },
  methods: {
    //获取目录
    getlistList() {
      treeListApi().then(res => {
        if (res.data.code === 200) {
          this.treeData = res.data.data;
          let route = this.$route.query.columnId;
          if (route) {
            this.orgId = route;
          } else {
            if (this.treeData.length > 0) {
              this.orgId = this.treeData[0].id;
            }
          }
        }
      });
    },
    // 树点击事件
    handleTreeClick(data) {
      this.orgId = data.id;
      this.departTitle = data.name;
    }
  }
};
</script>

树子组件:

<template>
  <div class="tree-box">
    <el-input
      v-model.trim="searchValue"
      class="input-search"
      placeholder="搜索"
      clearable
    />
    <div class="group-wrap">
      <el-button
        type="info"
        size="small"
        @click="addEditFn('新建一级节点', null)"
      >
        <i class="iconfont icon_a-zu66381"></i>
        新建一级节点</el-button
      >
    </div>
    <el-tree
      ref="tree"
      class="filter-tree"
      :data="filterList"
      :props="defaultProps"
      :check-strictly="true"
      :default-expand-all="expand"
      :filter-node-method="filterNode"
      node-key="id"
      :check-on-click-node="true"
      :current-node-key="currentValue"
      :expand-on-click-node="false"
      :highlight-current="true"
      @node-click="handleNodeClick"
      :default-expanded-keys="keys"
      draggable
      @node-drop="handleDrop"
      :allow-drop="allowDrop"
    >
      <span class="ltree-content-item" slot-scope="{ data }">
        <span class="ltree-content-name">{{ data.name }}</span>
        <el-popover
          placement="bottom"
          popper-class="thematic_popper"
          trigger="click"
          @show="handleStatusWarn(data.id)"
          v-has="'labelManagement/labelEdit'"
        >
          <ul class="popover-wrap" v-if="popId == data.id">
            <li @click="addEditFn('编辑', data)">编辑</li>
            <li @click="deleteFn(data.id)" v-if="data.pid !== '-1'">删除</li>
            <li @click="addEditFn('添加下级', data)">添加下级</li>
          </ul>
          <div slot="reference">
            <i
              slot="reference"
              class="icon_a-lujing4703 iconfont ltree-content-icon"
            ></i>
          </div>
        </el-popover>
      </span>
    </el-tree>
    <!-- 弹窗 -->
    <el-dialog
      :title="title"
      :visible.sync="dialogVisible"
      width="520px"
      @open="openTagFn"
      @close="closeTagFn"
    >
      <el-form
        :model="ruleForm"
        :rules="rules"
        ref="ruleForm"
        label-width="100px"
        class="search-form"
      >
        <el-form-item
          v-if="typeTitle == '添加下级' || ruleForm.pid != ''"
          label="所属上级"
          prop="pName"
          class="work-item-label"
        >
          <el-input v-model="ruleForm.pName" disabled></el-input>
        </el-form-item>
        <el-form-item label="节点名称" prop="name" class="work-item-label">
          <el-input
            v-model="ruleForm.name"
            maxlength="20"
            placeholder="请输入节点名称"
          ></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button size="mini" type="default" @click="dialogVisible = false"
          >取 消</el-button
        >
        <el-button size="mini" type="info" @click.prevent="submit('ruleForm')"
          >确 定</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>

<script>
import {
  treeListApi,
  addApi,
  editApi,
  delApi,
  detailApi,
  updateSortApi
} from '@/http/thematicLibrary';
import CircularJSON from 'circular-json';
export default {
  props: {
    treeData: Array
  },
  data() {
    return {
      keys: [],
      defaultProps: {
        children: 'children',
        label: 'name',
        value: 'id'
      },
      popId: '',
      expand: false, //默认展开
      searchValue: '', //搜索的
      currentValue: '', //高亮的
      filterList: [],
      title: '', //弹窗标题
      dialogVisible: false,
      pidList: [], //新建一级节点列表
      ruleForm: {
        name: '',
        id: '',
        level: '',
        pName: '', //父名称
        pid: ''
      },
      typeTitle: '', //弹窗标题区别
      typeFlag: 'add', //编辑还是删除区别
      rules: {
        name: [{ required: true, message: '请输入节点名称', trigger: 'blur' }]
      }
    };
  },
  watch: {
    treeData(val) {
      this.$nextTick(() => {
        this.filterList = val;
        if (this.filterList.length > 0) {
          console.log(this.$route.query.columnId, 'columnId');
          console.log(this.$route.query.departName, 'departName');
          this.currentValue = this.$route.query.columnId
            ? this.$route.query.columnId
            : this.filterList[0].id;
          this.$nextTick(() => {
            this.$refs.tree.setCurrentKey(this.currentValue); //一定要加这个选中了否则样式没有出来
          });
        }
      }, 500);
    },
    searchValue(val) {
      this.$refs.tree.filter(val);
      this.getFilterData();
    }
  },
  mounted() {
    this.$nextTick(() => {
      setTimeout(() => {
        if (this.treeData.length > 0) {
          this.currentValue = this.$route.query.columnId
            ? this.$route.query.columnId
            : this.treeData[0].id;

          this.$refs.tree.setCurrentKey(this.currentValue);
        }
      }, 500);
    });
  },
  methods: {
    //拖动后的回调函数
    handleDrop(draggingNode, dropNode, dropType, ev) {
      let params = [];
      if (draggingNode.data.pid == '-1') {
        params = this.filterList;
      } else {
        this.filterList.map((v) => {
          abc(v);
        });
      }
      //循环
      function abc(item) {
        if (item.id == draggingNode.data.pid) {
          params = item.children;
        } else {
          item.children.map((val) => {
            abc(val);
          });
        }
      }
      updateSortApi({ thematicLibraryUpdateDTOList: params }).then((res) => {
        if (res.data.code == 200) {
          this.$message({
            type: 'success',
            message: '排序成功!'
          });
          this.getList();
          this.expand = false;
        }
      });
    },
    //哪些不能拖动
    allowDrop(draggingNode, dropNode, type) {
      if (draggingNode.data.pid == dropNode.data.pid && type != 'inner') {
        return true;
      } else {
        return false;
      }
    },
    //获取列表树
    getList() {
      treeListApi().then((res) => {
        if (res.data.code === 200) {
          this.filterList = res.data.data;
          setTimeout(() => {
            let route = this.$route.query.columnId;
            this.currentValue = route;
            this.$refs.tree.setCurrentKey(this.currentValue);
          }, 500);
        }
      });
    },
    //新增或编辑
    addEditFn(type, data) {
      console.log(data, 'dataaaa');
      this.typeTitle = type;
      this.title = type;
      if (type == '添加下级') {
        this.ruleForm.pName = data.name;
        this.ruleForm.pid = data.id;
        this.ruleForm.level = Number(data.level) + 1;
        this.ruleForm.name = '';
        this.ruleForm.id = '';
      } else if (type == '新建一级节点') {
        this.ruleForm.pName = '';
        this.ruleForm.pid = '';
        this.ruleForm.level = 0;
        this.ruleForm.name = '';
        this.ruleForm.id = '';
      } else {
        if (data.level == 0) {
          this.ruleForm.pName = '';
          this.ruleForm.pid = '';
          this.ruleForm.level = 1;
          this.ruleForm.name = data.name;
          this.ruleForm.id = data.id;
        } else {
          detailApi(data.id).then((res) => {
            if (res.data.code == 200) {
              this.ruleForm.pName = res.data.data.pidName;
              this.ruleForm.pid = res.data.data.pid;
              this.ruleForm.level = Number(data.level) + 1;
              this.ruleForm.name = data.name;
              this.ruleForm.id = data.id;
            }
          });
        }
      }
      this.dialogVisible = true;
    },
    // 提交
    submit(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          let data = {};
          if (this.typeTitle == '添加下级') {
            data = {
              pid: this.ruleForm.pid,
              name: this.ruleForm.name,
              level: this.ruleForm.level
            };
            addApi(data).then((res) => {
              if (res.data.code == 200) {
                this.$message({
                  type: 'success',
                  message: '添加成功!'
                });
                this.getList();
                this.keys = res.data.data
                this.dialogVisible = false;
              }
            });
          } else if (this.typeTitle == '新建一级节点') {
            data = {
              level: '0',
              pid: '-1',
              name: this.ruleForm.name
            };
            addApi(data).then((res) => {
              if (res.data.code == 200) {
                this.$message({
                  type: 'success',
                  message: '添加成功!'
                });
                this.getList();
                this.dialogVisible = false;
              }
            });
          } else {
            data = {
              id: this.ruleForm.id,
              name: this.ruleForm.name
            };
            editApi(data).then((res) => {
              if (res.data.code == 200) {
                this.$message({
                  type: 'success',
                  message: '修改成功!'
                });
                this.getList();
                this.dialogVisible = false;
              }
            });
          }
        } else {
          return false;
        }
      });
    },

    //打开弹窗
    openTagFn() {},
    //关闭
    closeTagFn() {
      this.$refs['ruleForm'].resetFields();
    },
    //关闭其他popover
    handleStatusWarn(id) {
      this.popId = id;
    },
    //点击节点
    handleNodeClick(data) {
      this.popId = data.id;
      if (data.id === this.$route.query.columnId) return;
      this.$emit('handleTreeClick', data);
      this.$router.push({
        query: {
          columnId: data.id,
          departName: data.name
        }
      });
    },
    //删除
    deleteFn(id) {
      this.$confirm('是否删除此条数据?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        delApi(id).then((res) => {
          if (res.data.code === 200) {
            this.$message({
              type: 'success',
              message: '删除成功!'
            });
            this.getList();
          }
        });
      });
    },
    //过滤后的数据
    getFilterData() {
      if (this.searchValue) {
        const rootData = this.$refs.tree.root;
        if (rootData.visible) {
          const childNodesStr = CircularJSON.stringify(rootData.childNodes);
          const childNodes = CircularJSON.parse(childNodesStr);
          this.filterData = this.recursionNodes(childNodes);
          this.handleNodeClick(this.filterData[0]);
          this.currentValue = this.filterData[0].id;
          this.$nextTick(() => {
            this.$refs.tree.setCurrentKey(this.currentValue); //一定要加这个选中了否则样式没有出来
          });
        } else {
          console.log('暂无数据');
        }
      }
    },
    //递归遍历数据
    recursionNodes(childNodes) {
      const nodes = childNodes;
      const result = [];
      for (const item of nodes) {
        if (item.visible) {
          result.push(item.data);
        }
        if (item.childNodes && item.childNodes.length) {
          const tempResult = this.recursionNodes(item.childNodes);
          item.data.children = tempResult;
        }
      }
      return result;
    },
    //搜索
    filterNode(value, data, node) {
      if (!value) return true;
      let parentNode = node.parent,
        labels = [node.label],
        level = 1;
      while (level < node.level) {
        labels = [...labels, parentNode.label];
        parentNode = parentNode.parent;
        level++;
      }
      return labels.some((label) => label.indexOf(value) !== -1);
    }
  }
};
</script>

<style lang="scss" scoped>
.input-search {
  width: 280px;
  margin: 5px 20px 15px 20px;
  /deep/ .el-input__suffix {
    margin-top: -4px;
  }
}
/deep/.el-input__inner {
  background: #f4f6fc;
  border-radius: 6px;
  color: #6b7177;
  border: none;
}

.tree-box {
  width: 100%;
  height: calc(100% - 50px);
  // 添加按钮
  .group-wrap {
    display: block;
    padding: 0 20px !important;
    margin-bottom: 10px;
    box-sizing: border-box;
    /deep/ .el-button {
      height: 30px;
      display: inline-block;
      padding: 0px 11px !important;
      font-size: 12px !important;
      .iconfont {
        font-size: 12px;
        margin: 0 !important;
      }
    }
    /deep/ .el-button:hover {
      opacity: 0.8;
    }
  }
  .el-tree {
    background: #fff;
    min-width: 100%;
    display: inline-block !important;
    box-sizing: border-box;
    padding: 0 10px;
  }
  // 添加下级按钮
  .ltree-content-item {
    // width: 150px;
    height: 40px;
    line-height: 40px;
    padding: 0 28px 0 0px;
    box-sizing: border-box;
    display: flex;
    justify-content: space-between;
    cursor: pointer;
    .ltree-content-name {
      font-size: 13px;
      font-family: Microsoft YaHei;
      font-weight: 400;
      color: #6b7177;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .ltree-content-icon {
      width: 20px;
      color: #6b7177;
      font-size: 10px;
      cursor: pointer;
      display: none;
    }
  }
  .ltree-content-item:hover {
    .ltree-content-name {
      color: #0069ba;
    }
    .ltree-content-icon {
      display: block;
    }
  }
}

::v-deep .el-tree-node__content {
  height: 40px;
  &:hover {
    background: #f4f6fc;
    .el-tree-node__label {
      color: #0069ba;
    }
    .ltree-content-icon {
      display: block;
    }
  }

  .el-tree-node__label {
    color: #6b7177;
    // max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

::v-deep
  .el-tree--highlight-current
  .el-tree-node.is-current
  > .el-tree-node__content {
  background: #f4f6fc;
  .el-tree-node__label {
    color: #0069ba;
  }
  .ltree-content-name {
    color: #0069ba;
  }
  .ltree-content-icon {
    display: block;
  }
}
/deep/ .el-dialog .el-form-item__content {
  margin-left: 10px !important;
  .el-select {
    width: 100%;
  }
}
.popover-wrap {
  width: 70px;
  border-radius: 6px;
  li {
    font-size: 12px;
    font-family: Microsoft YaHei;
    font-weight: 400;
    line-height: 35px;
    color: #6b7177;
    background: #fff;
    cursor: pointer;
    padding-left: 20px;
  }
  li:first-child {
    border-radius: 6px 6px 0 0;
  }
  li:last-child {
    border-radius: 0 0 6px 6px;
  }
  li:hover {
    color: #0069ba;
  }
}
</style>
<style>
.thematic_popper {
  width: 85px !important;
  border: none;
}
.thematic_popper.el-popper {
  margin-top: -2px !important;
}
.el-popper.thematic_popper {
  border: 0px;
  min-width: 70px;
  background: #fff;
  padding: 0;
  border-radius: 6px !important;
  transform: translateX(20px);
}
.thematic_popper .popper__arrow {
  top: -7px !important;
  background: transparent !important;
  border: none !important;
}
.thematic_popper .popper__arrow::after {
  background: transparent !important;
  border: none !important;
}
</style>

 

posted @ 2023-06-12 15:53  如意酱  阅读(79)  评论(0编辑  收藏  举报