网站首页 > 知识剖析 正文
要求
效果图
实际
功能
- 白天/暗夜模式
- 注册
- 未跳转,还是本页面注册,点击注册切换按钮即可。
- 登录
- 和注册在同页面。
- 登录实现了动态菜单和路由守卫。
- 多角色登录
- 忘记密码
- 弹窗形式
- 首页
- 日历查看
- 公告显示
- 个人中心
- 可通过其进行更改个人信息。
- 权限管理
- 权限菜单
- 分页查询
- 搜索/重置
- 新增/导出
- 修改/删除
- 权限分配
- 分页查询
- 搜索/重置
- 修改/删除
- 角色权限查看
- 分配权限
- 新增角色
- 系统功能
- 用户管理
- 分页查询
- 搜索/重置
- 修改/删除/批量删除
- 新增用户
- 导入/导出
- 分配角色
- 公告管理
- 分页查询
- 搜索/重置
- 修改/删除/批量删除/新增
- 公告内容查看
- 导入/导出
- 发布按钮功能
- 系统日志
- 分页查询
- 搜索/重置/删除/批量删除
效果图
数据库
数据库:permission、字符集:utf8mb4、规则:utf8mb4_bin
user (用户信息表)、perm (权限信息表)、role(角色信息表)、perm_role(角色权限表)、notice(公告信息表)、logs(日志信息表)
user表
字段 | 类型 | 键 | 备注 | 实体类对应类型 |
id | int(11) | 主键、自增 | 用户id | Integer |
username | varchar(255) | 账号 | String | |
password | varchar(255) | 密码 | String | |
phone | varchar(255) | 手机号 | String | |
role | varchar(255) | 角色(默认为SYS-YONGHU) | String | |
name | varchar(255) | 昵称 | String | |
avatar | longtext | 头像(base64编码上传) | String |
role表
字段 | 类型 | 键 | 备注 | 实体类对应类型 |
id | int(11) | 主键、自增 | 用户id | Integer |
role_name | varchar(255) | !null | 角色名称 | String |
perm_role_id | int(11) | !null、外键(关联perm_role表role_id)同步更新 | 角色权限id | Integer |
remark | longtext | 角色备注 | String |
perm_role表
字段 | 类型 | 键 | 备注 | 实体类对应类型 |
id | int(11) | 主键、自增 | 角色权限id | Integer |
role_id | int(11) | 角色关联字段 | Integer | |
perm_id | int(11) | 权限id | Integer |
perm表
字段 | 类型 | 键 | 备注 | 实体类对应类型 |
id | int(11) | 主键、自增 | 权限id | Integer |
name | varcahr(255) | !null | 权限名称 | String |
visit_path | varcahr(255) | !null | 访问路径 | String |
path | varcahr(255) | !null | 文件路径 | String |
icon | varcahr(255) | 菜单图标 | String | |
p_no | varcahr(255) | 父级菜单编号 | String | |
is_one | int(11) | !null | 是否为一级菜单(1是/0不是) | Integer |
notice表
字段 | 类型 | 键 | 备注 | 实体类对应类型 |
id | int(11) | 主键、自增 | 公告id | Integer |
title | varchar(255) | 标题 | String | |
content | text | 内容 | String | |
user_id | int(11) | 用户id | Integer | |
time | varchar(255) | 创建时间 | String | |
open | tinyint(4) | 是否发布 | boolean |
logs表
字段 | 类型 | 键 | 备注 | 实体类对应类型 |
id | int(11) | 主键、自增 | 日志id | Integer |
operation | varchar(255) | 操作名称 | String | |
type | varchar(255) | 操作类型 | String | |
user | varchar(255) | 操作人 | String | |
time | varchar(255) | 操作时间 | String | |
ip | varchar(255) | ip地址 | String | |
place | varchar(255) | 操作人位置 | String |
关系图
后端
使用技术
- SpringBoot框架
- Mybatis框架
- jdk17
依赖
<!-- ali地域 -->
<dependency>
<groupId>com.maxmind.geoip2</groupId>
<artifactId>geoip2</artifactId>
<version>2.6.0</version>
</dependency>
<!-- JSON格式的数据结构(key-value 结构) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.12</version>
</dependency>
<!-- excel操作依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
<!-- aop系统日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 数据加密jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.3.0</version>
</dependency>
<!--hutool工具库-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
</dependency>
<!--mybatis框架-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--数据库连接-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
登录业务层代码
/**
* 登录
* @param user 用户信息(username、password)
*/
@Override
public User login(User user) {
// 通过用户名查询用户信息
User dbUser = findByUserName(user.getUsername(),null);
if (dbUser == null) {
throw new ServiceException("用户不存在");
}
if (!dbUser.getPassword().equals(user.getPassword())) {
throw new ServiceException("密码错误!");
}
// 权限获取,并包装传前端主要代码
String token = JwtUtil.createToken(String.valueOf(dbUser.getId()), dbUser.getPassword());
// 获取用户权限
List<Role> role = roleMapper.findRole(null, dbUser.getRole());
if (role == null) {
throw new ServiceException("系统错误");
}
// 通过role表的perm_role_id查perm_role表的role_id的数据
List<PermRole> permRoleList = permRoleMapper.findPermRole(null, role.get(0).getPermRoleId());
List<Perm> permList = new ArrayList<>();
// 数据为空查所有
if (permRoleList != null && !permRoleList.isEmpty()) {
for (PermRole permRole : permRoleList) {
// 通过perm_id查perm表数据,并添加到permList中
List<Perm> perm = permMapper.findPerm(permRole.getPermId(), null, null, null, 1);
permList.add(perm.get(0));
}
} else {
permList = permMapper.findPerm(null,null,null,null,1);
}
// 包装数据
ArrayList<Perm> permList1 = new ArrayList<>();
// 父级
for (Perm perm : permList) {
if (perm.getIsOne() == 0 && perm.getPNo() == null) {
permList1.add(perm);
}
}
// 子级
for (Perm perm1 : permList1) {
ArrayList<Perm> permList2 = new ArrayList<>();
for (Perm perm : permList) {
if (StrUtil.equals(perm1.getPath(),perm.getPNo())) {
permList2.add(perm);
}
}
perm1.setPerms(permList2);
}
// 一级菜单
for (Perm perm : permList) {
if (perm.getIsOne() == 1) {
permList1.add(perm);
}
}
dbUser.setPermList(permList1);
dbUser.setToken(token);
return dbUser;
}
权限获取,并包装传前端思路
- 用户登录成功后获取数据库该用户数据
- 使用该用户的role角色名称,查询出该用户的角色对应的编号
- 使用该角色编号查询,角色对应的权限id,(可能多个使用List集合)
- 将查询出的数据进行分类,分为父级、子级、一级
- 通过pNo和isOne进行判断分类(pNo为null和isOne为0,则为父级/pNo不为null和isOne为0,则为子级,isOne为1,则为一级)
- 使用2层循环将子级菜单加入到父级菜单中。
后端目录
前端
使用技术
- Vue3框架
- ElementPlus组件库
- Axios交互
request.js代码
import axios from 'axios'
import router from "@/router";
// 创建可一个新的axios对象
const request = axios.create({
baseURL: 'http://localhost:6767', // 后端的接口地址 ip:port
timeout: 30000
})
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
// 发送token数据(添加到请求头上,使已经添加到请求头上的token在每次访问时加上token)
let user = JSON.parse(sessionStorage.getItem("user-data") || '{}')
config.headers['token'] = user.token // 设置请求头
return config;
}, error => {
console.error('request error: ' + error) // for debug
return Promise.reject(error)
});
// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 兼容服务端返回的字符串数据 JSON.parse(res)转化为json对象
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res;
}
// 拦截没有token的请求
if (res.code === "401") {
router.push('/login')
}
return res;
},
error => {
console.error('response error: ' + error) // for debug
return Promise.reject(error)
}
)
export default request
动态菜单生成
import router from '@/router'
import HomeView from '@/views/HomeView.vue'
export async function activeRouter(permList){
let root = {
path: '/',
name: '首页',
component: HomeView,
redirect: '/home',
children: [],
}
permList.forEach(p1 => {
if (p1.perms == null) {
let obj = {
path: p1.visitPath,
name: p1.path,
meta: {name: p1.name},
component: () => import("@/views/" + p1.path),
};
console.log("一级:", obj);
root.children.push(obj);
} else {
p1.perms.forEach(p2 => {
let obj = {
path: p2.visitPath,
name: p2.path,
meta:{name: p2.name},
component: ()=> import("@/views/"+ p2.path),
}
console.log("二级:",obj)
root.children.push(obj);
});
}
});
router.addRoute(root);
}
<div style="width: 130px;position: absolute;right: 0;">
<el-dropdown>
<div style="cursor: pointer;align-items: center;display: inline-flex;margin-left: -30px">
<el-avatar v-if="user.avatar" :src="user.avatar" />
<el-avatar v-else :icon="UserFilled" />
<p v-if="user.name != null" style="margin-left: 10px">{{user.name}}</p>
<div style="margin-left: 10px">
<el-switch
v-model="isDark"
:active-icon="Moon"
:inactive-icon="Sunny"
inline-prompt
@change="toggleDark"
/>
</div>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="$router.push('/person')">个人中心</el-dropdown-item>
<el-dropdown-item @click="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
路由
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView,
redirect: 'home',
/* 子路由 */
children:[
{path: 'home',name: 'Home',meta:{name: '主页'},component:() => import('../views/Home.vue')},
]
},
{
path: '/login',
name: 'Login',
meta: {name: '登录'},
component: () => import('../views/Login.vue')
},
{
path: '/:catchAll(.*)',
name: '404',
component: () => import('../views/404.vue'),
meta: {name: '无法访问'},
},
];
const router = createRouter({
mode: 'history',
history: createWebHistory(process.env.BASE_URL),
routes
})
function activeRouter(){
let userStr = sessionStorage.getItem('user-data');
let user = JSON.parse(userStr);
if (user) {
let root = {
path: '/',
name: '首页',
component: HomeView,
redirect: '/home',
children: [],
}
// 一级
user.permList.forEach(p1 => {
if (p1.perms == null) {
let obj = {
path: p1.visitPath,
name: p1.path,
meta: {name: p1.name},
component: () => import("@/views/" + p1.path),
};
root.children.push(obj);
} else {
// 二级
p1.perms.forEach(p2 => {
let obj = {
path: p2.visitPath,
name: p2.path,
meta:{name: p2.name},
component: ()=> import("@/views/"+ p2.path),
}
root.children.push(obj);
});
}
});
if (router) {
router.addRoute(root);
}
}
}
activeRouter();
// 路由守卫
router.beforeEach((to, from, next) => {
/*
to: 到达的路由信息
from: 来源的路由信息
next: 跳转路由的函数
*/
let user = JSON.parse(sessionStorage.getItem('user-data'));
if (user == null) {
if (to.path === '/login') {
next()
} else {
next('/login')
}
} else {
if (routes.indexOf(to.path) > -1) {
next('/login')
} else {
next()
}
}
});
export default router
前端功能及对应路径
功能:
首页--> /home
个人中心--> /person
权限管理--> perm
权限菜单--> /permMenu
权限分配--> /permAllot
系统功能--> system
用户管理--> /userManage
公告管理--> /noticeManage
系统日志--> /log
前端文件目录
思路
分配权限
查
前端应显示权限的名称,其对应的应该是权限的id.
分配
选择完后点击分配,应获取该列的perm_role_id值,并在perm_role中通过role_id添加perm_id(对应选择的id)
猜你喜欢
- 2024-11-12 原来隐藏一个DOM元素可以有这么多种方式,最后一种你肯定不知道
- 2024-11-12 你知道什么是BFC么 你知道什么是bfc么英语
- 2024-11-12 为什么我写的z-index不生效? z index无效
- 2024-11-12 开发人员在编写 HTML 和 CSS 时最常犯的六大错误
- 2024-11-12 css中如何让div水平居中(上) 怎么让div水平居中
- 2024-11-12 CSS 12个趣味小技巧大公开 | 原力计划
- 2024-11-12 谈谈工作中常用的设计模式 工作设计模型
- 2024-11-12 CSS 父元素中的绝对定位 父元素设置相对定位
- 2024-11-12 菜鸟学习记:第二十五天 菜鸟学习记
- 2024-11-12 响应式网页中的高度设计,你认真的吗?
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)