项目4:后台管理的开发和使用(前端)
1.npm包管理器的基本学习
2.利用现成后台管理系统开发
3.后台管理系统的路由配置
4.后台管理系统的地址访问配置
5.前后端联调
6.完善积分等级的前端系统
7.对前端系统的全面分析(Vue组件)
项目4:积分等级表接口的开发和使用(前端)
1.npm包管理器基本学习
①npm是什么?
- npm类似于后端的maven,用于管理前端依赖包的
 
②npm常用命令
- npm init -y
初始化一个项目,生成一个package.json,里面包含依赖管理 - npm i axios@0.08.x
i为install的简写,下载axios的具体版本号,放在node_modules - npm i -D mockjs
-D为–save-dev的简写,即开发环境时导入的包,打包时不导入 - npm i
如果只有package.json而没有具体的node_modules,可以使用此命令下载包 - 注意npm修改下载镜像
 
#经过下面的配置,以后所有的 npm install 都会经过淘宝的镜像地址下载
npm config set registry https://registry.npm.taobao.org 
#查看npm配置信息
npm config list
 
2.利用现成后台管理系统开发
①vue-element-admin
- 简化版(提供较少功能组件)
 - 全面版(提供完整的功能组件)
 
②安装和运行
- 下载vue-admin-template-permission-control.zip
 - 解压并进入目录
 - 安装依赖并启动
npm install
npm run dev 
③配置前端页面
- 禁用ESLint语法检查(过于严格)
 - 使用prettiert格式化配置(单引号格式,无分号格式)
 - vue.config.js中30行禁用ESLint语法检查
 
lintOnSave: false, 
 
- 在根目录下创建.prettierrc文件
 
{
  "semi": false,
  "singleQuote": true,
  "htmlWhitespaceSensitivity": "ignore"
}
 
- 简单修改配置
修改首页名称
src/settings.js 第3行处修改页面标题

国际化配置
rc/main.js 第7行处修改语言(为引入的插件语言而不是网页本身,网页本身组件的名称为英语都是写死的,需要手动修改)

下拉菜单修改
src/layout/components/Navbar.vue

登录页的修改
src/views/login/index.vue修改标题为中文和登录为中文并删除无关组件


 
3.后台管理系统的路由配置
①简介路由配置
- 即通过点击对应的路由标题
 - 跳转到对应的模板中
 
②路由配置
- 定义模板
在src/views文件夹下创建core/integral-grade/list.vue和core/integral-grade/form.vue - 编写模板内容
list.vue 
<template>
  <div class="app-container">
    积分等级列表
  </div>
</template>
 
form.vue
<template>
  <div class="app-container">
    积分等级表单
  </div>
</template>
 
- 编写路由跳转
修改 src/router/index.js 文件,重新定义constantRoutes, 
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
//在整个路由中匹配,匹配到了跳转,没有匹配到有*跳转到404
/* Layout */
import Layout from '@/layout'
/**
 * Note: sub-menu only appear when route children.length >= 1
 * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
 *
 * hidden: true                   if set true, item will not show in the sidebar(default is false)
 * alwaysShow: true               if set true, will always show the root menu
 *                                if not set alwaysShow, when item has more than one children route,
 *                                it will becomes nested mode, otherwise not show the root menu
 * redirect: noRedirect           if set noRedirect will no redirect in the breadcrumb
 * name:'router-name'             the name is used by <keep-alive> (must set!!!)
 * meta : {
    roles: ['admin','editor']    control the page roles (you can set multiple roles)
    title: 'title'               the name show in sidebar and breadcrumb (recommend set)
    icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
    breadcrumb: false            if set false, the item will hidden in breadcrumb(default is true)
    activeMenu: '/example/list'  if set path, the sidebar will highlight the path you set
  }
 */
/**
 * constantRoutes
 * a base page that does not have permission requirements
 * all roles can be accessed
 */
