Vue+Element-UI搭建admin-shiro-ui后台页面

1 环境准备

后端代码:https://blog.csdn.net/qq_45660133/article/details/128498518

1.1 安装Node.js

官网下载地址 http://nodejs.cn/download,如图所示:
Vue+Element-UI搭建admin-shiro-ui后台页面_第1张图片

  1. 安装 Node.js 淘宝镜像加速器( npm )
npm install cnpm -g
# 或使用如下语句解决 npm 速度慢的问题
npm install --registry=https://registry.npm.taobao.org
// 测试是否安装成功
node -v npm -v
  1. 安装 vue-cli
// 最新版本
npm install -g @vue/cli
// 稳定版本
npm install -g @vue/[email protected]
// 测试是否安装成功
vue --version
// 卸载
npm uninstall -g @vue/cli

Vue+Element-UI搭建admin-shiro-ui后台页面_第2张图片

  1. 安装Webpack: js打包即压缩(可以忽略)
// 2个安装方式你自己选
npm install webpack -g
npm install webpack-cli -g
// 测试是否安装成功
webpack -v

1.2 创建项目

cmd d:\javaidea\admin-vue\admin-ui    创建一个文件夹放vue项目(admin-ui项目名)

vue create admin-ui     创建项目

根据下面图片选择配置:空格是选择,回车是确认!!
Vue+Element-UI搭建admin-shiro-ui后台页面_第3张图片

cd admin-ui         进入刚刚创建的项目

npm run serve   启动项目

2 导入插件

2.1 Element-UI 插件

// element-ui
npm install --save element-ui
// avue
npm i @smallwei/avue -S

在main.js 里面引用element-ui 组件

// 引用 element-ui 以及样式
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 引用 Avue 以及样式
import Avue from '@smallwei/avue';

// 安装ElementUI 配置全局
Vue.use(ElementUI, {size: 'small'});
// 安装Avue 配置全局
Vue.use(Avue, {size: 'small'});

2.2 Axios

npm install --save axios
  1. 创建utils文件夹 request.js
import axios from 'axios'

//请求超时时间
axios.defaults.timeout = 10000;
//设置请求头以json格式发送到后端
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
    
export default axios

  1. 创建 api 文件夹login.js,路径:api\user\login.js

data为post请求的携带信息
params是get请求携带的参数

import request from '@/utils/request'

//登录
export function login(data) {
    return request({
        url: '/login',
        method: 'post',
        data: data
    })
}

//注册
export function register(data) {
    return request({
        url: '/auth/register',
        method: 'post',
        data: data
    })
}

export function fetchList(query) {
    return request({
        url: '/admin/user/page',
        method: 'get',
        params: query
    })
}

export function addObj(obj) {
    return request({
        url: '/admin/user',
        method: 'post',
        data: obj
    })
}

export function getMenu() {
    return request({
        url: '/sysMenu/getMenu',
        method: 'get'
    })
}

export function getList(obj) {
  return request({
    url: '/proj/projPlan/getList',
    method: 'get',
    params: obj
  })
}

使用

<script>
import {getMenu} from "@/api/login";
export default {
  created() {
    console.log(this.$router.options.routes);
    getMenu().then(resp => {
        console.log(resp)
    })
    //初始化菜单激活状态
    this.activePath = window.sessionStorage.getItem("activePath");
  },
  methods: {
    // get 用法一
    var params = {"planId": this.planId,"current": page.currentPage,"size": page.pageSize};
    getList(params).then(response => {
      let data = response.data.data.records
      this.tableData = data
      this.page.total = response.data.data.total
      this.tableLoading = false
    }).catch(() => {
      this.tableLoading = false
    }),
    // get 用法二
    getList(Object.assign({
        current: this.page.currentPage,
        size: this.page.pageSize
     }, {
        "planStatus": 0,
        "planId": this.editableTabs[Number(this.editableTabsValue)].planId,
        "deptId": this.deptId
     }, this.searchForm)).then(response => {
        this.tableData = response.data.data.records
        this.page.total = response.data.data.total
        this.tableLoading = false
     }).catch(() => {
        this.tableLoading = false
     }),
    // post 用法
    addObj(row).then(res => {
      this.$message.success('添加成功')
      this.getList(this.page)
    }).catch(() => {
      loading();
    });
  }
};
</script>

2.3 Vuex 状态管理模式

注意:vuex分3.x版本和4.x版本,分别对应vue2.0与3.0,也就是说,vue2.0只能安装vuex3.x版本,最高3.6.2,vue3.0才能装vuex4.x版本。

// 最新版本
npm install vuex --save
// 推荐指定版本号
npm install -g vuex@3.4.0

创建store文件

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

export default new Vuex.Store({
    state: {},
    //同步执行操作
    mutations: {},
    //异步执行操作
    actions: {},
    modules: {}
})
import store from './store'

