基于Springboot2+Vue3的Partner交友项目(四)

本文主要是介绍基于Springboot2+Vue3的Partner交友项目(四),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

    • 后台管理
      • 环境搭建
      • 后台登录及数据请求
      • 后台管理增删改查
      • 导入导出和批量删除
      • 问题集

后台管理

环境搭建

1 创建vue3项目 npm init vue@latest

2 运行项目 cd partner-managernpm installnpm run dev

3 npm 配置淘宝镜像 npm config set registry https://registry.npm.taobao.org

4 配置启动

在这里插入图片描述

5 vite.config.js 配置端口号

export default defineConfig({server: {host: '0.0.0.0',port: 7000,https: false,}
})

6 安装依赖

element-plus 安装:npm install element-plus --save

// main.js
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'app.use(ElementPlus)

iicon组件 安装:npm i @element-plus/icons-vue -S

axios 安装:npm i axios -S 封装 request.js 和 config.js

// config.js
export default {serverUrl: 'localhost:9090'
}
// request.js   直接复制前端项目的  

缓存持久化插件 pinia数据是缓存到内存的,开启持久化后才能缓存到浏览器

安装: npm i pinia-plugin-persistedstate -S

// main.js 引用
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)

7 布局主页面
在这里插入图片描述

8 分页插件中文设置:

// 在main.js里引入:
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'app.use(ElementPlus, {locale: zhCn,
})

9 完成后台管理基本框架:头部菜单、左侧菜单、主体。主体分文搜索区域、表格区域、分页区域。

后台登录及数据请求

后台登录:实现 Login.vue,用到了 request.js 、config.js 、user.js。直接复制前端项目的,但 user.js 里面的 user 属性需

​ 要改为 manager。否则前后端用的同一个缓存且变量名一样,退出登录时删除后端数据会导致前台数据被删。

数据请求:实现 load 方法。

