- 分类基本增删改查功能:表设计,生成持久层代码,从电子书管理拷贝出一套分类管理代码
- 分类表格显示优化:不需要分页,树形表格
- 分类编辑功能优化:新增/编辑类时,支持选中某一分类作为父分类,或者无分类
- 电子书管理功能优化:编辑电子书时,可以选择分类一,分类二,表格显示分类名称
- 首页显示分类菜单
- 点击某一分类时,显示该分类下所有的电子书
✿分类表设计与代码生成
1. 分类表设计
我们执行下面的代码sql,这个就是可以形成树形分类的一个设计,每条数据都包含一个id和一个父id,这样可以递归出一个树形结构,我们这个是一个二级分类:
drop table if exists `category`;
create table `category` (
`id` bigint not null comment 'id',
`parent` bigint not null default 0 comment '父id',
`name` varchar(50) not null comment '名称',
`sort` int comment '顺序',
primary key (`id`)
) engine=innodb default charset=utf8mb4 comment='分类';
insert into `category` (id, parent, name, sort) values (100, 000,'前端开发', 100);
insert into `category` (id, parent, name, sort) values (101, 100,'Vue', 101);
insert into `category` (id, parent, name, sort) values (102, 100,'HTML & CSS', 102);
insert into `category` (id, parent, name, sort) values (200, 000,'Java', 200);
insert into `category` (id, parent, name, sort) values (201, 200,'基础应用', 201);
insert into `category` (id, parent, name, sort) values (202, 200,'框架应用', 202);
insert into `category` (id, parent, name, sort) values (300, 000,'Python', 300);
insert into `category` (id, parent, name, sort) values (301, 300,'基础应用', 301);
insert into `category` (id, parent, name, sort) values (302, 300,'进阶方向应用', 302);
insert into `category` (id, parent, name, sort) values (400, 000,'数据库', 400);
insert into `category` (id, parent, name, sort) values (401, 400,'MySQL', 401);
insert into `category` (id, parent, name, sort) values (500, 000,'其他', 500);
insert into `category` (id, parent, name, sort) values (501, 500,'服务器', 501);
insert into `category` (id, parent, name, sort) values (502, 500,'开发工具', 502);
insert into `category` (id, parent, name, sort) values (503, 500,'热门服务端语言', 503);
2. 生成持久层代码
我们依旧进入generator-config.xml,修改最后的table标签,然后去执行mybatis-generator命令,特别注意生成的四个文件不要去动,这样后续数据库有扩展的话可以重新生成。
<table tableName="category"/>
✿分类基本增删改查
1. 后端改造
从后端改造这个分类的增删改查,我们可以直接去复制一下之前的ebook的一下代码,改造的顺序应该是controller -> service -> 各种实体类,其中进入文件就Ctrl + R键,将ebook改成category,Ebook改为Category即可:
@RestController
@RequestMapping("/category")
public class CategoryController {
@Resource
private CategoryService categoryService;
@GetMapping("/list")
public CommonResp list(@Valid CategoryQueryReq req) {
// controller层尽量不要出现Category这个实体类
// 因为实体类是和数据库一一对应的
CommonResp<PageResp<CategoryQueryResp>> resp = new CommonResp<>();
PageResp<CategoryQueryResp> list = categoryService.list(req);
resp.setContent(list);
return resp;
}
@PostMapping("/save")
public CommonResp save(@Valid @RequestBody CategorySaveReq req) {
CommonResp resp = new CommonResp<>();
categoryService.save(req);
return resp;
}
@DeleteMapping("/delete/{id}")
public CommonResp delete(@PathVariable long id) {
CommonResp resp = new CommonResp<>();
categoryService.delete(id);
return resp;
}
}
@Service
public class CategoryService {
// 这个就是用来打印的,在WikiApplication当中也用到了
private static final Logger LOG = LoggerFactory.getLogger(CategoryService.class);
@Resource
private CategoryMapper categoryMapper;
@Resource
private SnowFlake snowFlake;
/**
* 查询电子书
* @param req 电子书查询参数
* @return 电子书分页列表
*/
public PageResp<CategoryQueryResp> list(CategoryQueryReq req) {
CategoryExample categoryExample = new CategoryExample();
// createCriteria相当于where条件
CategoryExample.Criteria criteria = categoryExample.createCriteria();
// 根据categoryExample条件查询
PageHelper.startPage(req.getPage(), req.getSize());
List<Category> categorysList = categoryMapper.selectByExample(categoryExample);
PageInfo<Category> pageInfo = new PageInfo<>(categorysList);
LOG.info("总行数:{}",pageInfo.getTotal()); // 总行数
LOG.info("总页数:{}",pageInfo.getPages()); // 总页数,一般不需要,前端会根据总数自动计算总页数
List<CategoryQueryResp> respList = CopyUtil.copyList(categorysList, CategoryQueryResp.class);
PageResp<CategoryQueryResp> pageResp = new PageResp();
pageResp.setTotal(pageInfo.getTotal());
pageResp.setList(respList);
return pageResp;
}
/**
* 电子书保存接口(保存包括编辑保存->更新, 也包括新增保存->新增, 根据req是否有id来判断)
* @param req 电子书保存参数
*/
public void save(CategorySaveReq req) {
Category category = CopyUtil.copy(req, Category.class);
if(ObjectUtils.isEmpty(req.getId())) {
// 新增
category.setId(snowFlake.nextId());
categoryMapper.insert(category);
} else {
// 更新
// 因为updateByPrimaryKey传递的Category类型的参数,所以需要将CategorySaveReq 转化成Category
categoryMapper.updateByPrimaryKey(category);
}
}
/**
* 电子书删除接口(按照id进行删除)
* @param id
*/
public void delete(long id) {
categoryMapper.deleteByPrimaryKey(id);
}
}
接下来就是实体类,我们有三个实体类,分别在对应的req和resp文件下创建即可:CategoryQueryReq, CategorySaveReq, CategoryQueryResp。
2. 前端代码展示
前端需要在views/admin下面创建admin-category.vue文件,然后实际上就是将admin-ebook修修改改即可:
<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-button type="primary" @click="handleQuery()">
查询
</a-button>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="add()">
新增
</a-button>
</a-form-item>
</a-form>
</p>
<p>
<a-alert
class="tip"
message="小提示:这里的分类会显示到首页的侧边菜单"
type="info"
closable
/>
</p>
<a-table
v-if="level1.length > 0"
:columns="columns"
:row-key="record => record.id"
:data-source="level1"
:loading="loading"
:pagination="false"
:defaultExpandAllRows="true"
>
<template #cover="{ text: cover }">
<img v-if="cover" :src="cover" alt="avatar" />
</template>
<template v-slot:action="{ text, record }">
<a-space size="small">
<a-button type="primary" @click="edit(record)">
编辑
</a-button>
<a-popconfirm
title="删除后不可恢复,确认删除?"
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-modal
title="分类表单"
v-model:visible="modalVisible"
:confirm-loading="modalLoading"
@ok="handleModalOk"
>
<a-form :model="category" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
<a-form-item label="名称">
<a-input v-model:value="category.name" />
</a-form-item>
<a-form-item label="父分类">
<a-select
v-model:value="category.parent"
ref="select"
>
<a-select-option :value="0">
无
</a-select-option>
<a-select-option v-for="c in level1" :key="c.id" :value="c.id" :disabled="category.id === c.id">
{{c.name}}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="顺序">
<a-input v-model:value="category.sort" />
</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: 'AdminCategory',
setup() {
const param = ref();
param.value = {};
const categorys = ref();
const loading = ref(false);
const columns = [
{
title: '名称',
dataIndex: 'name'
},
// {
// title: '父分类',
// key: 'parent',
// dataIndex: 'parent'
// },
{
title: '顺序',
dataIndex: 'sort'
},
{
title: 'Action',
key: 'action',
slots: { customRender: 'action' }
}
];
/**
* 一级分类树,children属性就是二级分类
* [{
* id: "",
* name: "",
* children: [{
* id: "",
* name: "",
* }]
* }]
*/
const level1 = ref(); // 一级分类树,children属性就是二级分类
level1.value = [];
/**
* 数据查询
**/
const handleQuery = () => {
loading.value = true;
// 如果不清空现有数据,则编辑保存重新加载数据后,再点编辑,则列表显示的还是编辑前的数据
level1.value = [];
axios.get("/category/all").then((response) => {
loading.value = false;
const data = response.data;
if (data.success) {
categorys.value = data.content;
console.log("原始数组:", categorys.value);
level1.value = [];
level1.value = Tool.array2Tree(categorys.value, 0);
console.log("树形结构:", level1);
} else {
message.error(data.message);
}
});
};
// -------- 表单 ---------
const category = ref({});
const modalVisible = ref(false);
const modalLoading = ref(false);
const handleModalOk = () => {
modalLoading.value = true;
axios.post("/category/save", category.value).then((response) => {
modalLoading.value = false;
const data = response.data; // data = commonResp
if (data.success) {
modalVisible.value = false;
// 重新加载列表
handleQuery();
} else {
message.error(data.message);
}
});
};
/**
* 编辑
*/
const edit = (record: any) => {
modalVisible.value = true;
category.value = Tool.copy(record);
};
/**
* 新增
*/
const add = () => {
modalVisible.value = true;
category.value = {};
};
const handleDelete = (id: number) => {
axios.delete("/category/delete/" + id).then((response) => {
const data = response.data; // data = commonResp
if (data.success) {
// 重新加载列表
handleQuery();
} else {
message.error(data.message);
}
});
};
onMounted(() => {
handleQuery();
});
return {
param,
// categorys,
level1,
columns,
loading,
handleQuery,
edit,
add,
category,
modalVisible,
modalLoading,
handleModalOk,
handleDelete
}
}
});
</script>
<style scoped>
img {
width: 50px;
height: 50px;
}
</style>
✿分类表格显示优化
分类查询实际上不需要分页,一次查出全部数据,另外我们需要改为树形表格展示,所以我们前后端都来改造一下:
1. 后端改造
后端我们之前书写的list先留着,我们新书写一个all的接口,一次性将所有的分类查出来:
@GetMapping("/all")
public CommonResp all(@Valid CategoryQueryReq req) {
// controller层尽量不要出现Category这个实体类
// 因为实体类是和数据库一一对应的
CommonResp<List<CategoryQueryResp>> resp = new CommonResp<>();
List<CategoryQueryResp> list = categoryService.all(req);
resp.setContent(list);
return resp;
}
/**
* 查询分类
* @param req 分类查询参数
* @return 分类分页列表
*/
public List<CategoryQueryResp> all(CategoryQueryReq req) {
CategoryExample categoryExample = new CategoryExample();
categoryExample.setOrderByClause("sort asc"); // 按照sort排序
List<Category> categorysList = categoryMapper.selectByExample(categoryExample);
List<CategoryQueryResp> respList = CopyUtil.copyList(categorysList, CategoryQueryResp.class);
return respList;
}
2. 前端改造
前端的代码我们前面在分类基本增删改查的时候就全部展示了,现在我们需要展示一下如何将数据展示成为树形结构,这个递归比较重要,所以好好理解一下:
/**
* 使用递归将数组转为树形结构
* 父ID属性为parent
*/
public static array2Tree (array: any, parentId: number) {
if (Tool.isEmpty(array)) {
return [];
}
const result = [];
for (let i = 0; i < array.length; i++) {
const c = array[i];
// console.log(Number(c.parent), Number(parentId));
if (Number(c.parent) === Number(parentId)) {
result.push(c);
// 递归查看当前节点对应的子节点
const children = Tool.array2Tree(array, c.id);
if (Tool.isNotEmpty(children)) {
c.children = children;
}
}
}
return result;
}
分类编辑功能优化
由于分类比较特殊,在编辑(新增/修改)分类时,支持选中某一个分类作为父分类,或者没有分类,所以我们需要将,分类应该将输入框改变成为下拉框,下拉框的选项我们在这里是写死的,如果有活的必须够短有提供接口,代码我们可以在分类基本增删改查-前端代码展示当中看到完整的前端代码。
电子书管理增加分类选择
电子书的管理页面当中的分类进行了优化,显示的就不再是具体的分类代码,而是使用a-cascader级联组件进行了优化,所以具体的项目代码我们展示在下面,前端代码无法仔细详解,大家仔细研究:
<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-popconfirm
title="删除后不可恢复,确认删除?"
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-modal
title="电子书表单"
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-cascader
v-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 = commonResp
if (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 = commonResp
if (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(() => {
handleQueryCategory();
});
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>
首页显示分类菜单
思路非常简单,第一步加载数据变成树形结构,第二步将菜单做出循环,然后使用a-sub-menu这个组件写成树形的菜单。
<template>
<a-layout>
<a-layout-sider width="200" style="background: #fff">
<a-menu
mode="inline"
:style="{ height: '100%', borderRight: 0 }"
@click="handleClick"
:openKeys="openKeys"
>
<a-menu-item key="welcome">
<MailOutlined />
<span>欢迎</span>
</a-menu-item>
<a-sub-menu v-for="item in level1" :key="item.id" :disabled="true">
<template v-slot:title>
<span><user-outlined />{{item.name}}</span>
</template>
<a-menu-item v-for="child in item.children" :key="child.id">
<MailOutlined /><span>{{child.name}}</span>
</a-menu-item>
</a-sub-menu>
<a-menu-item key="tip" :disabled="true">
<span>以上菜单在分类管理配置</span>
</a-menu-item>
</a-menu>
</a-layout-sider>
<a-layout-content
:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
>
<div class="welcome" v-show="isShowWelcome">
<the-welcome></the-welcome>
</div>
<a-list v-show="!isShowWelcome" item-layout="vertical" size="large" :grid="{ gutter: 20, column: 3 }" :data-source="ebooks">
<template #renderItem="{ item }">
<a-list-item key="item.name">
<template #actions>
<span>
<component v-bind:is="'FileOutlined'" style="margin-right: 8px" />
{{ item.docCount }}
</span>
<span>
<component v-bind:is="'UserOutlined'" style="margin-right: 8px" />
{{ item.viewCount }}
</span>
<span>
<component v-bind:is="'LikeOutlined'" style="margin-right: 8px" />
{{ item.voteCount }}
</span>
</template>
<a-list-item-meta :description="item.description">
<template #title>
<router-link :to="'/doc?ebookId=' + item.id">
{{ item.name }}
</router-link>
</template>
<template #avatar><a-avatar :src="item.cover"/></template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</a-layout-content>
</a-layout>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, reactive, toRef } from 'vue';
import axios from 'axios';
import { message } from 'ant-design-vue';
import {Tool} from "@/util/tool";
import TheWelcome from '@/components/the-welcome.vue';
export default defineComponent({
name: 'Home',
components: {
TheWelcome
},
setup() {
const ebooks = ref();
// const ebooks1 = reactive({books: []});
const openKeys = ref();
const level1 = ref();
let categorys: any;
/**
* 查询所有分类
**/
const handleQueryCategory = () => {
axios.get("/category/all").then((response) => {
const data = response.data;
if (data.success) {
categorys = data.content;
console.log("原始数组:", categorys);
// 加载完分类后,将侧边栏全部展开
openKeys.value = [];
for (let i = 0; i < categorys.length; i++) {
openKeys.value.push(categorys[i].id)
}
level1.value = [];
level1.value = Tool.array2Tree(categorys, 0);
console.log("树形结构:", level1.value);
} else {
message.error(data.message);
}
});
};
const isShowWelcome = ref(true);
let categoryId2 = 0;
/**
* 查询数据
*/
const handleQueryEbook = () => {
axios.get("/ebook/list", {
params: {
page: 1,
size: 1000,
categoryId2: categoryId2
}
}).then((response) => {
const data = response.data;
ebooks.value = data.content.list;
// ebooks1.books = data.content;
});
};
const handleClick = (value: any) => {
// console.log("menu click", value)
if (value.key === 'welcome') {
isShowWelcome.value = true;
} else {
categoryId2 = value.key;
isShowWelcome.value = false;
handleQueryEbook();
}
// isShowWelcome.value = value.key === 'welcome';
};
onMounted(() => {
handleQueryCategory();
});
return {
ebooks,
pagination: {
onChange: (page: any) => {
console.log(page);
},
pageSize: 3,
},
handleClick,
level1,
isShowWelcome,
openKeys
}
}
});
</script>
<style scoped>
.ant-avatar {
width: 50px;
height: 50px;
line-height: 50px;
border-radius: 8%;
margin: 5px 0;
}
</style>
点击分类菜单显示电子书
后端我们就做两个事情,由于前端需要查询电子书的时候,需要添加上分类,所以查询的时候要带上分类参数categoryId2,于是我们就需要在EbookQueryReq.java当中去添加categoryId2。
package com.taopoppy.wiki.req;
public class EbookQueryReq extends PageReq{
private Long id;
private String name;
private Long categoryId2; // 添加该参数
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getCategoryId2() {
return categoryId2;
}
public void setCategoryId2(Long categoryId2) {
this.categoryId2 = categoryId2;
}
@Override
public String toString() {
return "EbookQueryReq{" +
"id=" + id +
", name='" + name + '\'' +
", categoryId2=" + categoryId2 +
'}';
}
}
/**
* 查询电子书
* @param req 电子书查询参数
* @return 电子书分页列表
*/
public PageResp<EbookQueryResp> list(EbookQueryReq req) {
EbookExample ebookExample = new EbookExample();
EbookExample.Criteria criteria = ebookExample.createCriteria();
if(!ObjectUtils.isEmpty(req.getName())) {
criteria.andNameLike("%" + req.getName() + "%");
}
// 添加category的动态sql的写法
if(!ObjectUtils.isEmpty(req.getCategoryId2())) {
criteria.andCategory2IdEqualTo(req.getCategoryId2());
}
PageHelper.startPage(req.getPage(), req.getSize());
List<Ebook> ebooksList = ebookMapper.selectByExample(ebookExample);
PageInfo<Ebook> pageInfo = new PageInfo<>(ebooksList);
LOG.info("总行数:{}",pageInfo.getTotal());
LOG.info("总页数:{}",pageInfo.getPages());
List<EbookQueryResp> respList = CopyUtil.copyList(ebooksList, EbookQueryResp.class);
PageResp<EbookQueryResp> pageResp = new PageResp();
pageResp.setTotal(pageInfo.getTotal());
pageResp.setList(respList);
return pageResp;
}
前端的代码在home.vue,完整的代码我们已经在首页显示分类菜单当中完整的展示。