一、项目基本信息
项目名称 | 大学生成长档案后台管理系统 | ||
开始时间 | 2024.7.22 | 结束时间(第一版) | 2024.9.25 |
项目介绍 | 这个系统我负责的模块主要是登录、首页、学生管理、菜单管理、成长之星推选这几个模块,还包括整个系统的权限处理。 | ||
源码地址 |
二、项目复盘
G(Goal)目标回顾
目标一:保证这个项目的功能实现能够达到甲方的要求
目标二:通过这个项目来使自己vue2的使用更加熟练
目标三:通过自主完成项目来凸显出来自己的一些知识误区并得到指正
R(Result)结果陈述
目标 | 结果 | 是否满足预期 |
目标一 | 跟甲方多次商讨确认过后,根据其需求多次更改,目前第一版已经正式进入用户测试阶段 | 完成 |
目标二 | 目前已经适应了vue2的代码书写,并且在项目实践过程中对一些知识的理解使用更加透彻 | 完成 |
目标三 | 在项目开发的过程中很多知识的使用、包括代码书写上的问题都得到了指正,但还有部分问题存在,后续再继续精进 | 不足 |
A(Analysis)过程分析
阶段 | 任务 | 预期情况 | 实际情况 | 评价总结 |
筹备 | 确定字段名文档 | 前后端都严格按照这个文档中的字段名来进行命名 | 在刚开始实际开发时还是会忘记,后续又严格要求后就按照文档中的来 | 低于预期(以后写项目从刚开始就严格执行) |
确定接口文档 | 整个项目期间不会出现一个模块开发完后还要改接口 | 接口基本没有出现后来突然进行更改的 | 符合预期 | |
实施 | 登录 | 用户通过账号、密码、验证码进行登录 | 用户能按照预期的情况进行登录 | 符合预期 |
首页 | 各项数据能够正确展示 | 页面上通过图表的形式清晰地展示了用户的数量、访问量等等数据,清晰直观 | ||
学生管理 | 全面的管理学生的信息 | 可以对学生的信息进行增删改查、封禁、批量导出等功能 | ||
成长之星 | 按照班级、院级、校级这三级来层层推送成长之星 | 各级能成功推选,并且全面考虑用户需求,也解决用户误点的各种情况 | ||
菜单管理 | 来控制不同等级的管理员登录时显示的菜单不一样,并且页面中的某些权限也进行区分 | 每一级的用户只能看到自己权限内的模块,甚至同一个模块内不具有的功能按钮也会直接不显示 | ||
交付 | 与甲方对接 | 项目能够完工 | 甲方觉得有些模块的设计有些欠缺,所以就又按需求进行修改,目前已经完工了 | 符合预期(虽然中间有些波折) |
I(Insight)归类总结
总结
通过以上的分析,对本次项目进行总结:
1、项目已经完结,目前在交付测试
2、第一个对接甲方的项目,除了效果实现,也开始去考虑用户的需求,努力实现用户利益最大化,增加了项目经验
3、对一些技术栈的使用更加熟练
4、在项目开发中,加强了团队合作意识,体味到了伙伴们并肩作战的幸福感
规律
背景:前后端交互报500
解决:
1、去网络的预览里看自己传的数据是否符合当时在apifox上制定的结构以及字段名是否一样
2、去请求标头里看传的数据类型是什么,vue的文本输入的组件有时候会将传的数据自动转为form-data类型,而后端如果需要的是json格式就会有问题
三、技术总结
项目精彩部分——权限设置
前提:此处的精彩是指在这一模块学到了很多
背景:几乎所有完整的后台管理系统都是要配备权限管理的,它能够使不同等级用户登录时动态的去设置这个用户该有的权利,也有利于后期的维护
提醒:权限设置部分应该在项目最开始的时候去完成,也有助于对整个项目把控。并且这里的权限设置不是指的某个页面,而是整个项目的一大部分,涉及到多个模块
具体实现:
1、菜单管理
这是权限设置的基础,这个模块主要是用来控制这个项目所有可能出现的目录、菜单以及按钮,并且可以对他们进行增删改查。
- 首先来搞清楚目录、菜单、按钮具体指的是什么:
名称 | 具体表现 | 用途(为什么管理它) |
目录 | | 让菜单能够准确找到他的“家”在哪 |
菜单 | | 控制侧边栏是否会出现这个模块 |
按钮 | 模块中的某个功能 | 控制用户是否能实现某个功能 |
- 三者操作的区别
目录其实没有什么关键的字段;菜单类似于一个页面所以它会有路由的相关字段;按钮部分最重要的是权限标识(后面会用到!!),接口的路径和方法是因为后端需要,所以也需要进行管理。
总结:其实菜单管理这个模块就跟其他模块一样只需要考虑增删改查就可以了,它只是管理权限的相关字段,它本身不受权限的影响(它只是用来做准备工作的!!)
2、侧边栏
本质:后端从数据库中存储的所有菜单项中筛查选出来当前登录用户权限所能看到的菜单项,然后传到前端这边,前端拿到数据进行渲染。这样不同权限用户收到的数据都是不一样的,渲染出来的侧边栏也是不一样的
下面是我这块的逻辑实现代码,大家可以参考,也可以直接去仓库里看源码:
//我是借助elementUI组件实现的
<script>
import { initSidebar } from "../../../api/sidebar";
export default {
data() {
return {
sidebarData: [], //用来接收后端传来的数据
activeMenu: "", //识别当前聚焦的菜单项
};
},
created() {
this.init();
},
methods: {
//初始化方法获取数据,并且在这里我是对后端传来的数据进行了层级转换
async init() {
const initData = await initSidebar();
//拿到的数据中至少要包含的字段有:
1、菜单id和父级菜单id(用来转换层级结构)
2、路由路径、路由参数、重定向路由(实现点击某个菜单项跳转到对应页面)
3、菜单名称(要往页面上进行渲染)
4、菜单是否被隐藏了
const data = initData.data;
const result = [];
const map = new Map();
for (const item of data) {
map.set(item.id, { ...item, children: [] });
}
for (const item of data) {
if (item.parentId !== null && map.has(item.parentId)) {
map.get(item.parentId).children.push(map.get(item.id));
} else {
result.push(map.get(item.id));
}
}
this.sidebarData = result;
},
},
};
3、页面中功能按钮显示
知识点:自定义指令
- 在main.js文件中创建自定义指令
//注册自定义指令 控制功能权限
Vue.directive('permission', {
//会在指令作用的元素插入dom之后执行
inserted(el, binding) {
//el是当前指令作用的dom元素的对象
//binding是v-permission=“表达式” 表达式的信息
//这个points数据(里面一般放的是当前登录用户等级所能拥有的所有权限标识)来源本质上也是从后端获取的,可以自己决定获取方式,我是收到数据后存到本地然后从本地拿出来的
const points = JSON.parse(localStorage.getItem('perm')) || []
//判断points中是否包含v-permission=“表达式” 表达式的值
if (!points.includes(binding.value)) {
el.disabled = true //禁用按钮
el.remove() ///删除按钮 两者可以根据自己的需求进行选择
}
}
})
- 具体用法
<el-button v-permission="'right:menu:build'" >
点击
</el-button>
//right:menu:build这个就是在菜单管理那提到的权限标识
//权限标识就相当于给每个按钮发了一个号码牌,在自定义指令里去判断你收到的所有权限标识中是否有这个号码牌,如果有,则按钮可以正常操作;没有的话,按钮就删除或被禁用
4、路由
与以往不同:以往的路由都是写死的,只要配置到对应的页面上就好了;但与权限有关的路由是动态的,也是根据后端传来的当前用户的权限来进行判定的。
实现思路:
- 先把所有可能用到的路由写到一个文件1中(对应的文件在仓库中src/router/menuList.js),但真正配置路由的地方(src/router/index.vue)只写不涉及权限的页面(例如登录)
- 在vuex中单独开一个模块来获取从后端传来的数据,这里的数据中必须要包含一个唯一标识能跟文件1中对应的模块匹配上(这里可以去看仓库中src/store/menu.js)
- 我在写匹配方法时是根据菜单名称进行匹配的,这个方法也是在menu.js文件中调用的:
//定义菜单的匹配规则
//menu是前端定义的路由文件
//dyMenu后端动态路由文件
import { cloneDeep } from "lodash";
export function ruleMenu(menu,dyMenu){
let menuArr=[];
let arr = cloneDeep(menu)
//遍历前端的路由,判断menu里面的每一项的meta.title,是否后端返回里面有这个信息
arr.forEach(element => {
dyMenu.forEach(item=>{
if(element.meta.title === item.meta.title){
menuArr.push(element)
}
})
});
return menuArr
这个方法返回的就是匹配出来后的路由的数组
- 上面的操作结束后就已经把对应权限的路由存到vuex中,接下来就是要把准备好的路由添加到路由对象上去(这部分一般放在路由守卫中去写)
store.dispatch('menu/getMenuList').then(mybaseRoutes => {
router.addRoutes([...mybaseRoutes],{ path: '*', redirect: '/404', hidden: true });
注:我用的是Vue Router 3.x,所以用addRoutes;Vue Router 4.x使用的是addRoute
遇到的问题:
问题一:在实现动态化路由时,最开始的打算是完全靠后端传来的数据实现路由跳转,但发现后端传来的组件名是一个字符串,但在路由无法识别这个字符串
解决:跟学长沟通之后还是按照最常规的方法去实现(上面已经讲述)
问题二:在写表格时,利用组件不好实现分页情况下,跳到第二页后第一页的数据还处于选中状态
解决:手搓一个表格,组件库比较有局限性,但原生的多选框具备的功能很全面
问题三:前后端在接口联调时一直报400
解决:把所有中间有联系的字段名都检查了一遍,又把请求头模块看了一遍,才发现是两边的token属性名不一致
四、总述
以上就是我这个项目的一些复盘总结,在技术分享的模块,如果大家发现有问题,欢迎指正!也希望大家看得开心!!