• vue2构建一个后端权限管理模板


    参考

    • 博客: https://www.cnblogs.com/xifengxiaoma
    • 代码:https://gitee.com/liuge1988/mango-platform

    使用vue脚手架创建一个vue后端管理项目

    • vue create xxx(项目名)
    • 项目创建过程中会需要我们选择一些配置,我这里就不讲了,网上都有
    • 在官网找到这个地方点进去vue cli看看

    在这里插入图片描述

    • 打开package.json这个文件相当于是依赖管管理,项目启动命令也是在这配置,npm run server可以改为npm run dev代码是开发状态

    在这里插入图片描述

    • 项目创建后之后会提示我们进入项目然后运行 npm run serve

    vue.config.js

    • 项目创建好之后新建一个vue.config.js文件

    • 这是项目的配置文件包含打包路径、css、跨域、插件配置、别名配置等等,可以进入webpack这块看一看,会带我们入门

    在这里插入图片描述

    • 配置vue.config.js是需要点前置知识

      webpack的官网:https://webpack.docschina.org/concepts/

      node知识:https://www.runoob.com/nodejs/nodejs-global-object.html (比如有_dirname、requrire(paht)等等)

    • 看一看已经配置好的vue.config.js

    //引入path模块,提供了处理和转换文件路径的工具
    const path = require('path');
    
    //设置绝对路径,后面配置路径别名有用
    function resolve(dir) {
        //__dirname是__dirname 表示当前执行脚本所在的目录
        return path.join(__dirname, dir)
    }
    // 向外暴露该模块
    module.exports = {
        // 基本路径
        publicPath: process.env.NODE_ENV === 'prod' ? '' : './',
        // 输出文件目录
        outputDir: process.env.NODE_ENV === 'prod' ? 'dist' : 'devdist',
        //打包好的静态文件位置
        assetsDir: 'static',
        // eslint-loader 是否在保存的时候检查,代码格式检查,一般不开,不然一直报错
        lintOnSave: false,
        // 生产环境是否生成 sourceMap 文件
        productionSourceMap: false,
    
        // css相关配置
        css: {
            // 是否使用css分离插件 ExtractTextPlugin
            extract: true,
            // 开启 CSS source maps?
            sourceMap: false,
            // css预设器配置项
            loaderOptions: {
                // 全局样式配置,也可以不要
                // sass: {
                // 	prependData: `@import "./src/common/css/global.scss";`
                // }
                //scss: {
                //    prependData: `@import "./src/styles/main.scss";`
                //}
            },
            // 启用 CSS modules for all css / pre-processor files.
            requireModuleExtension: true,
        },
    
        //跨域配置
        devServer: {
            open: true, // 编译完成是否打开网页
            host: '0.0.0.0', // 指定使用地址,默认localhost,0.0.0.0代表可以被外界访问
            //port: 8080, // 访问端口
            https: false, // 编译失败时刷新页面
            hot: true, // 开启热加载
            hotOnly: false,
            proxy: {
                //设置代理
                '/api': {
                    // target: `${VUE_APP_BASE_API}`,自己的后端服务接口
                    // target: 'http://old.web-jshtml.cn/vue_admin_api',
                    target: 'http://localhost:8088/konglz',
                    changeOrigin: true,
                    ws: true,
                    secure: false, //如果是http接口,需要配置该参数
                    //将上面的dev-api变成''
                    pathRewrite: {
                        '^/api': ''
                    }
                }
            },
            overlay: { // 全屏模式下是否显示脚本错误
                warnings: true,
                errors: true
            },
            before: app => {
            }
        },
    
        //链式配置配置规则,这里是svg一些配置
        chainWebpack: config => {
            config.module
                .rule('svg')
                .exclude.add(resolve('src/icons')) //对应下面配置svg的路径
                .end();
    
            config.module
                .rule('icons')
                .test(/\.svg$/)
                .include.add(resolve('src/icons')) //对应下面配置svg的路径
                .end()
                .use('svg-sprite-loader')
                .loader('svg-sprite-loader')
                .options({
                    symbolId: 'icon-[name]'
                })
                .end();
        },
    
        //别名配置用到了我们上面的resove()函数
        configureWebpack: (config) => {
            config.resolve = { // 配置解析别名
                extensions: ['.js', '.json', '.vue'],
                alias: {
                    // 改变vue的运行模式由默认的runtime改为compiler,为了使用全局组件
                    'vue': 'vue/dist/vue.js',
                    '@': path.resolve(__dirname, './src'),
                    'public': path.resolve(__dirname, './public'),
                    'components': path.resolve(__dirname, './src/components'),
                    'api': path.resolve(__dirname, './src/api'),
                    'views': path.resolve(__dirname, './src/views'),
                    'data': path.resolve(__dirname, './src/data')
                }
            }
        },
    
        // 构建时开启多进程处理 babel 编译
        //是否为 Babel 或 TypeScript 使用 thread-loader。
        //该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建,在适当的时候开启几个子进程去并发的执行压缩
        parallel: require("os").cpus().length > 1,
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    导入element ui、axios、font-awesone依赖

    • npm是一个依赖管理器:https://docs.npmjs.com/cli/v8/commands/npm-install
    //-g是全局安装
    npm install --save element-ui
    //引入element-ui
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    Vue.use(ElementUI);
    
    npm install --save axios vue-axios
    
    npm install font-awesome --save
    /* 使用font-awesone图标库 */
    import 'font-awesome/css/font-awesome.min.css'
    
    //svg依赖
    npm install --save js-cookie
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    axios请求模块化封装

    • 参考:https://router.vuejs.org/zh/introduction.html

    • 目录结构

    在这里插入图片描述

    • config.js 、gobal.js配置一些常用的信息
    • 有些时候会配置一个gobal.js用的不是.env
    /**
     * 全局常量、方法封装模块
     * 通过原型挂载到Vue属性
     * 通过 this.Global 调用
     */
    
     // 后台管理系统服务器地址
    //这个配置的是跨域地址,它会转到帮我们转到${process.env.VUE_APP_BASE_URL}
    export const baseUrl = 'http://localhost:8080/api'
    //也可以直接使用后端的地址,这时不用配跨域把vue.config.js的proxy注释掉
    //export const baseUrl = 'http://localhost:8088/konglz'
     // 系统数据备份还原服务器地址
    export const backupBaseUrl = 'http://localhost:8080'
    
    export default {
        baseUrl,
        backupBaseUrl
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    // const BASEURL = process.env.NODE_ENV === 'production' ? process.env.VUE_APP_BASE_API : process.env.VUE_APP_BASE_API;
    import { baseUrl } from '@/utils/global'
    export default {
      method: 'get',
      // 基础url前缀
      // baseUrl: BASEURL,
      baseUrl: baseUrl,
      // 请求头信息
      headers: {
        'Content-Type': 'application/json;charset=UTF-8'
      },
      // 参数
      data: {},
      // 设置超时时间
      timeout: 10000,
      // 携带凭证
      withCredentials: true,
      // 返回数据类型
      responseType: 'json'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • request.js
    import axios from 'axios';
    import config from './config';
    import Cookies from "js-cookie";
    import router from '@/router'
    
    const service  = axios.create({
      baseURL: config.baseUrl,
      headers: config.headers,
      timeout: config.timeout,
      withCredentials: config.withCredentials
    })
    
    
    service.interceptors.request.use(
      config => {
        let token = Cookies.get('token')
        // 发送请求时携带token
        if (token) {
          config.headers.token = token
        } else {
          // 重定向到登录页面
          router.push('/login')
        }
        return config
      },
      error => {
        // 请求发生错误时
        console.log('request:', error)
        // 判断请求超时
        if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
          console.log('timeout请求超时')
        }
        // 需要重定向到错误页面
        const errorInfo = error.response
        console.log(errorInfo)
        if (errorInfo) {
          error = errorInfo.data  // 页面那边catch的时候就能拿到详细的错误信息,看最下边的Promise.reject
          const errorStatus = errorInfo.status; // 404 403 500 ...
          router.push({
            path: `/error/${errorStatus}`
          })
        }
        return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
      }
    )
    
    service.interceptors.response.use(
      response => {
        return response.data
      },
      err => {
        if (err && err.response) {
          switch (err.response.status) {
            case 400:
              err.message = '请求错误'
              break
            case 401:
              err.message = '未授权,请登录'
              break
            case 403:
              err.message = '拒绝访问'
              break
            case 404:
              err.message = `请求地址出错: ${err.response.config.url}`
              break
            case 408:
              err.message = '请求超时'
              break
            case 500:
              err.message = '服务器内部错误'
              break
            case 501:
              err.message = '服务未实现'
              break
            case 502:
              err.message = '网关错误'
              break
            case 503:
              err.message = '服务不可用'
              break
            case 504:
              err.message = '网关超时'
              break
            case 505:
              err.message = 'HTTP版本不受支持'
              break
            default:
          }
        }
        console.error(err)
        return Promise.reject(err) // 返回接口返回的错误信息
      }
    )
    
    export default service
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • api.js
    /* 
     * 接口统一集成模块
     */
    import * as login from './modules/login'
    import * as user from './modules/user'
    import * as menu from './modules/menu'
    
    
    // 默认全部导出
    export default {
        login,
        user,
        menu,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    login.js

    import service from '@/api/request.js';     //导入request中创建的实例
    //参考文档:http://axios-js.com/zh-cn/docs/
    
    /* 
     * 系统登录模块
     */
    // 获取验证码
    export const getverifyCode = () =>{
        return service.request({
            method: "get",
            url: "/getVerifyCode"
            // data: data, 左边的data是变量名(key)后台接收的。右边的Data是接收的参数。如果两者都是同名的情况下,可以写成单一个即可(ES6的写法)
        })
    }
    
    // 登录接口
    export const login = (data) =>{
        return service.request({
            url: '/login',
            method: 'post',
            data: data
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • index.js 将api.js注册到原型上面
    // 导入所有接口
    import api from './api'
    
    const install = Vue => {
        if (install.installed)
            return;
    
        install.installed = true;
    
        Object.defineProperties(Vue.prototype, {
            // 注意,此处挂载在 Vue 原型的 $api 对象上
            $api: {
                get() {
                    return api
                }
            }
        })
    }
    
    export default install
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 使用
        getVeCode(){
           this.$api.login.getverifyCode().then(res=>{
            console.log(res)
            this.imageUrl = res.data.base64
            this.code = res.data.code
          })
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    store模块化封装

    • 参考:https://vuex.vuejs.org/zh/

    • 注意: 使用namespaced: true 需要在调用添加具体的模块名比如 this.$store.commit(‘app/onCollapse’)

    • 目录结构

    在这里插入图片描述

    • index.js
    import Vue from 'vue'
    import vuex from 'vuex'
    
    Vue.use(vuex);
    
    // 引入子模块
    import app from './modules/app'
    import tab from './modules/tab'
    import user from './modules/user'
    import menu from './modules/menu'
    
    const store = new vuex.Store({
        modules: {
            app: app,
            tab: tab,
            user: user,
            menu: menu
        }
    })
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • app.js actions一般是存放请求的,请求放这里是异步的,但是挂载到原型上的api在js好像使用不了
    • 这个设计只在有必要的时候使用,像公共数据可以使用异步请求,然后交给vuex管理,有个弊端是刷新页面之后,vuex管理数据就没了,一般都会用cookie、localstorage、sessionstorage来存储公共的数据
    import {login,getverifyCode} from '@/api/modules/login.js'
    
    const state = {
        appName: "Mango Platform",  // 应用名称
            themeColor: "#14889A",  // 主题颜色
                oldThemeColor: "#14889A",   // 上一次主题颜色
                    collapse: false,  // 导航栏收缩状态
                        menuRouteLoaded: false    // 菜单和路由是否已经加载
    }
    const  getters = {
        collapse(state){// 对应着上面state
            return state.collapse
        }
    }
    const mutations = {
        onCollapse(state){  // 改变收缩状态
            state.collapse = !state.collapse
        },
        setThemeColor(state, themeColor){  // 改变主题颜色
            state.oldThemeColor = state.themeColor
            state.themeColor = themeColor
        },
        menuRouteLoaded(state, menuRouteLoaded){  // 改变菜单和路由的加载状态
            state.menuRouteLoaded = menuRouteLoaded;
        }
    }
    const actions= {
        getverifyCode(content) {
            return new Promise((resolve, reject) => {
                getverifyCode().then(response=>{
                    resolve(response)
                }).catch(error => {
                    reject(error)
                })
            })
        },
    
        
        // getClassifyList(content,data) {
        //     return new Promise((resolve, reject) => {
        //         getClassifyList(data).then(response => {
        //             resolve(response)
        //         }).catch(error => {
        //             reject(error)
        //         })
        //     })
        // },
    }
    
    
    export default {
        //namespaced: true,
        state,
        getters,
        mutations,
        actions
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    svg制作

    • 目录结构

    在这里插入图片描述

    • 依赖导入
    npm install --save svg-sprite-loader
    
    • 1
    • webpack配置关于svg的loader
        //链式配置配置规则,这里是svg一些配置
        chainWebpack: config => {
            config.module
                .rule('svg')
                .exclude.add(resolve('src/assets/icons')) //对应下面配置svg的路径
                .end();
    
            config.module
                .rule('icons')
                .test(/\.svg$/)
                .include.add(resolve('src/assets/icons')) //对应下面配置svg的路径
                .end()
                .use('svg-sprite-loader')
                .loader('svg-sprite-loader')
                .options({
                    symbolId: 'icon-[name]'
                })
                .end();
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • index.js 加载svg文件
    import Vue from 'vue';
    import SvgIcon from '@/components/SvgIcon/index.vue';
    // import SvgIcon from '@/components/SvgIcon.vue';
    Vue.component('svg-icon',SvgIcon)
    
    
    //读取指定目录的所有文件,用来读取svg文件夹下存的图标
    /**
    第一个:目录
    第二个:是否遍历子级目录
    第三个:定义遍历文件规则
     */
    const req = require.context('./svg', false, /\.svg$/)
    const requireAll = requireContext => {
        console.log(requireContext.keys().map(requireContext));
      return requireContext.keys().map(requireContext);
    }
    requireAll(req)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • index.vue svg组件
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 使用svg组件
      <svg-icon  :iconClass="collapse === false ? 'fold-left' : 'fold-right' "  :className="collapse === false ? 'fold-left' : 'fold-right'" />
    
    • 1

    i18n中英文切换

    npm install --save vue-i18n
    //vue2 用下面这个
    npm i --save vue-i18n@8
    
    • 1
    • 2
    • 3
    • en_us.json /src/assets/languages
    {
        "common": {
            "home": "Home",
            "login": "Login",
            "logout": "Logout",
            "doc": "Document",
            "blog": "Blog",
            "projectRepo": "Project",
            "myMsg": "My Message",
            "config": "Config",
            "backup": "Backup",  
            "restore": "Restore",  
            "backupRestore": "Backup Restore",  
            "versionName": "Version",  
            "exit": "Exit"
        },
        "action": {
            "operation": "Operation",
            "add": "Add",
            "edit": "Edit",
            "delete": "Delete",
            "batchDelete": "Batch Delete",
            "search": "Search",
            "loading": "loading",
            "submit": "Submit",
            "comfirm": "Comfirm",
            "cancel": "Cancel",
            "reset": "Reset"
        },
        "nav": {
            "home": "Home", 
            "monitor": "Monitor",
            "analyze": "Analyze",
            "alarm": "Alarm", 
            "maintenance": "Maintenance", 
            "config": "Config", 
            "device": "Device", 
            "scada": "Scada"
          },
          "confirm": {
            "ok": "Logout", 
            "cancel": "Cancel", 
            "content": "Are you sure you want to quit the system?",
            "logout": "Logout"
          },
          "breadCrumb": {
            "home": "Home",
            "sysIntro": "SysIntro",
            "sysAdmin": "SysAdmin",
            "userAdmin": "UserAdmin",
            "roleAdmin": "RoleAdmin",
            "menuAdmin": "MenuAdmin",
            "loginLog": "LoginLog",
            "operateLog": "OperateLog"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • zh_cn.json
    {
        "common": {
            "home": "首页",
            "login": "登录",
            "logout": "退出登录",
            "doc": "文档",
            "blog": "博客",
            "projectRepo": "项目",
            "myMsg": "我的消息",
            "config": "系统配置",
            "backup": "备份",
            "restore": "还原",
            "backupRestore": "备份还原",
            "versionName": "版本名称",
            "exit": "退出"
        },
        "action": {
            "operation": "操作",
            "add": "新增",
            "edit": "编辑",
            "delete": "删除",
            "batchDelete": "批量删除",
            "search": "查询",
            "loading": "拼命加载中",
            "submit": "提交",
            "comfirm": "确定",
            "cancel": "取消",
            "reset": "重置"
        },
        "nav": {
            "home": "首页",
            "monitor": "监控",
            "analyze": "分析",
            "alarm": "报警",
            "maintenance": "运维",
            "config": "配置",
            "device": "设备",
            "scada": "画面"
        },
        "confirm": {
            "ok": "确认",
            "cancel": "取消",
            "content": "你确认要退出系统吗?",
            "logout": "退 出"
        },
        "breadCrumb": {
            "home": "首页",
            "sysIntro": "系统介绍",
            "sysAdmin": "系统管理",
            "userAdmin": "用户管理",
            "roleAdmin": "角色管理",
            "menuAdmin": "菜单管理",
            "loginLog": "登录日志",
            "operateLog": "操作日志"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • index.js /src/i18n/
    import Vue from 'vue'
    import VueI18n from 'vue-i18n'
    
    Vue.use(VueI18n)
     
    // 注册i18n实例并引入语言文件,文件格式等下解析
    const i18n = new VueI18n({
      locale: 'zh_cn',
      messages: {
        'zh_cn': require('@/assets/languages/zh_cn.json'),
        'en_us': require('@/assets/languages/en_us.json')
      }
    })
    
    export default i18n
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • main.js
    //i18n插件
    import i18n from './i18n'
    new Vue({
      i18n,
      router,
      store,
      render: h => h(App)
    }).$mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 使用
    //使用$t(xxx),xxx是i18n模块下的变量,这样方式不太适用从后端获取的动态菜单数据
    {{$t("common.doc")}}
    // 语言切换
    changeLanguage(lang) {
    	lang === '' ? 'zh_cn' : lang
    	this.$i18n.locale = lang
    	this.langVisible = false
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    主题色组件

    • index.vue /src/components/ThemePicker
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • app.js /store/modules
    import { login, getverifyCode } from '@/api/modules/login.js'
    
    const state = {
        appName: "konglz-admin",  // 应用名称
        themeColor: "#14889A",  // 主题颜色
        oldThemeColor: "#14889A",   // 上一次主题颜色
        collapse: false,  // 导航栏收缩状态
        menuRouteLoaded: false,   // 菜单和路由是否已经加载
    }
    const getters = {
        collapse(state) {// 对应着上面state
            return state.collapse
        }
    }
    const mutations = {
        onCollapse(state) {  // 改变收缩状态
            state.collapse = !state.collapse
        },
        setThemeColor(state, themeColor) {  // 改变主题颜色
            state.oldThemeColor = state.themeColor
            state.themeColor = themeColor
        },
        menuRouteLoaded(state, menuRouteLoaded) {  // 改变菜单和路由的加载状态
            state.menuRouteLoaded = menuRouteLoaded;
        },
    }
    const actions = {
        getverifyCode(content) {
            return new Promise((resolve, reject) => {
                getverifyCode().then(response => {
                    resolve(response)
                }).catch(error => {
                    reject(error)
                })
            })
        },
    
    
        // getClassifyList(content,data) {
        //     return new Promise((resolve, reject) => {
        //         getClassifyList(data).then(response => {
        //             resolve(response)
        //         }).catch(error => {
        //             reject(error)
        //         })
        //     })
        // },
    
      
    }
    
    
    export default {
        //namespaced: true,
        state,
        getters,
        mutations,
        actions
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • Head.vue /src/views/layout/
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215

    左侧栏菜单树组件

    • index.vue /src/components/MenuTree/
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • Nav.vue /src/views/layout/
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129

    面包屑

    • index.vue
    
    
    
    // Breadcrumb.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • Head.vue /src/views/layout/
    <template> 
      <div class="headbar" :style="{'background':themeColor}"  
        :class="collapse?'position-collapse-left':'position-left'">
        <!-- 导航收缩 -->
        <span class="hamburg">
          <el-menu class="el-menu-demo" :background-color="themeColor" text-color="#fff" 
            :active-text-color="themeColor" mode="horizontal">
            <el-menu-item index="1" @click="onCollapse">
              <!-- <hamburger :isActive="collapse"></hamburger> -->
               <svg-icon  :iconClass="collapse === false ? 'fold-left' : 'fold-right' "  :className="collapse === false ? 'fold-left' : 'fold-right'" />
            </el-menu-item>
          </el-menu>
    
        </span>
        
        <span class="bread">  <bread-crumb></bread-crumb></span>
         
        <!-- 工具栏 -->
        <span class="toolbar">
          <el-menu class="el-menu-demo" :background-color="themeColor" text-color="#14889A" 
            :active-text-color="themeColor" mode="horizontal">
            <el-menu-item index="1">
              <!-- 主题切换 -->
              <theme-picker class="theme-picker" :default="themeColor" 
                @onThemeChange="onThemeChange">
              </theme-picker>
            </el-menu-item>
            <el-menu-item index="2" v-popover:popover-lang>
              <!-- 语言切换 -->
              <li style="color:#fff;" class="fa fa-language fa-lg"></li>
              <el-popover ref="popover-lang" placement="bottom-start" trigger="click" v-model="langVisible">
                <div class="lang-item" @click="changeLanguage('zh_cn')">简体中文</div>
                <div class="lang-item" @click="changeLanguage('en_us')">English</div>
              </el-popover>
            </el-menu-item>
            <el-menu-item index="3" v-popover:popover-message>
              <!-- 我的私信 -->
              <el-badge :value="5" :max="99" class="badge">
                <li style="color:#fff;" class="fa fa-envelope-o fa-lg"></li>
              </el-badge>
              <el-popover ref="popover-message" placement="bottom-end" trigger="click">
                <message-panel></message-panel>
              </el-popover>
            </el-menu-item>
            <el-menu-item index="4" v-popover:popover-notice>
              <!-- 系统通知 -->
              <el-badge :value="4" :max="99" class="badge">
                <li style="color:#fff;" class="fa fa-bell-o fa-lg"></li>
              </el-badge>
              <el-popover ref="popover-notice" placement="bottom-end" trigger="click">
                <notice-panel></notice-panel>
              </el-popover>
            </el-menu-item>
            <el-menu-item index="5" v-popover:popover-personal>
              <!-- 用户信息 -->
              <!-- <span class="user-info"><img :src="user.avatar" />{{user.name}}</span> -->
                      <span class="el-dropdown-link">
              <el-avatar :size="40">klz</el-avatar>
            </span>
              <el-popover ref="popover-personal" placement="bottom-end" trigger="click" :visible-arrow="false">
                <personal-panel :user="user"></personal-panel>
              </el-popover>
            </el-menu-item>
          </el-menu>
        </span>
      </div>
    </template>
    
    <script>
    import BreadCrumb from '@/components/BreadCrumb'
    import SvgIcon from '@/components/SvgIcon'
    import { mapState } from 'vuex'
    // import Hamburger from "@/components/Hamburger"
    import ThemePicker from "@/components/ThemePicker"
    // import NoticePanel from "@/views/Core/NoticePanel"
    // import MessagePanel from "@/views/Core/MessagePanel"
    // import PersonalPanel from "@/views/Core/PersonalPanel"
    export default {
      components:{
        // Hamburger,
        ThemePicker,
        BreadCrumb
        // NoticePanel,
        // MessagePanel,
        // PersonalPanel
      },
      data() {
        return {
          user: {
            name: "konglz",
            avatar: "",
            role: "超级管理员",
            registeInfo: "注册时间:2018-12-20 "
          },
          activeIndex: '1',
          langVisible: false,
          foldMenu: 'fold-left',
          // collapse: this.$store.app.state.collapse
        }
      },
      methods: {
        openWindow(url) {
          window.open(url)
        },
        selectNavBar(key, keyPath) {
          console.log(key, keyPath)
        },
        // 折叠导航栏
        onCollapse: function() {
          this.$store.commit('onCollapse')
          // console.log(this.collapse)
            // if(this.collapse){
            //     this.foldMenu = 'fold-right'
            // }else{
            //     this.foldMenu = 'fold-left'
            // } 
        },
        // 切换主题
        onThemeChange: function(themeColor) {
          this.$store.commit('setThemeColor', themeColor)
        },
        // 语言切换
        changeLanguage(lang) {
          lang === '' ? 'zh_cn' : lang
          this.$i18n.locale = lang
          this.langVisible = false
        }
      },
      mounted() {
        var user = sessionStorage.getItem("user")
        // if (user) {
        //   this.user.name = user
        //   this.user.avatar = require("@/assets/user.png")
        // }
      },
      computed:{
        ...mapState({
          themeColor: state=>state.app.themeColor,
          collapse: state=>state.app.collapse
        })
      }
    }
    </script>
    
    <style scoped lang="scss">
    .headbar {
      position: fixed;
      top: 0;
      right: 0;
      z-index: 1030;
      height: 60px;
      line-height: 60px;
      border-color: rgba(180, 190, 190, 0.8);
      border-left-width: 1px;
      border-left-style: solid;
    }
    .hamburg {
      float: left;
    }
    
    .bread{
      float: left;
      padding: 0px;
      margin: 0px;
      // border: 1px solid red;
      margin-top: 20px;
    }
    
    .navbar {
      float: left;
    }
    .toolbar {
      float: right;
    }
    .lang-item {
      font-size: 16px;
      padding-left: 8px;
      padding-top: 8px;
      padding-bottom: 8px;
      cursor: pointer;
    }
    .lang-item:hover {
      font-size: 18px;
      background: #b0d6ce4d;
    }
    .user-info {
      font-size: 20px;
      color: #fff;
      cursor: pointer;
      img {
        width: 40px;
        height: 40px;
        border-radius: 10px;
        margin: 10px 0px 10px 10px;
        float: right;
      }
    }
    .badge {
      line-height: 18px;
    }
    .position-left {
      left: 200px;
    }
    .position-collapse-left {
      left: 65px;
    }
    
    // svg格式图标的大小,不知道为什么设置
      // .svg-icon {
      //   font-size: 25px;
      //   width: 60px;
      //   cursor: pointer;
      //   align-items: center;
      // }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215

    动态菜单与动态路由

    • index.js 路由配置文件
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import api from '@/api/api'
    import store from '@/store'
    import { getIFramePath, getIFrameUrl } from '@/utils/iframe'
    
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: '/',
        name: '首页',
        component: () => import('@/views/layout/Home.vue'),
        children: [
          {
            path: '',
            name: '系统介绍',
            component: () => import('@/views/Intro.vue'),
            meta: {
              icon: 'fa fa-home fa-lg',
              index: 0
            }
          }
        ]
    
      },
    
      {
        path: '/login',
        name: 'Login',
        component: () => import('@/views/Login.vue')
      },
    
      // {
      //   path: '/register',
      //   name: 'Register',
      //   component: ()=> import('@/views/Register.vue')
      // },
    
    ]
    
    const router = new VueRouter({
      //路由模式默认是hash会在拒绝后面加上#
      // mode: 'history',
      routes
    })
    
    //路由守卫
    router.beforeEach((to, from, next) => {
      // 登录界面登录成功之后,会把用户信息保存在会话
      // 存在时间为会话生命周期,页面关闭即失效。
      let userName = sessionStorage.getItem('user')
      if (to.path === '/login') {
        // 如果是访问登录界面,如果用户会话信息存在,代表已登录过,跳转到主页
        if(userName) {
          next({ path: '/' })
        } else {
          next()
        }
      } else {
        if (!userName) {
          // 如果访问非登录界面,且户会话信息不存在,代表未登录,则跳转到登录界面
          next({ path: '/login' })
        } else {
          // 加载动态菜单和路由
          addDynamicMenuAndRoutes(userName, to, from)
          next()
        }
      }
    })
    
    
    /**
    * 加载动态菜单和路由
    */
    function addDynamicMenuAndRoutes(userName, to, from) {
      // 处理IFrame嵌套页面
      handleIFrameUrl(to.path)
      if(store.state.app.menuRouteLoaded) {
        console.log('动态菜单和路由已经存在.')
        return
      }
      api.menu.findNavTree({'userName':userName})
      .then(res => {
        // 添加动态路由
        let dynamicRoutes = addDynamicRoutes(res.data)
        // 处理静态组件绑定路由
        router.options.routes[0].children = router.options.routes[0].children.concat(dynamicRoutes)
        router.addRoutes(router.options.routes)
        // 保存加载状态
        store.commit('menuRouteLoaded', true)
        // 保存菜单树
        store.commit('setNavTree', res.data)
      }).then(res => {
        api.user.findPermissions({'name':userName}).then(res => {
          // 保存用户权限标识集合
          store.commit('setPerms', res.data)
        })
      })
      .catch(function(res) {
      })
    }
    
    /**
     * 处理IFrame嵌套页面
     */
    function handleIFrameUrl(path) {
      // 嵌套页面,保存iframeUrl到store,供IFrame组件读取展示
      let url = path
      let length = store.state.iframe.iframeUrls.length
      for(let i=0; i<length; i++) {
        let iframe = store.state.iframe.iframeUrls[i]
        if(path != null && path.endsWith(iframe.path)) {
          url = iframe.url
          store.commit('setIFrameUrl', url)
          break
        }
      }
    }
    
    /**
    * 添加动态(菜单)路由
    * @param {*} menuList 菜单列表
    * @param {*} routes 递归创建的动态(菜单)路由
    */
    function addDynamicRoutes (menuList = [], routes = []) {
     var temp = []
     for (var i = 0; i < menuList.length; i++) {
       if (menuList[i].children && menuList[i].children.length >= 1) {
         temp = temp.concat(menuList[i].children)
       } else if (menuList[i].url && /\S/.test(menuList[i].url)) {
          menuList[i].url = menuList[i].url.replace(/^\//, '')
          // 创建路由配置
          var route = {
            path: menuList[i].url,
            component: null,
            name: menuList[i].name,
            meta: {
              icon: menuList[i].icon,
              index: menuList[i].id
            }
          }
          let path = getIFramePath(menuList[i].url)
          if (path) {
            // 如果是嵌套页面, 通过iframe展示
            route['path'] = path
            route['component'] = resolve => require([`@/views/IFrame/IFrame`], resolve)
            // 存储嵌套页面路由路径和访问URL
            let url = getIFrameUrl(menuList[i].url)
            let iFrameUrl = {'path':path, 'url':url}
            store.commit('addIFrameUrl', iFrameUrl)
          } else {
           try {
             // 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储
             // 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到
             let array = menuList[i].url.split('/')
             let url = ''
             for(let i=0; i<array.length; i++) {
                url += array[i].substring(0,1).toUpperCase() + array[i].substring(1) + '/'
             }
             url = url.substring(0, url.length - 1)
             route['component'] = resolve => require([`@/views/${url}`], resolve)
           } catch (e) {}
         }
         routes.push(route)
       }
     }
     if (temp.length >= 1) {
       addDynamicRoutes(temp, routes)
     } else {
       console.log('动态路由加载...')
       console.log(routes)
       console.log('动态路由加载完成.')
     }
     return routes
    }
    
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • app.js store模块下, 存储一些状态量
    import { login, getverifyCode } from '@/api/modules/login.js'
    
    const state = {
        appName: "Mango Platform",  // 应用名称
        themeColor: "#14889A",  // 主题颜色
        oldThemeColor: "#14889A",   // 上一次主题颜色
        collapse: false,  // 导航栏收缩状态
        menuRouteLoaded: false    // 菜单和路由是否已经加载
    }
    const getters = {
        collapse(state) {// 对应着上面state
            return state.collapse
        }
    }
    const mutations = {
        onCollapse(state) {  // 改变收缩状态
            state.collapse = !state.collapse
        },
        setThemeColor(state, themeColor) {  // 改变主题颜色
            state.oldThemeColor = state.themeColor
            state.themeColor = themeColor
        },
        menuRouteLoaded(state, menuRouteLoaded) {  // 改变菜单和路由的加载状态
            state.menuRouteLoaded = menuRouteLoaded;
        }
    }
    const actions = {
        getverifyCode(content) {
            return new Promise((resolve, reject) => {
                getverifyCode().then(response => {
                    resolve(response)
                }).catch(error => {
                    reject(error)
                })
            })
        },
    
    
        // getClassifyList(content,data) {
        //     return new Promise((resolve, reject) => {
        //         getClassifyList(data).then(response => {
        //             resolve(response)
        //         }).catch(error => {
        //             reject(error)
        //         })
        //     })
        // },
    }
    
    
    export default {
        namespaced: true,
        state,
        getters,
        mutations,
        actions
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • menu.js 存储侧边栏菜单
    export default {
        state: {
            navTree: [],  // 导航菜单树
        },
        getters: {
       
        },
        mutations: {
            setNavTree(state, navTree){  // 设置导航菜单树
                state.navTree = navTree;
            }
        },
        actions: {
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • tab.js 存储标签
    export default {
      state: {
        // 主入口标签页
        mainTabs: [],
        // 当前标签页名
        mainTabsActiveName: ''
      },
      mutations: {
        updateMainTabs (state, tabs) {
          state.mainTabs = tabs
        },
        updateMainTabsActiveName (state, name) {
          state.mainTabsActiveName = name
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    动态菜单生成tab标签页

    • tab.js /store/modules/
    export default {
      state: {
        // 主入口标签页
        mainTabs: [],
        // 当前标签页名
        mainTabsActiveName: ''
      },
      mutations: {
        updateMainTabs (state, tabs) {
          state.mainTabs = tabs
        },
        updateMainTabsActiveName (state, name) {
          state.mainTabsActiveName = name
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • Nav.vue /src/views/layout/
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • Main.vue /src/views/layout/
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159

    组件权限

    • 一般设计权限是到侧边菜单或者按钮及区别,所以我们要封装组件级别的权限常见的有按钮、表格
    • IButton /src/viewscomponentPermission/
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • index.js /src/permission
    import store from '@/store'
    /**
     * 判断用户是否拥有操作权限
     * 根据传入的权限标识,查看是否存在用户权限标识集合
     * @param perms
     */
    export function hasPermission (perms) {
        let hasPermission = false
        let permissions = store.state.user.perms
        for(let i=0, len=permissions.length; i<len; i++) {
            if(permissions[i] === perms) {
                hasPermission = true;
                break
            }
        }
        return hasPermission
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 使用
    		
    		
    
    • 1
    • 2

    效果图

    在这里插入图片描述

  • 相关阅读:
    使用领域引导图卷积神经网络GCNN增强基于脑电图EEG的神经疾病诊断完整代码
    HTML标签学习
    主数据管理理论与实践
    如何安装虚拟机
    Python 接口测试框架
    react-面试题
    C语言中volatile修饰变量,及其余const的区别
    鸿鹄电子招投标系统:基于Spring Boot、Mybatis、Redis和Layui的企业电子招采平台源码与立项流程
    主键、外键、建表范式、MySQL索引、用户管理
    opencv入门笔记(二)
  • 原文地址:https://blog.csdn.net/qq_42977003/article/details/126912964