2.router+axios+mock整合菜单导航

1、MOCK封装

1.1、在mock目录下新建modules文件夹.并创建menu.js

// 获取菜单树
const menuTreeData = {
  "code": 200,
  "msg": '成功',
  "data": [
    {
      "menuId": 1,
      "parentId": 0,
      "name": "系统管理",
      "url": null,
      "perms": null,
      "type": 0,
      "icon": "el-icon-setting",
      "orderNum": 0,
      "level": 0,
      "children": [
        {
          "menuId": 2,
          "parentId": 1,
          "name": "微信用户",
          "parentName": "系统管理",
          "url": "/miniUser",
          "perms": null,
          "type": 1,
          "icon": "el-icon-service",
          "orderNum": 1,
          "delFlag": 0,
          "level": 1,
          "children": [{
            "menuId": 4,
            "parentId": 2,
            "name": "删除",
            "parentName": "微信用户",
            "url": null,
            "perms": 'wx:miniUser:del',
            "type": 2,
            "icon": null,
            "orderNum": 5,
            "delFlag": 0,
            "level": 2
          }]
        },
        {
          "menuId": 3,
          "parentId": 1,
          "name": "菜单管理",
          "parentName": "系统管理",
          "url": "/menu",
          "perms": null,
          "type": 1,
          "icon": "el-icon-news",
          "orderNum": 2,
          "delFlag": 0,
          "level": 1,
          "children": []
        }
      ]
    }
  ]
}
export function getMenu() {
  //console.log('menu.json');
  return {
    url: '/menu.json',
    type: 'get',
    data: menuTreeData
  }
}

1.2、将mock/index.js进行封装

本人参考的是博客园的朝雨忆轻尘:
工具模块封装

import Mock from "mockjs";
import * as menu from "./modules/menu";


// 1. 开启/关闭[所有模块]拦截, 通过调[openMock参数]设置.
// 2. 开启/关闭[业务模块]拦截, 通过调用fnCreate方法[isOpen参数]设置.
// 3. 开启/关闭[业务模块中某个请求]拦截, 通过函数返回对象中的[isOpen属性]设置.
// let openMock = true
let openMock = true;
fnCreate(menu, openMock);
/**
 * 创建mock模拟数据
 * @param {*} mod 模块
 * @param {*} isOpen 是否开启?
 */
function fnCreate(mod, isOpen = true) {
  if (isOpen) {
    for (var key in mod) {
      (res => {
        if (res.isOpen !== false) {
          let url = process.env.BASE_API + res.url;
          Mock.mock(new RegExp(url), res.type, opts => {
            delete opts.body;
            console.log("\n");
            console.log("%cmock拦截, 请求: ", "color:blue", opts);
            console.log("%cmock拦截, 响应: ", "color:blue", res.data);
            return res.data;
          });
        }
      })(mod[key]() || {});
    }
  }
}

这样以后开关、封装都会非常的方便

2、整合axios的http请求

2.1、src下新建api目录并创建axios.js

统一配置封装axios的请求.详情看代码

/*src/api/axios.js*/
import axios from "axios";

export default function $axios(options) {
  return new Promise((resolve, reject) => {
    const service = axios.create({
      baseURL: process.env.BASE_API,// src/config目录下的dev和prod两个文件里配置
      method: "get",
      timeout: 10000,//请求超时时间
      responseType: "json" // 返回数据类型
    });

    // request拦截器
    service.interceptors.request.use(
      config => {
        config.headers["xxx-token"] = 'xiaobusi'; // 让每个请求携带自定义token 这里先写死请根据实际情况自行修改
        config.headers["Content-Type"] = "application/json";
        return config;
      },
      error => {
        // Do something with request error
        console.log(error); // for debug
        Promise.reject(error);
      }
    );
    
    // 请求处理
    service(options)
      .then(res => {
        resolve(res);
        return false;
      })
      .catch(error => {
        reject(error);
      });
  });
}