export const constantRoutes = [
  {
    //路径是/login
    path: '/login',
    //登录页的路由
    component: () => import('@/views/login/index'),
    hidden: true,
  },
  //直接可以进入/404页
  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true,
  },
  //只展示首页访问/跳转到/dashboard等于跳转到/src/views/dashboard/index
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/index'),
        meta: { title: '首页', icon: 'dashboard' },
      },
    ],
  },
  {
    //父路由 ,最终完整的路由为/core/integral-grade+子路由的path
    path: '/core/integral-grade',
    //布局组件,在大布局组件下的右侧为路由出口
    component: Layout,
    //访问/core/integral-grade后默认发跳转/core/integral-grade/list
    redirect: '/core/integral-grade/list',
    //名称都不能相同
    name: 'coreIntegralGrade',
    //显示节点名称和图标
    meta: { title: '积分等级管理', icon: 'el-icon-s-marketing' },
    //true任何时候都显示父节点和子节点,false如果只有一个子节点则只显示子节点不显示父节点
    alwaysShow: true,
    //三个子节点,只有两个子节点
    children: [
      {
        path: 'list',
        //为组件节点的定义名称,每个节点的name节点不能相同
        name: 'coreIntegralGradeList',
        //组件节点,引入模块 指向src/views/core/integral-grade/list
        component: () => import('@/views/core/integral-grade/list'),
        meta: { title: '积分等级列表' },
      },
      {
        path: 'create',
        name: 'coreIntegralGradeCreate',
        component: () => import('@/views/core/integral-grade/form'),
        meta: { title: '新增积分等级' },
      },
      {
        //:id是一个占位符,表示这部分url是任何一个id
        path: 'edit/:id',
        name: 'coreIntegralGradeEdit',
        component: () => import('@/views/core/integral-grade/form'),
        meta: { title: '编辑积分等级' },
        //隐藏子节点,希望通过新增积分等级的按钮进入该子节点
        hidden: true,
      },
    ],
  },
  {
    path: '/example',
    component: Layout,
    redirect: '/example/table',
    name: 'Example',
    meta: { title: '例子', icon: 'el-icon-s-help' },
    children: [
      {
        path: 'table',
        name: 'Table',
        component: () => import('@/views/table/index'),
        meta: { title: 'Table', icon: 'table' },
      },
      {
        path: 'tree',
        name: 'Tree',
        component: () => import('@/views/tree/index'),
        meta: { title: 'Tree', icon: 'tree' },
      },
    ],
  },
  {
    path: '/form',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'Form',
        component: () => import('@/views/form/index'),
        meta: { title: '表单', icon: 'el-icon-goods' },
      },
    ],
  },
]
/**
 * asyncRoutes
 * the routes that need to be dynamically loaded based on user roles
 */
export const asyncRoutes = [
  {
    path: '/nested',
    component: Layout,
    redirect: '/nested/menu1',
    name: 'Nested',
    meta: {
      title: 'Nested',
      icon: 'nested',
    },
    children: [
      {
        path: 'menu1',
        component: () => import('@/views/nested/menu1/index'), // Parent router-view
        name: 'Menu1',
        meta: { title: 'Menu1' },
        children: [
          {
            path: 'menu1-1',
            component: () => import('@/views/nested/menu1/menu1-1'),
            name: 'Menu1-1',
            meta: { title: 'Menu1-1' },
          },
          {
            path: 'menu1-2',
            component: () => import('@/views/nested/menu1/menu1-2'),
            name: 'Menu1-2',
            meta: { title: 'Menu1-2' },
            children: [
              {
                path: 'menu1-2-1',
                component: () =>
                  import('@/views/nested/menu1/menu1-2/menu1-2-1'),
                name: 'Menu1-2-1',
                meta: { title: 'Menu1-2-1' },
              },
              {
                path: 'menu1-2-2',
                component: () =>
                  import('@/views/nested/menu1/menu1-2/menu1-2-2'),
                name: 'Menu1-2-2',
                meta: { title: 'Menu1-2-2' },
              },
            ],
          },
          {
            path: 'menu1-3',
            component: () => import('@/views/nested/menu1/menu1-3'),
            name: 'Menu1-3',
            meta: { title: 'Menu1-3' },
          },
        ],
      },
      {
        path: 'menu2',
        component: () => import('@/views/nested/menu2/index'),
        meta: { title: 'menu2' },
      },
    ],
  },
  {
    path: 'external-link',
    component: Layout,
    children: [
      {
        path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
        meta: { title: 'External Link', icon: 'link' },
      },
    ],
  },
  // 404 page must be placed at the end !!!
  { path: '*', redirect: '/404', hidden: true },
]
const createRouter = () =>
  new Router({
    // mode: 'history', // require service support
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes,
  })
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}
export default router
 
