【wiki知识库】04.SpringBoot后端实现电子书的增删改查以及前端界面的展示

  📝个人主页:哈__

期待您的关注 

目录

一、🔥今日内容

二、🌏前端页面的改造

2.1新增电子书管理页面

2.2新增路由规则

2.3修改the-header代码

三、🚗SpringBoot后端Ebook模块改造

3.1增加电子书增/改接口

3.1.1新增EbookSaveParam

3.1.2添加Controller代码

3.1.3在Ebook实体类上增加一个注解

3.2 增加电子书删除接口

四、🔨测试 

4.1添加功能测试

4.2修改功能测试。

4.3删除功能测试


一、🔥今日内容

【wiki知识库】03.前后端的初步交互(展现所有的电子书)-CSDN博客

上一次带领大家把前端的首页部分实现了一下,成功的从数据库当中取出了我们的信息并且展示在前端页面,到了下图的部分。

今天主要是把这个网页的界面初步优化一下,修改一下导航栏以及增加电子书管理模块。包含电子书的查询功能、新增功能、编辑功能和删除功能(不包括文档管理)。

二、🌏前端页面的改造

2.1新增电子书管理页面

我在src下新建了admin文件夹,这个文件夹中的内容是给网站管理员看到的,所以放到了admin目录,名字为admin-ebook.vue。

 admin-ebook.vue的具体内容如下。这个文件里我注释掉了一些信息,而且这个文件中的内容包含了页面需要的功能很多,有的一些并不是今天要讲解的内容,所以并没有使用到。今天主要就是想带着大家做出一个电子书管理的模块来。

