智慧园区后台

总结

vue-admin

场景: 二次迭代

vue-admin-template 下载项目 找到核心内容 (自己分到的业务模块)

项目目录

固定的抽象模式 语义化 + 增加可维护性

  1. src - 业务代码 最终浏览器中运行 (目录划分相似 语义化 模块化 - 维护方便)
  2. src之外 - 开发阶段配置文件

关键文件

  1. package.json 包管理文件

    1. scripts 可执行的命令 可以自定义
    2. dependencies 生产依赖 这里面的包参与业务开发最终打包上线 npm i axios 参与打包 运行浏览器
    3. devDependencies 开发依赖 开发阶段生效 不参与打包 npm i sass -D 不参与打包 开发阶段生效
      团队开发模式下 整个团队要保证每个人在本地依赖的包都是一样的
  2. main.js
    不写业务代码 全局初始化的事情 初始化三方组件/样式初始化/store/router

    全局注册elementui 挂载vuerouter 挂载vuex 渲染app根组件

  3. 组件化的开发模式 组件树

  4. vueRouter

    1. 如果左侧菜单只有一项 如何配置
    2. 如果左侧菜单有嵌套 如何配置
  5. vuex

    解决的问题:把应用中很多地方都会用到的状态统一管理
    模块化的开发模式:1. 定义模块(state - mutation - action) 2. 把子模块在modules选项中注册好
    使用vuex中的数据或者方法:加上模块名

    1. 模块化 namespaced modules: {子模块}
    2. 和数据相关的所有操作都放到vuex中维护 + 组件只做一个事儿 触发action

request.js + apis

  1. request 产生一个实例对象给每一个api函数使用 一对多的关系 request配置一次 所有的api函数都会跟着生效
  2. asiox基础封装
    1. 实例化 axios.create({}) baseURL + timeout
    2. 请求拦截器 拦截请求处理请求参数再交给后端 headers中注入token
    3. 响应拦截器 拦截返回数据做处理再交给客户端 401 return response.data 401状态 数据剥离
  3. 拓展
    1. axios.create() 可以执行多次
    2. 拦截器可以有多个(串联参数的模式 前一个拦截器处理完 会把处理之后的数据 下发给下一个拦截器继续处理)
    3. 拦截器函数中千万别弄丢 return
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
import axios from 'axios'
// 1. 通用配置
// 2. 定制化配置

// 通用配置
// 1. axios实例化 axios.create() 基地址配置 baseURL + 超时时间 timeout(100ms)
// 拓展:create方法可以调用多次 每次执行都会生成一个独一无二的实例
// export default const a = asiox.create({ baseURL: 'a.com' })
// export default const b = asiox.create({ baseURL: 'b.com' })

// 2. 请求拦截器 请求头中添加token数据 接口鉴权 统一配置
// 客户端发送请求 - 请求拦截器(针对请求参数做处理) - 后端
// 拓展:可以添加多个请求拦截器
// 客户端请求 - 拦截器1(处理参数) - 拦截器2 - 后端

// 3. 响应拦截器 数据剥离 res.data / 401错误处理 / 前端自定义错误处理?
// 后端 - 响应拦截器 - 客户端
// 成功回调 200-300
// 失败回调 不在这个之间

// 如果后端不管接口成功还是失败统一返回200?

const service = axios.create({
baseURL: 'https://api-hmzs.itheima.net/v1',
timeout: 5000 // request timeout
})
// 请求拦截器
service.interceptors.request.use(
config => {
return config
},
error => {
return Promise.reject(error)
}
)

// 响应拦截器
service.interceptors.response.use(
response => {
return response.data
},
error => {
return Promise.reject(error)
}
)

export default service