④结果展示
点击对应的模块,跳转对应的路由,前端访问对应的模板
 


4.后台管理系统的地址访问配置
①需求
- 由于系统是由mock服务器模拟返回的数据的,故我们需要真实发送数据给我们的服务器
 - 故需要改变地址,但是我们不写登录的逻辑,即登录的逻辑仍然用原来的mock服务器
 - 访问后台时,用nginx服务器
在nginx.conf配置 
server {
	listen       80;
	server_name  localhost;
	location ~ /core/ {           
	    proxy_pass http://localhost:8110;
	}
	location ~ /sms/ {           
	    proxy_pass http://localhost:8120;
	}
	location ~ /oss/ {           
            proxy_pass http://localhost:8130;
	}
}
 
②实际地址更改
- 修改全局访问地址,此时所有的访问默认给到nginx服务器
srb-admin.env.development 
# just a flag
ENV = 'development'
# base api
#VUE_APP_BASE_API = '/dev-api'
#原来访问login: http://localhost:8528/dev-api/vue-admin-template/user/login
#更改后访问login:http://localhost/vue-admin-template/user/login
#登录不了,因为更改了
#登录登出为原来的登录登出的模拟(登录登出硬编码)
#其他为访问我们的nginx服务器(其他的)
#将地址更改为实际的nginx服务器端口80
#为真正的界面配置的
VUE_APP_BASE_API = 'http://localhost'
 