2.2、封装各种模块并整合挂载到Vue的原型链上

2.2.1、新建modules目录并创建menu.js

/*src/api/modules/menu.js*/
import axios from '../axios'

/* 
 * 菜单管理模块
 */

export const getMenu = () => {
    return axios({
        url: '/menu.json',
        method: 'get'
    })
}

2.2.2、新建api.js加载所有模块并导出

/*src/api/api.js*/
/* 
 * 接口统一集成模块
 */
import * as menu from './modules/menu'

// 默认全部导出
export default {
    menu
}

2.2.3、新建index.js.并所有api请求挂载到Vue上

/*src/api/index.js*/
// 导入所有接口
import apis from './api'

const install = Vue => {
    if (install.installed)
        return;

    install.installed = true;

    Object.defineProperties(Vue.prototype, {
        // 注意,此处挂载在 Vue 原型的 $api 对象上
        $api: {
            get() {
                return apis
            }
        }
    })
}

export default install

开始测试

    <script>
import axios from 'axios';
import Mock from '@/mock';

export default {
  name: 'HelloWorld',
  methods:{
    getMenu(){
      this.$api.menu.getMenu().then(res=>{
         alert(JSON.stringify(res.data));
      });
    }
  }
}
</script>
测试成功

3、整合页面菜单导航

3.1、src下新建views目录

主要还是参考Element布局容器
<el-container>:外层容器。当子元素中包含 <el-header><el-footer> 时,全部子元素会垂直上下排列,否则会水平左右排列。

<el-header>:顶栏容器。

<el-aside>:侧边栏容器。

<el-main>:主要区域容器。

<el-footer>:底栏容器。

在views下新建Home和目录layout并新建Header、Menu、Footer

views/Home.vue

<template>
  <el-container style="height: 100%; border: 1px solid #eee">
    <el-header style="text-align: right; font-size: 12px">
      <Header></Header>
    </el-header>
    <el-container>
      <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
        <Menu></Menu>
      </el-aside>
      <el-container>
        <el-main>
          <router-view></router-view>
        </el-main>
        <el-footer>
          <Footer></Footer>
        </el-footer>
      </el-container>
    </el-container>
  </el-container>
</template>
<script>
import Mock from '@/mock';
import Menu from "@/views/layout/Menu";
import Header from "@/views/layout/Header";
import Footer from "@/views/layout/Footer";
export default {
  name: "Home",
  components: {
    Menu,
    Header,
    Footer
  }
};
</script>
<style lang='scss'>
.el-header {
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;
}

.el-aside {
  color: #333;
}
</style>

views/layout/Header.vue

<template>
  <div>
    <el-dropdown>
      <i class="el-icon-setting" style="margin-right: 15px"></i>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item>查看</el-dropdown-item>
        <el-dropdown-item>新增</el-dropdown-item>
        <el-dropdown-item>删除</el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
    <span>王小虎</span>
  </div>
</template>

views/layout/Footer.vue

<template>
    <span>Footer</span>
</template>

views/layout/Menu.vue

<template>
  <!-- 导航菜单 -->
  <el-menu :default-openeds="['1', '3']">
    <!-- 导航菜单树组件,动态加载菜单 -->
    <el-menu-item-group v-for="menu in menuTrees" :key="menu.menuId">
      <el-submenu v-if="menu.children && menu.children.length >= 1" :index="menu.menuId + ''">
        <template slot="title">
          <i :class="menu.icon"></i>
          <span slot="title">{{menu.name}}</span>
        </template>
        <el-menu-item v-for="item in menu.children" :key="item.menuId" :index="item.menuId + ''" @click="handleRoute(item)">
          <i :class="item.icon"></i>
          <span slot="title">{{item.name}}</span>
        </el-menu-item>
      </el-submenu>
      <el-menu-item v-else :index="menu.menuId + ''" @click="handleRoute(menu)">
        <i :class="menu.icon"></i>
        <span slot="title">{{menu.name}}</span>
      </el-menu-item>
    </el-menu-item-group>
  </el-menu>
