提交 0596afaf 编写于 作者: yubinCloud's avatar yubinCloud

8-3 从分类管理复制出文档管理的前后端代码

遗留问题:编辑表单时,只能选择一级文档作为父文档
上级 3ab76dbf
package io.github.yubincloud.fairywiki.controller;
import io.github.yubincloud.fairywiki.dto.req.DocQueryReqDto;
import io.github.yubincloud.fairywiki.dto.req.DocSaveReqDto;
import io.github.yubincloud.fairywiki.dto.resp.DocQueryRespDto;
import io.github.yubincloud.fairywiki.dto.resp.ErrorCode;
import io.github.yubincloud.fairywiki.dto.resp.PageRespDto;
import io.github.yubincloud.fairywiki.dto.resp.RestfulModel;
import io.github.yubincloud.fairywiki.service.DocService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/doc")
public class DocController {
@Resource
private DocService docService;
/**
* 获取全部 Doc 的接口
*/
@GetMapping("/all")
public RestfulModel<List<DocQueryRespDto>> allCategories() {
List<DocQueryRespDto> docList = docService.fetchAllCategories();
return new RestfulModel<>(ErrorCode.SUCCESS, "", docList);
}
/**
* 对 doc 进行查询的接口
* @param docQueryReqDto 查询条件的参数
* @return 查询到的所有doc
*/
@GetMapping("/query")
public RestfulModel<PageRespDto<DocQueryRespDto>> queryDocs(@Valid DocQueryReqDto docQueryReqDto) {
PageRespDto<DocQueryRespDto> bookList = docService.queryDocs(docQueryReqDto);
return new RestfulModel<>(ErrorCode.SUCCESS, "", bookList);
}
/**
* 根据请求的参数保存一个 doc,若id非空则为更新,否则为新增
*/
@PostMapping("/save")
public RestfulModel<Integer> saveDoc(@RequestBody @Valid DocSaveReqDto docSaveReqDto) {
docService.save(docSaveReqDto);
return new RestfulModel<>(ErrorCode.SUCCESS, "", 0);
}
@DeleteMapping("/delete/{docId}")
public RestfulModel<Integer> deleteDoc(@PathVariable Long docId) {
docService.deleteOneDoc(docId);
return new RestfulModel<>(ErrorCode.SUCCESS, "", 0);
}
}
package io.github.yubincloud.fairywiki.dto.req;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class DocQueryReqDto extends PageReqDto {
}
package io.github.yubincloud.fairywiki.dto.req;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class DocSaveReqDto {
private Long id;
@NotNull(message = "【电子书】不能为空")
private Long ebookId;
@NotNull(message = "【父文档】不能为空")
private Long parent;
@NotNull(message = "【名称】不能为空")
private String name;
@NotNull(message = "【顺序】不能为空")
private Integer sort;
private Integer viewCount;
private Integer voteCount;
@NotNull(message = "【内容】不能为空")
private String content;
}
package io.github.yubincloud.fairywiki.dto.resp;
import lombok.Data;
@Data
public class DocQueryRespDto {
private Long id;
private Long ebookId;
private Long parent;
private String name;
private Integer sort;
private Integer viewCount;
private Integer voteCount;
}
package io.github.yubincloud.fairywiki.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.github.yubincloud.fairywiki.domain.Doc;
import io.github.yubincloud.fairywiki.domain.DocExample;
import io.github.yubincloud.fairywiki.dto.req.DocQueryReqDto;
import io.github.yubincloud.fairywiki.dto.req.DocSaveReqDto;
import io.github.yubincloud.fairywiki.dto.resp.DocQueryRespDto;
import io.github.yubincloud.fairywiki.dto.resp.PageRespDto;
import io.github.yubincloud.fairywiki.mapper.DocMapper;
import io.github.yubincloud.fairywiki.utils.CopyUtil;
import io.github.yubincloud.fairywiki.utils.SnowFlake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.util.List;
@Service
public class DocService {
private static final Logger LOG = LoggerFactory.getLogger(DocService.class);
@Resource
private DocMapper docMapper;
@Resource
private SnowFlake snowFlake;
/**
* 获取全部 Doc
*/
public List<DocQueryRespDto> fetchAllCategories() {
DocExample docExample = new DocExample();
docExample.setOrderByClause("sort asc");
List<Doc> docList = docMapper.selectByExample(docExample);
return CopyUtil.copyList(docList, DocQueryRespDto.class);
}
/**
* 根据查询条件对数据库中的 doc 进行查询并返回查询到的 doc
*/
public PageRespDto<DocQueryRespDto> queryDocs(DocQueryReqDto reqDto) {
DocExample docExample = new DocExample();
DocExample.Criteria criteria = docExample.createCriteria();
PageHelper.startPage(reqDto.getPageNum(), reqDto.getPageSize());
List<Doc> docList = docMapper.selectByExample(docExample);
PageInfo<Doc> pageInfo = new PageInfo<>(docList);
LOG.info("总行数:{}", pageInfo.getTotal());
LOG.info("总页数:{}", pageInfo.getPages());
// 列表复制
List<DocQueryRespDto> list = CopyUtil.copyList(docList, DocQueryRespDto.class);
PageRespDto<DocQueryRespDto> pageRespDto = new PageRespDto<>();
pageRespDto.setTotal(pageInfo.getTotal());
pageRespDto.setList(list);
return pageRespDto;
}
/**
* 根据 DocSaveReqDto 来保存一个 doc 记录,若 id 为空则新增,不为空则更新
*/
public void save(DocSaveReqDto reqDto) {
Doc docRecord = CopyUtil.copy(reqDto, Doc.class);
if (ObjectUtils.isEmpty(docRecord.getId())) { // 判断 id 是否为空
docRecord.setId(snowFlake.nextId());
docMapper.insertSelective(docRecord);
} else {
docMapper.updateByPrimaryKey(docRecord);
}
}
public void deleteOneDoc(Long docId) {
docMapper.deleteByPrimaryKey(docId);
}
}
......@@ -36,3 +36,24 @@ export interface Category {
export interface CategoryQueryForm {
name: string;
}
/**
* 文档类
*/
export interface Doc {
id: string;
ebookId: string;
parent: string;
name: string;
sort: number;
viewCount: number;
voteCount: number;
}
/**
* 文档的查询类
*/
export interface DocQueryForm {
name: string;
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ import Home from '../views/home.vue'
import About from '../views/about.vue'
import AdminEbook from '../views/admin/admin-ebook.vue'
import AdminCategory from '../views/admin/admin-category.vue'
import AdminDoc from '../views/admin/admin-doc.vue'
const routes: Array<RouteRecordRaw> = [
{
......@@ -24,6 +25,11 @@ const routes: Array<RouteRecordRaw> = [
path: '/admin/category',
name: 'AdminCategory',
component: AdminCategory
},
{
path: '/admin/doc',
name: 'AdminDoc',
component: AdminDoc
}
]
......
<template>
<a-layout>
<a-layout-content
:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
>
<p>
<a-form
layout="inline"
:model="docQueryForm"
@finish="handleQueryFormSubmit"
>
<a-form-item>
<a-button
type="primary"
html-type="submit"
size="large"
>
查询
</a-button>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="add()" size="large">新增</a-button>
</a-form-item>
</a-form>
</p>
<a-table
:columns="columns"
:row-key="record => record.id"
:data-source="level1"
:pagination="false"
:loading="loading"
>
<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="Yes"
cancel-text="No"
@confirm="handleDeleteDoc(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="doc" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
<a-form-item label="名称">
<a-input v-model:value="doc.name" />
</a-form-item>
<a-form-item label="父文档">
<a-select
v-model:value="doc.parent"
style="width: 120px"
@focus="focus"
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="doc.id === c.id">
{{ c.name }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="顺序">
<a-input v-model:value="doc.sort" type="textarea" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, UnwrapRef, reactive } from 'vue';
import axios from 'axios';
import { message } from 'ant-design-vue'
import { Tool } from "@/util/tool";
import { Doc, DocQueryForm } from "@/models";
export default defineComponent({
name: 'AdminDoc',
setup() {
const docQueryForm: UnwrapRef<DocQueryForm> = reactive({
name: ''
});
const docs = ref<Doc[]>([]);
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属性就是二级文档
/**
* 数据查询
**/
const handleQuery = () => {
loading.value = true;
axios.get("/doc/all").then((response) => {
loading.value = false;
const respData = response.data;
if (respData.code == 0) {
docs.value = respData.data;
console.log("原始数组:", docs.value);
level1.value = [];
level1.value = Tool.array2Tree(docs.value, 0);
console.log("树形结构:", level1);
} else {
message.error(respData.msg);
}
});
};
/**
* 根据表单提交的数据进行查询
**/
const handleQueryFormSubmit = () => {
handleQuery();
};
// -------- 表单 ---------
const doc = ref();
const modalVisible = ref(false);
const modalLoading = ref(false);
const handleModalOk = () => {
modalLoading.value = true;
axios.post("/doc/save", doc.value).then((response) => {
const respData = response.data;
modalLoading.value = false;
if (respData.code == 0) {
modalVisible.value = false;
} else {
message.error(respData.msg);
}
handleQuery();
})
};
/**
* 编辑
*/
const edit = (record: any) => {
modalVisible.value = true;
doc.value = Tool.copy(record);
};
/**
* 新增
*/
const add = () => {
modalVisible.value = true;
doc.value = {};
}
/**
* 删除
*/
const handleDeleteDoc = (docId: string) => {
console.log(docId);
axios.delete("/doc/delete/" + docId).then((response) => {
const respData = response.data;
if (respData.code == 0) {
handleQuery();
}
});
}
onMounted(() => {
handleQuery();
});
return {
docQueryForm,
level1,
labelCol: { span: 4 },
wrapperCol: { span: 14 },
columns,
loading,
edit,
add,
handleDeleteDoc,
handleQueryFormSubmit,
doc,
modalVisible,
modalLoading,
handleModalOk
}
}
});
</script>
<style scoped>
img {
width: 50px;
height: 50px;
}
</style>
......@@ -45,6 +45,11 @@
</template>
<template v-slot:action="{ text, record }">
<a-space size="small">
<router-link to="/admin/doc">
<a-button type="primary">
文档管理
</a-button>
</router-link>
<a-button type="primary" @click="edit(record)">
编辑
</a-button>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册