- 修改mock服务器的地址
srb-admin\mock\mock-server.js防止连接不了mock服务器 
// for mock server
const responseFake = (url, type, respond) => {
  return {
    //连接mock服务器
    //动态获取登录地址,此时硬编码写死
    //原来是${process.env.VUE_APP_BASE_API}${url}
    url: new RegExp(`/dev-api${url}`),
 
- 硬编码登录登出的地址
srb-admin\src\api\user.js 
//request中仍然引的是VUE_APP_BASE_API 
import request from '@/utils/request'
export function login(data) {
  return request({
    //原来的baseURL:ocess.env.VUE_APP_BASE_API-->VUE_APP_BASE_API = 'http://localhost'
    //给模拟登录的路径配置的
    baseURL: '/dev-api',
    url: '/vue-admin-template/user/login',
    method: 'post',
    data,
  })
}
export function getInfo(token) {
  return request({
    baseURL: '/dev-api',
    url: '/vue-admin-template/user/info',
    method: 'get',
    params: { token },
  })
}
export function logout() {
  return request({
    baseURL: '/dev-api',
    url: '/vue-admin-template/user/logout',
    method: 'post',
  })
}
 
③最终实现逻辑
- 访问登录登出时

 - 其他访问
前缀为http://localhost:80然后通过nginx转发 
5.前后端联调
①原理
- 后端接口开发完毕
 - 路由开发完毕(点击模块调到指定的页面)
 - 页面开发(编写template页面的渲染,script引入api模块,css为样式)
 - 前端api(编写axios具体api发送)

 
②流程
- 利用已提供的request模板写axios请求
 - 在具体的页面组件中script引入request模板,然后利用template渲染
 - 最后再路由调用模块时获取数据
 
③代码
- request模板
在此路径下编写srb-admin\src\api\core\integral-grade.js 
//引入axios的初始化模块
import request from '@/utils/request'
//导出默认模块
export default {
  //定义模块成员
  //成员方法:获取积分等级列表
  list() {
    //调用axios的初始化模块,发送远程ajax请求
    return request({
      //baseURL我们配置过为http://localhost
      url: '/admin/core/integralGrade/list',
      method: 'get',
    })
  },
}
 
- 页面组件的编写
srb-admin\src\views\core\integral-grade\list.vue 
<template>
  <div class="app-container">
    <!-- 表格 -->
    <el-table :data="list" border stripe>
      <el-table-column type="index" width="50" />
      <el-table-column prop="borrowAmount" label="借款额度" />
      <el-table-column prop="integralStart" label="积分区间开始" />
      <el-table-column prop="integralEnd" label="积分区间结束" />
    </el-table>
  </div>
</template>
<script>
//引入api模块
import integralGradeApi from '@/api/core/integral-grade'
export default {
  data() {
    return {
      list: [], //积分等级列表
    }
  },
  created() {
    this.fetchData()
  },
  methods: {
    fetchData() {
      integralGradeApi.list().then((response) => {
        this.list = response.data.list
      })
    },
  },
}
</script>
 
- 路由前面已编辑好
注意,由于mock服务器的响应码和我们的服务器响应码不同,需要更改srb-admin\src\utils\request.js模板,过滤器的更改,原理mock服务器访问成功只响应20000,现在需要更改为我们的服务器响应0,但是由于登录也需要20000,故20000和0都可以成功 
  (response) => {
    const res = response.data
    // if the custom code is not 20000, it is judged as an error.
    //mockserver 访问成功返回20000,自己的结果成功返回0
    if (res.code !== 20000 && res.code != 0) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000,
      })
 
6.完善积分等级的前端系统
①积分等级的需求
- 查询列表
 - 增加积分
 - 保存积分
 - 删除积分
 
②实现思路
- 在引入路由后,只需编码api和页面即可。
三个路由块。
第一个 访问list页面 积分等级列表
第二个 访问form页面 新增积分等级
第三个 访问form页面 编辑积分等级(默认不显示) - 通过form页面完成新增积分和编辑积分
新增积分功能直接在新增积分等级路由中更改。
编辑积分通过在积分等级列表路由中点击修改按钮跳到编辑积分等级路由。
通过判断是否有id来决定修改还是增加(如果是新增则id为空,如果是编辑则会自带$route对象中的占位符id属性) 
③代码总编辑
- 新增路由块(前面做过)
srb-admin\src\router\index.js 
{
    //父路由 ,最终完整的路由为/core/integral-grade+子路由的path
    path: '/core/integral-grade',
    //布局组件,在大布局组件下的右侧为路由出口
    component: Layout,
    //访问/core/integral-grade后默认发跳转/core/integral-grade/list
    redirect: '/core/integral-grade/list',
    //名称都不能相同
    name: 'coreIntegralGrade',
    //显示节点名称和图标
    meta: { title: '积分等级管理', icon: 'el-icon-s-marketing' },
    //true任何时候都显示父节点和子节点,false如果只有一个子节点则只显示子节点不显示父节点
    alwaysShow: true,
    //三个子节点,只有两个子节点
    children: [
      {
        path: 'list',
        //为组件节点的定义名称,每个节点的name节点不能相同
        name: 'coreIntegralGradeList',
        //组件节点,引入模块 指向src/views/core/integral-grade/list
        component: () => import('@/views/core/integral-grade/list'),
        meta: { title: '积分等级列表' },
      },
      {
        path: 'create',
        name: 'coreIntegralGradeCreate',
        component: () => import('@/views/core/integral-grade/form'),
        meta: { title: '新增积分等级' },
      },
      {
        //:id是一个占位符,表示这部分url是任何一个id
        path: 'edit/:id',
        name: 'coreIntegralGradeEdit',
        component: () => import('@/views/core/integral-grade/form'),
        meta: { title: '编辑积分等级' },
        //隐藏子节点,希望通过新增积分等级的按钮进入该子节点
        hidden: true,
      },
    ],
  },
 
- 添加api(调用具体接口的axios)
srb-admin\src\api\core\integral-grade.js 
//引入axios的初始化模块
import request from '@/utils/request'
//导出默认模块
export default {
  //定义模块成员
  //成员方法:获取积分等级列表
  list() {
    //调用axios的初始化模块,发送远程ajax请求
    return request({
      //baseURL我们配置过为http://localhost
      url: '/admin/core/integralGrade/list',
      method: 'get',
    })
  },
  removeById(id) {
    return request({
      url: '/admin/core/integralGrade/remove/' + id,
      method: 'delete',
    })
  },
  save(integralGrade) {
    return request({
      url: '/admin/core/integralGrade/save',
      method: 'post',
      //如果数据类型为json,需要data
      data: integralGrade,
    })
  },
  getById(id) {
    return request({
      url: '/admin/core/integralGrade/get/' + id,
      method: 'get',
    })
  },
  updateById(integralGrade) {
    return request({
      url: '/admin/core/integralGrade/update',
      method: 'put',
      data: integralGrade,
    })
  },
}
 
- list组件
srb-admin\src\views\core\integral-grade\list.vue 
<template>
  <div class="app-container">
    <!-- 表格 -->
    <el-table :data="list" border stripe>
      <el-table-column type="index" width="50" />
      <el-table-column prop="borrowAmount" label="借款额度" />
      <el-table-column prop="integralStart" label="积分区间开始" />
      <el-table-column prop="integralEnd" label="积分区间结束" />
      <el-table-column label="操作" width="200" align="center">
        <!-- 自定义模板内容 -->
        <!-- slot-scope="scope"能够获得当前行的所有数据的id -->
        <template slot-scope="scope">
          <router-link
            :to="'/core/integral-grade/edit/' + scope.row.id"
            style="margin-right: 5px"
          >
            <el-button type="primary" size="mini" icon="el-icon-edit">
              修改
            </el-button>
          </router-link>
          <el-button
            type="danger"
            size="mini"
            icon="el-icon-delete"
            @click="removeById(scope.row.id)"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
//引入api模块
import integralGradeApi from '@/api/core/integral-grade'
export default {
  data() {
    return {
      list: [], //积分等级列表
    }
  },
  created() {
    this.fetchData()
  },
  methods: {
    fetchData() {
      integralGradeApi.list().then((response) => {
        this.list = response.data.list
      })
    },
    removeById(id) {
      //debugger自动设置断点调试
      this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return integralGradeApi.removeById(id)
        })
        .then((response) => {
          //调用message的模块
          this.$message({
            showClose: true,
            message: response.message,
            type: 'success',
          })
          //删除成功后重新获取list
          this.fetchData()
        })
        .catch((error) => {
          //因为response的模板里面也会有catch的模块,在例如url发生错误时但是又点击删除
          //由于response模板先捕获删除,显示network error,又返回error对象
          //此时进入这里的catch,那个error类型为'network error',故又会显示 已取消删除
          //解决:加入判断error类型,如果error类型为前面的cancel则执行,如果为response里抛出的error则不执行
          if (error == 'cancel') {
            this.$message({
              type: 'info',
              message: '已取消删除',
            })
          }
        })
    },
  },
}
</script>
 