const namex = ref('')
const address = ref('')
const pageNum= ref(1)
const pageSize = ref(6)
const total = ref(0)const state = reactive({tableData: [],form: {} // 绑定表单类容
})// 拿后台数据
const load = () => {request.get('user/page',{params: {namex: namex.value, // 注意.valueaddress: address.value,pageNum: pageNum.value,pageSize: pageSize.value,}}).then(res => {console.log(res)state.tableData = res.data.recordstotal.value = res.data.total})
}
load()

后台管理增删改查

实现退出登录

   // UserServiceImpl@Overridepublic void logout(String uid) {// 退出登录 satoken提供StpUtil.logout(uid);log.info("用户{}退出成功", uid);}//  WebController @ApiOperation(value = "用户退出登录接口")@GetMapping("/logout/{uid}")public Result logout(@PathVariable String uid) {userService.logout(uid);return Result.success();}
  const logout = () => {request.get('/logout/' + user.uid).then(res => {if (res.code === '200') {userStore.logout() // 把当前用户信息删除} else {ElMessage.error(res.msg)}})}

实现分页 分页插件 Mybatis-plus官网:https://baomidou.com/

package com.partner.boot.common;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MybatisPlusConfig {/*** 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}

实现查询和重置 多条件分页模糊查询 前端还是调用load

     // UserController @GetMapping("/page")public Result findPage(@RequestParam(defaultValue = "") String namex,@RequestParam(defaultValue = "") String address,@RequestParam Integer pageNum,@RequestParam Integer pageSize) {System.out.print(namex + "  " +address  + "  " + pageNum  + "  " + pageSize);QueryWrapper<User> queryWrapper = new QueryWrapper<User>().orderByDesc("id");// !"" = StrUtil.isNotBlank(name)  name and addressqueryWrapper.like(StrUtil.isNotBlank(namex), "namex", namex);queryWrapper.like(StrUtil.isNotBlank(address), "address", address);return Result.success(userService.page(new Page<>(pageNum, pageSize), queryWrapper));}
// 刷新
const reset = () => {namex.value = ''load()
}

设置每页信息数 当前页面数

    // 页面数const  currentChange = (num) => {console.log(num)pageNum.value = numload()}// 页面大小const handleSizeChange = (size) => {console.log(size)pageSize.value = sizeload()}<!-- 分页 @=v-on--><div style="margin: 10px 0"><el-pagination@current-change="currentChange"@size-change="handleSizeChange"v-model:current-page="pageNum"v-model:page-size="pageSize"background:page-sizes="[2, 4, 6, 10, 20]"layout="total, sizes, prev, pager, next, jumper":total=totalclass="mt-4"/></div>

实现新增与编辑,element弹窗组件,dialog

const dialogFormVisible = ref(false)const ruleFormRef = ref()// 新增校验规则
const rules = reactive({namex: [{ required: true, message: '请输入名称', trigger: 'blur' },{min: 3, max: 20, message: '长度在3-20之间', trigger: 'blur'}]
})// 新增
const handleAdd = () => {dialogFormVisible.value = trueruleFormRef.value.resetFields() // 取消校验提示state.form = {}
}// 保存
const save = () => {ruleFormRef.value.validate(valid => {// valid就是校验的结果// if (valid) {//   request.post('/user',state.form).then(res => {//     // 保存新用户//     if (res.code === '200') {//       ElMessage.success('保存成功')//       // 关闭弹窗//       dialogFormVisible.value = false//       load()  // 刷新表格数据//     } else {//       ElMessage.error(res.msg)//     }//   })// 区分新增与修改request.request({url: '/user',method: state.form.id ? 'put' : 'post',data: state.form}).then(res => {if (res.code === '200') {ElMessage.success('保存成功')dialogFormVisible.value = falseload()  // 刷新表格数据} else {ElMessage.error(res.msg)}})})
}// 编辑
const handleEdit = (raw) => {state.form = JSON.parse(JSON.stringify(raw))  // 以下两段代码位置不可以互换dialogFormVisible.value = trueruleFormRef.value.resetFields() // 取消校验提示
}<!-- 弹窗 --><el-dialog v-model="dialogFormVisible" title="信息" width="40%"><el-form ref="ruleFormRef" :rules="rules" :model="state.form" label-width="80px" style="padding: 0 20px" status-icon><!-- 保存新用户--><el-form-item prop="username" label="用户名" ><el-input v-model="state.form.username" autocomplete="off" /></el-form-item><el-form-item prop="namex" label="姓名" ><el-input v-model="state.form.namex" autocomplete="off" /></el-form-item><el-form-item prop="email" label="邮箱" ><el-input v-model="state.form.email" autocomplete="off" /></el-form-item><el-form-item prop="address" label="地址" ><el-input v-model="state.form.address" autocomplete="off" /></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogFormVisible = false"> 取消 </el-button><el-button type="primary" @click="save"> 保存 </el-button></span></template></el-dialog>

增加地址字段,在数据库中加字段,在后端的实体类里也要增加。

实现删除

// 删除
const del = (id) => {request.delete('/user/' + id).then(res => {if (res.code === '200') {ElMessage.success('操作成功')load()  // 刷新表格数据} else {ElMessage.error(res.msg)}})
}

删除是逻辑删除,防止数据误删除

导入导出和批量删除

导出 先实现导出,因为导入需要一个模板导,导出成一个模板,再修改模板然后实现导入。 导出实际就是打开一个新

​ 的链接下载表格数据。导出是没有权限验证的,需要在 MyWebMvcConfig.java里面进行放 行: “/**/export”。

    // 导出接口const exportData = () => {window.open(`http://${config.serverUrl}/user/export`)}<!-- 导出 --><el-button type="primary" @click="exportData" class="ml5"><el-icon style="vertical-align: middle"><Top /></el-icon>  <span style="vertical-align: middle"> 导出 </span></el-button>

加上这个依赖,才能实现Excel导入导出

<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version>
</dependency>
    /***  UserController 导出接口*/@GetMapping("/export")public void export(HttpServletResponse response) throws Exception {// 从数据库查询出所有的数据List<User> list = userService.list();// 在内存操作,写出到浏览器 1操作ExcelWriter的一个对象ExcelWriter writer = ExcelUtil.getWriter(true);// 一次性写出list内的对象到excel,使用默认样式,强制输出标题writer.write(list, true);// 设置浏览器响应的格式 2 response对象response.setContentType("application/vnd.openxmlformats-"+ "officedocument.spreadsheetml.sheet;charset=utf-8");// 编码String fileName = URLEncoder.encode("User信息表", "UTF-8");// attachment 附件形式response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");// 3 写出到浏览器ServletOutputStream out = response.getOutputStream();writer.flush(out, true);out.close();writer.close();}

输出为excel统一请求头

application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8

导入 导入可以理解为文件上传 ,后端没有对导入进行放行,因为是一个Post请求,所以需要校验token

    const userStore = useUserStore()const token = userStore.getBearerTokenconst handleImportSuccess = () => {// 刷新表格load()ElMessage.success("导入成功")}<!--  导入 --><el-uploadclass="ml5":show-file-list="false"style="display: inline-block; position: relative; top: 3px":action='`http://${config.serverUrl}/user/import`':on-success="handleImportSuccess":headers="{ Authorization: token}"><el-button type="primary"><el-icon style="vertical-align: middle"><Bottom /></el-icon>  <span style="vertical-align: middle"> 导入 </span></el-button></el-upload>
    /*** excel 导入* @param file* @throws Exception*/@PostMapping("/import")public Result imp(MultipartFile file) throws Exception {// 1 理解为文件上传 将file转换为流InputStream inputStream = file.getInputStream();// 2 操作ExcelReader的一个对象 从流里读数据ExcelReader reader = ExcelUtil.getReader(inputStream);// 3 通过 javabean 的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来List<User> list = reader.readAll(User.class);// userService.saveBatch(list);// 应该对用户存的数据进行校验for (User user : list) {userService.saveUser(user);}return Result.success();}

导入用户数据的时候,注意校验数据得合法性,我是通过 saveUser 方法进行保存数据的

注意模板的选择,id列要删除,否则会报错。敏感的数据要清空,比如:uid。

在这里插入图片描述

批量删除

    // 批量删除const multipleSelection = ref([])const handleSelectionChange = (val) => {multipleSelection.value = val}const confirmDelBatch = () => {if (!multipleSelection.value || !multipleSelection.value.length) {ElMessage.warning("请选择数据")return}// 把对象数组 multipleSelection.value  转化为只包含 id 的纯数字 的数组 idArrconst idArr = multipleSelection.value.map(v => v.id)console.log(idArr)request.post('/user/del/batch', idArr).then(res => {if (res.code === '200') {ElMessage.success('操作成功')load()  // 刷新表格数据} else {ElMessage.error(res.msg)}})}<!-- 批量删除 --><el-popconfirm title="您确定删除吗?" @confirm="confirmDelBatch"><template #reference><el-button type="danger" style="margin-left: 5px"><el-icon style="vertical-align: middle"><Remove /></el-icon>  <span style="vertical-align: middle"> 批量删除 </span></el-button></template></el-popconfirm>
    //  UserController@PostMapping("/del/batch")public Result deleteBatch(@RequestBody List<Integer> ids) {// 后端是list列表对象,前端是list数组,springMVC在做转换的时候,// 他会把前端的数组 通过json的方式 映射成一个listuserService.removeByIds(ids);return Result.success();}

注意批量删除触发的时机,点击行的时候只是保存了行id数组,真正的删除是在点击

[批量删除] 按钮的时候触发的!

问题集

排查错误:先看请求,即前端传的值是否有问题;再看后台,后台记录可能有问题。

问题一:退出登录时删除后端数据会导致前台数据也被删除

解决:前后端用的同一个缓存且变量名一样,将前端变量名改为manager

在这里插入图片描述

问题二:新增保存不成功

解决:后台报错没设置默认密码,修改UserController里的save方法,将调用save改为调用saveUser,会校验用户是否存

​ 在,也会设置初始密码。同时需要在UserServiceImpl将saveUser方法改为public,在IUserService里的定义

​ saveUser方法。

问题三:form = JSON.parse(JSON.stringify(raw)) 这么赋值,form会变成响应式,就没法编辑

解决: const state = reactive({ form: {} })

这篇关于基于Springboot2+Vue3的Partner交友项目(四)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/727768

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p