接着上一个的内容,代码我就不全部贴了,
有变化的部分发一下
springboot后端
pojo
book.java
package com.shrimpking.pojo;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
* 图书表
* </p>
*
* @author shrimpking
* @since 2023-11-12
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("an_book")
@ApiModel(value="Book对象", description="图书表")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键id")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "图书名称")
private String name;
@ApiModelProperty(value = "价格")
private Double price;
@ApiModelProperty(value = "作者")
private String author;
@ApiModelProperty(value = "出版社")
private String express;
@ApiModelProperty(value = "图书封面")
private String img;
}
mapper
BookMapper.java
package com.shrimpking.mapper;
import com.shrimpking.pojo.Book;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 图书表 Mapper 接口
* </p>
*
* @author shrimpking
* @since 2023-11-12
*/
public interface BookMapper extends BaseMapper<Book> {
}
MapperXml
BookMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shrimpking.mapper.BookMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.shrimpking.pojo.Book">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="price" property="price" />
<result column="author" property="author" />
<result column="express" property="express" />
<result column="img" property="img" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, name, price, author, express, img
</sql>
</mapper>
service
BookService.java
package com.shrimpking.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.pojo.Book;
import com.baomidou.mybatisplus.extension.service.IService;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;
/**
* <p>
* 图书表 服务类
* </p>
*
* @author shrimpking
* @since 2023-11-12
*/
public interface BookService extends IService<Book> {
IPage<Book> findBySearchPage(BookParams bookParams);
}
serviceImpl
BookServiceImpl.java
package com.shrimpking.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shrimpking.pojo.Book;
import com.shrimpking.mapper.BookMapper;
import com.shrimpking.pojo.User;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;
import com.shrimpking.service.BookService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 图书表 服务实现类
* </p>
*
* @author shrimpking
* @since 2023-11-12
*/
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {
@Override
public IPage<Book> findBySearchPage(BookParams bookParams)
{
//声明分页
IPage<Book> page = new Page<>(bookParams.getCurrentPage(), bookParams.getPageSize());
//声明查询条件
LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<>();
//图书名称不为空,有条件值时,加入此条件
queryWrapper.like(
StringUtils.isNotBlank(bookParams.getName()),
Book::getName, bookParams.getName())
//作者不为空,有条件值时,加入此条件
.or().like(
StringUtils.isNotBlank(bookParams.getAuthor()),
Book::getAuthor,bookParams.getAuthor())
//出版社不为空,加入此条件
.or().like(
StringUtils.isNotBlank(bookParams.getExpress()),
Book::getExpress,bookParams.getExpress())
.orderByDesc(Book::getId);
//返回结果
return this.baseMapper.selectPage(page, queryWrapper);
}
}
controller
bookController.java
package com.shrimpking.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.pojo.Book;
import com.shrimpking.pojo.User;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;
import com.shrimpking.res.Result;
import com.shrimpking.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 图书表 前端控制器
* </p>
*
* @author shrimpking
* @since 2023-11-12
*/
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
private BookService bookService;
/**
* 有查询条件时,获取分页数据
* @param queryParams
* @return
*/
@GetMapping("/searchPage")
public Result findBySearchPage(BookParams bookParams){
IPage<Book> list = this.bookService.findBySearchPage(bookParams);
return Result.success(list);
}
@PostMapping("/save")
public Result save(@RequestBody Book book){
//先查询有无同名图书
LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Book::getName,book.getName());
int count = this.bookService.count(queryWrapper);
if(count > 0) return Result.error("此图书已存在");
boolean save = this.bookService.save(book);
if(!save) return Result.error("保存失败");
return Result.success("保存成功");
}
@PostMapping("/update")
public Result update(@RequestBody Book book){
//先查询有无同名图书
LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Book::getName,book.getName());
Book one = this.bookService.getOne(queryWrapper);
if(one != null && !one.getId().equals(book.getId())){
return Result.error("此图书已存在");
}
boolean save = this.bookService.updateById(book);
if(!save) return Result.error("更新失败");
return Result.success("更新成功");
}
@DeleteMapping("/delete")
public Result delete(@RequestParam("id") Integer id){
boolean remove = this.bookService.removeById(id);
if(!remove) return Result.error("删除失败");
return Result.success("删除成功");
}
}
fileController.java
package com.shrimpking.controller;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.shrimpking.res.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/11/13 11:07
*/
@RestController
@RequestMapping("/files")
public class FileController
{
/**
* 文件上传存储路径
*/
private static final String filePath = System.getProperty("user.dir") + "/file/";
/**
* 上传文件
* @param file
* @return
*/
@PostMapping("/upload")
public Result upload(MultipartFile file){
synchronized (FileController.class){
//获取当前时间戳
String flag = System.currentTimeMillis() + "";
//获取原始文件名
String fileName = file.getOriginalFilename();
try
{
//如果文件夹不存在,在项目的根目录中创建文件夹
if(!FileUtil.isDirectory(filePath)){
FileUtil.mkdir(filePath);
}
//文件存储形式: 时间戳 + 文件名
FileUtil.writeBytes(file.getBytes(),filePath + flag + "-" + fileName);
System.out.println(fileName + "--上传成功");
Thread.sleep(1L);
}catch (Exception e){
System.err.println(fileName + "--上传失败");
}
return Result.success(flag);
}
}
/**
* 下载文件
* @param flag
* @param response
*/
@GetMapping("/down/{flag}")
public void avatarPath(@PathVariable String flag, HttpServletResponse response){
//创建目录
if(!FileUtil.isDirectory(filePath)){
FileUtil.mkdir(filePath);
}
OutputStream os;
//获取目录下全部文件列表
List<String> fileNames = FileUtil.listFileNames(filePath);
//逐一核对,获取该文件名称
String avatar = fileNames.stream().filter(item -> item.contains(flag)).findAny().orElse("");
try
{
if(StrUtil.isNotEmpty(avatar)){
response.addHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(avatar,"UTF-8"));
response.setContentType("application/octet-stream");
byte[] bytes = FileUtil.readBytes(filePath + avatar);
os = response.getOutputStream();
os.write(bytes);
os.flush();
os.close();
}
}catch (Exception e){
System.out.println("文件下载失败");
}
}
}
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!--swagger依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- jwt验证 -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.7</version>
</dependency>
</dependencies>
application.prpperties
server.port=8089
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=mysql123
#日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置别名
mybatis-plus.type-aliases-package=com.shrimpking.pojo
#开启逻辑删除,标识字段
mybatis-plus.global-config.db-config.logic-delete-field=is_deleted
#删除
mybatis-plus.global-config.db-config.logic-delete-value=1
#未删除
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#swagger
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
Vue前端
router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/login',
name:'LoginView',
component: ()=> import('@/views/LoginView.vue'),
},
{
path:'/register',
name: 'Register',
component: ()=> import('@/views/RegisterView.vue'),
},
{
path: '/',
redirect: '/home',
name: 'Layout',
component: ()=> import('@/views/Layout.vue'),
children:[
{
path: 'home',
name: 'HomeView',
component: ()=> import('@/views/HomeView.vue')
},
{
path: 'admin',
name: 'AdminView',
component: ()=> import('@/views/User/AdminView.vue'),
},
{
path:'user',
name:'UserView',
component: ()=> import('@/views/User/UserView.vue'),
},
{
path:'book',
name:'BookView',
component: ()=> import('@/views/Info/BookView.vue'),
},
]
},
]
const router = new VueRouter({
routes
})
//白名单
const IGNORE_URLS = ['/login','/register'];
//前置守卫
router.beforeEach((to, from, next) => {
//在白名单中,放行
if(IGNORE_URLS.includes(to.path)){
next();
}
//获取用户
let admin = JSON.parse(window.localStorage.getItem('access-admin'));
if(!admin && !IGNORE_URLS.includes(to.path)){
//没有登录 ,没有在白名单中,跳转登录
return next('/login');
}
next();
});
export default router
request.js
import axios from 'axios'
const request = axios.create({
baseURL: 'http://localhost:8089/api', //
timeout: 5000,
});
//request 拦截器
request.interceptors.request.use( config =>{
config.headers['Content-Type'] = 'application/json;charset=utf-8';
//获取token
const admin = JSON.parse(window.localStorage.getItem('access-admin'));
if(admin){
config.headers['z-token'] = admin.token;
}
return config;
},error => {
return Promise.reject(error);
});
//respose 拦截器
request.interceptors.response.use( response => {
//response.data即为后端返回的result, 也就是脱壳
let res = response.data;
//兼容服务端返回的字符串数据
if(typeof res === 'string'){
res = res ? JSON.parse(res) : res;
}
return res;
},error => {
console.log('error:' + error);
return Promise.reject(error);
});
export default request;
BookView.vue
<template>
<div>
<!-- 搜索区域 -->
<div style="margin-bottom:15px;">
<el-input
v-model="searchForm.name"
style="width:200px;"
placeholder="请输入图书名"
@clear="doSearch"
@keypress.native.enter="doSearch"
clearable>
</el-input>
<el-input
v-model="searchForm.author"
style="width:200px;margin-left: 10px;"
placeholder="请输入作者"
@clear="doSearch"
@keypress.native.enter="doSearch"
clearable>
</el-input>
<el-input
v-model="searchForm.express"
style="width:200px;margin-left: 10px;"
placeholder="请输入出版社"
@clear="doSearch"
@keypress.native.enter="doSearch"
clearable>
</el-input>
<el-button
type="warning"
style="margin-left: 10px;"
icon="el-icon-search"
@click="doSearch">查询</el-button>
<el-button
type="primary"
style="margin-left: 10px;"
icon="el-icon-toilet-paper"
@click="clearSearch">清空</el-button>
<el-button
type="primary"
style="margin-left: 10px;"
icon="el-icon-plus" @click="addBtn">新增</el-button>
</div>
<!-- 表格区域 -->
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="id"
label="ID">
</el-table-column>
<el-table-column
prop="name"
label="图书名称">
</el-table-column>
<el-table-column
prop="author"
label="作者">
</el-table-column>
<el-table-column
prop="express"
label="出版社">
</el-table-column>
<el-table-column
prop="price"
label="价格">
</el-table-column>
<el-table-column
prop="img"
label="图书封面">
<template slot-scope="scope">
<el-image
:src="scope.row.img"
:preview-src-list="[scope.row.img]"
style="width:60px;height:60px;border-radius: 50%;"></el-image>
</template>
</el-table-column>
<el-table-column label="操作" width="260px">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" @click="editBtn(scope.row)">编辑</el-button>
<el-button type="danger" icon="el-icon-delete" @click="deleteBtn(scope.row)">删除</el-button>
<el-button icon="el-icon-download" @click="downloadBtn(scope.row)">下载</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页区域 -->
<div style="margin-top:15px;">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.currentPage"
:page-sizes="[2, 5, 10, 20]"
:page-size="searchForm.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
<!-- 对话框 -->
<div>
<el-dialog
:title="dialogTitle"
:visible.sync="dialogFormVisible"
:close-on-click-modal="false"
@close="closeDialog"
width="35%">
<el-form
:model="addForm"
:rules="rules"
ref="addForm"
:label-width="formLabelWidth"
label-postion="left">
<el-form-item label="图书名称" prop="name">
<el-input v-model="addForm.name" clearable></el-input>
</el-form-item>
<el-form-item label="作者" prop="author">
<el-input v-model="addForm.author" clearable></el-input>
</el-form-item>
<el-form-item label="出版社" prop="express">
<el-input v-model="addForm.express" clearable></el-input>
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input-number v-model="addForm.price" :max="9999.9" :min="0.1" :step="0.5"></el-input-number>
</el-form-item>
<el-form-item label="图片封面" prop="img">
<!-- <el-input v-model="addForm.img" clearable></el-input>-->
<el-upload :action="uploadPath" :on-success="successUpload">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="resetBtn" v-show="dialogTitle === '新增图书'">重 置</el-button>
<el-button type="primary" @click="submitBtn">确 定</el-button>
</div>
</el-dialog>
</div>
</div>
</template>
<script>
import request from "@/utils/request";
export default {
name: "BookView",
data() {
return {
//上传路径
uploadPath:'http://localhost:8089/api/files/upload/',
//下载路径
downloadPath:'http://localhost:8089/api/files/down/',
//添加表单
addForm:{
name:'',
author:'',
express:'',
price:'',
img:''
},
rules:{
name:[{required: true, message: '请输入图书名称', trigger: 'blur'}],
author:[{required: true, message: '请输入作者', trigger: 'blur'}],
express:[{required: true, message: '请输入出版社', trigger: 'blur'}],
price:[{required: true, message: '请输入价格', trigger: 'blur'}],
img:[{required: true, message: '请输入封面地址', trigger: 'blur'}],
},
//表单标题宽度
formLabelWidth:'80px',
//对话框标题
dialogTitle:'',
//对话框
dialogFormVisible: false,
//搜索条件
searchForm:{
name: '',
author: '',
express:'',
currentPage: 1,
pageSize: 5
},
tableData: [],
total:0
}
},
methods: {
//下载按钮
downloadBtn(row){
window.location.href = row.img;
},
//文件上传成功的回调
successUpload(res){
this.addForm.img = this.downloadPath + res.data;
},
//删除
deleteBtn(row){
this.$confirm(`您确定要删除【${row.name}】吗`,'删除提示',{
confirmButtonText:'删除',
cancelButtonText:'取消',
type:'warning',
}).then(()=>{
request.delete('/book/delete',{
params:{ id : row.id}
}).then(res => {
if(res.code === '200'){
this.$message.success(res.data);
this.doSearch();
}
})
}).catch(_=>{
this.$message.warning('已取消删除');
})
},
//编辑
editBtn(row){
let obj = JSON.parse(JSON.stringify(row));
this.addForm = obj;
this.dialogTitle = "编辑图书";
this.dialogFormVisible = true;
},
//关闭对话框
closeDialog(){
this.resetBtn();
this.dialogFormVisible = false;
},
//新增保存
submitBtn(){
this.$refs.addForm.validate((valid)=>{
if(valid){
//校验通过
//有id,编辑,没有id是新增
request.post(this.addForm.id ? '/book/update':'/book/save',this.addForm)
.then(res=>{
if(res.code === '200'){
this.$message.success(res.data);
this.resetBtn();
this.dialogFormVisible = false;
this.doSearch();
}else {
this.$message.error(res.msg);
}
})
}
})
},
//新增重置
resetBtn(){
this.$refs.addForm.resetFields();
//修复bug
this.addForm = {};
},
addBtn(){
this.dialogTitle = '新增图书';
this.dialogFormVisible = true;
},
clearSearch(){
this.searchForm.name = '';
this.searchForm.author = '';
this.searchForm.express = '';
this.doSearch();
},
//搜索
doSearch(){
//修复bug
this.searchForm.currentPage = 1;
this.getData();
},
handleSizeChange(val) {
this.searchForm.pageSize = val;
this.getData();
},
handleCurrentChange(val) {
this.searchForm.currentPage = val;
this.getData();
},
//获取数据
getData(){
request.get('/book/searchPage',{
params: this.searchForm
}).then(res=>{
if(res.code === '200'){
this.tableData = res.data.records; //数据
this.searchForm.currentPage = res.data.current; //当前页
this.searchForm.pageSize = res.data.size; //页条数
this.total = res.data.total; //总条数
}else {
this.$message.error(res.msg);
}
});
}
},
created(){
//获取数据
this.getData();
}
}
</script>
<style scoped>
</style>