- form组件
srb-admin\src\views\core\integral-grade\form.vue 
<template>
  <div class="app-container">
    <!-- 输入表单 -->
    <el-form label-width="120px">
      <el-form-item label="借款额度">
        <!-- 数字输入框 -->
        <el-input-number v-model="integralGrade.borrowAmount" :min="0" />
      </el-form-item>
      <el-form-item label="积分区间开始">
        <el-input-number v-model="integralGrade.integralStart" :min="0" />
      </el-form-item>
      <el-form-item label="积分区间结束">
        <el-input-number v-model="integralGrade.integralEnd" :min="0" />
      </el-form-item>
      <el-form-item>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          @click="saveOrUpdate()"
        >
          保存
        </el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
import integralGradeApi from '@/api/core/integral-grade'
export default {
  data() {
    return {
      saveBtnDisabled: false, //是否禁用保存按钮,防止表单重复提交
      integralGrade: {}, //积分等级对象
    }
  },
  created() {
    //当id存在时回显,id不存在时新增
    //route对象是在发送response时自带对象route,能够获得前面url占位符id的值,没有则不调用
    if (this.$route.params.id) {
      this.fetchById(this.$route.params.id)
    }
  },
  methods: {
    fetchById(id) {
      integralGradeApi.getById(id).then((response) => {
        this.integralGrade = response.data.record
      })
    },
    //保存或者更新
    saveOrUpdate() {
      //禁用保存按钮
      this.saveBtnDisabled = true
      if (!this.integralGrade.id) {
        this.saveData()
        this.$router.push('/core/integral-grade/list')
      } else {
        //调用更新
        this.updateData()
      }
      //调用新增,调用完之后路由跳转
    },
    saveData() {
      integralGradeApi.save(this.integralGrade).then((response) => {
        this.$message({
          type: 'success',
          message: response.message,
        })
      })
    },
    // 根据id更新记录
    updateData() {
      // 数据的获取
      integralGradeApi.updateById(this.integralGrade).then((response) => {
        this.$message({
          type: 'success',
          message: response.message,
        })
        //重新加载list数据,跳转页面
        this.$router.push('/core/integral-grade/list')
      })
    },
  },
}
</script>
 