<template><a-layout><a-layout-content:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"><p><a-form layout="inline" :model="param"><a-form-item><a-input v-model:value="param.name" placeholder="名称"></a-input></a-form-item><a-form-item><a-button type="primary" @click="handleQuery({page: 1, size: pagination.pageSize})">查询</a-button></a-form-item><a-form-item><a-button type="primary" @click="add()">新增</a-button></a-form-item></a-form></p><a-table:columns="columns":row-key="record => record.id":data-source="ebooks":pagination="pagination":loading="loading"@change="handleTableChange"><template #cover="{ text: cover }"><img v-if="cover" :src="cover" alt="avatar" /></template><template v-slot:category="{ text, record }"><!-- <span>{{ getCategoryName(record.category1Id) }} / {{ getCategoryName(record.category2Id) }}</span> --></template><template v-slot:action="{ text, record }"><a-space size="small"><!-- <router-link :to="'/admin/doc?ebookId=' + record.id"> --><a-button type="primary">文档管理</a-button><!-- </router-link> --><a-button type="primary" @click="edit(record)">编辑</a-button><a-popconfirmtitle="删除后不可恢复,确认删除?"ok-text="是"cancel-text="否"@confirm="handleDelete(record.id)"><a-button type="danger">删除</a-button></a-popconfirm></a-space></template></a-table></a-layout-content></a-layout><a-modaltitle="电子书表单"v-model:visible="modalVisible":confirm-loading="modalLoading"@ok="handleModalOk"><a-form :model="ebook" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }"><a-form-item label="封面"><a-input v-model:value="ebook.cover" /></a-form-item><a-form-item label="名称"><a-input v-model:value="ebook.name" /></a-form-item><a-form-item label="分类"><a-cascaderv-model:value="categoryIds":field-names="{ label: 'name', value: 'id', children: 'children' }":options="level1"/></a-form-item><a-form-item label="描述"><a-input v-model:value="ebook.description" type="textarea" /></a-form-item></a-form></a-modal>
</template><script lang="ts">import { defineComponent, onMounted, ref } from 'vue';import axios from 'axios';import { message } from 'ant-design-vue';import {Tool} from "@/util/tool";export default defineComponent({name: 'AdminEbook',setup() {const param = ref();param.value = {};const ebooks = ref();const pagination = ref({current: 1,pageSize: 10,total: 0});const loading = ref(false);const columns = [{title: '封面',dataIndex: 'cover',slots: { customRender: 'cover' }},{title: '名称',dataIndex: 'name'},{title: '分类',slots: { customRender: 'category' }},{title: '文档数',dataIndex: 'docCount'},{title: '阅读数',dataIndex: 'viewCount'},{title: '点赞数',dataIndex: 'voteCount'},{title: 'Action',key: 'action',slots: { customRender: 'action' }}];/*** 数据查询**/const handleQuery = (params: any) => {loading.value = true;// 如果不清空现有数据,则编辑保存重新加载数据后,再点编辑,则列表显示的还是编辑前的数据ebooks.value = [];axios.get("/ebook/list", {params: {page: params.page,size: params.size,name: param.value.name}}).then((response) => {loading.value = false;const data = response.data;if (data.success) {ebooks.value = data.content.list;// 重置分页按钮pagination.value.current = params.page;pagination.value.total = data.content.total;} else {message.error(data.message);}});};/*** 表格点击页码时触发*/const handleTableChange = (pagination: any) => {console.log("看看自带的分页参数都有啥:" + pagination);handleQuery({page: pagination.current,size: pagination.pageSize});};// -------- 表单 ---------/*** 数组,[100, 101]对应:前端开发 / Vue*/const categoryIds = ref();const ebook = ref();const modalVisible = ref(false);const modalLoading = ref(false);const handleModalOk = () => {modalLoading.value = true;ebook.value.category1Id = categoryIds.value[0];ebook.value.category2Id = categoryIds.value[1];axios.post("/ebook/save", ebook.value).then((response) => {modalLoading.value = false;const data = response.data; // data = commonRespif (data.success) {modalVisible.value = false;// 重新加载列表handleQuery({page: pagination.value.current,size: pagination.value.pageSize,});} else {message.error(data.message);}});};/*** 编辑*/const edit = (record: any) => {modalVisible.value = true;ebook.value = Tool.copy(record);categoryIds.value = [ebook.value.category1Id, ebook.value.category2Id]};/*** 新增*/const add = () => {modalVisible.value = true;ebook.value = {};};const handleDelete = (id: number) => {axios.delete("/ebook/delete/" + id).then((response) => {const data = response.data; // data = commonRespif (data.success) {// 重新加载列表handleQuery({page: pagination.value.current,size: pagination.value.pageSize,});} else {message.error(data.message);}});};const level1 =  ref();let categorys: any;/*** 查询所有分类**/const handleQueryCategory = () => {loading.value = true;axios.get("/category/all").then((response) => {loading.value = false;const data = response.data;if (data.success) {categorys = data.content;console.log("原始数组:", categorys);level1.value = [];level1.value = Tool.array2Tree(categorys, 0);console.log("树形结构:", level1.value);// 加载完分类后,再加载电子书,否则如果分类树加载很慢,则电子书渲染会报错handleQuery({page: 1,size: pagination.value.pageSize,});} else {message.error(data.message);}});};const getCategoryName = (cid: number) => {// console.log(cid)let result = "";categorys.forEach((item: any) => {if (item.id === cid) {// return item.name; // 注意,这里直接return不起作用result = item.name;}});return result;};onMounted(() => {handleQuery({page: pagination.value.current,size: pagination.value.pageSize,});});return {param,ebooks,pagination,columns,loading,handleTableChange,handleQuery,getCategoryName,edit,add,ebook,modalVisible,modalLoading,handleModalOk,categoryIds,level1,handleDelete}}});
</script><style scoped>img {width: 50px;height: 50px;}
</style>

上边的内容很多,但我们今天核心的前端调用部分是下边的代码。

const handleQuery = (params: any) => {loading.value = true;// 如果不清空现有数据,则编辑保存重新加载数据后,再点编辑,则列表显示的还是编辑前的数据ebooks.value = [];axios.get("/ebook/list", {params: {page: params.page,size: params.size,name: param.value.name}}).then((response) => {loading.value = false;const data = response.data;if (data.success) {ebooks.value = data.content.list;// 重置分页按钮pagination.value.current = params.page;pagination.value.total = data.content.total;} else {message.error(data.message);}});};

当我们进去这个页面的时候,首先就会调用下方代码,请求路径也恰好是我们后端之前写过的list接口,用来分页查询电子书信息。

onMounted(() => {handleQuery({page: pagination.value.current,size: pagination.value.pageSize,});});

2.2新增路由规则

既然都要新增一个电子书的管理页面了,那我们也要为这个页面分配一个能够匹配到的路由路径。

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import AdminEbook from '@/views/admin/admin-ebook.vue'
import AboutView from '../views/AboutView.vue'
const routes: Array<RouteRecordRaw> = [{path: '/',name: 'home',component: HomeView},{path: '/about',name: 'about',component:AboutView},{path: '/admin/ebook',name: 'AdminEbook',component: AdminEbook},
]const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes
})export default router

2.3修改the-header代码

 我们新增的组件是通过点击the-header组件中的按钮实现跳转的,这里要修改一些代码。我在这个页面添加了一些路由用于跳转我们的组件。

<template><a-layout-header class="header"><div class="logo" ></div><a-menutheme="dark"mode="horizontal"v-model:selectedKeys="sselectedKeys1":style="{ lineHeight: '64px' }"><a-menu-item key="/"><router-link to="/">首页</router-link></a-menu-item><a-menu-item key="/admin/ebook"><router-link to="/admin/ebook">电子书管理</router-link></a-menu-item><a-menu-item key="/about"><router-link to="/about">关于我们</router-link></a-menu-item></a-menu></a-layout-header>
</template>

至此我们前端改造成功,接下来就是后端了。

三、🚗SpringBoot后端Ebook模块改造

3.1增加电子书增/改接口

在我们点击新增按钮或者编辑按钮的时候,会弹出一个窗口来添加或者修改电子书的信息,当我们点击确定之后会向后端发送请求。请求接口是/ebook/save,注意,这里的save指代两个功能,第一个是新增,第二个是修改。

3.1.1新增EbookSaveParam

这个实体类用于封装我们前端传过来的电子书的信息。

@Data
public class EbookSaveParam {private Long id;@NotNull(message = "【名称】不能为空")private String name;private Long category1Id;private Long category2Id;private String description;private String cover;private Integer docCount;private Integer viewCount;private Integer voteCount;
}

3.1.2添加Controller代码

这里我直接使用的MybatisPlus封装好的函数

    /***  保存/修改电子书* @param ebookQueryParam* @return*/@PostMapping("/save")public CommonResp save(@Validated @RequestBody EbookSaveParam ebookQueryParam){boolean res = ebookService.saveOrUpdate(CopyUtil.copy(ebookQueryParam,Ebook.class));String message = Boolean.TRUE.equals(res) ? "操作成功":"操作失败";return new CommonResp<>(true,message,null);}

3.1.3在Ebook实体类上增加一个注解

我们要使用雪花算法生成的id存储在数据库当中。

  /*** id*/@TableId(type = IdType.ASSIGN_UUID)private Long id;

当然除了雪花id还有其他的id可供选择。这里就不一一给大家说了。


3.2 增加电子书删除接口

删除功能的接口是下边图中所示。采用的是Restful风格的请求。

 对应Controller代码。

 /*** 删除电子书* @param id 电子书id* @return*/@DeleteMapping("/delete/{id}")public CommonResp delete(@PathVariable("id") Long id){boolean res = ebookService.removeById(id);String message = Boolean.TRUE.equals(res) ? "删除成功":"删除失败";return new CommonResp<>(true,message,null);}

四、🔨测试 

4.1添加功能测试

测试之前还要注释两行代码。因为我们的分类模块还没写,这里不能传值。

随便加一个电子书上去。

结果还是没问题的。

4.2修改功能测试。

不在截图展示了,点击编辑按钮之后哦修改数据我这里是正确的。

4.3删除功能测试

这时就有问题了,我删除怎么成功不了?那么你是否会分析原因呢?先看看前端的打印。

仔细看看我们传过去的id是什么,再看看你的数据库里是否有这个id? 显然是没有的。

这里就要说一下前后端传输数据的数据精度丢失问题了,因为我们传的数据是一个整形,而且数值很大,在传输的过程总是有精度问题得,想要解决就需要在后端加一个配置类。

package com.my.hawiki.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;/*** 统一注解,解决前后端交互Long类型精度丢失的问题*/
@Configuration
public class JacksonConfig {@Beanpublic ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {ObjectMapper objectMapper = builder.createXmlMapper(false).build();SimpleModule simpleModule = new SimpleModule();simpleModule.addSerializer(Long.class, ToStringSerializer.instance);objectMapper.registerModule(simpleModule);return objectMapper;}
}

之后在运行代码试试。大功告成。

 电子书管理页面的基本几个功能差不多就这么多了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/21496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot+Vue租房网站设计和实现(源码+LW+部署文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…

基于全志T507-H的Igh EtherCAT主站案例分享

基于全志T507-H的Linux-RT IgH EtherCAT主站演示 下文主要介绍基于全志T507-H&#xff08;硬件平台&#xff1a;创龙科技TLT507-EVM评估板&#xff09;案例&#xff0c;按照创龙科技提供的案例用户手册进行操作得出测试结果。 本次演示的开发环境&#xff1a; Windows开发环…

数据库----1

一、MVCC多版本并发控制 MVCC&#xff0c;多版本并发控制是现代数据库引擎中常用的处理读写冲突的手段&#xff0c;目的在于提高数据库高并发场景下的吞吐性能。&#xff08;可以用来解决可重复读的问题&#xff09; 二、数据库ACID实现原理 MYSQL通过支持ACID原理来保证事务…

【二进制部署k8s-1.29.4】九、cilium1.15.4安装

文章目录 简介 一.安装helm和cilium 客户端命令二.安装cilium2.1.安装cilium对于系统内核的版本的要求2.2.安装的命令及参数如下&#xff1a;参数注释&#xff1a; 三.确认cilium的安装状态四.访问cilium的监控页面hubble-ui 简介 本章节主要讲解k8s集群网络插件cni的安装&…

mysql GROUP BY 语句报错处理

1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.test_baobiao.qty’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_modeonly_full_group_by, Time: 0.00000…

react-native运行程序 出现 Application XXX is waiting for the debugger

1.重启adb: adb kill-server、 adb start-server. 2、确定USB调试模式是否开启&#xff0c;如果已经开启了&#xff0c;关闭了重新打开一下 3.选择调试模式并关闭等待调试程序

kaggle竞赛实战7——其他方案之lightgbm

本文换种方案&#xff0c;用wrapperlightgbm建模TPE调优 接下来是特征筛选过程&#xff0c;此处先择使用Wrapper方法进行特征筛选&#xff0c;通过带入全部数据训练一个LightGBM模型&#xff0c;然后通过观察特征重要性&#xff0c;选取最重要的300个特征。当然&#xff0c;为…

4.00002 Postgresql的内存管理-一条sql执行过程中的内存上下文是怎么样的?

文章目录 调试前动作调试步骤总结一条sql的内存上下文复杂查询简单查询调试前动作 1、后台启动postgresql -D x 2、使用客户端登录psql,且执行\d+ 3、此时查看进程 ps ux [db_postg@iZ2ze5nkcqlxobs6vm854iZ ~]$ ps ux USER PID %CPU %MEM VSZ RSS TTY STA…

PTA 7-4 按层遍历二叉树

用先序和中序序列构造一棵二叉树&#xff08;树中结点个数不超过10个&#xff09;&#xff0c;通过用队记录结点访问次序的方法实现对二叉树进行按层遍历&#xff0c;即按层数由小到大、同层由左到右输出按层遍历序列。 输入格式: 第一行输入元素个数 第二行输入先序序列&am…

撸广告小游戏开发app源码搭建

开发一个基于观看广告赚钱的小游戏App&#xff0c;源码搭建的过程会涉及到多个方面&#xff0c;包括游戏设计、广告集成、用户奖励机制、后端服务搭建等。以下是一个大致的开发流程和考虑点&#xff1a; 游戏设计与规划 确定游戏类型&#xff1a;选择适合观看广告赚钱的游戏类…

STM32(十):SPI (标准库函数)

前言 上一篇文章已经介绍了如何用STM32单片机中USART通信协议来串口通信&#xff0c;并向XCOM串口助手发送信息。这篇文章我们来介绍一下如何用STM32单片机中SPI接口来实现LED的闪亮并玩转WS2812B灯带。 一、实验原理 串行通信之前的博客里有所介绍&#xff0c;可以查看以下…

Web Components与框架集成:Vue React案例分析

一直以来&#xff0c;都梦想着打造一个完全自定义、可复用且跨框架的UI组件库&#xff0c;让我的项目开发变得更加高效。在一次偶然的机会下&#xff0c;我接触到了Web Components&#xff0c;一种原生的Web API&#xff0c;它允许我们创建自定义的HTML标签&#xff0c;实现组件…

【Qt】styleSheet设置

1. ui中设置 #toolButton1{color: rgb(190, 192, 193); background-color:#404142; border:0px solid #2E2E2E;padding-top: 15px } #toolButton1::hover{background-color:rgb(94,95,96); } #toolButton1::checked{color:black; background-color:#f0f0f0; border:0px soli…

2024.6.2力扣刷题记录

目录 一、力扣每日一题-575. 分糖果 模拟 二、100307. 候诊室中的最少椅子数 模拟 三、100311. 无需开会的工作日 1.差分&#xff08;超内存&#xff09; 2.合并数组 一、力扣每日一题-575. 分糖果 模拟 class Solution:def distributeCandies(self, candyType: List[i…

Docker面试整理-Docker与虚拟机的区别是什么?

Docker 容器和传统的虚拟机(VM)都是提供隔离的运行环境以部署和运行应用程序的技术,但它们在架构和性能上存在显著的不同。了解这些差异可以帮助你选择最适合特定需求的解决方案: 基础架构:虚拟机:每个虚拟机都包括完整的操作系统、应用程序、必需的库和二进制文件,运行在…

故障预警 vs 故障分类:哪个更有意义,哪个更具挑战性?

故障预警 vs 故障分类&#xff1a;哪个更有意义&#xff0c;哪个更具挑战性&#xff1f; 在现代工业系统中&#xff0c;风力发电机、制造设备等关键装置的可靠性和稳定性对生产效率至关重要。为此&#xff0c;故障预警和故障分类成为保障设备正常运行的重要手段。那么&#xf…

UI案例——登陆系统

UI的登陆界面实例 在学习了UILabel&#xff0c;UIButton&#xff0c;UIView&#xff0c;UITextField的内容之后&#xff0c;我们就可以写一个简单的登陆界面 我们可以通过UILabel来编写我们显示在登陆界面上的文字比方说下面这两行字就是通过UILabel去实现的。 下面给出一下实现…

每日5题Day17 - LeetCode 81 - 85

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;81. 搜索旋转排序数组 II - 力扣&#xff08;LeetCode&#xff09; class Solution {public boolean search(int[] nums, int target) {int n nums.length;if (n…

【MySQL】MySQL 图形化界面 - 使用说明(MySQL Workbench)

一、安装软件 Navicat&#xff0c;SQLyog 这些软件都不错&#xff0c;不过都需要收费&#xff0c;当然也有破解版。下面用 MySQL Workbench&#xff0c;它是官方提供的工具。 二、使用操作 这个软件本质是一个客户端&#xff0c;现在要让数据库能够远程登录。不过一般不会远程…

Love-Yi情侣网站3.0存在SQL注入漏洞

目录 1. 前言 2. 网站简介 3. 寻找特征点 3.1 第一次尝试 3.2 第二次尝试 4.资产搜索 5.漏洞复现 5.1 寻找漏洞点 5.2 进行进一步测试 5.2.1 手动测试 1.寻找字段 2.寻找回显位 3.查询当前用户 5.2.2 sqlmap去跑 6.总结 1. 前言 朋友说自己建了一个情侣网站,看到…