new Vue({
  router,
  store,//引用
  render: h => h(App)
}).$mount('#app');

2.4 Scss

// 安装 sass-loader
npm i -D sass-loader@8.x 
// 安装 node-sass
npm i node-sass@4.14.1 
npm audit fix
npm audit fix --force
npm install

错误解决:
Vue+Element-UI搭建admin-shiro-ui后台页面_第4张图片

检查代码中并无写错的地方
其实涉及到这个问题,就是版本原因了,我安装的 scss-loader 版本太高,卸载安装低版本即可
卸载:npm uninstall 名字比如:sass

npm uninstall --save sass-loader // 卸载
npm uninstall --save node-sass // 卸载

2.5 图标

我们使用了 Font Awesome 的图标做为菜单图标,使用前先安装 Font Awesome

npm install font-awesome

导入 Font Awesome (main.js)

// 图标
import 'font-awesome/css/font-awesome.min.css'

3 配置全局响应拦截器(业务逻辑错误)

创建utils文件夹,路径:utils/request.js

import axios from 'axios'
import { Message } from 'element-ui'
import router from '../router'

//请求超时时间
axios.defaults.timeout = 10000;
//设置请求头以json格式发送到后端
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'


//响应拦截器
axios.interceptors.response.use(success => {
  //业务逻辑错误
  if (success.status && success.status == 200) {
    //500 业务逻辑错误,401 未登录,403 权限错误
    if (success.data.code == 500 || success.data.code == 401 || success.data.code == 403) {
      Message.error({ message: success.data.msg });
      return;
    }
    if (success.data.message) {
      Message.success({ message: success.data.msg });
    }
  }
  return success.data;
}, error => {
  //504 服务器有问题,404 页面找不到
  if (error.response.code == 504 || error.response.code == 404) {
    Message.error({ message: '服务器没有了' });
  } else if (error.response.code == 403) {
    Message.error({ message: '权限不足,请联系管理员!' })
  } else if (error.response.code == 401) {
    Message.error({ message: '尚未登录,请登录' })
    router.replace('/');
  } else {
    if (error.response.data.message) {
      Message.error({ message: error.response.data.msg });
    } else {
      Message.error({ message: '未知错误!' });
    }
  }
  return;
});

export default axios

4 登录页面

4.1 配置跨越

创建vue.config.js文件
Vue+Element-UI搭建admin-shiro-ui后台页面_第5张图片

let proxyObj = {}//代理对象

proxyObj['/'] = {//代理路径,这里的api 表示如果我们的请求地址有/api的时候,就出触发代理机制
    //websocket
    ws: true,
    //目标地址
    target: 'http://localhost:8080',
    // target: 'http://47.115.143.129:8080',
    //发送请求头中host会设置成target
    changeOrigin: true,// 开启跨域
    //不重写请求地址
    pathReWrite:{
        // 重新路由  localhost:8888/api/login  => www.baidu.com/api/login
        '^/': '/'// 假设我们想把 localhost:8888/api/login 变成www.baidu.com/login 就需要这么做 
    }
};

module.exports = {
    assetsDir: 'static', // 静态资源保存路径
    outputDir: 'admin-shiro-ui', // 打包后生成的文件夹
    lintOnSave: false,
    productionSourceMap: false, // 取消错误日志
    runtimeCompiler: true, // 实时编译
    devServer: {
        open: true,
        host: 'localhost',
        port: 80,
        proxy: proxyObj //代理
    }
};

4.2 配置路由器

image.png

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'
import Register from '../views/Register.vue'
import Home from '../views/Home.vue'
import Console from '../views/console/index'
// 引用 store 状态管理模式
import store from '../store';

Vue.use(VueRouter)

// 判断 session 缓存中是否存在 token
if (window.sessionStorage.getItem("token")){
    // 设置到 store 状态管理器中
    store.commit("set_token", window.sessionStorage.getItem("token"))
}

const routes = [
    {
        path: '/',
        name: 'Login',
        component: Login,
        hidden: true//隐藏路由
    },
    {
        path: '/register',
        name: '注册',
        component: Register,
        hidden: true//隐藏路由
    },
    {
        path: '/home',//路径
        name: '控制台',//名字
        redirect: 'console',//重定向路由
        component: Home,//文件地址
        hidden: true,//隐藏路由
        children: [//子级菜单
            {
                path: '/console',
                name: '控制台',
                component: Console
            }
        ]
    }
]

const router = new VueRouter({
    routes
})

export default router

4.3 封装 get、post 请求后端接口

image.png

import request from '@/utils/request'

//登录
export function login(data) {
    return request({
        url: '/login',
        method: 'post',
        data: data
    })
}

//注销登录
export function logout(query) {
    return request({
        url: '/logout',
        method: 'get',
        params: query
    })
}

4.4 在 store 中设置 token

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

