美文网首页
树形菜单维护 vue+ts+element

树形菜单维护 vue+ts+element

作者: 蓝海00 | 来源:发表于2020-03-26 17:03 被阅读0次

本篇博客只分享代码

<template>
  <div class="tree">
    <h1 class="main_app_title">系统菜单维护</h1>
    <el-row class="header_row">
      <el-col :span="12">
        <el-input placeholder="输入关键字进行过滤" v-model="filterText">
          <i slot="prefix" class="el-input__icon el-icon-search"></i>
        </el-input>
      </el-col>
      <el-col :span="12">
        <lhBtn
          size="mini"
          shading
          :otherStyle="{'box-shadow':'0px 6px 12px 0px rgba(253,114,24,0.58)','background':'#FE5D18','margin-right':'20px'}"
          @click.native="configUpdate"
          v-show="updateTree"
        >确认更新</lhBtn>
        <lhBtn
          size="mini"
          shading
          :otherStyle="{'box-shadow':'0px 6px 12px 0px rgba(253,147,24,0.58)'}"
          @click.native="append(false,data)"
        >添加主菜单</lhBtn>
      </el-col>
    </el-row>
    <el-tree
      :data="data"
      node-key="id"
      default-expand-all
      draggable
      ref="tree"
      :expand-on-click-node="false"
      @node-drag-enter="handleDragEnter"
      :filter-node-method="filterNode"
      :allow-drop="allowDrop"
      @node-drop="handleDropFinish"
    >
      <span class="custom-tree-node" slot-scope="{ node, data }">
        <span>
          <icon-svg :icon-class="data.meta.icon | GetOfString" class="icon_location" />
          {{ data.meta.title }}
        </span>
        <span>
          <el-button type="text" v-if="data.children" class="add" size="mini" @click="() => append(true,data)"><icon-svg icon-class="RectangleCopy20" size="18" color="#CACACA" /></el-button>
          <el-button type="text" size="mini" class="update" @click="() => update(node,data)"><icon-svg icon-class="weibiaoti2010104" size="13" color="#CACACA" /></el-button></el-button>
          <el-button type="text" v-if="node.parent.childNodes.length>1" size="mini" class="remove" @click="() => remove(node, data)"><icon-svg icon-class="RectangleCopy41" size="18" color="#CACACA" /></el-button></el-button>
        </span>
      </span>
    </el-tree>
    <el-dialog
      :title="appendOrUpdate?'添加':'修改'"
      :visible.sync="treeDialogVisible"
      :modal-append-to-body="false"
      width="55.46%"
      top="20.83vh"
    >
      <el-form
        label-position="top"
        label-width="80px"
        :rules="formTreeRules"
        ref="formTree"
        :model="formTree"
        class="treeForm"
        :hide-required-asterisk="true"
      >
        <el-form-item prop="path" label="path">
          <el-input v-model="formTree.path" placeholder="URL"></el-input>
        </el-form-item>
        <el-form-item prop="componentF" label="component(父级架构组件路径)" v-if="isFatherTree">
          <el-input v-model="formTree.componentF" placeholder="父级架构组件路径 如:Layout"></el-input>
        </el-form-item>
        <el-form-item prop="redirect" label="redirect" v-if="isFatherTree">
          <el-input v-model="formTree.redirect" placeholder="重定向 对应子级URL"></el-input>
        </el-form-item>
        <el-form-item prop="componentC" label="component(子级视图路径)" v-if="!isFatherUpdate">
          <el-input v-model="formTree.componentC" placeholder="子级视图"></el-input>
        </el-form-item>
        <el-form-item prop="meta" label="meta(填写对应的title及icon,也可新增meta属性)">
          <el-input v-model="formTree.meta"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <lhBtn
          size="small"
          @click.native="submitFormTree('formTree')"
          :otherStyle="{'box-shadow':'0px 6px 12px 0px rgba(253,147,24,0.58)'}"
        >{{appendOrUpdate?'确定添加':'确定修改'}}</lhBtn>
      </span>
    </el-dialog>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator";
