一、蘑菇街后台管理系统

1、简介

技术栈:Vue3 + Ts + Vuex + Axios 项目核心:权限管理、路由设计、国际化、组件二次封装、用户验证、Axios封装 该项目基于后台管理系统的基本逻辑、实现了用户登录验证、用户权限管理、商品数据可视化、 数据的增删改查等业务功能

2、项目复盘

1)登录成功后逻辑

可以将这些逻辑放到vuex里面,整个项目都能用

  1. 实现登录的逻辑(网络请求,拿到数据后的处理)
    • 数据包括:token、userInfo、userMenus等
  2. 数据保存到某一个位置(本地缓存 localstorage)
  3. 发送其它的请求(请求当前用户的信息、权限、部门)
  4. 拿到用户的菜单(动态路由,根据用户的角色选择对应的路由),这里会用到用户权限
  5. 跳转到首页
    • 在跳转之前做一些逻辑处理:当跳转的不是登录页时进行判断,如果本地缓存中没有token,则跳转到登录页
  6. 用户登录成功后,刷新页面的话vuex会清空,因为vuex保存在内存中,刷新页面会清空内存,那么需要将本地缓存内的数据放到vuex中,对vuex进行初始化

2)动态注册路由

前提是要先写好所有菜单的路由信息

  1. 在vuex中,在actions中通过用户权限请求用户菜单,通过commit提交给mutations并保存在state中,在mutations中进行路由动态注册
  2. 通过编写工具函数,获取匹配后的路由信息
    • 先加载默认所有的routes保存在allRoutes数组中
      • 通过webpack的require.context()工具函数加载路由文件夹并获取文件夹下的路由文件routeFiles
      • 通过routeFiles.keys()可以获取路由文件的path
      • 提供require加载对应的路由文件并将路由信息保存在allRoutes数组中
    • 拿到了包含所有路由信息的路由数组,接下来就是通过刚获取的用户菜单进行匹配
      • 通过递归用户菜单匹配allRoutes数组中路由path与菜单url是否一致,一致就添加到routes数组当中,进行返回
  3. 拿到匹配完的路由信息,通过router.addRoute()给对应路由添加子路由

3)按钮权限匹配

:::info 💡根据用户的权限有不同的操作权限,比如超级管理员可以对用户进行编辑和删除,或新建用户,但某些用户权限却不能做这些事情,这时候按钮就需要根据权限进行安排,如果有该按钮的操作权限则才可操作 ::: :::danger 💡按钮权限是放在菜单信息中的 当type===3的时候就是该用户所拥有的操作权限 :::

  1. 在获取到用户菜单信息之后就要获取该用户的按钮权限
  2. 通过递归菜单信息,当type===3时将对应的menu.permission放入数组内
  3. 将用户按钮权限返回,并存入vuex中
  4. 在组件中通过v-if来判断用户是否有该按钮的使用权限

4)对Element-plus组件的二次封装(以page-content为例)

:::info 💡对于设计页面我们会用到element-plus,可以通过里面提供的组件快速的搭建想要的效果。但有可能有许多页面都会用到该组件,如果每个地方都再把代码重写,那么代码的复用性就很差,可以把公共部分的组件进行抽取。通过传入配置的方式,比如通过prop的方式传入需要的数据,对封装的组件进行相应的配置,根据每个页面的需求创建合适的组件。当然封装的组件里面要有一些处理逻辑,要将所有的情况囊括,对配置文件进行判断,生成相应的页面组件 ::: 以table为例

  1. 列表中的数据可以通过props传入
  2. 列表中每一列的数据样式可能是不一样的,有可能要给这一列传入按钮,也有可能将这列的数据进行数据处理,比如添加价钱符号等,这时候就可以通过插槽的方式,让外部来自定义该列的数据
  3. 通过具名插槽和作用域插槽
    • 在项目中通过作用域插槽拿到element-plus内置的属性
    • 再通过具名插槽和作用域插槽,让父组件可以拿到table中的数据,并对特定一列进行修改