export default new Vuex.Store({
    state: {
        token: ''
    },
    // 同步执行操作
    mutations: {
        //获取token
        set_token(state, token){
            //设置 token
            state.token = token;
            //设置 token 到 session 缓存里面
            window.sessionStorage.token = token
        }
    },
    //异步执行操作
    actions: {},
    modules: {}
})

4.5 将 token 设置到 Authorization 请求头中

添加请求拦截器,在发送请求前的操作,判断是否存在token,如果存在将每个页面header添加token

import axios from 'axios'
import { Message } from 'element-ui'
import router from '../router'
// 引用 store 状态管理模式
import store from '../store';

//请求超时时间
axios.defaults.timeout = 10000;
//设置请求头以json格式发送到后端
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求前的操作
    // 判断是否存在token,如果存在将每个页面header添加token
    if (store.state.token) {
        config.headers['Authorization'] = store.state.token
    }
    return config
}, function (error) {
    //发生错误跳转到登录页面
    router.push('/login')
    return Promise.reject(error)
})

//响应拦截器
axios.interceptors.response.use(success => {
    console.log("success状态码:",success)
    //业务逻辑错误
    if (success.status && success.status == 200) {
        //500 业务逻辑错误,401 未登录,403 权限错误
        if (success.data.code == 500 || success.data.code == 401 || success.data.code == 403) {
            Message.error({ message: success.data.message });
            return;
        }
        if (success.data.message) {
            Message.success({ message: success.data.message });
        }
    }
    return success.data;
}, error => {
    console.log("error状态码:",error)
    //504 服务器有问题,404 页面找不到
    if (error.response.code == 504 || error.response.data.code == 404) {
        Message.error({ message: '服务器没有了' });
    } else if (error.response.code == 403) {
        Message.error({ message: '权限不足,请联系管理员!' })
    } else if (error.response.code == 401) {
        Message.error({ message: '尚未登录,请登录' })
        router.replace('/');
    } else {
        if (error.response.data.message) {
            Message.error({ message: error.response.data.message });
        } else {
            Message.error({ message: '未知错误!' });
        }
    }
    return;
});

export default axios

4.6 创建Login.vue

<template>
  <div class="login">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
      <h3 class="title">后台管理系统</h3>
      <el-form-item prop="userName">
        <el-input v-model="loginForm.userName"
                  auto-complete="false"
                  placeholder="账号"
                  type="text">
        </el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input v-model="loginForm.password"
                  auto-complete="false"
                  placeholder="密码"
                  type="password">
        </el-input>
      </el-form-item>
      <el-form-item v-if="captchaOnOff" prop="code">
        <el-input v-model="loginForm.code"
                  auto-complete="false"
                  placeholder="验证码"
                  style="width: 63%">
        </el-input>
        <div class="login-code">
          <img :src="codeUrl" class="login-code-img"/>
        </div>
      </el-form-item>
      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
      <el-form-item style="width:100%;">
        <el-button :loading="loading" size="medium" style="width:100%;" type="primary" @click="submitLogin">
          <span v-if="!loading">登 录</span>
          <span v-else>登 录 中...</span>
        </el-button>
        <div v-if="register" style="float: right;">
          <router-link :to="'/register'" class="link-type">立即注册</router-link>
        </div>
      </el-form-item>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2018-2022 admin All Rights Reserved.</span>
    </div>
  </div>
</template>

<script>
import {login} from '@/api/user/login'

export default {
  name: "Login",
  data() {
    return {
      loginForm: {
        userName: "admin",
        password: "123456",
        rememberMe: false,
        code: ""
      },
      loginRules: {//错误提示信息
        userName: [{required: true, trigger: "blur", message: "请输入您的账号"}],
        password: [{required: true, trigger: "blur", message: "请输入您的密码"}],
        code: [{required: true, trigger: "change", message: "请输入验证码"}]
      },
      loading: false,
      // 验证码开关
      captchaOnOff: true,
      // 注册开关
      register: false
    };
  },
  methods: {
    
  }
};
</script>

<style lang="scss" rel="stylesheet/scss">
.login {
  display: flex;
  position: fixed;
  justify-content: center;
  align-items: center;
  height: 100%;
  width: 100%;
  background-image: url("../assets/images/login-background.jpg");
  background-size: cover;
}

.title {
  margin: 0px auto 30px auto;
  text-align: center;
  color: #707070;
}

.login-form {
  border-radius: 6px;
  background: #ffffff;
  width: 400px;
  padding: 25px 25px 5px 25px;

  .el-input {
    height: 38px;

    input {
      height: 38px;
    }
  }

  .input-icon {
    height: 39px;
    width: 14px;
    margin-left: 2px;
  }
}

.login-tip {
  font-size: 13px;
  text-align: center;
  color: #bfbfbf;
}

.login-code {
  width: 33%;
  height: 38px;
  float: right;

  img {
    cursor: pointer;
    vertical-align: middle;
  }
}

