feat(security): 更新 JWT 密钥并改进加密方式
- 更新 JWT 密钥为一个足够长的 Base64 编码密钥,满足 HS512 的要求 - 修改 JwtTokenUtil 类,使用 Keys.hmacShaKeyFor 生成密钥 - 优化 token 解析和生成过程,使用 parserBuilder 设置密钥 refactor(category): 重构分类组件并优化分类选择逻辑 - 移除原有的分类层级结构,改为使用树形结构 - 优化分类选择界面,使用级联选择器- 重构分类相关的数据结构和方法,提高可维护性 fix(application.yml): 优化配置文件格式 - 更新 JWT 密钥配置,确保密钥长度符合要求
This commit is contained in:
@@ -3,10 +3,14 @@ package com.test.bijihoudaun.util;
|
|||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.Key;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -21,6 +25,13 @@ public class JwtTokenUtil {
|
|||||||
@Value("${jwt.expiration}")
|
@Value("${jwt.expiration}")
|
||||||
private Long expiration;
|
private Long expiration;
|
||||||
|
|
||||||
|
private Key key;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
// 从token中获取用户名
|
// 从token中获取用户名
|
||||||
public String getUsernameFromToken(String token) {
|
public String getUsernameFromToken(String token) {
|
||||||
return getClaimFromToken(token, Claims::getSubject);
|
return getClaimFromToken(token, Claims::getSubject);
|
||||||
@@ -38,7 +49,7 @@ public class JwtTokenUtil {
|
|||||||
|
|
||||||
// 为了从token中获取任何信息,我们都需要密钥
|
// 为了从token中获取任何信息,我们都需要密钥
|
||||||
private Claims getAllClaimsFromToken(String token) {
|
private Claims getAllClaimsFromToken(String token) {
|
||||||
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查token是否过期
|
// 检查token是否过期
|
||||||
@@ -57,7 +68,7 @@ public class JwtTokenUtil {
|
|||||||
private String doGenerateToken(Map<String, Object> claims, String subject) {
|
private String doGenerateToken(Map<String, Object> claims, String subject) {
|
||||||
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
|
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
|
||||||
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
|
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
|
||||||
.signWith(SignatureAlgorithm.HS512, secret).compact();
|
.signWith(key, SignatureAlgorithm.HS512).compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证token
|
// 验证token
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ mybatis-plus:
|
|||||||
|
|
||||||
# JWT 配置
|
# JWT 配置
|
||||||
jwt:
|
jwt:
|
||||||
secret: mysecretkeymysecretkeymysecretkeymysecretkeymysecretkey # 至少256位的密钥
|
secret: V2VsbCwgSSBzdXBwb3NlIHRoYXQgaWYgeW91J3JlIHJlYWRpbmcgdGhpcywgeW91J3JlIHByZXR0eSBjdXJpb3VzLg== # 这是一个足够长的Base64编码密钥,满足HS512的要求
|
||||||
expiration: 86400 # token有效期,单位秒,这里是24小时
|
expiration: 86400 # token有效期,单位秒,这里是24小时
|
||||||
header: Authorization # JWT存储的请求头
|
header: Authorization # JWT存储的请求头
|
||||||
tokenHead: "Bearer " # JWT负载中拿到开头
|
tokenHead: "Bearer " # JWT负载中拿到开头
|
||||||
|
|||||||
@@ -22,25 +22,10 @@
|
|||||||
popper-effect="light"
|
popper-effect="light"
|
||||||
:collapse-transition="false"
|
:collapse-transition="false"
|
||||||
>
|
>
|
||||||
<!-- 分组分类 -->
|
<!-- 递归菜单组件 -->
|
||||||
<el-sub-menu v-for="group in groupings" :key="group.id" :index="`group-${group.id}`">
|
<template v-for="menu in categoryTree" :key="menu.id">
|
||||||
<template #title>
|
<component :is="renderMenu(menu)" />
|
||||||
<div class="menu-item-title">
|
</template>
|
||||||
<el-icon><Folder /></el-icon>
|
|
||||||
<span>{{ group.grouping }}</span>
|
|
||||||
<el-icon class="edit-icon" @click.stop="openRenameDialog(group, 'group')"><Edit /></el-icon>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-menu-item
|
|
||||||
v-for="sub in jb22.filter(j => +j.parentId === +group.id)"
|
|
||||||
:key="sub.id"
|
|
||||||
:index="`sub-${sub.id}`"
|
|
||||||
@click="selectFile(sub); selectedFile = null"
|
|
||||||
>
|
|
||||||
<el-icon><Document /></el-icon>
|
|
||||||
{{ sub.grouping }}
|
|
||||||
</el-menu-item>
|
|
||||||
</el-sub-menu>
|
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</el-aside>
|
</el-aside>
|
||||||
|
|
||||||
@@ -117,21 +102,15 @@
|
|||||||
<!-- 分类创建对话框 -->
|
<!-- 分类创建对话框 -->
|
||||||
<el-dialog v-model="showCreateGroupDialog" title="新建分类" width="400px" @close="resetGroupForm">
|
<el-dialog v-model="showCreateGroupDialog" title="新建分类" width="400px" @close="resetGroupForm">
|
||||||
<el-form :model="newGroupForm" :rules="groupFormRules" ref="groupFormRef" label-width="80px">
|
<el-form :model="newGroupForm" :rules="groupFormRules" ref="groupFormRef" label-width="80px">
|
||||||
<el-form-item label="分类级别">
|
<el-form-item label="父级分类">
|
||||||
<el-radio-group v-model="isGroup1">
|
<el-cascader
|
||||||
<el-radio :label="true">一级分类</el-radio>
|
v-model="newGroupForm.parentId"
|
||||||
<el-radio :label="false">二级分类</el-radio>
|
:options="categoryCascaderOptions"
|
||||||
</el-radio-group>
|
:props="{ checkStrictly: true, emitPath: false }"
|
||||||
</el-form-item>
|
clearable
|
||||||
<el-form-item v-if="!isGroup1" label="父级分类" prop="parentId">
|
placeholder="不选则为一级分类"
|
||||||
<el-select v-model="newGroupForm.parentId" placeholder="请选择父级分类">
|
style="width: 100%;"
|
||||||
<el-option
|
></el-cascader>
|
||||||
v-for="group in groupings"
|
|
||||||
:key="group.id"
|
|
||||||
:label="group.grouping"
|
|
||||||
:value="group.id"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="分类名称" prop="name">
|
<el-form-item label="分类名称" prop="name">
|
||||||
<el-input v-model="newGroupForm.name" autocomplete="off"></el-input>
|
<el-input v-model="newGroupForm.name" autocomplete="off"></el-input>
|
||||||
@@ -149,25 +128,15 @@
|
|||||||
<el-form-item label="笔记标题" prop="title">
|
<el-form-item label="笔记标题" prop="title">
|
||||||
<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="选择大分类" prop="parentId">
|
|
||||||
<el-select v-model="fenlei1" placeholder="请选择" @change="getjb2">
|
|
||||||
<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="选择分类" prop="groupingId">
|
<el-form-item label="选择分类" prop="groupingId">
|
||||||
<el-select v-model="newNoteForm.groupingId" placeholder="请选择">
|
<el-cascader
|
||||||
<el-option
|
v-model="newNoteForm.groupingId"
|
||||||
v-for="group in fenlei2"
|
:options="categoryTree"
|
||||||
:key="group.id"
|
:props="{ checkStrictly: true, emitPath: false }"
|
||||||
:label="group.grouping"
|
clearable
|
||||||
:value="group.id"
|
placeholder="请选择笔记所属分类"
|
||||||
></el-option>
|
style="width: 100%;"
|
||||||
</el-select>
|
></el-cascader>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -187,14 +156,14 @@
|
|||||||
|
|
||||||
<!-- 导入选择分类对话框 -->
|
<!-- 导入选择分类对话框 -->
|
||||||
<el-dialog v-model="showSelectGroupDialog" title="选择导入的分类" width="400px">
|
<el-dialog v-model="showSelectGroupDialog" title="选择导入的分类" width="400px">
|
||||||
<el-select v-model="importGroupId" placeholder="请选择分类">
|
<el-cascader
|
||||||
<el-option
|
v-model="importGroupId"
|
||||||
v-for="group in jb22"
|
:options="categoryTree"
|
||||||
:key="group.id"
|
:props="{ checkStrictly: true, emitPath: false }"
|
||||||
:label="group.grouping"
|
clearable
|
||||||
:value="group.id"
|
placeholder="请选择要导入的分类"
|
||||||
></el-option>
|
style="width: 100%;"
|
||||||
</el-select>
|
></el-cascader>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="showSelectGroupDialog = false">取消</el-button>
|
<el-button @click="showSelectGroupDialog = false">取消</el-button>
|
||||||
<el-button type="primary" @click="confirmImport">确定</el-button>
|
<el-button type="primary" @click="confirmImport">确定</el-button>
|
||||||
@@ -206,15 +175,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, ref, nextTick, watch} from 'vue';
|
import {onMounted, ref, nextTick, watch, h, computed} from 'vue';
|
||||||
import {ElMessage} from 'element-plus';
|
import {ElMessage, ElSubMenu, ElMenuItem, ElIcon} from 'element-plus';
|
||||||
import Vditor from 'vditor';
|
import Vditor from 'vditor';
|
||||||
import 'vditor/dist/index.css';
|
import 'vditor/dist/index.css';
|
||||||
import {
|
import {
|
||||||
addGroupings,
|
addGroupings,
|
||||||
deleteImages, deleteMarkdown,
|
deleteImages, deleteMarkdown,
|
||||||
groupingAll,
|
groupingAll,
|
||||||
groupingId,
|
|
||||||
markdownAll, markdownList,
|
markdownAll, markdownList,
|
||||||
Preview,
|
Preview,
|
||||||
updateMarkdown, uploadImage,
|
updateMarkdown, uploadImage,
|
||||||
@@ -222,7 +190,7 @@ import {
|
|||||||
updateGroupingName,
|
updateGroupingName,
|
||||||
updateMarkdownTitle
|
updateMarkdownTitle
|
||||||
} from '@/api/CommonApi.js'
|
} from '@/api/CommonApi.js'
|
||||||
import { DArrowRight, Plus, Fold, Expand, Folder, Document, Search, Edit } from "@element-plus/icons-vue";
|
import { Plus, Fold, Expand, Folder, Document, Search, Edit } from "@element-plus/icons-vue";
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '../stores/user';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
@@ -230,14 +198,8 @@ const userStore = useUserStore();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchKeyword = ref('');
|
const searchKeyword = ref('');
|
||||||
|
|
||||||
const isGroup1=ref(true)
|
|
||||||
// 创建新文件中大分类的信息
|
|
||||||
const fenlei1=ref(null)
|
|
||||||
// 创建新文件中分类信息
|
|
||||||
const fenlei2=ref(null)
|
|
||||||
const markdownFiles = ref([]);
|
const markdownFiles = ref([]);
|
||||||
const groupings = ref([]);
|
const categoryTree = ref([]);
|
||||||
// 二级分类下的markdown文件
|
|
||||||
const groupMarkdownFiles = ref({});
|
const groupMarkdownFiles = ref({});
|
||||||
const showEditor = ref(false);
|
const showEditor = ref(false);
|
||||||
const selectedFile = ref(null);
|
const selectedFile = ref(null);
|
||||||
@@ -256,7 +218,6 @@ const groupFormRef = ref(null);
|
|||||||
const newGroupForm = ref({ name: '', parentId: null });
|
const newGroupForm = ref({ name: '', parentId: null });
|
||||||
const groupFormRules = ref({
|
const groupFormRules = ref({
|
||||||
name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }],
|
name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }],
|
||||||
parentId: [{ required: true, message: '请选择父级分类', trigger: 'change' }],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const noteFormRef = ref(null);
|
const noteFormRef = ref(null);
|
||||||
@@ -264,37 +225,31 @@ const newNoteForm = ref({
|
|||||||
id: null,
|
id: null,
|
||||||
title: '',
|
title: '',
|
||||||
groupingId: null,
|
groupingId: null,
|
||||||
parentId: null,
|
|
||||||
fileName: '',
|
fileName: '',
|
||||||
content: ''
|
content: ''
|
||||||
});
|
});
|
||||||
const noteFormRules = ref({
|
const noteFormRules = ref({
|
||||||
title: [{ required: true, message: '请输入笔记标题', trigger: 'blur' }],
|
title: [{ required: true, message: '请输入笔记标题', trigger: 'blur' }],
|
||||||
parentId: [{ required: true, message: '请选择大分类', trigger: 'change' }],
|
groupingId: [{ required: true, message: '请选择分类', trigger: 'change' }],
|
||||||
groupingId: [{ required: true, message: '请选择二级分类', trigger: 'change' }],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建新笔记的多级菜单
|
|
||||||
const options=ref([])
|
|
||||||
// 编辑笔记的数据
|
|
||||||
const editData=ref(null)
|
const editData=ref(null)
|
||||||
// 笔记中的所有图片url
|
|
||||||
const imageUrls = ref([]);
|
const imageUrls = ref([]);
|
||||||
// 刚开始笔记中的所有图片url
|
|
||||||
const originalImages = ref([]);
|
const originalImages = ref([]);
|
||||||
// 分类为二级的数据
|
|
||||||
const jb22=ref([])
|
|
||||||
|
|
||||||
// Vditor 实例
|
|
||||||
const vditor = ref(null);
|
const vditor = ref(null);
|
||||||
const previewHtml = ref('');
|
const previewHtml = ref('');
|
||||||
const saveStatus = ref('空闲');
|
const saveStatus = ref('空闲');
|
||||||
let debounceTimer = null;
|
let debounceTimer = null;
|
||||||
|
|
||||||
|
const categoryCascaderOptions = computed(() => {
|
||||||
|
return [{ id: 0, grouping: '根分类', value: 0, label: '根分类' }, ...categoryTree.value];
|
||||||
|
});
|
||||||
|
|
||||||
const initVditor = () => {
|
const initVditor = () => {
|
||||||
vditor.value = new Vditor('vditor', {
|
vditor.value = new Vditor('vditor', {
|
||||||
height: 'calc(100vh - 120px)',
|
height: 'calc(100vh - 120px)',
|
||||||
mode: 'ir', // 即时渲染模式
|
mode: 'ir',
|
||||||
after: () => {
|
after: () => {
|
||||||
if (editData.value) {
|
if (editData.value) {
|
||||||
vditor.value.setValue(editData.value.content);
|
vditor.value.setValue(editData.value.content);
|
||||||
@@ -312,56 +267,41 @@ const initVditor = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建md文件时通过大分类获取二级分类
|
const buildTree = (items, parentId = 0) => {
|
||||||
const getjb2 = async () => {
|
return items
|
||||||
if (fenlei1.value != null) {
|
.filter(item => +item.parentId === +parentId)
|
||||||
const response = await groupingAll(fenlei1.value)
|
.map(item => {
|
||||||
fenlei2.value=response.data
|
const children = buildTree(items, item.id);
|
||||||
}
|
return {
|
||||||
}
|
...item,
|
||||||
|
value: item.id,
|
||||||
|
label: item.grouping,
|
||||||
|
children: children.length > 0 ? children : undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 获取所有分组
|
|
||||||
const fetchGroupings = async () => {
|
const fetchGroupings = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await groupingAll("")
|
const response = await groupingAll("");
|
||||||
const jb1 = []
|
const allCategories = response.data || [];
|
||||||
const jb2 = []
|
categoryTree.value = buildTree(allCategories);
|
||||||
for (let i = 0; i <response.data.length; i++) {
|
|
||||||
if (+response.data[i].parentId===0){
|
|
||||||
jb1.push(response.data[i])
|
|
||||||
}else{
|
|
||||||
jb2.push(response.data[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
groupings.value=jb1
|
|
||||||
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));
|
||||||
groupings.value = [];
|
categoryTree.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取二级分类下的Markdown文件
|
|
||||||
const selectFile = async (data) => {
|
const selectFile = async (data) => {
|
||||||
const promise = await markdownList(data.id);
|
const promise = await markdownList(data.id);
|
||||||
groupMarkdownFiles.value=promise.data
|
groupMarkdownFiles.value = promise.data;
|
||||||
|
selectedFile.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取所有Markdown文件(确保ID为字符串)
|
|
||||||
const fetchMarkdownFiles = async () => {
|
const fetchMarkdownFiles = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await markdownAll()
|
const response = await markdownAll()
|
||||||
// 确保文件ID为字符串
|
|
||||||
markdownFiles.value = (response.data || []).map(file => ({
|
markdownFiles.value = (response.data || []).map(file => ({
|
||||||
...file,
|
...file,
|
||||||
id: String(file.id)
|
id: String(file.id)
|
||||||
@@ -371,13 +311,16 @@ const fetchMarkdownFiles = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建新分类
|
|
||||||
const createGrouping = async () => {
|
const createGrouping = async () => {
|
||||||
if (!groupFormRef.value) return;
|
if (!groupFormRef.value) return;
|
||||||
await groupFormRef.value.validate(async (valid) => {
|
await groupFormRef.value.validate(async (valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
try {
|
try {
|
||||||
const response = await addGroupings(newGroupForm.value)
|
const payload = {
|
||||||
|
name: newGroupForm.value.name,
|
||||||
|
parentId: newGroupForm.value.parentId || 0
|
||||||
|
};
|
||||||
|
await addGroupings(payload);
|
||||||
ElMessage.success('分类创建成功');
|
ElMessage.success('分类创建成功');
|
||||||
showCreateGroupDialog.value = false;
|
showCreateGroupDialog.value = false;
|
||||||
await fetchGroupings();
|
await fetchGroupings();
|
||||||
@@ -388,7 +331,6 @@ const createGrouping = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置新建分类表单
|
|
||||||
const resetGroupForm = () => {
|
const resetGroupForm = () => {
|
||||||
newGroupForm.value = { name: '', parentId: null };
|
newGroupForm.value = { name: '', parentId: null };
|
||||||
if (groupFormRef.value) {
|
if (groupFormRef.value) {
|
||||||
@@ -396,17 +338,27 @@ const resetGroupForm = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建新笔记
|
|
||||||
const createNote = async () => {
|
const createNote = async () => {
|
||||||
if (!noteFormRef.value) return;
|
if (!noteFormRef.value) return;
|
||||||
await noteFormRef.value.validate(async (valid) => {
|
await noteFormRef.value.validate(async (valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
try {
|
try {
|
||||||
newNoteForm.value.fileName = newNoteForm.value.title+'.md'
|
const groupingId = newNoteForm.value.groupingId;
|
||||||
editData.value=newNoteForm.value
|
if (!groupingId) {
|
||||||
showCreateNoteDialog.value = false
|
ElMessage.error('必须选择一个分类');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const payload = {
|
||||||
|
id: null,
|
||||||
|
title: newNoteForm.value.title,
|
||||||
|
groupingId: groupingId,
|
||||||
|
fileName: newNoteForm.value.title + '.md',
|
||||||
|
content: ''
|
||||||
|
};
|
||||||
|
editData.value = payload;
|
||||||
|
showCreateNoteDialog.value = false;
|
||||||
showEditor.value = true;
|
showEditor.value = true;
|
||||||
selectedFile.value=editData.value
|
selectedFile.value = editData.value;
|
||||||
await nextTick(() => {
|
await nextTick(() => {
|
||||||
initVditor();
|
initVditor();
|
||||||
});
|
});
|
||||||
@@ -417,16 +369,32 @@ const createNote = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置新建笔记表单
|
|
||||||
const resetNoteForm = () => {
|
const resetNoteForm = () => {
|
||||||
newNoteForm.value = { id: null, title: '', groupingId: null, parentId: null, fileName: '', content: '' };
|
newNoteForm.value = { id: null, title: '', groupingId: null, fileName: '', content: '' };
|
||||||
fenlei1.value = null;
|
|
||||||
fenlei2.value = null;
|
|
||||||
if (noteFormRef.value) {
|
if (noteFormRef.value) {
|
||||||
noteFormRef.value.resetFields();
|
noteFormRef.value.resetFields();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderMenu = (item) => {
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
return h(ElSubMenu, { index: `group-${item.id}` }, {
|
||||||
|
title: () => h('div', { class: 'menu-item-title' }, [
|
||||||
|
h(ElIcon, () => h(Folder)),
|
||||||
|
h('span', null, item.grouping),
|
||||||
|
h(ElIcon, { class: 'edit-icon', onClick: (e) => { e.stopPropagation(); openRenameDialog(item, 'group'); } }, () => h(Edit))
|
||||||
|
]),
|
||||||
|
default: () => item.children.map(child => renderMenu(child))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return h(ElMenuItem, { index: `group-${item.id}`, onClick: () => selectFile(item) }, {
|
||||||
|
default: () => [
|
||||||
|
h(ElIcon, () => h(Document)),
|
||||||
|
h('span', null, item.grouping)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 选择文件预览
|
// 选择文件预览
|
||||||
const previewFile = async (file) => {
|
const previewFile = async (file) => {
|
||||||
if (file.id === null){
|
if (file.id === null){
|
||||||
@@ -438,21 +406,24 @@ const previewFile = async (file) => {
|
|||||||
const response = await Preview(file.id)
|
const response = await Preview(file.id)
|
||||||
|
|
||||||
// 确保内容为字符串
|
// 确保内容为字符串
|
||||||
let content = response.data;
|
const content = String(response.data || '');
|
||||||
if (typeof content !== 'string') {
|
|
||||||
// 如果返回的是对象,尝试转换为字符串
|
|
||||||
if (content && typeof content === 'object') {
|
|
||||||
content = JSON.stringify(content);
|
|
||||||
} else {
|
|
||||||
content = String(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedFile.value = {
|
selectedFile.value = {
|
||||||
...file,
|
...file,
|
||||||
content: content
|
content: content
|
||||||
};
|
};
|
||||||
Vditor.preview(document.querySelector('.markdown-preview'), content);
|
await nextTick();
|
||||||
|
const previewElement = document.querySelector('.markdown-preview');
|
||||||
|
if (previewElement) {
|
||||||
|
Vditor.preview(previewElement, content, {
|
||||||
|
// 在这里提供一个基本的配置对象
|
||||||
|
mode: 'light', // 或者 'dark',可以根据当前主题动态设置
|
||||||
|
hljs: {
|
||||||
|
enable: true,
|
||||||
|
style: 'github'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
} 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