5)路由非法拦截

通过路由守卫router.beforeEach()来拦截每次一跳转,当需要访问登录页以外的页面时,判断当前跳转时是否已经登录成功,也就是判断有没有token。如果没有则跳转到登录页面,否则不进行拦截

3、项目难点/项目中遇到的问题

1)刷新页面之后,页面跳转到Not-fount去了

  1. 为了解决这个问题,在路由守卫里打印`router.getRoutes()`所有注册的路由,发现能获取到已经注册的菜单路由,但通过路径并不能匹配到对应的路由;再打印`to(即将跳转的路由对象)`发现匹配到的对象是`NotFound`。猜测是在刷新页面后,路由匹配在菜单路由注册之前就已经匹配好了,但这时候找不到对应路由则匹配到`NotFound`。查看`main.ts`发现`setupStore()``app.use(router)`之后调用,`setupStore()`是用于初始化`vuex`,因为刷新页面就会清除vuex中的数据,必须从本地缓存中将数据重新传给`vuex`,在这其中也会注册菜单路由(由用户的菜单数据动态注册),所以将`setupStore()`放在`app.use(router)`之前调用就可

💡为什么在路由守卫中 to 获取到了对应路由,但匹配错误呢?

因为路由守卫是一个回调函数,首先在main.ts中从上往下依次执行,当执行到app.use(router) 会调用install(),然后获取当前路径的path,然后进行路由匹配,但这时匹配到的是NotFound 然后才注册菜单路由,接着在路由守卫中打印to会发现虽然是对应的路由路径,但会匹配成NotFound 因为路由守卫是回调函数,在回调之前就已经匹配了

2)刷新页面后,vuex中的数据被清空

:::warning 🌻刷新页面后,vuex中的数据会被清空,所以一开始就要对vuex进行初始化 ::: 初始化过程:

  1. 从localstorage当中读取到存储的数据,通过commit进行提交给mutations将数据保存在state中
  2. 像一些容易变动的数据,就通过dispatch交给actions去请求下来然后commit提交保存在state中
  3. 为了能够响应式地获得数据,在组件中通过computed去获取vuex中的数据,并进行缓存

4、优化方案

  1. 路由懒加载
  2. Element-plus按需引入

二、Canvas画图

1、简介

技术栈:JavaScript、Canvas、ES6 该项目使用原生JS实现了绘画、调色、橡皮擦、保存、更换背景等功能,完全由我独立开发

2、项目难点/项目中遇到的问题

1)如何使得画笔画出来的线更加圆滑?

遇到的问题> 1. canvas通过lineTo画出来的线是折线,不是曲线

  1. 由于movesmove事件触发需要间隔一小段时间,当鼠标移动速度越快,采集到的坐标点相隔就越远,所以折线感约明显,画出来的线不够平滑

解决方案:利用二次贝塞尔曲线让线更加圆滑:::info

  1. 二次贝塞尔曲线需要三个点:起始点、控制点、终点
  2. 利用一个数组收集坐标点
  3. 当画笔下落时,这个坐标点作为起始点 beginPoint
  4. 当画笔移动时,收集坐标点,当点数大于3时,才开始绘制
  5. 从数组中获取最后两个点的坐标点,取得中间值作为终点 endPoint
  6. 以倒数第二个点的坐标为控制点 controlPoint
  7. 将画笔移动到起始点 moveTo -> beginPoint 利用canvas.quadraticCurveTo()进行绘制
  8. 最后将 beginPoint = endPoint :::

2)如何实现一个橡皮擦,清除圆形区域?

遇到的问题> 因为canvas清除画布只有clearRect()这个方法,这个方法清除的区域是一个矩形

解决方案:::info

  1. 那么想要擦除圆形区域就需要将圆形切成多个平行的矩形,然后从中间到圆的两端逐渐递减的操作
  2. 从圆心的左边开始清除区域的x,y不断向中间移动,清除区域的width不断减小,height不断增加,类似于多个矩形叠加形成类似于圆的区域 :::