</template>
<script>
export default {
  data() {
    return {
      menuTrees: []
    };
  },
  methods: {
    handlerMenu: function() {
      this.$api.menu.getMenu().then(res => {
        this.menuTrees = res.data;
      });
    },
    handleRoute(menu) {
      // 通过菜单URL跳转至指定路由
      this.$router.push("/");
      this.$router.push(menu.url);
    }
  },
  mounted() {
    this.handlerMenu();
  }
};
</script>

添加菜单对应的路由

@/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/views/Home'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
      children: [
        { path: "test", component: () => import('@/views/test/index'), name: "test" },
        { path: "miniUser", component: () => import('@/views/miniUser/index'), name: "miniUser" },
        { path: "menu", component: () => import('@/views/system/menu/index'), name: "menu" }
      ]
    }
  ]
})

<font color=#DC143C>注意:</font>
Home.vue中的<router-view></router-view>是一个嵌套组件
router/index.js中home下的children组件就会嵌套在里面

3.2、整合菜单

3.2.1、新建模块@/views/core/TableTreeColumn

<template>
  <div class="container" style="width:99%;margin-top:-25px;">
    <!--工具栏-->
    <div class="toolbar" style="float:left;padding-top:10px;padding-left:15px;">
      <el-form :inline="true" :model="filters" :size="size">
        <el-form-item>
          <el-input v-model="filters.name" placeholder="名称"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button @click="search">查询</el-button>
        </el-form-item>
        <el-form-item>
          <el-button @click="search">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!--表格树内容栏-->
    <el-table
      :data="tableTreeDdata"
      stripe
      size="mini"
      style="width: 100%;"
      v-loading="loading"
      element-loading-text="拼命加载中"
    >
      <el-table-column prop="menuId" header-align="center" align="center" width="80" label="ID"></el-table-column>
      <TableTreeColumn prop="name" header-align="center" treeKey="menuId" width="150" label="名称"></TableTreeColumn>
      <el-table-column header-align="center" align="center" label="图标">
        <template slot-scope="scope">
          <i :class="scope.row.icon || ''"></i>
        </template>
      </el-table-column>
      <el-table-column prop="type" header-align="center" align="center" label="类型">
        <template slot-scope="scope">
          <el-tag v-if="scope.row.type === 0" size="small">目录</el-tag>
          <el-tag v-else-if="scope.row.type === 1" size="small" type="success">菜单</el-tag>
          <el-tag v-else-if="scope.row.type === 2" size="small" type="info">按钮</el-tag>
        </template>
      </el-table-column>
      <el-table-column
        prop="parentName"
        header-align="center"
        align="center"
        width="120"
        label="上级菜单"
      ></el-table-column>
      <el-table-column
        prop="url"
        header-align="center"
        align="center"
        width="150"
        :show-overflow-tooltip="true"
        label="菜单URL"
      ></el-table-column>
      <el-table-column
        prop="perms"
        header-align="center"
        align="center"
        width="150"
        :show-overflow-tooltip="true"
        label="授权标识"
      ></el-table-column>
      <el-table-column prop="orderNum" header-align="center" align="center" label="排序"></el-table-column>
      <el-table-column fixed="right" header-align="center" align="center" width="240" label="操作">
        <template slot-scope="scope">
          <el-row>
            <el-button icon="el-icon-edit" @click="handleEdit(scope.row)" round>修改</el-button>
            <el-button icon="el-icon-delete" type="danger" @click="handleDelete(scope.row)" round>删除</el-button>
          </el-row>
        </template>
      </el-table-column>
    </el-table>
    <!-- 新增修改界面 -->
    <el-dialog
      :title="!dataForm.id ? '新增' : '修改'"
      width="40%"
      :visible.sync="dialogVisible"
      :close-on-click-modal="false"
    >
      <el-form
        :model="dataForm"
        :rules="dataRule"
        ref="dataForm"
        @keyup.enter.native="submitForm()"
        label-width="80px"
        :size="size"
        style="text-align:left;"
      >
        <el-form-item label="菜单类型" prop="type">
          <el-radio-group v-model="dataForm.type">
            <el-radio v-for="(type, index) in menuTypeList" :label="index" :key="index">{{ type }}</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item :label="menuTypeList[dataForm.type] + '名称'" prop="name">
          <el-input v-model="dataForm.name" :placeholder="menuTypeList[dataForm.type] + '名称'"></el-input>
        </el-form-item>
        <el-form-item label="上级菜单" prop="parentName">
          <PopupTreeInput
            :data="popupTreeData"
            :props="popupTreeProps"
            :prop="dataForm.parentName==null?'根节点':dataForm.parentName"
            :nodeKey="''+dataForm.parentId"
            :currentChangeHandle="handleTreeSelectChange"
          ></PopupTreeInput>
        </el-form-item>
        <el-form-item v-if="dataForm.type === 1" label="菜单路由" prop="url">
          <el-input v-model="dataForm.url" placeholder="菜单路由"></el-input>
        </el-form-item>
        <el-form-item v-if="dataForm.type !== 0" label="授权标识" prop="perms">
          <el-input
            v-model="dataForm.perms"
            placeholder="如: sys:user:add, sys:user:edit, sys:user:delete"
          ></el-input>
        </el-form-item>
        <el-form-item v-if="dataForm.type !== 2" label="排序编号" prop="orderNum">
          <el-input-number
            v-model="dataForm.orderNum"
            controls-position="right"
            :min="0"
            label="排序编号"
          ></el-input-number>
        </el-form-item>
        <el-form-item v-if="dataForm.type !== 2" label="菜单图标" prop="icon">
           <el-input v-model="dataForm.icon" placeholder="请输入菜单图标"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button :size="size" @click="dialogVisible = false">取消</el-button>
        <el-button :size="size" type="primary" @click="submitForm()">确定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import TableTreeColumn from "@/views/core/TableTreeColumn";