7.对前端系统的全面分析(Vue组件)
①一个页面有的(Vue组件)
- html数据
 - Vue对象
 
②Vue项目的全局渲染流程
-  
有唯一html文件(动态渲染,访问什么都是进入该html文件,包含一个div的id为app)
srb-admin\public\index.html -  
有唯一Vue对象(引入了路由并且动态渲染html中的id为app)
srb-admin\src\main.js -  
Vue对象渲染根组件
srb-admin\src\App.vue(定义了路由的出口) -  
通过路由渲染子组件(views视图–>侧边栏,导航栏,主内容栏)
srb-admin\src\router\index.js(路由)
srb-admin\src\layout\index.vue(要渲染的组件侧边栏,导航栏,主内容栏) -  
通过路由中定义的子节点来渲染主内容栏(获取积分数据)
srb-admin\src\layout\components\AppMain.vue(主内容栏定义了子路由的出口)
srb-admin\src\views\core\integral-grade\form.vue(路由子节点声明的模板渲染主内容栏) -  
难理解的问题
两个路由出口,第一次是路由整体渲染全部子节点的出口,第二次是子路由渲染子节点的出口。即父路由出口的位置和父路由+子路由渲染的出口的位置。 
③分析Vue项目结构
- 入口的html
srb-admin\public\index.html 
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
     <!--定义图标 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
     <!-- 定义项目名称 -->
    <title><%= webpackConfig.name %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
     <!-- 动态生成app.js代码并且自动嵌入到此 -->
    <!-- built files will be auto injected -->
  </body>
</html>
 
- 入口的js脚本
srb-admin\src\main.js
全局唯一Vue对象,初始化了Rout路由,渲染了根组件 
import Vue from 'vue' //Vue
import 'normalize.css/normalize.css' // A modern alternative to CSS resets//基础CSS样式
import ElementUI from 'element-ui' //element-ui
import 'element-ui/lib/theme-chalk/index.css'
import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n
import '@/styles/index.scss' // global css//全局css样式定义
import App from './App' //根组件
import store from './store' //前端信息存储工具
import router from './router' //路由模块
import '@/icons' // icon //扩展图标系统
import '@/permission' // permission control//角色控制权限系统
//mock为模拟接口(模拟接口服务器)
/**
 * If you don't want to use mock-server
 * you want to use MockJs for mock api
 * you can execute: mockXHR()
 *
 * Currently MockJs will be used in the production environment,
 * please remove it before going online ! ! !
 */
