【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开发环…

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

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

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

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

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

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

故障预警 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. 前言 朋友说自己建了一个情侣网站,看到…

Golang省市二级联动实现 从数据收集、清洗到数据存储

1.背景&#xff1a; 最近在写项目&#xff0c;在项目中有一个需求是获取用户的地理位置&#xff0c;一开始是打算让前端使用JSON包的形式去实现&#xff0c;但是考虑到后期可能需要对省市的数据做一些修改和控制操作&#xff0c;所以改为后端实现&#xff0c;并向后台暴露一套…

密码加密及验证

目录 为什么需要加密&#xff1f; 密码算法分类 对称密码算法 非对称密码算法 摘要算法 DigestUtils MD5在线解密工具原理 实现用户密码加密 代码实现 为什么需要加密&#xff1f; 在MySQL数据库中&#xff0c;我们常常需要对用户密码、身份证号、手机号码等敏感信息进…

【数据结构】树与二叉树——树的基本概念

树的基本概念 导读一、树的定义二、树的基本术语2.1 结点之间的关系2.2 基本概念2.3 度为m的树与m叉树 三、树的性质结语 导读 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;&#xff01; 从今天开始&#xff0c;我们将进入第五章的内容——树与二叉树的学…

微服务:Rabbitmq的WorkQueue模型的使用、默认消费方式(消息队列中间件)

文章目录 WorkQueue模型控制预取消息个数 WorkQueue模型 当然&#xff0c;一个队列&#xff0c;可以由多个消费者去监听。 来实现一下. 生产者&#xff1a; Testpublic void testWorkQueue() throws InterruptedException {// 队列名称String queueName "simple.queue…

NoSQL实战(MongoDB搭建主从复制)

什么是复制集&#xff1f; MongoDB复制是将数据同步到多个服务器的过程&#xff1b; 复制集提供了数据的冗余备份并提高了数据的可用性&#xff0c;通常可以保证数据的安全性&#xff1b; 复制集还允许您从硬件故障和服务中断中恢复数据。 保障数据的安全性 数据高可用性 (2…

Leecode---技巧---只出现一次的数字 / 多数元素

题解&#xff1a; 利用异或运算 a⊕a 0 的性质&#xff0c;可用来消除所有出现了两次的元素&#xff0c;最后剩余的即为所得。 class Solution { public:int singleNumber(vector<int>& nums){// 初始化为0int ans 0;for(int x: nums){// 异或操作ans ^ x;}retur…

关于Openstack删除卷出错的有效解决方案

关于Openstack删除卷时显示卷出错的解决方案 今天删除卷的时候突然发现 删除卷出错 但是还好解决方式还算简单 下面将简洁的写下我的解决方案 当在 Web界面 删除卷时 可能会出现上面的错误 这是因为服务器&#xff0c;出现BUG&#xff0c;卷被附加给了NONE&#xff0c;并且无…

HarmonyOS NEXT星河版之自定义List下拉刷新与加载更多

文章目录 一、加载更多二、下拉刷新三、小结 一、加载更多 借助List的onReachEnd方法&#xff0c;实现加载更多功能&#xff0c;效果如下&#xff1a; Component export struct HPList {// 数据源Prop dataSource: object[] []// 加载更多是否ingState isLoadingMore: bool…

ant X6高亮

先附上效果图 // 节点内属性的点击事件&#xff1a;node:port:click graph.on(‘node:port:click’, ({ node, port }) > { resetAllHighlights(); highlightPort(node, port, true); highlightEdgesForPort(port, new Set()); }); // 以下为源码 <template><div…

Python GNN图神经网络代码实战;GAT代码模版,简单套用,易于修改和提升,图注意力机制代码实战

1.GAT简介 GAT&#xff08;Graph Attention Network&#xff09;模型是一种用于图数据的深度学习模型&#xff0c;由Veličković等人在2018年提出。它通过自适应地在图中计算节点之间的注意力来学习节点之间的关系&#xff0c;并在节点表示中捕捉全局和局部信息。 GAT模型的核…