@Component({
  filters: {
    GetOfString(value: any) {
      if (!value) return "";
      return value.replace(/"/g, "");
    }
  }
})
export default class Tree extends Vue {
  private id = 10000;
  public fakeRouter = {
    router: [
      {
        path: "",
        component: "Layout",
        redirect: "dashboard",
        id: "1",
        meta: {
          title: "首页",
          icon: "RectangleCopy34"
        },
        children: [
          {
            id: "1",
            path: "dashboard",
            component: "homePage/index",
            meta: {
              title: "首页",
              icon: "RectangleCopy34"
            }
          }
        ]
      },
      {
        path: "/a",
        component: "Layout",
        redirect: "bpmn",
        id: "2",
        meta: {
          title: "bpmn",
          icon: "RectangleCopy56"
        },
        children: [
          {
            path: "bpmn",
            component: "bpmn/showBpmn",
            id: "2",
            meta: {
              title: "bpmn",
              icon: "RectangleCopy56"
            }
          }
        ]
      },
      {
        path: "/example",
        component: "Layout",
        redirect: "icon",
        name: "Example",
        id: "3",
        meta: {
          title: "组件",
          icon: "RectangleCopy25"
        },
        children: [
          {
            path: "icon",
            name: "Icon",
            component: "icon/index",
            id: "4",
            meta: {
              title: "图标库",
              icon: "RectangleCopy37"
            }
          },
          {
            path: "tree",
            name: "Tree",
            component: "tree/index",
            id: "5",
            meta: {
              title: "系统菜单维护",
              icon: "RectangleCopy44"
            }
          },
          {
            path: "upload",
            name: "Upload",
            component: "upload/index",
            id: "6",
            meta: {
              title: "上传组件",
              icon: "RectangleCopy48"
            }
          },
          {
            path: "map",
            name: "Map",
            component: "map/Map",
            id: "7",
            meta: {
              title: "高德地图",
              icon: "dingwei"
            }
          },
          {
            path: "effects",
            name: "effects",
            component: "effects/index",
            id: "8",
            meta: {
              title: "效果组件",
              icon: "RectangleCopy18"
            }
          }
        ]
      }
    ]
  };
  public data = JSON.parse(JSON.stringify(this.fakeRouter)).router;
  // 搜索
  private filterText = "";
  @Watch("filterText")
  private filterTextWatch(val: any) {
    (this.$refs as any).tree.filter(val);
  }
  // 过滤
  private filterNode(value: any, data: any) {
    if (!value) return true;
    return data.meta.title.indexOf(value) !== -1;
  }
  // 添加修改弹框
  private treeDialogVisible = false;
  private num = 0;
  @Watch("treeDialogVisible")
  private treeDialog(val: boolean) {
    if (val) {
      if (this.num) {
        (this.$refs["formTree"] as any).resetFields();
      }
      this.num = 1;
    }
    if (!val) {
      this.isFatherUpdate = false;
    }
  }
  // 新增or修改表单
  private formTree: any = {
    path: "",
    componentF: "Layout",
    redirect: "",
    componentC: "",
    meta: `{"title": "","icon": ""}`,
    id: ""
  };
  private formTreeRules = {
    path: [{ required: true, message: "请输入path", trigger: "blur" }],
    componentF: [
      { required: true, message: "请输入componentF", trigger: "blur" }
    ],
    redirect: [{ required: true, message: "请输入redirect", trigger: "blur" }],
    componentC: [
      { required: true, message: "请输入componentC", trigger: "blur" }
    ],
    meta: [
      {
        validator: (rule: any, value: any, callback: any) => {
          if (value === "" || value === `{"title": "","icon": ""}`) {
            callback(new Error("请输入meta"));
          } else {
            callback();
          }
        },
        trigger: "blur"
      }
    ]
  };
  // 新增or修改对话框 true新增 false修改
  private appendOrUpdate = true;
  // 父级更新
  private isFatherUpdate = false;
  // 更新拖拽
  private updateTree = false;
  // 拖拽完成
  private handleDropFinish(
    draggingNode: any,
    dropNode: any,
    dropType: any,
    ev: any
  ) {
    this.updateTree = true;
  }
  // 确定更新 发送请求
  private configUpdate() {
    // 发送请求
    this.updateTree = false;
  }
  // 父级新增或子级新增
  private isFatherTree = true;
  private childrenData: any = "";
  // 拖拽越级处理
  private allowDrop(draggingNode: any, dropNode: any, type: any) {
    if (draggingNode.level === dropNode.level) {
      if (
        dropNode.parent.data.children &&
        dropNode.parent.data.children.length > 0 &&
        draggingNode.parent.__ob__.value.data.children.length > 1
      ) {
        return type === "prev";
      } else if (draggingNode.parent.id === dropNode.parent.id) {
        return type === "prev";
      }
    } else {
      // 不同级进行处理
    }
  }
  public append(bool: boolean, data: any) {
    this.isFatherTree = !bool;
    this.childrenData = data;
    this.formTree = JSON.parse(
      JSON.stringify({
        path: "",
        componentF: "Layout",
        redirect: "",
        componentC: "",
        meta: `{"title": "","icon": ""}`,
        id: ""
      })
    );
    this.treeDialogVisible = this.appendOrUpdate = true;
  }
  public remove(node: any, data: any) {
    this.$confirm("您确定要删除当前菜单么?", "温馨提示", {
      confirmButtonText: "确定删除",
      cancelButtonText: "取消",
      center: true,
      customClass: "myConfirm",
      showClose: false
    })
      .then(() => {
        const parent = node.parent;
        const children = parent.data.children || parent.data;
        const index = children.findIndex((d: any) => d.id === data.id);
        // 调用后端请求 // 成功直接将页面中的那一条tree删除即可
        children.splice(index, 1);
        this.$message({
          type: "success",
          message: "删除成功!"
        });
      })
      .catch(() => {
        this.$message({
          type: "info",
          message: "已取消删除"
        });
      });
  }
  // 更新node
  public updateNode: any = "";
  public update(node: any, dataVal: any) {
    this.isFatherTree = dataVal.children ? true : false;
    this.childrenData = dataVal;
    this.appendOrUpdate = false;
    this.updateNode = node;

    if (dataVal.children) {
      // 父级
      this.formTree = JSON.parse(JSON.stringify(this.formTree));
      this.$set(this.formTree, "path", dataVal.path);
      this.$set(this.formTree, "componentF", dataVal.component);
      this.$set(this.formTree, "redirect", dataVal.redirect);
      this.$set(this.formTree, "id", dataVal.id);
      this.$set(this.formTree, "meta", JSON.stringify(dataVal.meta));
      this.isFatherUpdate = true;
    } else {
      // 子级
      this.formTree = JSON.parse(JSON.stringify(this.formTree));
      this.$set(this.formTree, "path", dataVal.path);
      this.$set(this.formTree, "componentC", dataVal.component);
      this.$set(this.formTree, "id", dataVal.id);
      this.$set(this.formTree, "meta", JSON.stringify(dataVal.meta));
    }
    this.treeDialogVisible = true;
  }
  // 处理tree
  private submitFormTree(formName: any) {
    // 新增
    if (this.appendOrUpdate) {
      (this.$refs[formName] as any).validate((valid: boolean) => {
        if (valid) {
          if (!this.isFatherTree) {
            // 子级
            this.childrenAdd(this.childrenData);
          } else {
            // 父级
            this.FatherAdd();
          }
        } else {
          return false;
        }
      });
      return;
    }
    // 更新
    const parent = this.updateNode.parent;
    const children = parent.data.children || parent.data;
    const index = children.findIndex((d: any) => d.id === this.formTree.id);
    if (this.isFatherUpdate) {
      // 父级更新
      console.log("父级更新", this.formTree, children[index]);
      // 调用后端请求 // 成功直接将页面中的那一条tree更新即可
      children[index].path = this.formTree.path;
      children[index].component = this.formTree.componentF;
      children[index].redirect = this.formTree.redirect;
      children[index].meta = JSON.parse(this.formTree.meta);
    } else {
      // 子级更新
      console.log("子级更新", this.formTree, children[index]);
      children[index].path = this.formTree.path;
      children[index].component = this.formTree.componentC;
      children[index].meta = JSON.parse(this.formTree.meta);
    }
    this.treeDialogVisible = false;
  }
  // 子级添加
  private childrenAdd(data: any) {
    const { path, componentC, meta } = this.formTree;
    // 发起后端请求 拿到成功相应后直接push数组即可 减少请求
    // 注意:要和后端沟通 需要返回当前这条数据的id push的时候一定要带上id
    data.children.push({
      path,
      id: this.id++, // 后端会自动生成
      component: componentC,
      meta: JSON.parse(meta)
    });
    this.treeDialogVisible = false;
  }
  // 父级添加
  private FatherAdd() {
    const { path, componentC, meta, componentF, redirect } = this.formTree;
    // 发起后端请求 拿到成功相应后直接push数组即可 减少请求
    // 注意:要和后端沟通 需要返回当前这条数据的id push的时候一定要带上id
    this.data.push({
      path,
      component: componentF,
      redirect,
      id: this.id++, // 后端会自动生成
      meta: JSON.parse(meta),
      children: [
        {
          path: redirect,
          component: componentC,
          id: this.id++, // 后端会自动生成
          meta: JSON.parse(meta)
        }
      ]
    });
    this.treeDialogVisible = false;
  }
  public handleDragEnter(draggingNode: any, dropNode: any, ev: any) {
    // console.log("tree drag enter: ", draggingNode.store.data);
  }
}
</script>

<style lang="scss" scoped>
.custom-tree-node {
  flex: 1;
  display: flex;
  text-align: left;
  justify-content: space-between;
  font-size: 14px;
  padding-right: 8px;
  .icon_location {
    position: relative;
    top: 4px;
  }
}
.tree {
  box-sizing: border-box;
  padding: 12px 20px 0;
  position: relative;
  .header_row {
    margin-bottom: 12px;
    ::v-deep {
      .el-col-12 {
        &:first-child {
          .el-input {
            width: 300px;
          }
          .el-input__inner {
            height: 28px;
            border-radius: 4px;
            font-size: 12px;
            &:focus {
              border-color: #fe9817;
              outline: 0;
            }
          }
          .el-input__icon {
            line-height: 28px;
          }
        }
        &:last-child {
          text-align: right;
        }
      }
    }
  }
  ::v-deep {
    .el-tree {
      overflow: hidden;
      > div {
        position: relative;
        .el-tree-node__content {
          height: 36px;
          background: #edeef3;
          border-radius: 6px;
          line-height: 36px;
          margin-bottom: 10px;
          font-size: 14px;
          font-weight: 500;
          color: rgba(62, 62, 62, 1);
          &:hover {
            background: #e8eaf0;
          }
        }
        &::before {
          content: "";
          width: 1px;
          height: calc(100% - 62px);
          background-color: #e8eaf0;
          position: absolute;
          display: inline-block;
          left: 12px;
          top: 36px;
        }
        .el-tree-node__children {
          > div[role="treeitem"] {
            position: relative;
            .el-tree-node__content {
              height: 36px;
              background: #f4f5f8;
              border-radius: 6px;
              line-height: 36px;
              margin-bottom: 8px;
              width: calc(100% - 24px);
              position: relative;
              right: -24px;
              padding-left: 0px !important;
              font-size: 14px;
              font-weight: 400;
              color: rgba(62, 62, 62, 1);
              &:hover {
                background: #e8eaf0;
              }
            }
            .el-tree-node__children {
              .el-tree-node__content {
                right: -48px;
                width: calc(100% - 48px);
                position: relative;
                background: #f9fafb;
                &:hover {
                  background: #e8eaf0;
                }
                &::before {
                  content: "";
                  width: 36px;
                  height: 1px;
                  background-color: #e8eaf0;
                  position: absolute;
                  display: inline-block;
                  left: -36px;
                  top: 17px;
                }
              }
            }
            &::before {
              content: "";
              width: 12px;
              height: 1px;
              background-color: #e8eaf0;
              position: absolute;
              display: inline-block;
              left: 13px;
              top: 17px;
            }
          }
        }
      }
      .el-button {
        width: 24px;
        height: 24px;
        background: rgba(255, 255, 255, 1);
        border-radius: 50%;
        padding: 0;
        line-height: 28px;
      }
      .add,
      .remove {
        line-height: 20px;
        line-height: 31px;
      }
    }
    .el-dialog__wrapper {
      .el-dialog {
        background: rgba(243, 244, 247, 1);
        border-radius: 6px;
      }
      .el-dialog__headerbtn .el-dialog__close {
        color: #cacaca;
        font-weight: 700;
        font-size: 22px;
        .el-dialog__title {
          line-height: 24px;
          font-size: 20px;
          font-weight: 500;
          color: rgba(62, 62, 62, 1);
        }
      }
      .el-dialog__header {
        padding: 18px 20px 12px 32px;
      }
      .el-dialog__body {
        padding: 0 32px;
      }
      .el-dialog__footer {
        padding: 20px 32px;
      }
      .treeForm {
        .el-form-item__error {
          padding-top: 0px;
          top: -14px;
          width: 100%;
          display: inline-block;
          text-align: right;
          box-sizing: border-box;
          padding-right: 12px;
        }
        .el-form-item {
          margin-bottom: 12px;
          background-color: #fff;
          border-radius: 6px;
          padding: 6px 0 0 12px;
          box-sizing: border-box;
          &:last-child {
            margin-bottom: 0;
          }
        }
        .el-form-item__label {
          padding: 0;
          font-size: 14px;
          font-weight: 500;
          color: rgba(62, 62, 62, 1);
          line-height: 20px;
        }
        .el-form-item__content {
          height: 38px;
          line-height: 38px;
        }
        .el-input__inner {
          border: none;
          height: 18px;
          line-height: 18px;
          padding-left: 0;
        }
      }
    }
  }
}
</style>

相关文章

网友评论

      本文标题:树形菜单维护 vue+ts+element

      本文链接:https://www.haomeiwen.com/subject/ucicuhtx.html