当前位置: 首页 > news >正文

springboot + Vue前后端项目(第十五记)

项目实战第十五记

  • 写在前面
  • 1.后端接口实现
    • 1.1 用户表添加角色字段
    • 1.2 角色表增加唯一标识字段
    • 1.3 UserDTO
    • 1.4 UserServiceImpl
    • 1.5 MenuServiceImpl
  • 2. 前端实现
    • 2.1 User.vue
    • 2.2 动态菜单设计
      • 2.2.1 Login.vue
      • 2.2.2 Aside.vue
    • 2.3 动态路由设计
      • 2.3.1 菜单表新增字段page_path
      • 2.3.2 路由设计
      • 2.3.3 登录界面Login.vue设置路由
  • 总结
  • 写在最后

写在前面

  1. 动态菜单设计
  2. 动态路由设计

1.后端接口实现

1.1 用户表添加角色字段

CREATE TABLE `sys_user` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',`username` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户名',`password` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',`nickname` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '昵称',`email` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮箱',`phone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '电话',`address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '地址',`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`avatar_url` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT 'https://himg.bdimg.com/sys/portraitn/item/public.1.23bd3c8c.ANoeKxl_gef9fnrikdXOYA' COMMENT '头像',`role` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色',`deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除0(未删除),1(删除)',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC

1.2 角色表增加唯一标识字段

CREATE TABLE `sys_role` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',`role_key` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '唯一标识',`name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '描述',`deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC

1.3 UserDTO

@Data
public class UserDTO {private String username;private String password;private String nickname;private String avatarUrl;private String token;private String role;private List<Menu> menus;
}

1.4 UserServiceImpl

