feat(qianduan): 优化笔记功能和图片处理
- 新增批量删除图片接口和功能- 实现笔记中图片的上传和删除 - 优化笔记保存时的图片处理逻辑 -调整分组展示和Markdown文件加载方式
This commit is contained in:
@@ -4,6 +4,8 @@ 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;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
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.*;
|
||||||
@@ -26,9 +28,16 @@ public class GroupingController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取全部分组")
|
@Operation(summary = "获取全部分组")
|
||||||
|
@Parameters({
|
||||||
|
@Parameter(name = "parentId", description = "0是一级,其他的是看它的父级id", required = true)
|
||||||
|
})
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public R<List<Grouping>> getAllGroupings() {
|
public R<List<Grouping>> getAllGroupings(String parentId) {
|
||||||
List<Grouping> groupings = groupingService.getAllGroupings();
|
if (parentId == null) {
|
||||||
|
return R.fail("参数不能为空");
|
||||||
|
}
|
||||||
|
long l = Long.parseLong(parentId);
|
||||||
|
List<Grouping> groupings = groupingService.getAllGroupings(l);
|
||||||
return R.success(groupings);
|
return R.success(groupings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,17 +25,14 @@ public class ImageController {
|
|||||||
|
|
||||||
@Operation(summary = "上传图片")
|
@Operation(summary = "上传图片")
|
||||||
@Parameters({
|
@Parameters({
|
||||||
@Parameter(name = "markdownId", description = "markdownid", required = true),
|
|
||||||
@Parameter(name = "file", description = "图片文件", required = true)
|
@Parameter(name = "file", description = "图片文件", required = true)
|
||||||
})
|
})
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public R<Image> uploadImage(
|
public R<Image> uploadImage(
|
||||||
@RequestParam(required = false) String markdownId,
|
|
||||||
@RequestParam("file") MultipartFile file) {
|
@RequestParam("file") MultipartFile file) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Long markdownIdLong = Long.parseLong(markdownId);
|
Image image = imageService.uploadImage(file);
|
||||||
Image image = imageService.uploadImage(markdownIdLong, file);
|
|
||||||
return R.success(image);
|
return R.success(image);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return R.fail();
|
return R.fail();
|
||||||
@@ -43,7 +40,7 @@ public class ImageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "根据id删除图片")
|
@Operation(summary = "根据id删除图片")
|
||||||
@DeleteMapping("/{id}")
|
@PostMapping("/{id}")
|
||||||
public R<Void> deleteImage(@PathVariable Long id) {
|
public R<Void> deleteImage(@PathVariable Long id) {
|
||||||
boolean result = imageService.deleteImage(id);
|
boolean result = imageService.deleteImage(id);
|
||||||
if (result) {
|
if (result) {
|
||||||
@@ -54,7 +51,7 @@ public class ImageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "根据url删除图片")
|
@Operation(summary = "根据url删除图片")
|
||||||
@DeleteMapping
|
@PostMapping("/deleteByUrl")
|
||||||
public R<Void> deleteImageByUrl(@RequestParam String url) {
|
public R<Void> deleteImageByUrl(@RequestParam String url) {
|
||||||
boolean result = imageService.deleteImageByUrl(url);
|
boolean result = imageService.deleteImageByUrl(url);
|
||||||
if (result) {
|
if (result) {
|
||||||
@@ -64,5 +61,16 @@ public class ImageController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据url批量删除图片")
|
||||||
|
@PostMapping("/batch")
|
||||||
|
public R<Void> deleteImageByUrls(@RequestBody List<String> urls) {
|
||||||
|
boolean result = imageService.deleteImageByUrls(urls);
|
||||||
|
if (result) {
|
||||||
|
return R.success();
|
||||||
|
} else {
|
||||||
|
return R.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,9 +86,13 @@ public class MarkdownController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "删除Markdown文件")
|
@Operation(summary = "删除Markdown文件")
|
||||||
@DeleteMapping("/{id}")
|
@Parameters({
|
||||||
public R<Void> deleteMarkdown(@PathVariable Long id) {
|
@Parameter(name = "id", description = "Markdown文件ID", required = true),
|
||||||
if (markdownFileService.deleteMarkdownFile(id)) {
|
})
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public R<Void> deleteMarkdown(String id) {
|
||||||
|
long l = Long.parseLong(id);
|
||||||
|
if (markdownFileService.deleteMarkdownFile(l)) {
|
||||||
return R.success();
|
return R.success();
|
||||||
}
|
}
|
||||||
return R.fail();
|
return R.fail();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.test.bijihoudaun.entity;
|
package com.test.bijihoudaun.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
@@ -15,6 +16,12 @@ public class Grouping {
|
|||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
@JsonFormat(shape = JsonFormat.Shape.STRING) // 仅作用于此字段
|
@JsonFormat(shape = JsonFormat.Shape.STRING) // 仅作用于此字段
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description ="上级id",implementation = Long.class)
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||||
|
@TableField("parentId")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
@Schema(description = "分组名称",implementation = String.class)
|
@Schema(description = "分组名称",implementation = String.class)
|
||||||
private String grouping;
|
private String grouping;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,28 @@ import com.test.bijihoudaun.entity.Grouping;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface GroupingService {
|
public interface GroupingService {
|
||||||
|
/**
|
||||||
|
* 创建分组
|
||||||
|
* @param grouping
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
Grouping createGrouping(Grouping grouping);
|
Grouping createGrouping(Grouping grouping);
|
||||||
List<Grouping> getAllGroupings();
|
|
||||||
|
/**
|
||||||
|
* 获取所有分组
|
||||||
|
* @param parentId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<Grouping> getAllGroupings(Long parentId);
|
||||||
|
/**
|
||||||
|
* 更新分组名称
|
||||||
|
* @param grouping
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
Grouping updateGrouping(Grouping grouping);
|
Grouping updateGrouping(Grouping grouping);
|
||||||
|
/**
|
||||||
|
* 删除分组
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
void deleteGrouping(Long id);
|
void deleteGrouping(Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,11 @@ import java.util.List;
|
|||||||
public interface ImageService extends IService<Image> {
|
public interface ImageService extends IService<Image> {
|
||||||
/**
|
/**
|
||||||
* 上传图片
|
* 上传图片
|
||||||
* @param markdownId Markdown文件ID(可选)
|
|
||||||
* @param file 图片文件
|
* @param file 图片文件
|
||||||
* @return 上传的图片对象
|
* @return 上传的图片对象
|
||||||
* @throws IOException 文件操作异常
|
* @throws IOException 文件操作异常
|
||||||
*/
|
*/
|
||||||
Image uploadImage(Long markdownId, MultipartFile file) throws IOException;
|
Image uploadImage(MultipartFile file) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除图片
|
* 删除图片
|
||||||
@@ -24,14 +23,6 @@ public interface ImageService extends IService<Image> {
|
|||||||
*/
|
*/
|
||||||
boolean deleteImage(Long id);
|
boolean deleteImage(Long id);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取Markdown文件关联的图片
|
|
||||||
* @param markdownId Markdown文件ID
|
|
||||||
* @return 图片列表
|
|
||||||
*/
|
|
||||||
List<Image> getMarkdownImages(Long markdownId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据URL删除图片
|
* 根据URL删除图片
|
||||||
* @param url
|
* @param url
|
||||||
@@ -40,10 +31,9 @@ public interface ImageService extends IService<Image> {
|
|||||||
boolean deleteImageByUrl(String url);
|
boolean deleteImageByUrl(String url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据URL批量更新图片ID
|
* 根据URL批量删除图片
|
||||||
* @param list
|
* @param urls
|
||||||
* @param markdownId
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
boolean updateImageId(List<String> list, Long markdownId);
|
boolean deleteImageByUrls(List<String> urls);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.test.bijihoudaun.service.impl;
|
package com.test.bijihoudaun.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.test.bijihoudaun.entity.Grouping;
|
import com.test.bijihoudaun.entity.Grouping;
|
||||||
import com.test.bijihoudaun.mapper.GroupingMapper;
|
import com.test.bijihoudaun.mapper.GroupingMapper;
|
||||||
import com.test.bijihoudaun.service.GroupingService;
|
import com.test.bijihoudaun.service.GroupingService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -13,6 +15,10 @@ public class GroupingServiceImpl
|
|||||||
extends ServiceImpl<GroupingMapper, Grouping>
|
extends ServiceImpl<GroupingMapper, Grouping>
|
||||||
implements GroupingService {
|
implements GroupingService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private GroupingMapper groupingMapper;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Grouping createGrouping(Grouping grouping) {
|
public Grouping createGrouping(Grouping grouping) {
|
||||||
this.save(grouping);
|
this.save(grouping);
|
||||||
@@ -20,8 +26,10 @@ public class GroupingServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Grouping> getAllGroupings() {
|
public List<Grouping> getAllGroupings(Long parentId) {
|
||||||
return this.list();
|
// return groupingMapper.selectList(new LambdaQueryWrapper<Grouping>()
|
||||||
|
// .eq(Grouping::getParentId, parentId));
|
||||||
|
return groupingMapper.selectList(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class ImageServiceImpl
|
|||||||
private ImageMapper imageMapper;
|
private ImageMapper imageMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Image uploadImage(Long markdownId, MultipartFile file) throws IOException {
|
public Image uploadImage( MultipartFile file) throws IOException {
|
||||||
// 创建上传目录
|
// 创建上传目录
|
||||||
Path uploadPath = Paths.get(uploadDir);
|
Path uploadPath = Paths.get(uploadDir);
|
||||||
if (!Files.exists(uploadPath)) {
|
if (!Files.exists(uploadPath)) {
|
||||||
@@ -51,7 +51,6 @@ public class ImageServiceImpl
|
|||||||
|
|
||||||
// 创建图片实体
|
// 创建图片实体
|
||||||
Image image = new Image();
|
Image image = new Image();
|
||||||
image.setMarkdownId(markdownId);
|
|
||||||
image.setOriginalName(originalFilename);
|
image.setOriginalName(originalFilename);
|
||||||
image.setStoredName(storedName);
|
image.setStoredName(storedName);
|
||||||
image.setUrl("/uploads/" + storedName);
|
image.setUrl("/uploads/" + storedName);
|
||||||
@@ -82,14 +81,6 @@ public class ImageServiceImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Image> getMarkdownImages(Long markdownId) {
|
|
||||||
QueryWrapper<Image> queryWrapper = new QueryWrapper<>();
|
|
||||||
queryWrapper.eq("markdown_id", markdownId)
|
|
||||||
.orderByDesc("created_at");
|
|
||||||
return this.list(queryWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteImageByUrl(String url) {
|
public boolean deleteImageByUrl(String url) {
|
||||||
Image image = imageMapper.selectOne(new QueryWrapper<Image>().eq("url", url));
|
Image image = imageMapper.selectOne(new QueryWrapper<Image>().eq("url", url));
|
||||||
@@ -108,17 +99,15 @@ public class ImageServiceImpl
|
|||||||
throw new RuntimeException("删除图片失败", e);
|
throw new RuntimeException("删除图片失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean updateImageId(List<String> list, Long markdownId) {
|
public boolean deleteImageByUrls(List<String> urls) {
|
||||||
if (CollUtil.isEmpty( list)) {
|
if (CollUtil.isEmpty(urls)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (String url : list) {
|
for (String url : urls) {
|
||||||
Image image = imageMapper.selectOne(new QueryWrapper<Image>().eq("url", url));
|
Image image = imageMapper.selectOne(new QueryWrapper<Image>().eq("url", url));
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
image.setMarkdownId(markdownId);
|
this.deleteImageByUrl(url);
|
||||||
this.updateById(image);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import axiosApi from '@/utils/axios.js'
|
|||||||
|
|
||||||
export const groupingId = (data) => axiosApi.get(`/api/markdown/grouping/${data}`)
|
export const groupingId = (data) => axiosApi.get(`/api/markdown/grouping/${data}`)
|
||||||
// 获取所有分组
|
// 获取所有分组
|
||||||
export const groupingAll = () => axiosApi.get(`/api/groupings`)
|
export const groupingAll = (data) => axiosApi.get(`/api/groupings?parentId=${data}`);
|
||||||
// 获取所有Markdown文件
|
// 获取所有Markdown文件
|
||||||
export const markdownAll = () => axiosApi.get(`/api/markdown`);
|
export const markdownAll = () => axiosApi.get(`/api/markdown`);
|
||||||
// 预览markdown文件
|
// 预览markdown文件
|
||||||
@@ -30,6 +30,33 @@ export const updateMarkdown = (id, data) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 批量删除图片
|
||||||
|
export const deleteImages = (list) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('urls', list)
|
||||||
|
return axiosApi.post('/api/images/batch', formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 上传图片
|
||||||
|
export const uploadImage = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
if (file) formData.append('file', file)
|
||||||
|
return axiosApi.post('/api/images?markdownId', formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除Markdown文件
|
||||||
|
export const deleteMarkdown = (id) => axiosApi.post(`/api/markdown/delete?id=${id}`);
|
||||||
|
// 根据分组ID获取Markdown文件列表
|
||||||
|
export const markdownList = (groupingId) => axiosApi.get(`/api/markdown/grouping/${groupingId}`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MD5哈希
|
// MD5哈希
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
:default-active="activeMenu"
|
:default-active="activeMenu"
|
||||||
class="el-menu-vertical-demo"
|
class="el-menu-vertical-demo"
|
||||||
:collapse="isCollapsed"
|
:collapse="isCollapsed"
|
||||||
@select="handleMenuSelect"
|
|
||||||
popper-effect="light"
|
popper-effect="light"
|
||||||
collapse-transition
|
collapse-transition
|
||||||
>
|
>
|
||||||
@@ -28,14 +27,11 @@
|
|||||||
<template #title>
|
<template #title>
|
||||||
<span>{{ group.grouping }}</span>
|
<span>{{ group.grouping }}</span>
|
||||||
</template>
|
</template>
|
||||||
<!-- 分组下的文件 -->
|
<el-menu-item v-for="sub in jb22.filter(j => +j.parentId === +group.id)"
|
||||||
<el-menu-item
|
:key="sub.id"
|
||||||
v-for="file in groupFiles[group.id] || []"
|
:index="`sub-${sub.id}`"
|
||||||
:key="file.id"
|
@click="selectFile(sub);selectedFile=null"
|
||||||
:index="`file-${file.id}`"
|
>{{ sub.grouping }}</el-menu-item>
|
||||||
>
|
|
||||||
{{ file.title }}
|
|
||||||
</el-menu-item>
|
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,7 +47,7 @@
|
|||||||
<el-button v-if="!showEditor" type="primary" @click="selectedFile=null">清空</el-button>
|
<el-button v-if="!showEditor" type="primary" @click="selectedFile=null">清空</el-button>
|
||||||
<el-button v-if="!showEditor" type="primary" @click="editNote(selectedFile);isCollapsed=true">编辑</el-button>
|
<el-button v-if="!showEditor" type="primary" @click="editNote(selectedFile);isCollapsed=true">编辑</el-button>
|
||||||
<el-button v-if="!showEditor" type="danger" @click="deleteNote(selectedFile)">删除</el-button>
|
<el-button v-if="!showEditor" type="danger" @click="deleteNote(selectedFile)">删除</el-button>
|
||||||
<el-button v-if="showEditor" type="primary" @click="showEditor=!showEditor;selectFile(editData)">返回</el-button>
|
<el-button v-if="showEditor" type="primary" @click="showEditor=!showEditor;previewFile(editData)">返回</el-button>
|
||||||
<el-button v-if="showEditor" type="success" @click="handleSave(editData.content)">保存</el-button>
|
<el-button v-if="showEditor" type="success" @click="handleSave(editData.content)">保存</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,9 +86,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="markdownFiles.length > 0" class="file-list">
|
<div v-if="groupMarkdownFiles.length > 0" class="file-list">
|
||||||
<div v-for="file in markdownFiles" :key="file.id" class="file-item">
|
<div v-for="file in groupMarkdownFiles" :key="file.id" class="file-item">
|
||||||
<div @click="selectFile(file)" class="file-title">{{ file.title }}</div>
|
<div @click="previewFile(file)" class="file-title">{{ file.title }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="empty-tip">暂无笔记,请创建或上传</div>
|
<div v-else class="empty-tip">暂无笔记,请创建或上传</div>
|
||||||
@@ -139,18 +135,26 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import {onMounted, ref} from 'vue';
|
||||||
import {ElMessage} from 'element-plus';
|
import {ElMessage} from 'element-plus';
|
||||||
import '@kangc/v-md-editor/lib/style/preview.css';
|
import '@kangc/v-md-editor/lib/style/preview.css';
|
||||||
import '@kangc/v-md-editor/lib/theme/style/github.css';
|
import '@kangc/v-md-editor/lib/theme/style/github.css';
|
||||||
import {groupingId, groupingAll, markdownAll, addGroupings, Preview, updateMarkdown} from '@/api/CommonApi.js'
|
import {
|
||||||
|
addGroupings,
|
||||||
|
deleteImages, deleteMarkdown,
|
||||||
|
groupingAll,
|
||||||
|
groupingId,
|
||||||
|
markdownAll, markdownList,
|
||||||
|
Preview,
|
||||||
|
updateMarkdown, uploadImage
|
||||||
|
} from '@/api/CommonApi.js'
|
||||||
import {DArrowRight} from "@element-plus/icons-vue";
|
import {DArrowRight} from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const markdownFiles = ref([]);
|
const markdownFiles = ref([]);
|
||||||
const groupings = ref([]);
|
const groupings = ref([]);
|
||||||
const groupFiles = ref({});
|
// 二级分类下的markdown文件
|
||||||
|
const groupMarkdownFiles = ref({});
|
||||||
const showEditor = ref(false);
|
const showEditor = ref(false);
|
||||||
const selectedFile = ref(null);
|
const selectedFile = ref(null);
|
||||||
const activeMenu = ref('all');
|
const activeMenu = ref('all');
|
||||||
@@ -162,45 +166,29 @@ const newNoteForm = ref({ title: '', groupingId: null });
|
|||||||
|
|
||||||
const editData=ref(null)
|
const editData=ref(null)
|
||||||
|
|
||||||
|
// 笔记中的所有图片url
|
||||||
|
const imageUrls = ref([]);
|
||||||
|
// 刚开始笔记中的所有图片url
|
||||||
|
const originalImages = ref([]);
|
||||||
|
|
||||||
|
const jb22=ref([])
|
||||||
|
|
||||||
// 获取所有分组
|
// 获取所有分组
|
||||||
const fetchGroupings = async () => {
|
const fetchGroupings = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await groupingAll()
|
const response = await groupingAll(0)
|
||||||
// 确保分组数据是数组
|
|
||||||
groupings.value = response.data
|
|
||||||
// 为每个分组获取文件
|
|
||||||
for (const group of groupings.value) {
|
|
||||||
try {
|
|
||||||
const filesRes = await groupingId(group.id);
|
|
||||||
// 确保文件ID为字符串
|
|
||||||
groupFiles.value[group.id] = (filesRes.data).map(file => ({
|
|
||||||
...file,
|
|
||||||
id: file.id
|
|
||||||
}));
|
|
||||||
} catch (fileError) {
|
|
||||||
console.error(`获取分组 ${group.id} 文件失败:`, fileError);
|
|
||||||
groupFiles.value[group.id] = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加"全部"分类 (ID=1)
|
const jb1 = []
|
||||||
if (!groupings.value.some(g => g.id === 1)) {
|
const jb2 = []
|
||||||
// groupings.value.unshift({ id: 1, name: '全部' });
|
for (let i = 0; i <response.data.length; i++) {
|
||||||
// 获取所有文件作为"全部"分类的内容
|
if (+response.data[i].parentId===0){
|
||||||
try {
|
jb1.push(response.data[i])
|
||||||
const allFilesRes = await markdownAll()
|
}else{
|
||||||
groupFiles.value[1] = (allFilesRes.data).map(file => ({
|
jb2.push(response.data[i])
|
||||||
...file,
|
|
||||||
id: file.id
|
|
||||||
}));
|
|
||||||
} catch (allFilesError) {
|
|
||||||
console.error('获取全部笔记失败:', allFilesError);
|
|
||||||
groupFiles.value[1] = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
groupings.value=jb1
|
||||||
|
jb22.value=jb2
|
||||||
} 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));
|
||||||
@@ -208,6 +196,13 @@ const fetchGroupings = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取二级分类下的Markdown文件
|
||||||
|
const selectFile = async (data) => {
|
||||||
|
const promise = await markdownList(data.id);
|
||||||
|
groupMarkdownFiles.value=promise.data
|
||||||
|
};
|
||||||
|
|
||||||
|
// 代码块复制成功回调
|
||||||
const handleCopyCodeSuccess = () => {
|
const handleCopyCodeSuccess = () => {
|
||||||
ElMessage.success('代码已复制到剪贴板');
|
ElMessage.success('代码已复制到剪贴板');
|
||||||
};
|
};
|
||||||
@@ -228,12 +223,13 @@ const fetchMarkdownFiles = async () => {
|
|||||||
|
|
||||||
// 创建新分类
|
// 创建新分类
|
||||||
const createGrouping = async () => {
|
const createGrouping = async () => {
|
||||||
|
// TODO 添加分类创建逻辑
|
||||||
try {
|
try {
|
||||||
const response = await addGroupings(newGroupForm.value.name)
|
const response = await addGroupings(newGroupForm.value.name)
|
||||||
ElMessage.success('分类创建成功');
|
ElMessage.success('分类创建成功');
|
||||||
showCreateGroupDialog.value = false;
|
showCreateGroupDialog.value = false;
|
||||||
newGroupForm.value.name = '';
|
newGroupForm.value.name = '';
|
||||||
fetchGroupings();
|
await fetchGroupings();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('创建分类失败: ' + error.message);
|
ElMessage.error('创建分类失败: ' + error.message);
|
||||||
}
|
}
|
||||||
@@ -241,6 +237,7 @@ const createGrouping = async () => {
|
|||||||
|
|
||||||
// 创建新笔记
|
// 创建新笔记
|
||||||
const createNote = async () => {
|
const createNote = async () => {
|
||||||
|
// TODO 添加笔记创建逻辑
|
||||||
try {
|
try {
|
||||||
await axios.post(`${API_BASE_URL}/api/markdown`, '# 新笔记内容', {
|
await axios.post(`${API_BASE_URL}/api/markdown`, '# 新笔记内容', {
|
||||||
params: {
|
params: {
|
||||||
@@ -253,16 +250,14 @@ const createNote = async () => {
|
|||||||
ElMessage.success('笔记创建成功');
|
ElMessage.success('笔记创建成功');
|
||||||
showCreateNoteDialog.value = false;
|
showCreateNoteDialog.value = false;
|
||||||
newNoteForm.value = { title: '', groupingId: null };
|
newNoteForm.value = { title: '', groupingId: null };
|
||||||
await fetchMarkdownFiles();
|
await chushihua()
|
||||||
await fetchGroupings();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('创建笔记失败: ' + error.message);
|
ElMessage.error('创建笔记失败: ' + error.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 选择文件预览
|
// 选择文件预览
|
||||||
const selectFile = async (file) => {
|
const previewFile = async (file) => {
|
||||||
console.log( file)
|
|
||||||
try {
|
try {
|
||||||
const response = await Preview(file.id)
|
const response = await Preview(file.id)
|
||||||
|
|
||||||
@@ -289,17 +284,26 @@ const selectFile = async (file) => {
|
|||||||
// 编辑笔记
|
// 编辑笔记
|
||||||
const editNote = (file) => {
|
const editNote = (file) => {
|
||||||
editData.value = file
|
editData.value = file
|
||||||
|
originalImages.value = extractImageUrls(file.content);
|
||||||
showEditor.value = true;
|
showEditor.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImageUpload=(event,insertImage,files)=>{
|
// 图片上传
|
||||||
|
const handleImageUpload=async (event, insertImage, files) => {
|
||||||
console.log(files)
|
console.log(files)
|
||||||
|
const promise = await uploadImage(files[0]);
|
||||||
|
if (promise.code !== 200) {
|
||||||
|
ElMessage.error(promise.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 插入图片(确保URL格式正确)
|
||||||
|
const imageUrl = promise.data.url.startsWith('/')
|
||||||
|
? `http://127.0.0.1:8084${promise.data.url}`
|
||||||
|
: promise.data.url;
|
||||||
// 插入图片
|
// 插入图片
|
||||||
insertImage({
|
insertImage({
|
||||||
// 图片地址
|
// 图片地址
|
||||||
url:
|
url: imageUrl
|
||||||
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1269952892,3525182336&fm=26&gp=0.jpg'
|
|
||||||
// 图片描述
|
// 图片描述
|
||||||
// desc: '七龙珠',
|
// desc: '七龙珠',
|
||||||
});
|
});
|
||||||
@@ -307,6 +311,8 @@ const handleImageUpload=(event,insertImage,files)=>{
|
|||||||
|
|
||||||
// 在编辑页面,按Ctrl+S保存笔记,或者点击保存,对数据进行保存
|
// 在编辑页面,按Ctrl+S保存笔记,或者点击保存,对数据进行保存
|
||||||
const handleSave= async (file) => {
|
const handleSave= async (file) => {
|
||||||
|
imageUrls.value = extractImageUrls(file);
|
||||||
|
extractDeletedImageUrls(imageUrls.value)
|
||||||
const filesRes = await updateMarkdown(editData.value.id,file);
|
const filesRes = await updateMarkdown(editData.value.id,file);
|
||||||
if (filesRes.code===200){
|
if (filesRes.code===200){
|
||||||
ElMessage.success(filesRes.msg);
|
ElMessage.success(filesRes.msg);
|
||||||
@@ -316,49 +322,83 @@ const handleSave= async (file) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 笔记保存后处理
|
// 保存时获取所有的图片url
|
||||||
const handleNoteSaved = () => {
|
const extractImageUrls = (data) => {
|
||||||
showEditor.value = false;
|
const content = data
|
||||||
fetchMarkdownFiles();
|
const urls = [];
|
||||||
fetchGroupings();
|
|
||||||
|
|
||||||
if (selectedFile.value) {
|
// 处理 URL 的内部函数
|
||||||
selectFile(selectedFile.value); // 刷新当前预览内容
|
const getPathFromUrl = (url) => {
|
||||||
|
try {
|
||||||
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||||||
|
// 使用 URL 对象解析路径
|
||||||
|
return new URL(url).pathname;
|
||||||
|
}
|
||||||
|
return url; // 非 HTTP(S) URL 直接返回(如 base64)
|
||||||
|
} catch (e) {
|
||||||
|
return url; // 解析失败时返回原始 URL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 匹配Markdown图片语法
|
||||||
|
const mdRegex = /!\[.*?\]\((.*?)\)/g;
|
||||||
|
let mdMatch;
|
||||||
|
while ((mdMatch = mdRegex.exec(content)) !== null) {
|
||||||
|
urls.push(getPathFromUrl(mdMatch[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配HTML img标签
|
||||||
|
const htmlRegex = /<img[^>]+src="([^">]+)"/g;
|
||||||
|
let htmlMatch;
|
||||||
|
while ((htmlMatch = htmlRegex.exec(content)) !== null) {
|
||||||
|
urls.push(getPathFromUrl(htmlMatch[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配base64图片
|
||||||
|
const base64Regex = /<img[^>]+src="(data:image\/[^;]+;base64[^">]+)"/g;
|
||||||
|
let base64Match;
|
||||||
|
while ((base64Match = base64Regex.exec(content)) !== null) {
|
||||||
|
urls.push(base64Match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤和去重
|
||||||
|
const validUrls = urls.filter(url => {
|
||||||
|
return url.startsWith('http') || url.startsWith('data:image');
|
||||||
|
});
|
||||||
|
return [...new Set(validUrls)]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 笔记保存时获取要删除的图片url
|
||||||
|
const extractDeletedImageUrls = (data) => {
|
||||||
|
const delImages = []
|
||||||
|
// 原来的url originalImages.value
|
||||||
|
for (let i = 0; i <originalImages.value.length; i++) {
|
||||||
|
for (let j = 0; j <data.length; j++) {
|
||||||
|
if (originalImages.value[i] !== data[j]){
|
||||||
|
delImages.push(originalImages.value[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (delImages.length>0){
|
||||||
|
deleteImages(delImages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 删除笔记
|
// 删除笔记
|
||||||
const deleteNote = async (file) => {
|
const deleteNote = async (file) => {
|
||||||
try {
|
try {
|
||||||
await axios.delete(`${API_BASE_URL}/api/markdown/${file.id}`);
|
await deleteMarkdown(file.id);
|
||||||
ElMessage.success('删除成功');
|
ElMessage.success('删除成功');
|
||||||
selectedFile.value = null;
|
selectedFile.value = null;
|
||||||
fetchMarkdownFiles();
|
await chushihua()
|
||||||
fetchGroupings();
|
await selectFile(groupingId)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('删除失败: ' + error.message);
|
ElMessage.error('删除失败: ' + error.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 菜单选择处理
|
// ---------------------------------TODO 下面的还没有还是看
|
||||||
const handleMenuSelect = (index) => {
|
|
||||||
if (index === 'all') {
|
|
||||||
selectedFile.value = null;
|
|
||||||
activeMenu.value = 'all';
|
|
||||||
} else if (index.startsWith('file-')) {
|
|
||||||
const fileId = index.split('-')[1];
|
|
||||||
// 确保markdownFiles.value是数组
|
|
||||||
if (Array.isArray(markdownFiles.value)) {
|
|
||||||
const file = markdownFiles.value.find(f => f.id === fileId);
|
|
||||||
if (file) {
|
|
||||||
selectFile(file);
|
|
||||||
activeMenu.value = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (index.startsWith('group-')) {
|
|
||||||
activeMenu.value = index;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 上传Markdown文件处理
|
// 上传Markdown文件处理
|
||||||
const handleMarkdownUpload = (file) => {
|
const handleMarkdownUpload = (file) => {
|
||||||
@@ -380,8 +420,7 @@ const handleMarkdownUpload = (file) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ElMessage.success('上传成功');
|
ElMessage.success('上传成功');
|
||||||
fetchMarkdownFiles();
|
await chushihua()
|
||||||
fetchGroupings();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('上传失败: ' + error.message);
|
ElMessage.error('上传失败: ' + error.message);
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
mydatabase.db
BIN
mydatabase.db
Binary file not shown.
Reference in New Issue
Block a user