1. 一般项目中多数的接口使用的配置是相似的,所以需要统一配置一次
2. 请求拦截器主要做的事情是在请求发送之前,针对请求参数对象做一些事情(比如添加鉴权Token
3. 响应拦截器主要做的事情是在响应数据返回到业务方之前,针对响应对象做一些事情(比如简化data,错误处理等)

登录

  1. 业务模式

    1. 表单基础校验

    2. 表单统一校验

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      1. 按照业务要求编写校验规则对象(rules)
      2. el-form组件绑定表单对象(model)和规则对象(rules)
      3. el-form-item组件通过prop属性指定要使用的校验规则

      <!--
      基础校验
      el-form :model="表单对象" :rules="规则对象"
      el-form-item prop属性指定一下要使用哪条规则
      el-input v-model双向绑定

      统一校验
      1. 获取表单的实例对象
      2. 调用validate方法
      -->
    3. 后续的业务处理(调用接口 - token - 存入token - 跳转首页 - 提示用户)

  2. 表单校验

    1. 先通过 devtools查看双向啊绑定是否成功
    2. 对照接口文档查看接口参数是否一致(拼写+类型)
    3. 校验时机实时校验和统一校验都需要
  3. token管理

    业务背景:Token的有效期会持续一段时间,在这段时间内没有必要重复请求token,但是Vuex本身是基于内存的管理方式,刷新浏览器Token会丢失,为了避免丢失需要配置持久化进行缓存
    基础思路:

    1. 存Token数据时,一份存入vuex,一份存入cookie
    2. vuex中初始化Token时,优先从本地cookie取,取不到再初始化为空串儿
    1. 多模块共享 - vuex维护
    2. 管理方式 vuex + cookie(localstorage)

    为什么要使用Vuex+Cookies

    俩种存储方式的优势都想要

    1. vuex 基于内存 存取快 但是刷新就丢失
    2. ls/cookie 基于磁盘 存取速度稍慢 刷新不丢失(持久化)

    因为我们既可以享受vuex速度优势封装优势 同时保持持久化

    1. 存数据的空间大小

    ​ ls 5M cookie kb

    1. 是否允许后端操作

    ​ ls 纯前端操作 cookie 前端可操作 后端也可操作(占多数)

    1. 是否跟随接口发送

    ​ cookie

token持久化

  1. 为什么要进行持久化
    基于vuex的存储页面刷新token丢失

  2. 如何来做

    1. 在获取到token之后 一式两份 vuex + cookie(ls)
    2. Vuex初始化state的时候 优先从本地取 取不到才初始化为空
  3. vuex vs cookie
    vuex - 基于内存 快 刷新就丢
    cookie - 基于磁盘 稍慢 持久化

请求头添加token(请求拦截器)

  1. 为什么要做
    接口鉴权

  2. 怎么做
    请求拦截器中统一配置 headers.Authorization = token(格式以后端要求的为主)

记住我优化

  1. 基础实现逻辑
    如果当前用户选中了记住,在登录时把用户的信息存入本地 在组件初始化的时候去取数据 回填
    如果当前没有选中,在登录时把数据清空

接口错误统一处理

  1. 为什么统一处理
    很多个接口都需要做这个事儿
    而且报错的提示位置和字段和后端协商好的

  2. 如何来做
    axios响应拦截器来做 判断错误信息存在 弹框提示

token是否存在控制路由权限跳转

  1. 根据流程图 -> js分支语句
  2. permission.js
    1. 权限相关的事儿都放到这里 模块化的思想
    2. 路由权限前置守卫
    3. main.js引入立刻执行

数据基础渲染

  1. 基础实现逻辑
    1. 准备静态模版(elementUI)
    2. 解决初始报错 在data中把模版绑定的数据都声明一遍
    3. 封装接口(url/ method / 参数[名称 + 类型 + 参数数量])
    4. 组件中封装一个独立的方法 在方法中调用接口函数 (复用的好处 调用之前做一些额外的参数处理)
    5. 选择一个合适生命周期钩子函数调用独立的方法 (created / mounted 都可以)
    6. 使用数据渲染模版 (数据驱动视图)

列表基础渲染

  1. 实现步骤

    1. 按照接口文档请求列表接口封装一下(url/method/参数)[拼写/类型]
    2. data准备响应式的数据(以后端接口实际返回为主)
    3. 在methods封装一个方法(参数的二次处理 + 调用接口 + 数据赋值)
    4. 生命周期钩子函数调用这个方法(created / mounted)
    5. 把响应式数据绑定组件身上(文档组件要求通过什么属性绑定就通过生命属性)
  2. 分页功能

    1. 分页的逻辑
      页数 = 总条数 / 单页的条数

    2. 组件分好页
      传入总数 :total=”100”
      单页条数 :pageSize=”2” 默认10

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <!-- 
      1. 页数分出来 (页数 = 总条数 / 每页条数)
      2. 点击每页的时候获取当前页的数据重新渲染到table上
      -->
      <el-pagination
      layout="total, prev, pager, next"
      :page-size="params.pageSize"
      :total="total"
      />
    3. 点击分页交互的实现

      1. 点击时拿到当前点击的页数(父组件从子组件获取内部的数据 子传父)
        @current-change=”pageChange”

        1
        2
        3
        4
        5
        6
        7
        pageChange(page) { 
        // console.log(page) // 回调参数 拿到的是当前页
        // 把点击的页数赋值给请求参数页数
        this.params.page = page
        // 使用最新的请求参数获取列表数据
        this.getCardList()
        }
      2. 使用当前的页数去后端要当前页的数据重新渲染到table

        1
        2
        this.params.page = page
        this.getList()

搜索功能

思路分析:把各种搜索条件当作请求参数发给后端 后端会根据字段对数据库数据做过滤筛选拿到符合条件返回

  1. 表单组件的双向绑定收集到当前的请求数据
        2. 把收集到的表单参数发送接口给后端那符合条件的数据
        3. 把拿到的数据关系显示在列表中

下拉组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<el-select v-model="params.cardStatus">
<!--
el-select: 双向绑定收集当前选中的数据
el-option: 下拉框中的每一项
label(中文显示)
value (选中之后赋值给v-model的数据将来传给后端)
-->
<el-option
v-for="item in cardStatusList"
:key="item.id"
:value="item.id"
:label="item.name"
/>
</el-select>

状态适配

  1. 场景
    后端返回的数据无法直接显示到页面中 0/1 男女

  2. 转化的状态码数量只有两个
    解决方案:三元表达式 status === 0? ‘女’ :’男’

    转化的状态码比较多
    解决方案:映射的方案

    1
    2
    3
    4
    5
    6
    7
    8
    function format(status) {
    const MAP = {
    0:'女',
    1:'男'
    }
    return MAP[status]
    }

新增功能

1. 步骤
   1. 点击跳转到新的路由页面/在当前页面打开一个弹框
   2. 准备表单项(通常只要有表单就会有校验 单独校验 + 统一校验)
   3. 收集表单数据(打开devtools 检测双向绑定是否ok)
   4. 提交 (接口字段不多不少 字段名称完全对应 类型完全匹配)
   5. 后续的逻辑处理 (提供用户 + 重新拉取列表 + 表单的重置 + 路由的重置)

表单校验

  1. 简单校验
    使用elementUI默认的配置项就可以完成

    1
    2
    3
    4
    5
    <!-- 
    el-form :model :rules
    el-form-item prop指定要用哪条规则
    el-input v-model双向绑定
    -->
  2. 自定义校验

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    data() {
    const validateMobile = (rule,value,cb) => {
    // 默认的配置 声明式配置
    // 自定义校验逻辑 命令式的校验 写的式逻辑代码
    // value: 输入框的数据 校验的那个数据
    // cb:校验放行函数 不管在通过还是未通过都需要调用它
    }
    }
    {
    validator: validateMobile
    }
  3. 对某些字段单独校验

    1. 场景:默认的表单校验管控不到 出现在自定义组件中 上传
    2. 调用单独校验实例方法 this.$refs.form.validateField(‘校验的字段’)
  4. 表单功能的时候要提前做数据验证 devtools 双向绑定是否生效 生效之后再调用接口

梳理新增和编辑

  1. 新增 点击新增按钮 - 跳转到新增路由 - 准备表单- 二次参数处理 - 提交新增接口 - 后续处理(提示/回跳)

  2. 编辑 点击编辑按钮 - 携带id跳转到新增路由 - 准备表单(回填) - 表单校验 - 二次参数处理 - 提交的更新接口 - 提示回跳

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!--
    scope 作用域插槽
    scope.row -> 当前行的数据对象

    如果我们只是想使用插槽渲染模板 #default
    如果我们除了想要使用插槽渲染模板 而且还想要拿到它内部的数据 #default="scope"
    scope 类似于函数的形参
    组件内部会把当前行数数据对象当成一个实参传到scope的位置

    在内部传递实参的时候 实参的格式
    {
    row: 当前行的对象数据
    }

    因为本来传下来的就是一个对象 所以通过解构赋值的方式去取row参数 #default="{row}"
    -->
    <el-button size="mini" type="text" @click="editCard(scope.row.id)">编辑</el-button>

重点:

  1. 新增和编辑状态 始终用的就是 id 有id代表当前是编辑状态 没有id代表就是新增状态

    1. 有id 才获取详情接口
    2. 有id 调用更新接口 没有id 调用新增接口
    3. 有id 显示编辑 没有id 显示新增
  2. 点击确定调用接口时区分状态之外 还需区分参数

    1. 如果是新增接口调用 没有任何id
    2. 如果是更新接口调用 请求参数中附加id[相关id数据在数据回填的时候加进去] 通知后端要更新谁

上传图片

  1. 上传的流程
    点击上传按钮 -> el-upload [打开本地文件选择框 + 上传前的文件校验] -> File ->
    new FormData()[接口要求传递一个formdata类型的数据] -> 往formData的对象中append字段
    -> append(‘file’,file) append(‘type’,’business…’) -> 使用完整的formData对象提交接口完成 上传

  2. 细节问题

    1. el-upload
      1. 非常简单 不需要做任何的自定义配置 默认的配置项完成上传就行了
      2. 需要自定义场景 :http-request=’function’
      3. 如果添加了上传前的校验 流程:先执行上传前的校验函数 函数返回值为true 再执行upload上传函数
      4. 函数参数 :上传前的函数 file对象 [size / type做校验] 上传函数 res对象 { file:File }
    2. 上传接口接口参数
      1. 常规的接口 contentType application/json
      2. 上传接口 contentType application/form-data
    3. 前后端校验逻辑要保持一致
      文件大小 小于5m / 文件类型 jpeg 前端要以这个为主
      [因为接口不只是可以通过浏览器提交 也可以通过其他方式提交 基于界面操作类校验会失败]
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
<!--
:http-request="uploadRequest" 自定义上传
action 本来是一个用来配置默认上传的接口地址
因为我们覆盖了 所以用一个 # 占个位置 消除必填警告
input type="file" 本身具备选择文件的能力
覆盖原因:默认的配置上传不够灵活 仅支持一些简单的上传

如果想要完全自定义上传 http-request
在选择文件之后 自动执行upload函数 并且把一个对象传给我们
对象中有一个file属性 就是我们要上传的对象

上传前校验:
1. 上传图片之前加一层校验 目的为了限制用户上传的文件类型和大小
2. 如果我们添加了beforeUpload这个属性方法 这个函数中必须return的数据为true
才会继续执行 upload 方法 如果校验不通过 暂停执行 不会走上传逻辑
3. file对象中两个属性
size: 文件大小 / 1024/1024 = M
type: 文件类型 image/文件类型
-->

<el-form-item label="营业执照">
<el-upload
action="#"
:http-request="uploadRequest"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
</el-form-item>

<!--
上传实现流程:
1. el-upload 打开本地文件 并且校验这个文件是否符合要求 - File
2. :http-request = 'upload'
3. 按照接口的要求格式 得到类型为FormData对象 new FormData()
4. 按照要求往 formData中添加字段数据 fd.append('字段名','字段值')
5. 调用上传接口
6. 拿到返回的文件地址和其有用的信息id 存入data中的响应式数据的位置 将来提交表单
-->

表单校验

  1. 基础校验 [按照配置]
  2. 统一校验 [validate 把所有需要校验的表单项都校验一便 把校验的结果布尔值返回valid]
  3. 自定义校验规则 [{ validator:校验方法 rule value cb } 不管是成功还是失败都必须调用cb]
  4. 单独校验某个表单字段
    1. 场景:表单中有一些特殊的字段 没有直接和el-form表单系统进行绑定 有值之后不会通知校验系统
    2. 解决办法:手动触发校验逻辑 调用el-form组件实例对象的单独校验方法 validateField(‘prop指定的那 个规则’)

vue2的响应式缺点

对象的属性动态添加 不是响应式的
Object.defineProperty
解决办法:
this.$set(要修改的对象,要添加的属性名,新属性的值)

特俗情况: v-model 视图修改的时候同样可以被收集到本来不存在的属性身上

下拉列表的数据

  1. 单独有一个下拉列表接口
  2. 没有单独下拉 复用table表格的列表接口(不需要判断状态 没有状态的所有数据)

知道一个事情:业务数据是由状态的 而且状态可以通过用户操作进行切换

表单的清空

  1. 调用表单组件的实例方法 resetFields

  2. 手动做数据的清除

    1
    2
    3
    4
    this.form = {
    name= ""
    }

网络请求的优化

  1. 场景: 限制了请求个数 保证只有打开时才请求
  2. 怎么做到?
    1. 判断第一个参数row是否能在第二个参数rows中找到 如果能找到代表打开了
    2. find 通过匹配找到符合条件的第一项 然后把找到的项返回
    3. findIndex 通过匹配找到符合条件的第一项 然后把找到项的下标值返回 splice(index,1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 只有展开时获取数据并绑定
async expandHandle(row, rows) {
// console.log('展开或关闭', row, rows) 点击行row.id rows数组对象
// row: 当前行的对象 rows数组对象
// 1. 先拿到当前行的数据
// 2. 使用当前行的企业数据,获取下面的合同列表数据
// 3. 把拿到的合同列表存入企业对象中 但是row里面没有存放的位置

// 优化网络请求 只在打开时才去触发 核心:拿到当前是打开的条件 做判断
// 判断条件:第一个row是否能在第二个rows中找到 如果找到了 代表打开了 如果找不到 代表收起了
// find findIndex
const isExpend = rows.find(item => item.id === row.id)
if (isExpend) {
// 如果找到了这一项,才回去调用接口
const res = await getRentListAPI(row.id)
// eslint-disable-next-line require-atomic-updates
row.rentList = res.data

}

带有模板的状态格式化

  1. 直接可以把后端的数据渲染出来 prop指定要渲染的字段

  2. 不能直接渲染 需要格式化 格式化出来的内容一九是一个文本 string

    1. :formatter=”formatStatus” 不需要手动传参 自动传入
    2. 插槽 + 插值表达式 [渲染出来的是函数的返回值]
    1
    2
    3
    4
    <template default="{row}">
    {{ formatStatus(row.status) }}
    </template>

// 格式化status
formatStatus(type) {
const TYPEMAP = {
0: ‘待生效’,
1: ‘生效中’,
2: ‘已到期’,
3: ‘已退租’
}
return TYPEMAP[type]
}

1
2
3
4
5
6
7
8
9

3. 不能直接渲染 格式化出来的是一个模板

插槽 + 插值表达式 [渲染出来的是函数的返回值]

```html
<template default="{row}">
<el-tag> {{ formatStatus(row.status) }} </el-tag>
</template>

对象动态添加响应式属性

  1. vue2 Object.defineProperty 无法监听到对象属性的添加 / 数组通过下标直接修改 也监听不到

  2. Vue.set / this.$set

    1
    2
    Vue.set(this.form,'age',18) // 就会变成响应式
    Vue.set(this.list,'3','new data')

后端返回的数据字段不够 需要前端自定义字段

默认的返回的企业列表数据中没有一个存放合同列表的数据的数组

1
2
3
4
5
6
this.list = res.data.rows.map(item => {
return {
...item,
rendList: [] // 通过映射自定义添加字段
}
})

word 文档的预览

解决方案:通过一个固定的预览地址 拼接 自己的合同url地址 然后通过a链接在新窗口打开

拼接方式:prewViewURL?src=url

业务数据状态变化

合同列表

  1. 当前时间还没有合同的起止时间 未开始
  2. 当前时间已经超过了合同的起止时间 已结束
  3. 当前时间正好式合同期限内,且用户没有退租 生效中

不同的业务数据状态影响显示及操作区域按钮的控制

实际开发时如何判断某块业务已经跑通了:接口通了并且对应的状态已经确认被修改了

excel导出

  1. 实际开发过程中的导出

    1. 前端主导(xlsx)

      流程:调用列表接口把要导出的数据拿到 -> 数据的二次转化 -> [excel 工作簿 - 工作表 - 单元格数据] - 使用xlsx创建一个工作簿 - 使用xlsx方法创建一个工作表 - 把工作表添加到工作簿 – [使用中文替换中文表头] 调用xlsx的导出方法

      工作中遇到了需求,参考代码 换接口 数据二次处理 处理表头

    2. 后端主导(最常见)

      流程:前端直接调用导出接口 - 后端会把数据转换成excel文件流当成返回值返回 - 直接触发浏览器的下载功能

  2. 两种方案的本质区别:

    把数据转化成excel的过程发生在哪里?如果发生在浏览器 前端主导 如果发生在后端服务器 后端主导

    前端主导 - 处理数据量不能太大

    后端主导 - 适合处理量大或量小都可

账单支付金额的接口计算

思想:

​ 操作表现流程:选中了楼宇 + 选中时间 => 调用接口 -> 计算之后的数据 -> 显示到input框内

1. 通过事件分别监听一下楼宇什么时候选中 时间什么时候选中  [通过事件绑定回调函数 两个组件分别绑定同一个回调函数]
   2. 在事件的回调中判断一下接口必须传参是否都具备了 [非空判断]
   3. 如果接口参数都不是空  正常发接口请求  获取计算金额
   4. 通过 v-model 进行正常的回填  [通过属性控制它不可编辑]

tab切换类交互

  1. 场景:角色列表切换 / 菜单列表切换 / tabs组件点击切换
  2. 通用实现方法
    1. 点击谁把谁index/id(唯一的标识)记录下来
    2. 准备一个激活的类名样式 active
    3. 通过vue中的动态类名:class = {acitve: index(当前项index) === curIndex(激活记录下标) }

树状组件

  1. 树:嵌套的数据结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const treeList = [
    {
    id:'1',
    title:'第一项',
    children: [
    {
    id:'1-1',
    title:'第二项',
    children: [
    {
    id:'1-1-1',
    title:'第三项'
    }
    ]
    },
    ]
    }
    ]
  2. 如何把一个平铺的数组处理成树形的数组?
    大多是情况下 都需要前端自己处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    const treeList = [
    {
    id: '1',
    title:'第一项',
    pid: null
    },
    {
    id:'1-1',
    title:'第二项',
    pid: '1'
    },
    {
    id:'1-1-1',
    title:'第三项'
    pid:'1-1'
    }
    ]

前端同步异步的问题

1
2
3
4
5
6
7
8
async mounted() {
await this.getRoleList()
await this.getTreeList()
// 初始化的时候使用第一项的roleId进行高亮处理
// 这个方法调用初始化时同步调用 而它依赖的roleList treeList都是通过异步获取的
// 为了保证数据已经准备好了 模拟同步 必须保证上面的俩个异步返回数据之后才调用方法
this.setTreeByPerms(this.roleList[0].roleId)
}

RBAC指的是什么?

基于角色的权限控制思想
员工 - 角色 - 权限点
先把权限点交给某个角色 然后再把角色交给某个员工 这个员工自动有了角色下所有的权限点

前端菜单路由权限控制

74.png

  1. 权限数据的生成(RBAC)
    1. 新增一个角色 给角色配置权限数据
    2. 新增一个员工 给员工分配这个角色 员工就有了当前角色下所有的权限数据
  2. 完整的实现流程
    1. 调用接口获取当前员工的权限数据 permission [‘park:building:add_edit’]

      1. 在Vuex中编写逻辑,user/state里存放个人用户信息
      2. action里调用时,把permissions目标数据return出去 给另一个js模块使用
      3. permission文件中触发action,获取用户信息(有token时)
    2. 对权限数据做格式化处理 产生两个权限数据 一级路由权限数组 + 二级路由权限数组

    3. 把路由表拆分成两部分 动态路由表[需要加权限控制] + 静态路由表[不需要加权限控制]

      1. 拆分动态路由表导出使用 asyncRoutes
      2. 初始化时候只处理静态路由表 routes: […routes]
    4. 以一级和二级权限数组作为对主动态路由表做过滤筛选处理 -> 有资格加入到路由系统中的动态路由表

      1. 使用一级权限点过滤一级路由 使用二级权限点过滤二级路由 最终得到显示左侧的路由表
      2. 调用函数获取最终的动态路由
    5. 调用router的addRoute方法把动态路由表依次添加到路由系统中 访问url可以渲染对应的组件

    6. 使用动态路由表数据通过存入Vuex然后利用它响应式的特性 渲染到左侧菜单中

      1. vuex新增一个模块,menu模块,先以静态的路由表作为初始值
      2. 在得到过滤之后的动态路由表之后,和之前的静态做一个结合
      3. 在sidebar组件中结合v-for指令做使用Vuex中的数据做渲染
    7. 解决切换用户有缓存的bug 方案:在用户退出登录时

      1. 调用清空路由的reset方法

      2. 手动把Vuex中的数据也清空

      3. 用户信息也清空

  3. 每一个独立的小功能封装成一个独立的小函数 维护方便
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (!store.state.user.profile.id) {
// 1. 调用action函数获取用户权限数据
const permissions = await store.dispatch('user/getProfile')
// 2. 把后端的权限数组格式化成我们自己的俩个权限数据
console.log('当前的权限数据为:', permissions)
const firstRoutePerms = getFirstRoutePerms(permissions)
console.log('一级路由权限', firstRoutePerms)
const secondRoutePerms = getSecondRoutePerms(permissions)
console.log('二级路由权限', secondRoutePerms)
// 3. 根据权限标识过滤路由表 最终得到显示到左侧的路由表
const routes = getRoutes(firstRoutePerms, secondRoutePerms, asyncRoutes)
console.log('最终路由表', routes)
// 4. addRoute动态添加 (当浏览器中访问路由的路径 显示渲染出来对应的组件)
routes.forEach(route => router.addRoute(route))
// 5. 存入Vuex渲染左侧菜单
store.commit('menu/setMenuList', routes)
}