feat(note): 新增笔记创建和编辑功能

- 实现了笔记创建和编辑的前端逻辑
- 更新了相关的后端接口和数据库操作
- 优化了分组获取和展示逻辑
-调整了 Markdown 文件更新接口
This commit is contained in:
ikmkj
2025-07-30 07:48:38 +08:00
parent 431e3dea1c
commit 57fb74dc49
9 changed files with 95 additions and 65 deletions

View File

@@ -1,5 +1,6 @@
package com.test.bijihoudaun.controller; package com.test.bijihoudaun.controller;
import cn.hutool.core.util.StrUtil;
import com.test.bijihoudaun.common.response.R; import com.test.bijihoudaun.common.response.R;
import com.test.bijihoudaun.entity.Grouping; import com.test.bijihoudaun.entity.Grouping;
import com.test.bijihoudaun.service.GroupingService; import com.test.bijihoudaun.service.GroupingService;
@@ -29,14 +30,14 @@ public class GroupingController {
@Operation(summary = "获取全部分组") @Operation(summary = "获取全部分组")
@Parameters({ @Parameters({
@Parameter(name = "parentId", description = "0是一级其他的是看它的父级id", required = true) @Parameter(name = "parentId", description = "0是一级其他的是看它的父级id", required = false)
}) })
@GetMapping @GetMapping
public R<List<Grouping>> getAllGroupings(String parentId) { public R<List<Grouping>> getAllGroupings(@RequestParam(required = false) String parentId) {
if (parentId == null) { Long l= null;
return R.fail("参数不能为空"); if (StrUtil.isNotEmpty(parentId)) {
l = Long.parseLong(parentId);
} }
long l = Long.parseLong(parentId);
List<Grouping> groupings = groupingService.getAllGroupings(l); List<Grouping> groupings = groupingService.getAllGroupings(l);
return R.success(groupings); return R.success(groupings);
} }

View File

@@ -10,6 +10,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.List; import java.util.List;
@Tag(name = "markdown接口") @Tag(name = "markdown接口")
@@ -61,22 +62,11 @@ public class MarkdownController {
} }
@Operation(summary = "更新Markdown文件") @Operation(summary = "更新Markdown文件")
@Parameters({ @PostMapping("/updateMarkdown")
@Parameter(name = "id", description = "Markdown文件ID", required = true), public R<MarkdownFile> updateMarkdown(@RequestBody MarkdownFile markdownFile) {
@Parameter(name = "content", description = "Markdown文件内容", required = true) MarkdownFile file = markdownFileService.updateMarkdownContent(markdownFile);
})
@PostMapping("/{id}")
public R<MarkdownFile> updateMarkdown(
@PathVariable String id,
String content) {
long l = Long.parseLong(id);
MarkdownFile file = markdownFileService.updateMarkdownContent(l, content);
if (file != null) {
return R.success(file); return R.success(file);
} }
return R.fail();
}
@Operation(summary = "获取所有Markdown文件") @Operation(summary = "获取所有Markdown文件")
@GetMapping @GetMapping

View File