package com.ppj.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ppj.constants.Constants;
import com.ppj.entity.Menu;
import com.ppj.entity.Role;
import com.ppj.entity.RoleMenu;
import com.ppj.entity.User;
import com.ppj.entity.dto.UserDTO;
import com.ppj.exception.ServiceException;
import com.ppj.mapper.MenuMapper;
import com.ppj.mapper.RoleMapper;
import com.ppj.mapper.RoleMenuMapper;
import com.ppj.mapper.UserMapper;
import com.ppj.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ppj.utils.TokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.sql.rowset.serial.SerialException;
import java.util.ArrayList;
import java.util.List;/*** <p>*  服务实现类* </p>** @author ppj* @since 2024-04-20*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {private static final Log LOG = Log.get();@Autowiredprivate RoleMapper roleMapper;@Autowiredprivate RoleMenuMapper roleMenuMapper;@Autowiredprivate MenuServiceImpl menuService;@Overridepublic UserDTO login(UserDTO userDTO) {if(StrUtil.isBlank(userDTO.getUsername()) || StrUtil.isBlank(userDTO.getPassword())){return null;}User user = getUserInfo(userDTO);if(user != null){String token = TokenUtils.genToken(user.getId().toString(), userDTO.getPassword());userDTO.setToken(token);// 把user相应的值传递给userDTOBeanUtil.copyProperties(user,userDTO,true);List<Menu> roleMenus = getRoleMenus(user.getRole());userDTO.setMenus(roleMenus);return userDTO;}else{  // 数据库查不到throw new ServiceException(Constants.CODE_600,"用户名或密码错误");}}@Overridepublic Boolean register(UserDTO userDTO) {if(StrUtil.isBlank(userDTO.getUsername()) || StrUtil.isBlank(userDTO.getPassword())){return false;}User user = getUserInfo(userDTO);if(user == null){User newUser = new User();// 值传递BeanUtil.copyProperties(userDTO,newUser,true);// 保存至数据库save(newUser);}else{throw new ServiceException(Constants.CODE_600,"用户已存在");}return true;}public User getUserInfo(UserDTO userDTO){QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("username",userDTO.getUsername()).eq("password",userDTO.getPassword());User user;try {// 可能查到多条记录,后台报异常,写个异常类(主动捕获异常)user = getOne(queryWrapper);}catch (Exception e){  // 可能查出多个符合条件的记录LOG.error(e);throw new ServiceException(Constants.CODE_500,"系统错误");}return user;}/*** 根据角色名称获取菜单列表* @param roleName* @return*/public List<Menu> getRoleMenus(String roleName){Integer roleId = roleMapper.getRoleIdByName(roleName);List<Integer> menuIds = roleMenuMapper.getMenuIdsByRoleId(roleId);// 查询系统中所有菜单,树结构List<Menu> menus = menuService.findMenus("");// new一个最后筛选完成之后的listArrayList<Menu> roleMenus = new ArrayList<>();for (Menu menu : menus) {if(menuIds.contains(menu.getId())){roleMenus.add(menu);}List<Menu> children = menu.getChildren();// 移除children中不在menuIds集合中的元素if (children != null) {children.removeIf(child -> !menuIds.contains(child.getId()));}}return roleMenus;}}

1.5 MenuServiceImpl

将写在MenuController中的方法提取到service中(更符合现实中的开发)
如下图所示:controller不含业务代码
在这里插入图片描述

package com.ppj.service.impl;import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ppj.entity.Menu;
import com.ppj.mapper.MenuMapper;
import com.ppj.service.IMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.stream.Collectors;/*** <p>*  服务实现类* </p>** @author ppj* @since 2024-05-29*/
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {public List<Menu> findMenus(String name) {QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();if(StrUtil.isNotBlank(name)){queryWrapper.like("name",name);}List<Menu> list = list(queryWrapper);//找出一级菜单List<Menu> parentNodes = list.stream().filter(menu -> menu.getPid() == null).collect(Collectors.toList());//找出一级菜单的子菜单for (Menu menu : parentNodes) {menu.setChildren(list.stream().filter(m -> menu.getId().equals(m.getPid())).collect(Collectors.toList()));}return parentNodes;}}

2. 前端实现

2.1 User.vue

主要是表格多添加role字段,添加框增加角色选择框

<template><div><div style="margin: 10px 0"><el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="username"></el-input><el-input style="width: 200px" placeholder="请输入地址" suffix-icon="el-icon-position" class="ml-5" v-model="address"></el-input><el-button class="ml-5" type="primary" @click="getList">搜索</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button></div><div style="margin: 10px 0"><el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i></el-button><el-button type="warning" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate">修改</el-button><el-button type="danger" :disabled="multiple" @click="handleDelete">删除 <i class="el-icon-remove-outline"></i></el-button><!--      <el-upload action="http://localhost:9000/user/import" :show-file-list="false" accept="xlsx" :on-success="handleImport" style="display: inline-block">-->
<!--        <el-button type="primary" class="ml-5">导入 <i class="el-icon-bottom"></i></el-button>-->
<!--      </el-upload>--><el-button type="success" @click="handleImport" class="ml-5">导入 <i class="el-icon-bottom"></i></el-button><el-button type="warning" @click="handleExport" class="ml-5">导出 <i class="el-icon-top"></i></el-button></div><el-table v-loading="loading" :data="tableData" border stripe :header-cell-class-name="headerBg" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55"></el-table-column><el-table-column prop="id" label="序号" width="140"></el-table-column><el-table-column prop="username" label="用户名" width="140"></el-table-column><el-table-column prop="nickname" label="昵称" width="140"></el-table-column><el-table-column prop="role" label="角色" width="140"><template v-slot="scope"><el-tag>{{ scope.row.role}}</el-tag></template></el-table-column><el-table-column prop="email" label="邮箱" width="200"></el-table-column><el-table-column prop="address" label="地址" width="140"></el-table-column><el-table-column prop="createTime" label="创建时间" width="140"></el-table-column><el-table-column label="操作"  align="center"><template v-slot="scope"><el-button type="success" @click="handleUpdate(scope.row)">编辑 <i class="el-icon-edit"></i></el-button><el-button type="danger" @click="handleDelete(scope.row)">删除 <i class="el-icon-remove-outline"></i></el-button></template></el-table-column></el-table><div style="padding: 10px 0"><el-paginationclass="page"@size-change="handleSizeChange"@current-change="handleCurrentChange":page-sizes="[5, 10]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div><!--  用户信息       --><el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%" ><el-form label-width="80px" size="small"><el-form-item label="用户名"><el-input v-model="form.username" autocomplete="off"></el-input></el-form-item><el-form-item label="昵称"><el-input v-model="form.nickname" autocomplete="off"></el-input></el-form-item><el-form-item label="角色" ><el-select v-model="form.role" placeholder="请选择" style="width: 100%"><el-optionv-for="item in roles":key="item.name":label="item.name":value="item.roleKey"></el-option></el-select></el-form-item><el-form-item label="邮箱"><el-input v-model="form.email" autocomplete="off"></el-input></el-form-item><el-form-item label="电话"><el-input v-model="form.phone" autocomplete="off"></el-input></el-form-item><el-form-item label="地址"><el-input v-model="form.address" autocomplete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogFormVisible = false">取 消</el-button><el-button type="primary" @click="save">确 定</el-button></div></el-dialog><!-- 用户导入对话框 --><el-dialog :title="upload.title" :visible.sync="upload.open" width="400px"><el-uploadref="upload":limit="1"accept=".xlsx, .xls":action="upload.url":disabled="upload.isUploading":on-progress="handleFileUploadProgress":on-success="handleFileSuccess":auto-upload="false"drag><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div><div class="el-upload__tip" slot="tip"><el-link type="info" style="font-size: 16px;color:green" @click="importTemplate">下载模板</el-link></div><div class="el-upload__tip" style="color: red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div></el-upload><div slot="footer" class="dialog-footer"><el-button type="primary" @click="submitFileForm">确 定</el-button><el-button @click="upload.open = false">取 消</el-button></div></el-dialog></div></template><script>
export default {name: "User",data(){return {tableData: [],pageSize: 5,total: 0,pageNum: 1,username: '',address: '',collapseBtnClass: 'el-icon-s-fold',isCollapse: false,sideWidth: 200,logoTextShow: true,headerBg: 'headerBg',dialogFormVisible: false,form: {},// 遮罩层loading: true,// 选中数组ids: [],// 非单个禁用single: true,// 非多个禁用multiple: true,//用户导入参数upload: {//是否显示弹出层(用户导入)open: false,//弹出层标题(用户导入)title: "",//是否禁用上传// isUploading: false,//是否更新已经存在的用户数据//updateSupport: 0,//设置上传的请求头部//headers: "",//上传的地址url: "http://localhost:9000/user/import",},roles: [],}},created() {this.getList();},methods: {getList(){this.loading = true;this.request.get('/user/page',{params: {pageNum: this.pageNum,pageSize: this.pageSize,username: this.username,address: this.address}}).then(res => {if(res.code === '200'){this.tableData = res.data.records;this.total = res.data.total;this.loading = false;}else{this.$message.error(res.msg)}})this.request.get('/role').then(res => {if(res.code === '200'){this.roles = res.data;}else{this.$message.error(res.msg)}})},handleSizeChange(val) {this.pageSize = val;},handleCurrentChange(val) {this.pageNum = val;this.getList();},// 多选框选中数据handleSelectionChange(selection) {this.ids = selection.map(item => item.id);this.single = selection.length != 1;this.multiple = !selection.length;},// 重置按钮resetQuery(){this.username = undefined;this.address = undefined;this.getList();},// 新增handleAdd(){this.dialogFormVisible = true;this.form = {};},save(){this.request.post("/user",this.form).then(res => {if(res.code === "200" || res.code === 200){this.$message.success("操作成功")}else {this.$message.error("操作失败")}this.dialogFormVisible = false;this.getList();})},// 修改handleUpdate(row){// 表单置空this.reset();// 重新查询数据const userId = row.id || this.ids;this.request.get('/user/'+userId).then(response => {this.form = response.data;this.dialogFormVisible = true;});},reset(){this.form.username = undefined;this.form.nickname = undefined;this.form.email = undefined;this.form.phone = undefined;this.form.address = undefined;},// 删除handleDelete(row){let _this = this;const userIds = row.id || this.ids;this.$confirm('是否确认删除用户编号为"' + userIds + '"的数据项?', '删除用户', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {_this.request.delete("/user/"+userIds).then(res=>{if(res.code === "200" || res.code === 200){_this.$message.success("删除成功")}else {_this.$message.error("删除失败")}this.getList();})}).catch(() => {});},// 导出handleExport(){window.open('http://localhost:9000/user/export');this.$message.success("导出成功");},// handleImport(){//   this.$message.success('导入成功')//   this.getList()// },handleImport(){this.upload.title = '用户导入'this.upload.open = true},importTemplate(){this.$message.success("正在下载模版");window.open('http://localhost:9000/user/download')},//文件上传处理handleFileUploadProgress(event,file,fileList){//this.upload.isUploading = true;this.loading = true;},//文件上传成功处理handleFileSuccess(response,file,fileList){this.loading = false;this.upload.open = false;// this.upload.isUploading = false;this.$refs.upload.clearFiles();this.$message.success("导入成功");this.getList()},//提交上传文件submitFileForm(){this.$refs.upload.submit();}}
}
</script><style scoped></style>

2.2 动态菜单设计

2.2.1 Login.vue

在登录成功的时候将菜单存到浏览器中

<template><div class="wrapper"><div style="margin: 200px auto; background-color: #fff; width: 350px; height: 300px; padding: 20px; border-radius: 10px"><div style="margin: 20px 0; text-align: center; font-size: 24px"><b>登 录</b></div><el-form :model="user" :rules="rules" ref="userForm"><el-form-item prop="username"><el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input></el-form-item><el-form-item prop="password"><el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input></el-form-item><el-form-item style="margin: 10px 0; text-align: right"><el-button type="primary" size="small"  autocomplete="off" @click="login">登录</el-button><el-button type="warning" size="small"  autocomplete="off" @click="$router.push('/register')">注册</el-button></el-form-item></el-form></div></div>
</template><script>
export default {name: "Login",data() {return {user: {},rules: {username: [{ required: true, message: '请输入用户名', trigger: 'blur' },{ min: 3, max: 10, message: '长度在 3 到 5 个字符', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }],}}},methods: {login() {this.$refs['userForm'].validate((valid) => {if (valid) {  // 表单校验合法this.request.post("/user/login", this.user).then(res => {if(res.code === 200 || res.code === '200') {localStorage.setItem('loginUser',JSON.stringify(res.data));localStorage.setItem("menus",JSON.stringify(res.data.menus));this.$router.push("/")this.$message.success("登录成功")} else {this.$message.error(res.msg)}})} else {return false;}});}}
}
</script><style>
.wrapper {height: 100vh;background-image: linear-gradient(to bottom right, #FC466B , #3F5EFB);overflow: hidden;
}
</style>

2.2.2 Aside.vue

侧边栏动态显示菜单

<template><el-menu :default-openeds="['1', '3']" style="min-height: 100%; overflow-x: hidden"background-color="rgb(48, 65, 86)"text-color="#fff"active-text-color="#ffd04b":collapse-transition="false":collapse="isCollapse"router><div style="height: 60px; line-height: 60px; text-align: center"><img src="../assets/logo.png" alt="" style="width: 20px; position: relative; top: 5px; right: 5px"><b style="color: white" v-show="logoTextShow">后台管理系统</b></div><div v-for="item in menus" :key="item.id"><!-- 一级菜单 --><div v-if="item.path"><el-menu-item :index="item.path"><template slot="title"><i :class="item.icon"></i><span slot="title">{{ item.name }}</span></template></el-menu-item></div><!-- 二级菜单 --><div v-else><el-submenu :index="item.id+''"><template slot="title"><i :class="item.icon"></i><span slot="title">{{ item.name }}</span></template><div v-for="subItem in item.children" :key="subItem.id"><el-menu-item :index="subItem.path"><template slot="title"><i :class="subItem.icon"></i><span slot="title">{{ subItem.name }}</span></template></el-menu-item></div></el-submenu></div></div></el-menu>
</template><script>
export default {name: "Aside",props: {isCollapse: Boolean,logoTextShow: Boolean},data() {return {menus: localStorage.getItem('menus') ? JSON.parse(localStorage.getItem('menus')) : []}},}
</script><style scoped></style>

2.3 动态路由设计

为什么设置动态路由,这是因为没有其他页面权限的用户也是可以访问其他页面

如下图所示:

安其拉是普通用户,只有主页的菜单权限(自己设置的角色所拥有的菜单)
在这里插入图片描述

2.3.1 菜单表新增字段page_path

对应每个页面组件名称

CREATE TABLE `sys_menu` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',`path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '路径',`page_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '页面路径',`pid` int(11) DEFAULT NULL COMMENT '父级id',`icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '图标',`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '描述',`deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC

2.3.2 路由设计

import Vue from 'vue'
import VueRouter from 'vue-router'
import Manage from '../views/Manage.vue'
import store from "@/store";Vue.use(VueRouter)
//定义一个路由对象数组
const routes = [{path: '/login',name: '登录',component: () => import('../views/Login.vue')},{path: '/register',name: '注册',component: () => import('../views/Register.vue')}]//使用路由对象数组创建路由实例,供main.js引用
const router = new VueRouter({mode: 'history',base: process.env.BASE_URL,routes
})// 注意:刷新页面会导致页面路由重置
export const setRoutes = () => {const storeMenus = localStorage.getItem("menus");if (storeMenus) {// 获取当前的路由对象名称数组const currentRouteNames = router.getRoutes().map(v => v.name)if (!currentRouteNames.includes('Manage')) {// 拼装动态路由const manageRoute = { path: '/', name: 'Manage', component: () => import('../views/Manage.vue'), redirect: "/home", children: [{ path: 'person', name: '个人信息', component: () => import('../views/Person.vue')},// { path: 'password', name: '修改密码', component: () => import('../views/Password.vue')}] }const menus = JSON.parse(storeMenus)menus.forEach(item => {if (item.path) {  // 当且仅当path不为空的时候才去设置路由let itemMenu = { path: item.path.replace("/", ""), name: item.name, component: () => import('../views/' + item.pagePath + '.vue'),meta: { title: item.name }}manageRoute.children.push(itemMenu)} else if(item.children.length) {item.children.forEach(item => {if (item.path) {let itemMenu = { path: item.path.replace("/", ""), name: item.name, component: () => import('../views/' + item.pagePath + '.vue'),meta: { title: item.name }}manageRoute.children.push(itemMenu)}})}})// 动态添加到现在的路由对象中去router.addRoute(manageRoute)}}
}// 重置我就再set一次路由
setRoutes()// 路由守卫
// router.beforeEach((to, from, next) => {
//   localStorage.setItem('currentPathName',to.name);   // 设置当前的路由名称,为了在Header组件中去使用
//   store.commit('setPath')    // 触发store的数据更新
//   next()   // 放行路由
// })export default router

2.3.3 登录界面Login.vue设置路由

改动登录页面在成功登录时,设置路由

// 导入
import {setRoutes} from "@/router";// 当登录成功时,同时设置路由
localStorage.setItem("user",JSON.stringify(res.data));  //存储用户信息到浏览器
localStorage.setItem("menus",JSON.stringify(res.data.menus));  //存储菜单到浏览器
setRoutes();
this.$router.push("/");
this.$message.success("登录成功");

总结

  • 本篇主要讲解动态菜单和动态路由的设计与实现

写在最后

如果此文对您有所帮助,请帅戈靓女们务必不要吝啬你们的Zan,感谢!!不懂的可以在评论区评论,有空会及时回复。
文章会一直更新

相关文章:

springboot + Vue前后端项目(第十五记)

项目实战第十五记 写在前面1.后端接口实现1.1 用户表添加角色字段1.2 角色表增加唯一标识字段1.3 UserDTO1.4 UserServiceImpl1.5 MenuServiceImpl 2. 前端实现2.1 User.vue2.2 动态菜单设计2.2.1 Login.vue2.2.2 Aside.vue 2.3 动态路由设计2.3.1 菜单表新增字段page_path2.3.…...

如何在Windows 11中恢复丢失的快速访问菜单?这里提供解决办法

序言 在电脑的“快速访问”菜单中找不到固定的项目?或者,整个菜单对你来说已经消失了吗?无论哪种方式,你都可以强制你的电脑恢复菜单并显示其中的所有项目。以下是如何在你的Windows 11电脑上做到这一点。 将文件资源管理器设置为打开到主页 当你在文件资源管理器的左侧…...

变声器软件免费版有哪些?国内外12大热门变声器大盘点!(新)

变声软件是一种人工智能AI音频处理工具&#xff0c;允许用户实时修改自己的声音或改变预先录制的音频。这些软件解决方案可提供不同的效果&#xff0c;如改变声音的音调或速度&#xff0c;或将我们的声音转换成其他人或其他东西的声音&#xff0c;如名人、卡通人物、机器人或不…...

计算机网络 —— 数据链路层(无线局域网)

计算机网络 —— 数据链路层&#xff08;无线局域网&#xff09; 什么是无线局域网IEEE 802.11主要标准及其特点&#xff1a; 802.11的MAC帧样式 我们来看看无线局域网&#xff1a; 什么是无线局域网 无线局域网&#xff08;Wireless Local Area Network&#xff0c;简称WLAN…...

SpringBoot图书管理系统【附:资料➕文档】

前言&#xff1a;我是源码分享交流Coding&#xff0c;专注JavaVue领域&#xff0c;专业提供程序设计开发、源码分享、 技术指导讲解、各类项目免费分享&#xff0c;定制和毕业设计服务&#xff01; 免费获取方式--->>文章末尾处&#xff01; 项目介绍048&#xff1a; 图…...

shell简介

一、Shell 概念定义 Shell 是用 C 语言编写的程序&#xff0c;是用户使用 Linux 的桥梁&#xff0c;既是命令语言又是程序设计语言。 shell 脚本为 Shell 编写的脚本程序&#xff0c;常说的 shell 通常指 shell 脚本。 包含一系列命令的文本文件&#xff0c;这些命令按照特定…...

使用 Scapy 库编写 ICMP 不可达攻击脚本

一、介绍 ICMP不可达攻击是一种利用ICMP&#xff08;Internet Control Message Protocol&#xff09;不可达消息来干扰或中断目标系统的网络通信的攻击类型。通过发送伪造的ICMP不可达消息&#xff0c;攻击者可以诱使目标系统认为某些网络路径或主机不可达&#xff0c;从而导致…...

Electron qt开发教程

模块安装打包 npm install -g electron-forge electron-forge init my-project --templatevue npm start //进入目录启动 //打包成一个目录到out目录下&#xff0c;注意这种打包一般用于调试&#xff0c;并不是用于分发 npm run package //打出真正的分发包&#xff0c;放在o…...

尝试用 GPT-4o 写 2024高考语文作文

文章目录 新课标I卷科技进步与问题的演变 新课标II卷抵达未知之境&#xff1a;探索与成长的旅程 全国甲卷坦诚交流&#xff1a;构建真正相遇的桥梁 北京卷历久弥新 天津卷定义与自定义&#xff1a;在世界的缤纷中前行 上海卷认可度的思考与反思 新课标I卷 阅读下面的材料&#…...

自动化Reddit图片收集:Python爬虫技巧

引言 Reddit&#xff0c;作为一个全球性的社交平台&#xff0c;拥有海量的用户生成内容&#xff0c;其中包括大量的图片资源。对于数据科学家、市场研究人员或任何需要大量图片资源的人来说&#xff0c;自动化地从Reddit收集图片是一个极具价值的技能。本文将详细介绍如何使用…...

自动驾驶人工智能

自动驾驶技术中使用的算法和滤波器 如何部署软件中的算法和滤波器&#xff0c;以增强传感器数据的可用性和应用性 自动驾驶人工智能 文章目录 一、介绍二、自动驾驶的算法2.1 感知算法2.2 本地化算法2.3 映射算法2.4 规划算法2.5 控制算法2.6 过滤 器2.7 卡尔曼滤波器2.8 颗粒过…...

基础乐理入门

基础概念 乐音&#xff1a;音高&#xff08;频率&#xff09;固定&#xff0c;振动规则的音。钢琴等乐器发出的是乐音&#xff0c;听起来悦耳、柔和。噪音&#xff1a;振动不规则&#xff0c;音高也不明显的音。风声、雨声、机器轰鸣声是噪音&#xff0c;大多数打击乐器&#…...

mysql 8 linux7,8安装教程

选择自己对应的linux版本 cat /etc/os-release //查看自己linux系统版本 1.mysql下载地址 MySQL :: Download MySQL Community Server (Archived Versions) 拉到下面找到 选择自己linux指定的版本&#xff0c;否则会很麻烦 cat /etc/os-release //查看系统版本 2.查…...

『矩阵论笔记』特征分解(eigendecomposition)通俗解释!

特征分解(eigendecomposition)通俗解释! 文章目录 一. 特征分解(eigendecomposition)通俗解释!1. 它是如何工作的2. 试图达到什么目的3. 为什么它有用(将一个方阵分解成这三个组成矩阵有什么好处呢?)二. 参考文献一. 特征分解(eigendecomposition)通俗解释! 大家好,欢迎回…...

顶级域名和二级域名的区别

互联网是一个由无数个网络节点组成的复杂系统&#xff0c;而域名则是这个系统中用于识别和定位这些节点的重要工具。在域名体系中&#xff0c;顶级域名(Top-Level Domain&#xff0c;TLD)和二级域名(Second-Level Domain&#xff0c;SLD)是两个基本的层级概念。本文将探讨这两者…...

深入解析Kafka消息丢失的原因与解决方案

深入解析Kafka消息丢失的原因与解决方案 Apache Kafka是一种高吞吐量、分布式的消息系统&#xff0c;广泛应用于实时数据流处理。然而&#xff0c;在某些情况下&#xff0c;Kafka可能会出现消息丢失的情况&#xff0c;这对于数据敏感的应用来说是不可接受的。本文将深入解析Ka…...

【Python列表解锁】:掌握序列精髓,驾驭动态数据集合

文章目录 &#x1f680;一、列表&#x1f308;二、常规操作&#x1f4a5;增&#x1f4a5;删&#x1f4a5;改&#x1f4a5;查 ⭐三、补充操作 &#x1f680;一、列表 列表是一个能够存储多个同一或不同元素的序列 列表&#xff1a;list ---- [] 列表属于序列类型&#xff08;容器…...

安卓打造安装包(应用打包、规范处理安装包、安全加固)

本章介绍应用安装包的基本制作规范&#xff0c;主要包括&#xff1a;如何导出既美观又精简的APK文件、如何按照上线规范调整App的相关设置、如何对APK文件进行安全加固以防止安装包被破解。 应用打包 本节介绍APK安装包的打包过程&#xff0c;包括&#xff1a;如何利用Androi…...

ElasticSearch教程(详解版)

本篇博客将向各位详细介绍elasticsearch&#xff0c;也算是对我最近学完elasticsearch的一个总结&#xff0c;对于如何在Kibana中使用DSL指令&#xff0c;本篇文章不会进行介绍&#xff0c;这里只会介绍在java中如何进行使用&#xff0c;保证你看完之后就会在项目中进行上手&am…...

[office] excel做曲线图的方法步骤详解 #经验分享#知识分享#其他

excel做曲线图的方法步骤详解 Excel是当今社会最流行用的办公软件之一&#xff0c;Excel可以用于数据的整理、分析、对比。可以更直观的看到数据的变化情况&#xff0c;而有很多时候需要制作曲线图表进行数据比较&#xff0c;因此&#xff0c;下面是小编整理的如何用excel做曲线…...

Git+Gitlab 远程库测试学习

Git远程仓库 1、Git远程仓库 何搭建Git远程仓库呢&#xff1f;我们可以借助互联网上提供的一些代码托管服务来实现 Gitee 码云是国内的一个代码托管平台&#xff0c;由于服务器在国内&#xff0c;所以相比于GitHub&#xff0c;码云速度会更快 码云 Gitee - 基于 Git 的代码托…...

Python可视化 | 使用matplotlib绘制面积图示例

面积图是数据可视化中的一个有效工具&#xff0c;用于说明时间上的关系和趋势。它们提供了一种全面的、视觉上迷人的方法&#xff0c;通过熟练地将折线图的可读性与填充区域的吸引力相结合来呈现数值数据。 在本文中&#xff0c;我们将学习更多关于在Python中创建面积折线图的…...

【环境搭建】2.阿里云ECS服务器 安装MySQL

在阿里云的 Alibaba Cloud Linux 3.2104 LTS 64位系统上安装 MySQL 8&#xff0c;可以按照以下步骤进行&#xff1a; 1.更新系统软件包&#xff1a; 首先&#xff0c;更新系统软件包以确保所有软件包都是最新的&#xff1a; sudo yum update -y2.下载 MySQL 8 官方 Yum 仓库…...

Python Flask 入门开发

Python基础学习&#xff1a; Pyhton 语法基础Python 变量Python控制流Python 函数与类Python Exception处理Python 文件操作Python 日期与时间Python Socket的使用Python 模块Python 魔法方法与属性 Flask基础学习&#xff1a; Python中如何选择Web开发框架&#xff1f;Pyth…...

PostgreSQL查看当前锁信息

PostgreSQL查看当前锁信息 基础信息 OS版本&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本&#xff1a;16.2 pg软件目录&#xff1a;/home/pg16/soft pg数据目录&#xff1a;/home/pg16/data 端口&#xff1a;5777查看当前锁信息的sql SELECT pg_s…...

毫米波雷达深度学习技术-1.6目标识别2

1.6.4 自动编码器和变体自动编码器 自编码器包括一个编码器神经网络&#xff0c;随后是一个解码器神经网络&#xff0c;其目的是在输出处重建输入数据。自动编码器的设计在网络中施加了一个瓶颈&#xff0c;它鼓励原始输入的压缩表示。通常&#xff0c;自编码器旨在利用数据中的…...

MineAdmin 前端打包后,访问速度慢原因及优化

前言&#xff1a;打包mineadmin-vue前端后&#xff0c;访问速度很慢&#xff0c;打开控制台&#xff0c;发现有一个index-xxx.js文件达7M&#xff0c;加载时间太长&#xff1b; 优化&#xff1a; 一&#xff1a;使用文件压缩&#xff08;gzip压缩&#xff09; 1、安装compre…...

使用Obfuscar 混淆WPF(Net6)程序

Obfuscar 是.Net 程序集的基本混淆器&#xff0c;它使用大量的重载将.Net程序集中的元数据&#xff08;方法&#xff0c;属性、事件、字段、类型和命名空间的名称&#xff09;重命名为最小集。详细使用方式参见&#xff1a;Obfuscar 在NetFramework框架进行的WPF程序的混淆比较…...

高中数学:数列-基础概念

一、什么是数列&#xff1f; 一般地&#xff0c;我们把按照确定的顺序排列的一列数称为数列&#xff0c;数列中的每一个数叫做这个数列的项&#xff0c;数列的第一项称为首项。 项数有限个的数列叫做有穷数列&#xff0c;项数无限个的数列叫做无穷数列。 二、一般形式 数列和…...

linux中dd命令以及如何测试读写速度

dd命令详解 dd命令是一个在Unix和类Unix系统中非常常用的命令行工具&#xff0c;它主要用于复制文件和转换文件数据。下面我会详细介绍一些dd命令的常见用法和功能&#xff1a; 基本语法 dd命令的基本语法如下&#xff1a; bash Copy Code dd [option]...主要选项和参数 if…...

微信网站怎么做的好/seo网站推广案例

UART与串口(serial port)的区别 UART&#xff1a;通用异步收发传输器&#xff0c;将并行数据与串行数据进行转换 串口&#xff1a;串行数据传输 所谓串行&#xff0c;就是按照顺序&#xff0c;一个一个的来&#xff0c;就比如食堂打饭&#xff0c;只有一个窗口&#xff0c;必须…...

欧莱雅的网络营销策划方案/合肥seo代理商

7:30&#xff1a;起床。英国威斯敏斯特大学的研究人员发现&#xff0c;那些在早上5:22―7:21 分起床的人&#xff0c;其血液中有一种能引起心脏病的物质含量较高&#xff0c;因此&#xff0c;在7:21之后起床对身体健康更加有益。  打开台灯。“一醒来&#xff0c;就将灯打开&…...

做网站需要啥备案之类的嘛/网站品牌推广策略

prn是什么文件?使用打印机打印文件时&#xff0c;如果你在打印设置界面中选择“输出到文件”&#xff0c;那么随后输出到指定目录的文件默认是以.prn为扩展名&#xff0c;这个文件就是所谓的“prn文件”。prn文件虽然扩展名都是.prn&#xff0c;但对于不同品牌的打印机&#x…...

电子商务网站开发流程/百度问答我要提问

收听地址S01E01-开发者关系 (DevRel) 是开源项目和开源社区的加速器嘉宾Feynman Zhou[1] (周鹏飞)主持人majinghe[2]&#xff08;开源爱好者、DevOps 技术布道师&#xff09;Rick[3]&#xff08;开源爱好者、业余开源布道师&#xff09;Wey Gu[4]&#xff08;开源相信者、程序员…...

个人网站做博客还是做论坛/百度seo分析工具

注&#xff1a;本作品曾获高校优秀论文奖&#xff0c;时隔4年后&#xff0c;现由gogogan授权WWW.ITPASSBIBLO.COM 独家发行&#xff0c;如有媒体转载请保留出处。中文摘要<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />随着INT…...

网站设计与网页制作毕业论文/黄金网站app视频播放画质选择

现在数据库或是某个文件里有一些数据需要显示在界面上该怎么办呢&#xff1f;我们的目的就是将这些数据设法放到界面上去可以显示出来&#xff0c;这个将数据放到界面上去的过程可以叫住数据绑定。在Flex应用中程序中&#xff0c;数据绑定的实际是借助事件机制来完成的&#xf…...