.el-login-footer {
  height: 40px;
  line-height: 40px;
  position: fixed;
  bottom: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}

.login-code-img {
  height: 38px;
}
</style>

4.7 验证码


  
  
  

//获取验证码
codeUrl: '/captcha?time=' + new Date(),

如图所示:
Vue+Element-UI搭建admin-shiro-ui后台页面_第6张图片

4.8 登录


  
    登 录
    登 录 中...
  
  
立即注册
<script>
import { login } from '@/api/user/login'

export default {
  name: "Login",
  data() {
    return {
      codeUrl: '/captcha?time=' + new Date(),
      loginForm: {
        userName: "admin",
        password: "123456",
        rememberMe: false,
        code: ""
      },
      loginRules: {//错误提示信息
        userName: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
        password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
        code: [{ required: true, trigger: "change", message: "请输入验证码" }]
      },
      loading: false,
      // 验证码开关
      captchaOnOff: true,
      // 注册开关
      register: false
    };
  },
  methods: {
    //获取验证码
    updateCaptcha() {
      this.codeUrl = '/captcha?time=' + new Date();
    },
    //登录事件
    submitLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          console.log(this.loginForm);
          this.loading = true;
          this.postRequest("/login", this.loginForm).then(resp => {
            if (resp) {
              this.loading = false;
              console.log(resp);
              //页面跳转
              let path = this.$route.query.redirect;
              this.$router.replace(
                  path == "/" || path == undefined ? "/home" : path
              );
              // this.$router.replace('/home');
            } else {
              this.loading = false;
            }
          });
        } else {
          this.$message.error("请输入所有字段");
          return false;
        }
      });
    }
  }
};
</script>

4.9 完整代码







5 侧边栏

Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
表字段:
Vue+Element-UI搭建admin-shiro-ui后台页面_第7张图片
后端响应菜单栏的格式

[
  {
    "mid": 1,
    "url": "/",
    "path": "/home",
    "component": "Home",
    "name": "商品管理",
    "iconcls": null,
    "keepalive": null,
    "requireauth": true,
    "parentid": 0,
    "enabled": true,
    "children": [
      {
        "mid": 9,
        "url": null,
        "path": "/product/product",
        "component": "product/product/list",
        "name": "商品列表",
        "iconcls": null,
        "keepalive": null,
        "requireauth": true,
        "parentid": 1,
        "enabled": true,
        "children": null
      },
      {
        "mid": 10,
        "url": null,
        "path": "/product/order",
        "component": "product/order/list",
        "name": "订单列表",
        "iconcls": null,
        "keepalive": null,
        "requireauth": true,
        "parentid": 1,
        "enabled": true,
        "children": null
      }
    ]
  },
  {
    "mid": 2,
    "url": "/",
    "path": "/home",
    "component": "Home",
    "name": "会员管理",
    "iconcls": null,
    "keepalive": null,
    "requireauth": true,
    "parentid": 0,
    "enabled": true,
    "children": [
      {
        "mid": 11,
        "url": null,
        "path": "/member/grade",
        "component": "menber/grade",
        "name": "会员等级",
        "iconcls": null,
        "keepalive": null,
        "requireauth": true,
        "parentid": 2,
        "enabled": true,
        "children": null
      }
    ]
  },
  {
    "mid": 3,
    "url": "/",
    "path": "/home",
    "component": "Home",
    "name": "系统管理",
    "iconcls": "el-icon-setting",
    "keepalive": null,
    "requireauth": true,
    "parentid": 0,
    "enabled": true,
    "children": [
      {
        "mid": 5,
        "url": null,
        "path": "/system/user",
        "component": "system/user/index",
        "name": "用户管理",
        "iconcls": "el-icon-user",
        "keepalive": null,
        "requireauth": true,
        "parentid": 3,
        "enabled": true,
        "children": null
      },
      {
        "mid": 6,
        "url": null,
        "path": "/system/role",
        "component": "system/role/index",
        "name": "角色管理",
        "iconcls": null,
        "keepalive": null,
        "requireauth": true,
        "parentid": 3,
        "enabled": true,
        "children": null
      },
      {
        "mid": 7,
        "url": null,
        "path": "/system/menu",
        "component": "system/menu/index",
        "name": "菜单栏管理",
        "iconcls": null,
        "keepalive": null,
        "requireauth": true,
        "parentid": 3,
        "enabled": true,
        "children": null
      }
    ]
  },
  {
    "mid": 4,
    "url": "/",
    "path": "/home",
    "component": "Home",
    "name": "系统工具",
    "iconcls": "el-icon-s-tools",
    "keepalive": null,
    "requireauth": true,
    "parentid": 0,
    "enabled": true,
    "children": [
      {
        "mid": 8,
        "url": null,
        "path": "/tools/logs",
        "component": "tools/logs/index",
        "name": "日志管理",
        "iconcls": null,
        "keepalive": null,
        "requireauth": true,
        "parentid": 4,
        "enabled": true,
        "children": null
      }
    ]
  }
]