import PopupTreeInput from "@/components/PopupTreeInput";
export default {
  components: { TableTreeColumn,PopupTreeInput},
  data() {
    return {
      size: "small",
      loading: false,
      filters: {
        name: ""
      },
      tableTreeDdata: [],
      dialogVisible: false,
      menuTypeList: ["目录", "菜单", "按钮"],
      dataForm: {
        id: 0,
        type: 1,
        name: "",
        parentId: 0,
        parentName: "",
        url: "",
        perms: "",
        orderNum: 0,
        icon: "",
        iconList: []
      },
      dataRule: {
        name: [
          { required: true, message: "菜单名称不能为空", trigger: "blur" }
        ],
        parentName: [
          { required: true, message: "上级菜单不能为空", trigger: "change" }
        ]
      },
      popupTreeData: [],
      popupTreeProps: {
        label: "name",
        children: "children"
      }
    };
  },
  methods: {
    search: function() {},
    // 获取数据
    findTreeData: function() {
      this.loading = true;
      this.$api.menu.getMenu().then(res => {
        this.tableTreeDdata = res.data;
        this.popupTreeData = this.getParentMenuTree(res.data);
        this.loading = false;
      });
    },
    // 获取上级菜单树
    getParentMenuTree: function(tableTreeDdata) {
      let parent = {
        parentId: -1,
        name: "根节点",
        children: tableTreeDdata
      };
      return [parent];
    },
    // 显示新增界面
    handleAdd: function() {
      this.dialogVisible = true;
      this.dataForm = {
        id: 0,
        type: 1,
        typeList: ["目录", "菜单", "按钮"],
        name: "",
        parentId: 0,
        parentName: "",
        url: "",
        perms: "",
        orderNum: 0,
        icon: "",
        iconList: []
      };
    },
    // 显示编辑界面
    handleEdit: function(row) {
      this.dialogVisible = true;
      Object.assign(this.dataForm, row);
    },
    // 删除
    handleDelete(row) {
      this.$confirm("确认删除选中记录吗?", "提示", {
        type: "warning"
      }).then(() => {
        let params = this.getDeleteIds([], row);
        this.$api.menu.batchDelete(params).then(res => {
          this.findTreeData();
          this.$message({ message: "删除成功", type: "success" });
        });
      });
    },
    // 获取删除的包含子菜单的id列表
    getDeleteIds(ids, row) {
      ids.push({ id: row.id });
      if (row.children != null) {
        for (let i = 0, len = row.children.length; i < len; i++) {
          this.getDeleteIds(ids, row.children[i]);
        }
      }
      return ids;
    },
    // 菜单树选中
    handleTreeSelectChange(data, node) {
      this.dataForm.parentId = data.id;
      this.dataForm.parentName = data.name;
    },
    // 图标选中
    iconActiveHandle(iconName) {
      this.dataForm.icon = iconName;
    },
    // 表单提交
    submitForm() {
      this.$refs["dataForm"].validate(valid => {
        if (valid) {
          this.$confirm("确认提交吗?", "提示", {}).then(() => {
            this.editLoading = true;
            let params = Object.assign({}, this.dataForm);
            this.$api.menu.save(params).then(res => {
              if (res.code == 200) {
                this.$message({ message: "操作成功", type: "success" });
              } else {
                this.$message({
                  message: "操作失败, " + res.msg,
                  type: "error"
                });
              }
              this.editLoading = false;
              this.$refs["dataForm"].resetFields();
              this.dialogVisible = false;
              this.findTreeData();
            });
          });
        }
      });
    }
  },
  mounted() {
    this.findTreeData();
  }
};
</script>

