github: github.com/heyongsheng…
码云: https://gitee.com/ihope_top/hevue3-admin
线上体验地址 ihope_top.gitee.io/hevue3-admi…
本章知识点:
基础部分讲完了,这一章开始就不会再讲那么详细了,因为代码量很多,一点一点写估计要写好久,也没人愿意看,所以只挑重点讲。
登录页面
登录页面其实没什么好说的,内容都比较简单,我也不怎么会设计,我就是用主题色简单做了几个色块,右上角加入了切换暗黑主题的按钮,个人感觉还可以,给大家看一下成品图看一下。
白天
晚上
这里背景色取的事主题色,所以你如果修改主题色的话,这里也会跟着变
界面没啥难的,这里就说几个小细节点吧。第一个是浏览器填充账号密码输入框默认背景颜色的问题,就像下面这样
这里我用的办法是给这个背景颜色变化加一个延迟,和动画过渡,只要时间设置的足够久,就相当于没有变。
input:-internal-autofill-selected {
background-color: transparent !important;
background-image: none !important;
color: rgb(255, 255, 255) !important;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
transition-delay: 500000s;
transition: background-color 50000s ease-out;
-webkit-transition-delay: 50000s;
-webkit-transition: background-color 50000s ease-out;
}
还有一个问题就是如何禁止浏览器填充密码,比如我们在修改密码的时候,就不想让浏览器给自动填充,因为用户要修改密码,肯定是要修改不一样的,自动填充的话还得删掉重新输入。网上说的方法有设置一个隐藏的输入框之类的,我这里采取的方式是给password框添加一个readonly
属性,等用户输入完验证码之后再移除该属性,就可以成功的阻止浏览器填充密码了,当然你也可以搞个定时器移除该属性。
权限管理
本套系统的登录流程其实和大多数都后台管理系统一样
另外就是本套系统的权限关联关系其实也是常规方案,就是用户关联角色,角色关联菜单。
还有就是本套系统暂未设计多级菜单,菜单层级就只有 菜单>页面>按钮 三级。
页面级权限
登录之后进行判断的步骤我们通常利用路由守卫来进行,我们先在根目录创建一个permission.ts
(你也可以在其他目录创建)。
import router from '@/router'
import { useStaffStore } from '@/stores/staff'
import { usePermissionStore } from '@/stores/permission'
const whiteList = ['/login', '/404'] // no redirect whitelist
// 路由前置守卫
router.beforeEach(async (to, _from, next) => {
const store = useStaffStore()
// 获取token
const token = store.token
// 如果token存在
if (token) {
// 如果是登录页
if (to.path === '/login') {
// 跳转到首页
next('/')
} else {
if (!store.staff) {
try {
await store.getStaffInfo()
} catch (error) {
store.logOut()
next('/login')
}
}
const permissionStore = usePermissionStore()
if (!permissionStore.routes || permissionStore.routes.length == 0) {
const accessRoutes = await permissionStore.getAccessRoutes()
accessRoutes.forEach((route) => {
router.addRoute(route)
})
next({ path: to.fullPath, replace: true, query: to.query })
} else {
next()
}
}
} else {
// 如果是白名单
if (whiteList.indexOf(to.path) !== -1) {
// 正常跳转
next()
} else {
// 否则跳转到登录页
next('/login')
}
}
})
一个很简单的路由跳转判断,我们这里设置了访问路由白名单,并引入了pinia的员工实例useStaffStore
用来判断用户是否存在,以及执行获取用户信息和退出登录的操作,还引入了pinia的权限实例usePermissionStore
来获取权限内的菜单,并添加到动态路由。
获取用户信息什么的就不说了,这里我们来看一下获取权限菜单的相关操作。这一部分我都放到了pinia中来处理。
首先我们来看一下后端返回的数据
这里附上我的菜单表的字段
// 类型
// 1:菜单 2:页面 3:按钮
@prop()
menuType: '1' | '2' | '3';
// 菜单名称
@prop()
title: string;
// 访问的路径
@prop()
path: string;
// 模板地址
@prop()
component: string;
// 路由名称
@prop()
name: string;
// 图标
@prop()
icon: string;
// 父级id
@prop()
parentId: string;
// 排序
@prop()
sort: string;
// 是否隐藏 0:不隐藏 1:隐藏
@prop()
hidden: '1' | '0';
// 权限标识(唯一)
@prop({ unique: true })
permission: string;
// 是否缓存 0:不缓存 1:缓存
@prop()
cache: string;
// 固定标签栏 0:不固定 1:固定
@prop()
affix: string;
// 常显菜单 0:否 1:是
@prop()
alwaysShow: string;
这里我在后端已经做好了分类,menus里返回的是菜单及页面,permissions里返回的是按钮权限列表。所以我们这里循环menus只用判断是菜单还是页面就可以了,当然,还需要转化为树形结构,因为我们后面要用来生成菜单用。其他的就是添加一些自定义属性,比如是否缓存啊,是否常显啊之类的,后面都会讲到。
import { defineStore } from 'pinia'
import { publicRouters } from '@/router'
import { getRolePermission } from '@/api/role'
import type { RouteRecordRaw } from 'vue-router'
import { arrToTree } from '@/utils/util'
import Layout from '@/layout/index.vue'
import { menuHideDic, menuCacheDic } from '@/dictionary/menu'
// 给RouteRecordRaw添加_id属性
//双星号是递归解释器遍历文件和文件夹的占位符或指令。它是一个简单的递归通配符,而只有一个星号表示全部没有递归
const modules = import.meta.glob('../views/**/**.vue')
export const usePermissionStore = defineStore('permission', {
state: () => ({
routes: [],
permissions: []
}),
actions: {
async getAccessRoutes() {
let result = (await getRolePermission()).data
let { menus, permissions } = result
//
menus.map((item: any) => {
if (!item.parentId) {
item.component = Layout
} else {
item.component = modules[`../views${item.component}.vue`]
}
item.meta = {
title: item.title,
icon: item.icon,
sort: item.sort,
cache: item.cache === menuCacheDic.trueValue,
affix: item.menuType === '2' && item.affix === menuHideDic.trueValue,
hidden: item.hidden === menuHideDic.trueValue,
alwaysShow:
item.menuType === '1' && item.alwaysShow === menuHideDic.trueValue
}
})
// 递归处理后台返回的路由数据
const routes: RouteRecordRaw[] = arrToTree({
list: menus,
id: '_id',
pid: 'parentId',
children: 'children'
})
this.routes = publicRouters.concat(routes)
this.permissions = permissions
return routes
}
}
})
这里大家可能还注意到了有一些带Dic的字段,这是我写的在前端的字典