if (process.env.NODE_ENV === 'production') {
  const { mockXHR } = require('../mock')
  mockXHR()
}
//将elemeneUI挂载到Vue中
// set ElementUI lang to EN
Vue.use(ElementUI, { locale })
// 如果想要中文版 element-ui,按如下方式声明
// Vue.use(ElementUI)
Vue.config.productionTip = false
//创建Vue对象(核心Vue对象,指定渲染的节点,app,渲染public/index.html中的id为app的div)
new Vue({
  el: '#app',
  //挂载路由(将srb-admin\src\router\index.js的路由挂载到这)
  router,
  store,
  //页面中展示的内容,组件(srb-admin\src\App.vue为根组件,最后所有组件都会嵌入)
  render: (h) => h(App),
})
 
- 根组件
srb-admin\src\App.vue 
<template>
  <div id="app">
    <!--路由出口显式模板,模板都是由srb-admin\src\views下的文件来定义的-->
    <router-view />
  </div>
</template>
<script>
export default {
  name: 'App',
}
</script>
 
- 路由定义
srb-admin\src\router\index.js
其中的Layout为真正的布局组件,然后下面路由和其子节点的都是渲染Layout布局 
{
    //父路由 ,最终完整的路由为/core/integral-grade+子路由的path
    path: '/core/integral-grade',
    //布局组件,在大布局组件下的右侧为路由出口
    component: Layout,
    //访问/core/integral-grade后默认发跳转/core/integral-grade/list
    redirect: '/core/integral-grade/list',
    //名称都不能相同
    name: 'coreIntegralGrade',
    //显示节点名称和图标
    meta: { title: '积分等级管理', icon: 'el-icon-s-marketing' },
    //true任何时候都显示父节点和子节点,false如果只有一个子节点则只显示子节点不显示父节点
    alwaysShow: true,
    //三个子节点,只有两个子节点
    children: [
      {
        path: 'list',
        //为组件节点的定义名称,每个节点的name节点不能相同
        name: 'coreIntegralGradeList',
        //组件节点,引入模块 指向src/views/core/integral-grade/list
        component: () => import('@/views/core/integral-grade/list'),
        meta: { title: '积分等级列表' },
      },
      {
        path: 'create',
        name: 'coreIntegralGradeCreate',
        component: () => import('@/views/core/integral-grade/form'),
        meta: { title: '新增积分等级' },
      },
      {
        //:id是一个占位符,表示这部分url是任何一个id
        path: 'edit/:id',
        name: 'coreIntegralGradeEdit',
        component: () => import('@/views/core/integral-grade/form'),
        meta: { title: '编辑积分等级' },
        //隐藏子节点,希望通过新增积分等级的按钮进入该子节点
        hidden: true,
      },
    ],
  },
  {
    path: '/example',
    component: Layout,
    redirect: '/example/table',
    name: 'Example',
    meta: { title: '例子', icon: 'el-icon-s-help' },
    children: [
      {
        path: 'table',
        name: 'Table',
        component: () => import('@/views/table/index'),
        meta: { title: 'Table', icon: 'table' },
      },
      {
        path: 'tree',
        name: 'Tree',
        component: () => import('@/views/tree/index'),
        meta: { title: 'Tree', icon: 'tree' },
      },
    ],
  },
  {
    path: '/form',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'Form',
        component: () => import('@/views/form/index'),
        meta: { title: '表单', icon: 'el-icon-goods' },
      },
    ],
  },
]
 
- LayOut组件
srb-admin\src\layout\index.vue 
    <div
      v-if="device === 'mobile' && sidebar.opened"
      class="drawer-bg"
      @click="handleClickOutside"
    />
    <!--侧边栏-->
    <sidebar class="sidebar-container" />
    <div class="main-container">
      <div :class="{ 'fixed-header': fixedHeader }">
        <!--导航栏-->
        <navbar />
      </div>
      <!--主内容区-->
      <app-main />
    </div>
  </div>
 


















