• 项目总结:中后台管理系统类


    项目类一:中后台管理系统类

    中后台系统类多为内部人员使用。采用Vue+elementUI开发。

    技术选型的理由有以下几点:

    1. 此类项目多为表单、列表、数据看板,vue生态有成熟的解决方案。
    2. 产品设计风格与elementUI设计风格相近。
    3. 团队成员更擅长使用Vue开发后台管理类项目,可以快速开发,节省成本。

    一般的中后台管理系统实现的功能有以下几类:

    1. 模块化开发:功能、组件、路由、vuex的实现。
    2. 权限控制:请求权限,动态路由,按钮权限的实现。
    3. 数据看板:Echart的使用。
    4. 项目mock:后端未提供API时,模拟数据。
    5. 多环境配置:dev(开发)、sit(测试)、stage(预发)prod(生产)
    6. 国际化:i18n的配置。

    模块化开发

    模块化开发可以解决命名冲突、文件依赖的问题,方便多人协作开发,提升开发效率,方便后期维护。

    1. 公共组件抽离出来可以复用。
    2. 页面、路由、vuex(namescaped)根据模块划分。

    权限控制

    请求权限的实现

    1. 登录拿到token,存入cookie
    2. 每一个请求,都会在请求header里面携带用户的token
    3. 后端会根据该token来验证用户是否有权限执行该操作

    动态路由的实现

    1. 创建vue实例的时候挂载vue-router,登录页和不用权限的页面
    2. 通过token获取用户的role和有权限的api列表perms['GET /aaa','POST /bbb',...]
    3. 算出其相应有权限的路由,通过router.addRoutes动态挂载路由
    4. 使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件
    // 用户登录拿到的perms为['GET /admin/goods/list', ...]
    let userInfo = {
      role: 'operator',
      perms: ['GET /admin/goods/list']
    }
    // 路由中的权限在meta中的perms中设置,该页面用到的的api
    let childRoute = {
      path: 'list',
      component: () => import('@/views/goods/list'),
      name: 'goodsList',
      meta: {
        perms: ['GET /admin/goods/list', 'POST /admin/goods/delete'],
        title: '商品列表',
        noCache: true
      }
    }
    // 在 vuex 中进行管理,算出其相应有权限的路由
    const permission = {
      state: {
        routers: constantRouterMap,
        addRouters: []
      },
      mutations: {
        SET_ROUTERS: (state, routers) => {
          state.addRouters = routers
          state.routers = constantRouterMap.concat(routers)
        }
      },
      actions: {
        GenerateRoutes({ commit }, data) {
          return new Promise(resolve => {
            const { perms } = data
            let accessedRouters
            if (perms.includes('*')) {
              accessedRouters = asyncRouterMap
            } else {
              accessedRouters = filterAsyncRouter(asyncRouterMap, perms)
            }
            commit('SET_ROUTERS', accessedRouters)
            resolve()
          })
        }
      }
    }
    
    // 在router.beforeEach中判断在第一次拉取权限时使用addRoutes添加可访问路由表
    router.beforeEach((to, from, next) => {
      // 判断当前用户是否已拉取完user_info信息
      if (getToken() && store.getters.perms.length === 0) { 
        // 拉取user_info
        store.dispatch('GetUserInfo').then(res => {
          // note: perms must be a array! such as: ['GET /aaa','POST /bbb']
          const perms = res.data.data.perms
          // 根据perms权限生成可访问的路由表
          store.dispatch('GenerateRoutes', { perms }).then(() => { 
            // 动态添加可访问路由表
            router.addRoutes(store.getters.addRouters) 
            // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
            next({ ...to, replace: true }) 
          })
        })
      }
    })
    

    按钮权限的实现

    1. 通过比较该按钮请求的api是否在该用户的有权限的api列表perms中
    2. 注册permission指令,如果权限不符合,就移除该节点
    <el-button v-permission="['POST /admin/goods/delete']" type="danger" @click="handleDelete">删除</el-button>
    
    const permission = {
      inserted(el, binding, vnode) {
        const { value } = binding
        const perms = store.getters && store.getters.perms
    
        if (value && value instanceof Array && value.length > 0) {
          const permissions = value
    
          var hasPermission = false
    
          if (perms.indexOf('*') >= 0) {
            hasPermission = true
          } else {
            hasPermission = perms.some(perm => {
              return permissions.includes(perm)
            })
          }
    
          if (!hasPermission) {
            el.parentNode && el.parentNode.removeChild(el)
          }
        } else {
          throw new Error(`need perms! Like v-permission="['GET /aaa','POST /bbb']"`)
        }
      }
    }
    
    const install = function(Vue) {
      Vue.directive('permission', permission)
    }
    
    if (window.Vue) {
      window['permission'] = permission
      Vue.use(install)
    }
    
    permission.install = install
    export default permission
    

    Echart的使用

    1. 按需引入ECharts
    // 引入 ECharts 主模块
    var echarts = require('echarts/lib/echarts');
    // 引入柱状图
    require('echarts/lib/chart/bar');
    // 引入提示框和标题组件
    require('echarts/lib/component/tooltip');
    require('echarts/lib/component/title');
    
    1. 在vue中的mounted生命周期里声明初始化ECharts。(ECharts初始化必须绑定dom。)
    mounted() {
      this.initCharts();
    },
    methods: {
      initCharts() {
        this.chart = echarts.init(this.$el);
        this.setOptions();
      },
      setOptions() {
        this.chart.setOption({
          title: {
            text: 'ECharts 入门示例'
          },
          tooltip: {},
          xAxis: {
            data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
          },
          yAxis: {},
          series: [{
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }]
        })
      }
    }
    
    
    1. 数据变化时,通过watch触发setOptions

    项目mock

    项目mock 模拟产生一些虚拟的数据,可以让前端在后端接口还没有开发出来时独立开发,加快开发速度。

    1. 使用 promise 和 setTimeout 模拟数据
    2. 使用 mockjs,mockjs可以拦截ajax请求,返回设定好的数据。
    3. 使用例如easy-mock/doc Docker等

    多环境配置:dev(开发)、sit(测试)、stage(预发)prod(生产)

    {
      "scripts": {
        "dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
        "build:stage": "cross-env NODE_ENV=production env_config=stage node build/build.js",
        "build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
        "lint": "eslint --ext .js,.vue src",
        "test": "npm run lint",
        "precommit": "lint-staged"
      },
    }
    

    在config文件夹下的prod.env.js配置如下

    module.exports = {
    	NODE_ENV: '"production"',
    	ENV_CONFIG: '"prod"',
      BASE_API: '"http://xxx/admin"'
    }
    

    在代码中即可用process.env.env_config使用判断啦

    国际化:i18n的配置。

    1. 安装npm install vue-i18n -D
    2. 在src下新建文件i18n.js
    import Vue from 'vue'
    import VueI18n from 'vue-i18n'
    
    Vue.use(VueI18n)
    
    export const i18n = new VueI18n({
      locale: 'CN',
      messages: {
        'CN': require('./assets/lang/cn'),
        'EN': require('./assets/lang/en')
      }
    })
    
    1. 在assets/lang下新建文件en.js和cn.js
    // en.js
    export const message = {
      hello: 'Hello, World'
    }
    // cn.js
    export const message = {
      hello: '你好, 世界'
    }
    
    1. 在main.js里引入i18n.js
    import { i18n } from './i18n'
    new Vue({
      el: '#app',
      i18n,
      ...
    })
    
    1. 使用<span>{{ $t("message.hello") }}</span>
  • 相关阅读:
    作业作业
    Alpha 冲刺 (4/10)
    Alpha 冲刺 (3/10)
    Alpha 冲刺 (2/10)
    Alpha 冲刺 (1/10)
    项目需求分析评审表
    项目需求分析答辩总结
    项目选题报告答辩总结
    UML
    各组项目答辩评分与存在问题
  • 原文地址:https://www.cnblogs.com/lilicat/p/12088075.html
Copyright © 2020-2023  润新知