<style scoped>
</style>

3.2.1、新建菜单页views/system/menu/index.vue

<template>
  <div class="container" style="width:99%;margin-top:-25px;">
    <!--工具栏-->
    <div class="toolbar" style="float:left;padding-top:10px;padding-left:15px;">
      <el-form :inline="true" :model="filters" :size="size">
        <el-form-item>
          <el-input v-model="filters.name" placeholder="名称"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button @click="search">查询</el-button>
        </el-form-item>
        <el-form-item>
          <el-button @click="search">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!--表格树内容栏-->
    <el-table
      :data="tableTreeDdata"
      stripe
      size="mini"
      style="width: 100%;"
      v-loading="loading"
      element-loading-text="拼命加载中"
    >
      <el-table-column prop="menuId" header-align="center" align="center" width="80" label="ID"></el-table-column>
      <TableTreeColumn prop="name" header-align="center" treeKey="menuId" width="150" label="名称"></TableTreeColumn>
      <el-table-column header-align="center" align="center" label="图标">
        <template slot-scope="scope">
          <i :class="scope.row.icon || ''"></i>
        </template>
      </el-table-column>
      <el-table-column prop="type" header-align="center" align="center" label="类型">
        <template slot-scope="scope">
          <el-tag v-if="scope.row.type === 0" size="small">目录</el-tag>
          <el-tag v-else-if="scope.row.type === 1" size="small" type="success">菜单</el-tag>
          <el-tag v-else-if="scope.row.type === 2" size="small" type="info">按钮</el-tag>
        </template>
      </el-table-column>
      <el-table-column
        prop="parentName"
        header-align="center"
        align="center"
        width="120"
        label="上级菜单"
      ></el-table-column>
      <el-table-column
        prop="url"
        header-align="center"
        align="center"
        width="150"
        :show-overflow-tooltip="true"
        label="菜单URL"
      ></el-table-column>
      <el-table-column
        prop="perms"
        header-align="center"
        align="center"
        width="150"
        :show-overflow-tooltip="true"
        label="授权标识"
      ></el-table-column>
      <el-table-column prop="orderNum" header-align="center" align="center" label="排序"></el-table-column>
      <el-table-column fixed="right" header-align="center" align="center" width="240" label="操作">
        <template slot-scope="scope">
          <el-row>
            <el-button icon="el-icon-edit" @click="handleEdit(scope.row)" round>修改</el-button>
            <el-button icon="el-icon-delete" type="danger" @click="handleDelete(scope.row)" round>删除</el-button>
          </el-row>
        </template>
      </el-table-column>
    </el-table>
    <!-- 新增修改界面 -->
    <el-dialog
      :title="!dataForm.id ? '新增' : '修改'"
      width="40%"
      :visible.sync="dialogVisible"
      :close-on-click-modal="false"
    >
      <el-form
        :model="dataForm"
        :rules="dataRule"
        ref="dataForm"
        @keyup.enter.native="submitForm()"
        label-width="80px"
        :size="size"
        style="text-align:left;"
      >
        <el-form-item label="菜单类型" prop="type">
          <el-radio-group v-model="dataForm.type">
            <el-radio v-for="(type, index) in menuTypeList" :label="index" :key="index">{{ type }}</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item :label="menuTypeList[dataForm.type] + '名称'" prop="name">
          <el-input v-model="dataForm.name" :placeholder="menuTypeList[dataForm.type] + '名称'"></el-input>
        </el-form-item>
        <el-form-item label="上级菜单" prop="parentName">
          <PopupTreeInput
            :data="popupTreeData"
            :props="popupTreeProps"
            :prop="dataForm.parentName==null?'根节点':dataForm.parentName"
            :nodeKey="''+dataForm.parentId"
            :currentChangeHandle="handleTreeSelectChange"
          ></PopupTreeInput>
        </el-form-item>
        <el-form-item v-if="dataForm.type === 1" label="菜单路由" prop="url">
          <el-input v-model="dataForm.url" placeholder="菜单路由"></el-input>
        </el-form-item>
        <el-form-item v-if="dataForm.type !== 0" label="授权标识" prop="perms">
          <el-input
            v-model="dataForm.perms"
            placeholder="如: sys:user:add, sys:user:edit, sys:user:delete"
          ></el-input>
        </el-form-item>
        <el-form-item v-if="dataForm.type !== 2" label="排序编号" prop="orderNum">
          <el-input-number
            v-model="dataForm.orderNum"
            controls-position="right"
            :min="0"
            label="排序编号"
          ></el-input-number>
        </el-form-item>
        <el-form-item v-if="dataForm.type !== 2" label="菜单图标" prop="icon">
           <el-input v-model="dataForm.icon" placeholder="请输入菜单图标"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button :size="size" @click="dialogVisible = false">取消</el-button>
        <el-button :size="size" type="primary" @click="submitForm()">确定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import TableTreeColumn from "@/views/core/TableTreeColumn";