@@ -18,11 +18,9 @@ public interface MarkdownFileService extends IService<MarkdownFile> {
/** /**
* 更新Markdown内容 * 更新Markdown内容
* @param id 文件ID
* @param content 新内容
* @return 更新后的文件对象 * @return 更新后的文件对象
*/ */
MarkdownFile updateMarkdownContent(Long id, String content); MarkdownFile updateMarkdownContent(MarkdownFile markdownFile);
/** /**
* 根据ID获取Markdown文件 * 根据ID获取Markdown文件

View File

@@ -27,10 +27,12 @@ public class GroupingServiceImpl
@Override @Override
public List<Grouping> getAllGroupings(Long parentId) { public List<Grouping> getAllGroupings(Long parentId) {
// return groupingMapper.selectList(new LambdaQueryWrapper<Grouping>() if (parentId == null){
// .eq(Grouping::getParentId, parentId));
return groupingMapper.selectList(null); return groupingMapper.selectList(null);
} }
return groupingMapper.selectList(new LambdaQueryWrapper<Grouping>()
.eq(Grouping::getParentId, parentId));
}
@Override @Override
public Grouping updateGrouping(Grouping grouping) { public Grouping updateGrouping(Grouping grouping) {

View File

@@ -40,14 +40,16 @@ public class MarkdownFileServiceImpl
} }
@Override @Override
public MarkdownFile updateMarkdownContent(Long id, String content) { public MarkdownFile updateMarkdownContent(MarkdownFile markdownFile) {
MarkdownFile file = this.getById(id); markdownFile.setUpdatedAt(new Date());
if (file != null) { if (markdownFile.getId() != null){
file.setContent(content); markdownFileMapper.update(markdownFile, new QueryWrapper<MarkdownFile>().eq("id", markdownFile.getId()));
file.setUpdatedAt(new Date()); }else {
this.updateById(file); markdownFile.setId(snowflakeIdGenerator.nextId());
markdownFile.setCreatedAt(new Date());
markdownFileMapper.insert(markdownFile);
} }
return file; return markdownFile;
} }
@Override @Override

View File

@@ -21,14 +21,8 @@ export const addGroupings = (name) => {
}) })
} }
//更新Markdown文件 //更新Markdown文件
export const updateMarkdown = (id, data) => { export const updateMarkdown = (data) => {
const formData = new FormData() return axiosApi.post(`/api/markdown/updateMarkdown`, data)
if (data) formData.append('content', data)
return axiosApi.post(`/api/markdown/${id}`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
} }
// 批量删除图片 // 批量删除图片
export const deleteImages = (list) => { export const deleteImages = (list) => {

View File

@@ -99,7 +99,14 @@
<!-- 分类创建对话框 --> <!-- 分类创建对话框 -->
<el-dialog v-model="showCreateGroupDialog" title="新建分类" width="30%"> <el-dialog v-model="showCreateGroupDialog" title="新建分类" width="30%">
<el-form :model="newGroupForm" label-width="80px"> <el-form :model="newGroupForm" label-width="80px">
<el-form-item label="分类名称"> <el-switch v-model="isGroup1" active-text="一级分类" inactive-text="二级分类" style="margin-bottom: 20px;margin-left:30%" />
<el-form-item label="一级名称">
<el-input v-model="newGroupForm.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="一级名称">
<el-input v-model="newGroupForm.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item v-if="!isGroup1" label="二级名称">
<el-input v-model="newGroupForm.name" autocomplete="off"></el-input> <el-input v-model="newGroupForm.name" autocomplete="off"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
@@ -115,12 +122,22 @@
<el-form-item label="笔记标题"> <el-form-item label="笔记标题">
<el-input v-model="newNoteForm.title" autocomplete="off"></el-input> <el-input v-model="newNoteForm.title" autocomplete="off"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="选择大分类">
<el-select v-model="fenlei1" :change="getjb2()" placeholder="请选择">
<el-option
v-for="item in groupings"
:key="item.id"
:label="item.grouping"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="选择分类"> <el-form-item label="选择分类">
<el-select v-model="newNoteForm.groupingId" placeholder="请选择"> <el-select v-model="newNoteForm.groupingId" placeholder="请选择">
<el-option <el-option
v-for="group in groupings" v-for="group in fenlei2"
:key="group.id" :key="group.id"
:label="group.name" :label="group.grouping"
:value="group.id" :value="group.id"
></el-option> ></el-option>
</el-select> </el-select>
@@ -150,7 +167,11 @@ import {
} from '@/api/CommonApi.js' } from '@/api/CommonApi.js'
import {DArrowRight} from "@element-plus/icons-vue"; import {DArrowRight} from "@element-plus/icons-vue";
const isGroup1=ref(true)
// 创建新文件中大分类的信息
const fenlei1=ref(null)
// 创建新文件中分类信息
const fenlei2=ref(null)
const markdownFiles = ref([]); const markdownFiles = ref([]);
const groupings = ref([]); const groupings = ref([]);
// 二级分类下的markdown文件 // 二级分类下的markdown文件
@@ -162,22 +183,36 @@ const isCollapsed = ref(false);
const showCreateGroupDialog = ref(false); const showCreateGroupDialog = ref(false);
const showCreateNoteDialog = ref(false); const showCreateNoteDialog = ref(false);
const newGroupForm = ref({ name: '' }); const newGroupForm = ref({ name: '' });
const newNoteForm = ref({ title: '', groupingId: null }); const newNoteForm = ref({
id: null,
title: '',
groupingId: null ,
fileName: '',
content: ''
});
// 创建新笔记的多级菜单
const options=ref([])
// 编辑笔记的数据
const editData=ref(null) const editData=ref(null)
// 笔记中的所有图片url // 笔记中的所有图片url
const imageUrls = ref([]); const imageUrls = ref([]);
// 刚开始笔记中的所有图片url // 刚开始笔记中的所有图片url
const originalImages = ref([]); const originalImages = ref([]);
// 分类为二级的数据
const jb22=ref([]) const jb22=ref([])
// 创建md文件时通过大分类获取二级分类
const getjb2 = async () => {
if (fenlei1.value != null) {
const response = await groupingAll(fenlei1.value)
fenlei2.value=response.data
}
}
// 获取所有分组 // 获取所有分组
const fetchGroupings = async () => { const fetchGroupings = async () => {
try { try {
const response = await groupingAll(0) const response = await groupingAll("")
const jb1 = [] const jb1 = []
const jb2 = [] const jb2 = []
for (let i = 0; i <response.data.length; i++) { for (let i = 0; i <response.data.length; i++) {
@@ -189,6 +224,15 @@ const fetchGroupings = async () => {
} }
groupings.value=jb1 groupings.value=jb1
jb22.value=jb2 jb22.value=jb2
for (let i = 0; i < jb1.length; i++){
jb1[i].children=[]
options.value.push(jb1[i])
for (let j = 0; j < jb2.length; j++) {
if (+jb2[j].parentId===+jb1[i].id){
options.value[i].children.push(jb2[i])
}
}
}
} catch (error) { } catch (error) {
console.error('获取分组失败:', error); console.error('获取分组失败:', error);
ElMessage.error('获取分组失败: ' + (error.response?.data?.message || error.message)); ElMessage.error('获取分组失败: ' + (error.response?.data?.message || error.message));
@@ -237,20 +281,13 @@ const createGrouping = async () => {
// 创建新笔记 // 创建新笔记
const createNote = async () => { const createNote = async () => {
// TODO 添加笔记创建逻辑
try { try {
await axios.post(`${API_BASE_URL}/api/markdown`, '# 新笔记内容', { newNoteForm.value.fileName = newNoteForm.value.title+'.md'
params: { editData.value=newNoteForm.value
groupingId: newNoteForm.value.groupingId, console.log(editData.value)
title: newNoteForm.value.title, showCreateNoteDialog.value = false
fileName: newNoteForm.value.title showEditor.value = true;
} selectedFile.value=editData.value
});
ElMessage.success('笔记创建成功');
showCreateNoteDialog.value = false;
newNoteForm.value = { title: '', groupingId: null };
await chushihua()
} catch (error) { } catch (error) {
ElMessage.error('创建笔记失败: ' + error.message); ElMessage.error('创建笔记失败: ' + error.message);
} }
@@ -258,6 +295,11 @@ const createNote = async () => {
// 选择文件预览 // 选择文件预览
const previewFile = async (file) => { const previewFile = async (file) => {
if (file.id === null){
editData.value=file
selectedFile.value=null
return;
}
try { try {
const response = await Preview(file.id) const response = await Preview(file.id)
@@ -313,7 +355,8 @@ const handleImageUpload=async (event, insertImage, files) => {
const handleSave= async (file) => { const handleSave= async (file) => {
imageUrls.value = extractImageUrls(file); imageUrls.value = extractImageUrls(file);
extractDeletedImageUrls(imageUrls.value) extractDeletedImageUrls(imageUrls.value)
const filesRes = await updateMarkdown(editData.value.id,file); editData.value.content = file
const filesRes = await updateMarkdown(editData.value);
if (filesRes.code===200){ if (filesRes.code===200){
ElMessage.success(filesRes.msg); ElMessage.success(filesRes.msg);
await chushihua() await chushihua()

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 KiB