在 src 目录下创建一个名为 store 的目录并新建一个名为 index.js 文件用来配置 Vuex。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

export default new Vuex.Store({
    state: {
    	routes:[],
      permission: {}
    },
    //同步执行操作
    mutations: {
    	//初始化
    	initRoutes(state, data) {
            state.routes = data;
    	},
		// 获取权限
      set_permission(state, permission){
          //设置 permission
          state.permission = permission;
      }
    },
    //异步执行操作
    actions: {},
    modules: {}
})
state 全局state对象,用于保存所有组件的公共数据
getters 监听state值的最新状态(计算属性)
actions 异步执行mutations方法
mutations 唯一可以改变state值的方法(同步执行)

修改 main.js 增加刚才配置的 store/index.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// 菜单栏路由器
import {initMenu} from "./utils/menus";

//使用钩子函数对路由进行权限跳转
router.beforeEach((to, from, next) => {
  if (to.path == '/') {
    next()
  } else {
    initMenu(router, store);
    next();
  }
});

Vue.config.productionTip = false

new Vue({
  router,
  store,//引用
  render: h => h(App)
}).$mount('#app')

封装菜单请求工具类
后端接口返回的数据中 component 的值为String,我们需要将其转换为前端所需的对象并且我们需要将数据放入到路由的配置里。所以我们需要封装菜单请求工具类实现我们的需求。

import { getMenu } from "@/api/user/login";

export const initMenu = (router, store) => {
    if (store.state.routes.length > 0) {
        return;
    }
    //查询菜单栏
    getMenu().then(res => {
        if (res) {
            //格式化Router
            let fmtRoutes = formatRoutes(res.data);
            //添加到router
            router.addRoutes(fmtRoutes);
            //将数据存入vuex
            store.commit('initRoutes', fmtRoutes);
        }
    })
		// 获取权限信息
    getPermission().then(res => {
        if (res) {
            //将数据存入vuex
            store.commit('set_permission', res.data);
        }
    })
};

export const formatRoutes = (routes) => {
    let fmtRoutes = [];
    routes.forEach(router => {
        let {
            path,
            component,
            name,
            iconCls,
            children,
        } = router;
        if (children && children instanceof Array) {
            //递归
            children = formatRoutes(children);
        }
        let fmRouter = {
            path: path,
            name: name,
            iconCls: iconCls,
            children: children,
            //这里注意了,数据库里的路径要对页面的路径,不能出错了
            component:()=>import(`@/views/${component}.vue`)
        };
        fmtRoutes.push(fmRouter);
    });
    return fmtRoutes;
};

导航守卫
菜单数据在用户点击刷新按钮时可能出现丢失的情况,解决办法

  1. 每个页面添加初始化菜单的方法,这显然很麻烦 。
  2. 路由导航守卫 。

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的,单个路由独享的, 或者组件级的。
记住参数或查询的改变并不会触发进入离开的导航守卫。我们可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'
import Register from '../views/Register.vue'
import Home from '../views/Home.vue'
import Console from '../views/console/index'

Vue.use(VueRouter)

const routes = [
    {
        path: '/',
        name: 'Login',
        component: Login,
        hidden: true//隐藏路由
    },
    {
        path: '/register',
        name: '注册',
        component: Register,
        hidden: true//隐藏路由
    },
    {
        path: '/home',//路径
        name: '控制台',//名字
        redirect: 'console',//重定向路由
        component: Home,//文件地址
        hidden: true,//隐藏路由
        children: [//子级菜单
            {
                path: '/console',
                name: '控制台',
                component: Console
            }
        ]
    }
]

const router = new VueRouter({
    // mode: 'history', // 去掉url中的#
    routes
})

export default router

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫resolve 完之前一直处于 等待中
每个守卫方法接收三个参数:

to: Route 即将要进入的目标路由对象。
from: Route 当前导航正要离开的路由。
next: Function 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
next() 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed (确认的)。
next(false) 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。next(‘/’) 或者 next({ path: ‘/’ }) : 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace:true 、 name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或router.push 中的选项。
next(error) (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给router.onError() 注册过的回调。

确保要调用 next 方法,否则钩子就不会被 resolved。

// 菜单栏路由器
import {initMenu} from "./utils/menus";

//使用钩子函数对路由进行权限跳转
router.beforeEach((to, from, next) => {
  if (to.path == '/') {
    next()
  } else {
    initMenu(router, store);
    next();
  }
});

  
  
    
    
      
      {{children.name}}
    
  



6 首页






7 菜单管理

接口:@/api/admin/menu

import request from '@/utils/request'

// 查询菜单栏
export function getMenuList(query) {
    return request({
        url: '/sysMenu/getMenuList',
        method: 'get',
        params: query
    })
}

// 查询下拉树
export function fetchMenuTree(lazy, parentId) {
    return request({
        url: '/sysMenu/tree',
        method: 'get',
        params: { lazy: lazy, parentId: parentId }
    })
}

// 添加
export function addObj(obj) {
    return request({
        url: '/sysMenu/addObj',
        method: 'post',
        data: obj
    })
}

// 根据 id 获取菜单信息
export function getObj(id) {
    return request({
        url: '/sysMenu/getObj',
        method: 'get',
        params: { mid: id }
    })
}

// 删除
export function delObj(id) {
    return request({
        url: '/sysMenu/delObj',
        method: 'delete',
        params: { mid: id }
    })
}

// 修改
export function putObj(obj) {
    return request({
        url: '/sysMenu/putObj',
        method: 'put',
        data: obj
    })
}

树表格:index.vue







添加/修改弹窗:menu-from.vue







阿里云图标:iconList.js

export default [
	{
		label: '阿里云图标',
		list: [
			{
				label: '工具',
				value: 'el-icon-s-tools',
			},
			{
				label: '设置',
				value: 'el-icon-setting',
			},
			{
				label: '角色管理',
				value: 'el-icon-user-solid',
			},
			{
				label: '角色',
				value: 'el-icon-user',
			},
			{
				label: '客户',
				value: 'el-icon-s-custom',
			},
			{
				label: '电话',
				value: 'el-icon-phone',
			},
			{
				label: '电话呼叫',
				value: 'el-icon-phone-outline',
			},
			{
				label: '更多',
				value: 'el-icon-more',
			},
			{
				label: '更多1',
				value: 'el-icon-more-outline',
			},
			{
				label: '五角星',
				value: 'el-icon-star-on',
			},
			{
				label: '五角星1',
				value: 'el-icon-star-off',
			},
			{
				label: '商城',
				value: 'el-icon-s-goods',
			},
			{
				label: '商城1',
				value: 'el-icon-goods',
			},
			{
				label: '帮助',
				value: 'el-icon-s-help',
			},
			{
				label: '帮助1',
				value: 'el-icon-help',
			},
			{
				label: '减少',
				value: 'el-icon-minus',
			},
			{
				label: '添加',
				value: 'el-icon-plus',
			},
			{
				label: '检查',
				value: 'el-icon-check',
			},
			{
				label: '关闭',
				value: 'el-icon-close',
			},
			{
				label: '图片',
				value: 'el-icon-picture',
			},
			{
				label: '图片1',
				value: 'el-icon-picture-outline',
			},
			{
				label: '图片2',
				value: 'el-icon-picture-outline-round',
			},
			{
				label: '上传',
				value: 'el-icon-upload',
			},
			{
				label: '上传1',
				value: 'el-icon-upload2',
			},
			{
				label: '下载',
				value: 'el-icon-download',
			},
			{
				label: '相机',
				value: 'el-icon-camera-solid',
			},
			{
				label: '相机1',
				value: 'el-icon-camera',
			},
			{
				label: '视频',
				value: 'el-icon-video-camera-solid',
			},
			{
				label: '视频1',
				value: 'el-icon-video-camera',
			},
			{
				label: '通知',
				value: 'el-icon-message-solid',
			},
			{
				label: '通知1',
				value: 'el-icon-bell',
			},
			{
				label: '短信',
				value: '#icon-duanxin2',
			},
			{
				label: '工具箱',
				value: 'el-icon-s-cooperation',
			},
			{
				label: '操作',
				value: 'el-icon-s-order',
			},
			{
				label: '屏幕',
				value: 'el-icon-s-platform',
			},
			{
				label: '折叠',
				value: 'el-icon-s-fold',
			},
			{
				label: '折叠1',
				value: 'el-icon-s-unfold',
			},
			{
				label: '折线',
				value: 'el-icon-s-operation',
			},
			{
				label: '提升',
				value: 'el-icon-s-promotion',
			},
			{
				label: '首页',
				value: 'el-icon-s-home',
			},
			{
				label: '释放',
				value: 'el-icon-s-release',
			},
			{
				label: '优惠券',
				value: 'el-icon-s-ticket',
			},
			{
				label: '管理',
				value: 'el-icon-s-management',
			},
			{
				label: '打开',
				value: 'el-icon-s-open',
			},
			{
				label: '购物',
				value: 'el-icon-s-shop',
			},
			{
				label: '市场',
				value: 'el-icon-s-marketing',
			},
			{
				label: '目标',
				value: 'el-icon-s-flag',
			},
			{
				label: '信息',
				value: 'el-icon-s-comment',
			},
			{
				label: '金融',
				value: 'el-icon-s-finance',
			},
			{
				label: '宣布',
				value: 'el-icon-s-claim',
			},
			{
				label: '用户',
				value: 'el-icon-s-custom',
			},
			{
				label: '提示',
				value: 'el-icon-s-opportunity',
			},
			{
				label: '数据',
				value: 'el-icon-s-data',
			},
			{
				label: '质检',
				value: 'el-icon-s-check',
			},
			{
				label: '全部',
				value: 'el-icon-menu',
			},
			{
				label: '分享',
				value: 'el-icon-share',
			},
			{
				label: '播放',
				value: 'el-icon-video-pause',
			},
			{
				label: '暂停',
				value: 'el-icon-video-play',
			},
			{
				label: '刷新',
				value: 'el-icon-refresh',
			},
			{
				label: '向后',
				value: 'el-icon-refresh-right',
			},
			{
				label: '撤回',
				value: 'el-icon-refresh-left',
			},
			{
				label: '完成',
				value: 'el-icon-finished',
			},
			{
				label: '排序',
				value: 'el-icon-sort',
			},
			{
				label: '全屏',
				value: 'el-icon-rank',
			},
			{
				label: '可见',
				value: 'el-icon-view',
			},
			{
				label: '日历',
				value: 'el-icon-date',
			},
			{
				label: '标记',
				value: 'el-icon-edit-outline',
			},
			{
				label: '文件',
				value: 'el-icon-folder',
			},
			{
				label: '文件类型',
				value: 'el-icon-folder-opened',
			},
			{
				label: '文件添加',
				value: 'el-icon-folder-add',
			},
			{
				label: '文件移除',
				value: 'el-icon-folder-remove',
			},
			{
				label: '文件删除',
				value: 'el-icon-folder-delete',
			},
			{
				label: '文件质检',
				value: 'el-icon-folder-checked',
			},
			{
				label: '文件复制',
				value: 'el-icon-document-copy',
			},
			{
				label: '票据',
				value: 'el-icon-tickets',
			},
			{
				label: '打印',
				value: 'el-icon-printer',
			},
			{
				label: '回形针',
				value: 'el-icon-paperclip',
			},
			{
				label: '收纳盒',
				value: 'el-icon-takeaway-box',
			},
			{
				label: '搜索',
				value: 'el-icon-search',
			},
			{
				label: '显示屏',
				value: 'el-icon-monitor',
			},
			{
				label: '吸磁',
				value: 'el-icon-attract',
			},
			{
				label: '手机',
				value: 'el-icon-mobile',
			},
			{
				label: '剪切',
				value: 'el-icon-scissors',
			},
			{
				label: '雨伞',
				value: 'el-icon-umbrella',
			},
			{
				label: '耳机',
				value: 'el-icon-headset',
			},
			{
				label: '刷子',
				value: 'el-icon-brush',
			},
			{
				label: '鼠标',
				value: 'el-icon-mouse',
			},
			{
				label: '配合',
				value: 'el-icon-coordinate',
			},
			{
				label: 'magic',
				value: 'el-icon-magic-stick',
			},
			{
				label: '阅读',
				value: 'el-icon-reading',
			},
			{
				label: '数据线',
				value: 'el-icon-data-line',
			},
			{
				label: '饼图',
				value: 'el-icon-pie-chart',
			},
			{
				label: '收藏夹',
				value: 'el-icon-collection-tag',
			},
			{
				label: '电影',
				value: 'el-icon-film',
			},
			{
				label: '急救箱',
				value: 'el-icon-suitcase',
			},
			{
				label: '急救箱1',
				value: 'el-icon-suitcase-1',
			},
			{
				label: '收藏夹1',
				value: 'el-icon-collection',
			},
			{
				label: '笔记本',
				value: 'el-icon-notebook-1',
			},
			{
				label: '笔记本1',
				value: 'el-icon-notebook-2',
			},
			{
				label: '卷纸',
				value: 'el-icon-toilet-paper',
			},
			{
				label: '办公楼',
				value: 'el-icon-office-building',
			},
			{
				label: '学校',
				value: 'el-icon-school',
			},
			{
				label: '台灯',
				value: 'el-icon-table-lamp',
			},
			{
				label: '首页1',
				value: 'el-icon-house',
			},
			{
				label: '禁止抽烟',
				value: 'el-icon-no-smoking',
			},
			{
				label: '抽烟',
				value: 'el-icon-smoking',
			},
			{
				label: '购物车',
				value: 'el-icon-shopping-cart-full',
			},
			{
				label: '购物车1',
				value: 'el-icon-shopping-cart-1',
			},
			{
				label: '购物车2',
				value: 'el-icon-shopping-cart-2',
			},
			{
				label: '购物袋',
				value: 'el-icon-shopping-bag-1',
			},
			{
				label: '购物袋1',
				value: 'el-icon-shopping-bag-2',
			},
			{
				label: '礼物',
				value: 'el-icon-present',
			},
			{
				label: '卡',
				value: 'el-icon-bank-card',
			},
			{
				label: '金钱',
				value: 'el-icon-money',
			},
			{
				label: '数据库',
				value: 'el-icon-coin',
			},
			{
				label: '钱包',
				value: 'el-icon-wallet',
			},
			{
				label: '折扣',
				value: 'el-icon-discount',
			},
			{
				label: '折扣1',
				value: 'el-icon-price-tag',
			},
			{
				label: '新闻',
				value: 'el-icon-news',
			},
			{
				label: '指导',
				value: 'el-icon-guide',
			},
			{
				label: 'male',
				value: 'el-icon-male',
			},
			{
				label: 'female',
				value: 'el-icon-female',
			},
			{
				label: '手指',
				value: 'el-icon-thumb',
			},
			{
				label: 'cpu',
				value: 'el-icon-cpu',
			},
			{
				label: '链接',
				value: 'el-icon-link',
			},
			{
				label: '关联',
				value: 'el-icon-connection',
			},
			{
				label: '打开1',
				value: 'el-icon-open',
			},
			{
				label: '关闭',
				value: 'el-icon-turn-off',
			},
			{
				label: '聊天',
				value: 'el-icon-chat-round',
			},
			{
				label: '聊天1',
				value: 'el-icon-chat-line-round',
			},
			{
				label: '短信',
				value: 'el-icon-chat-square',
			},
			{
				label: '信息',
				value: 'el-icon-chat-dot-round',
			},
			{
				label: '信息1',
				value: 'el-icon-chat-dot-square',
			},
			{
				label: '信息2',
				value: 'el-icon-chat-line-square',
			},
			{
				label: '消息',
				value: 'el-icon-message',
			},
			{
				label: 'postcard',
				value: 'el-icon-postcard',
			},
			{
				label: '方向',
				value: 'el-icon-position',
			},
			{
				label: '静音',
				value: 'el-icon-turn-off-microphone',
			},
			{
				label: '语音',
				value: 'el-icon-microphone',
			},
			{
				label: '关闭通知',
				value: 'el-icon-close-notification',
			},
			{
				label: '帮助',
				value: 'el-icon-bangzhu',
			},
			{
				label: '时间',
				value: 'el-icon-time',
			},
			{
				label: '截取',
				value: 'el-icon-crop',
			},
			{
				label: '目标',
				value: 'el-icon-aim',
			},
			{
				label: '关机',
				value: 'el-icon-switch-button',
			},
			{
				label: '权限',
				value: '#icon-quanxian',
			},
			{
				label: '屏幕',
				value: 'el-icon-full-screen',
			},
			{
				label: 'mic',
				value: 'el-icon-mic',
			},
			{
				label: '指向盘',
				value: 'el-icon-stopwatch',
			},
			{
				label: 'medal',
				value: 'el-icon-medal-1',
			},
			{
				label: '奖章',
				value: 'el-icon-medal',
			},
			{
				label: '奖杯',
				value: 'el-icon-trophy',
			},
			{
				label: '奖杯1',
				value: 'el-icon-trophy-1',
			},
			{
				label: '医疗箱',
				value: 'el-icon-first-aid-kit',
			},
			{
				label: 'place',
				value: 'el-icon-place',
			},
			{
				label: '定位',
				value: 'el-icon-location',
			},
			{
				label: '定位1',
				value: 'el-icon-location-outline',
			},
			{
				label: '定位2',
				value: 'el-icon-location-information',
			},
			{
				label: '闹钟',
				value: 'el-icon-alarm-clock',
			},
			{
				label: 'timer',
				value: 'el-icon-timer',
			},
			{
				label: '手表',
				value: 'el-icon-watch-1',
			},
			{
				label: '手表1',
				value: 'el-icon-watch',
			},
			{
				label: '关锁',
				value: 'el-icon-lock',
			},
			{
				label: '开锁',
				value: 'el-icon-unlock',
			},
			{
				label: '钥匙',
				value: 'el-icon-key',
			},
			{
				label: '客服',
				value: 'el-icon-service',
			},
			{
				label: '自行车',
				value: 'el-icon-bicycle',
			},
			{
				label: '卡车',
				value: 'el-icon-truck',
			},
			{
				label: 'sunset',
				value: 'el-icon-sunset',
			},
		]
	}
]

  border: true,
  index: true,
  indexLabel: '序号',
  stripe: true,//高亮
  menuAlign: 'center',
  editBtn: false,//编辑
  delBtn: false,//删除
  searchMenuSpan: 6,
  menuWidth: 300,
  dialogWidth: '50%',
  align: 'center',//居中
  addBtn: false,//添加按钮
  viewBtn: true,//查看

下拉树菜单

npm install --save @riophae/vue-treeselect

  



你可能感兴趣的:(vue,SpringBoot,shiro,vue.js,elementui,前端)