import PopupTreeInput from "@/components/PopupTreeInput";
export default {
  components: { TableTreeColumn,PopupTreeInput},
  data() {
    return {
      size: "small",
      loading: false,
      filters: {
        name: ""
      },
      tableTreeDdata: [],
      dialogVisible: false,
      menuTypeList: ["目录", "菜单", "按钮"],
      dataForm: {
        id: 0,
        type: 1,
        name: "",
        parentId: 0,
        parentName: "",
        url: "",
        perms: "",
        orderNum: 0,
        icon: "",
        iconList: []
      },
      dataRule: {
        name: [
          { required: true, message: "菜单名称不能为空", trigger: "blur" }
        ],
        parentName: [
          { required: true, message: "上级菜单不能为空", trigger: "change" }
        ]
      },
      popupTreeData: [],
      popupTreeProps: {
        label: "name",
        children: "children"
      }
    };
  },
  methods: {
    search: function() {},
    // 获取数据
    findTreeData: function() {
      this.loading = true;
      this.$api.menu.getMenu().then(res => {
        this.tableTreeDdata = res.data;
        this.popupTreeData = this.getParentMenuTree(res.data);
        this.loading = false;
      });
    },
    // 获取上级菜单树
    getParentMenuTree: function(tableTreeDdata) {
      let parent = {
        parentId: -1,
        name: "根节点",
        children: tableTreeDdata
      };
      return [parent];
    },
    // 显示新增界面
    handleAdd: function() {
      this.dialogVisible = true;
      this.dataForm = {
        id: 0,
        type: 1,
        typeList: ["目录", "菜单", "按钮"],
        name: "",
        parentId: 0,
        parentName: "",
        url: "",
        perms: "",
        orderNum: 0,
        icon: "",
        iconList: []
      };
    },
    // 显示编辑界面
    handleEdit: function(row) {
      this.dialogVisible = true;
      Object.assign(this.dataForm, row);
    },
    // 删除
    handleDelete(row) {
      this.$confirm("确认删除选中记录吗?", "提示", {
        type: "warning"
      }).then(() => {
        let params = this.getDeleteIds([], row);
        this.$api.menu.batchDelete(params).then(res => {
          this.findTreeData();
          this.$message({ message: "删除成功", type: "success" });
        });
      });
    },
    // 获取删除的包含子菜单的id列表
    getDeleteIds(ids, row) {
      ids.push({ id: row.id });
      if (row.children != null) {
        for (let i = 0, len = row.children.length; i < len; i++) {
          this.getDeleteIds(ids, row.children[i]);
        }
      }
      return ids;
    },
    // 菜单树选中
    handleTreeSelectChange(data, node) {
      this.dataForm.parentId = data.id;
      this.dataForm.parentName = data.name;
    },
    // 图标选中
    iconActiveHandle(iconName) {
      this.dataForm.icon = iconName;
    },
    // 表单提交
    submitForm() {
      this.$refs["dataForm"].validate(valid => {
        if (valid) {
          this.$confirm("确认提交吗?", "提示", {}).then(() => {
            this.editLoading = true;
            let params = Object.assign({}, this.dataForm);
            this.$api.menu.save(params).then(res => {
              if (res.code == 200) {
                this.$message({ message: "操作成功", type: "success" });
              } else {
                this.$message({
                  message: "操作失败, " + res.msg,
                  type: "error"
                });
              }
              this.editLoading = false;
              this.$refs["dataForm"].resetFields();
              this.dialogVisible = false;
              this.findTreeData();
            });
          });
        }
      });
    }
  },
  mounted() {
    this.findTreeData();
  }
};
</script>

<style scoped>
</style>
效果图

项目已经托管到码云(分支learn2.0):https://gitee.com/xiao1990/myEle.git

有问题加我QQ:317722960

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 相关概念 混合开发和前后端分离 混合开发(服务器端渲染) 前后端分离后端提供接口,前端开发界面效果(专注于用户的交...
    他爱在黑暗中漫游阅读 2,765评论 4 45
  • 一直纠结迷茫不安了很久,今天被一篇文章所折服,或者说被文章的主旨总结了这段时间的领悟,终于拨云见日,阳光满怀。 原...
    云朵飞子阅读 209评论 0 0
  • 清除什念,集中精力。 放下包袱,轻装上陣。 人生苦旅,切忌负重前行。 天道酬勤, 有志者亊竟成。 只要有一个理想,...
    浪淘沙0706阅读 627评论 3 17
  • 团购客就是只在有团购活动时购买,活动结束后就再也不来的人员,要想避免这种情况的出现,可以采取三种措施:不团购套餐,...
    423429d90f38阅读 130评论 0 0
  • 培训机构现在特别多 琳琅满目 包括教育连锁机构,互联网教育 以及众多的培训小作坊 小教室 让人眼花缭乱 面对如...
    易则知阅读 248评论 0 0