feat: 实现笔记编辑器的自动保存功能与UI优化
refactor: 重构用户登录注册逻辑与数据验证 fix: 修复图片上传安全漏洞与路径处理问题 perf: 优化笔记列表分页加载与滚动性能 style: 改进侧边栏菜单的视觉设计与交互体验 chore: 更新环境变量与数据库连接配置 docs: 添加用户信息视图对象的Swagger文档 test: 增加用户注册登录的输入验证测试 ci: 配置JWT密钥环境变量与安全设置 build: 调整前端构建配置与模块加载方式
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<el-header class="editor-header">
|
||||
<h2 class="editor-title">{{ editData.title }}</h2>
|
||||
<div class="actions">
|
||||
<el-button type="primary" @click="$emit('back', editData)">返回</el-button>
|
||||
<el-button type="primary" @click="handleBack">返回</el-button>
|
||||
<el-button type="success" @click="save">保存</el-button>
|
||||
<span class="save-status">{{ saveStatus }}</span>
|
||||
</div>
|
||||
@@ -29,13 +29,18 @@ const props = defineProps({
|
||||
const emit = defineEmits(['back', 'update:editData']);
|
||||
|
||||
const vditor = ref(null);
|
||||
const bijiId=ref(null);
|
||||
|
||||
const currentId = ref(null);
|
||||
const isInitialized = ref(false);
|
||||
const saveStatus = ref('');
|
||||
let saveTimeout = null;
|
||||
const isProgrammaticChange = ref(false);
|
||||
let lastSavedContent = ref('');
|
||||
let isSaving = ref(false);
|
||||
|
||||
const initVditor = () => {
|
||||
if (vditor.value) {
|
||||
vditor.value.destroy();
|
||||
}
|
||||
|
||||
vditor.value = new Vditor('vditor-editor', {
|
||||
height: 'calc(100vh - 120px)',
|
||||
mode: 'ir',
|
||||
@@ -43,23 +48,26 @@ const initVditor = () => {
|
||||
enable: false,
|
||||
},
|
||||
after: () => {
|
||||
if (props.editData && props.editData.content) {
|
||||
isProgrammaticChange.value = true;
|
||||
vditor.value.setValue(props.editData.content);
|
||||
isProgrammaticChange.value = false;
|
||||
}
|
||||
vditor.value.focus();
|
||||
isInitialized.value = true;
|
||||
if (props.editData && props.editData.content) {
|
||||
vditor.value.setValue(props.editData.content);
|
||||
lastSavedContent.value = props.editData.content;
|
||||
}
|
||||
if (props.editData && props.editData.id) {
|
||||
currentId.value = props.editData.id;
|
||||
}
|
||||
vditor.value.focus();
|
||||
},
|
||||
input: (value) => {
|
||||
if (isProgrammaticChange.value) {
|
||||
return;
|
||||
}
|
||||
// 启动定时器,延迟5秒后执行保存
|
||||
if (!isInitialized.value) return;
|
||||
|
||||
clearTimeout(saveTimeout);
|
||||
saveStatus.value = '正在输入...';
|
||||
saveTimeout = setTimeout(() => {
|
||||
save(value);
|
||||
}, 5000);
|
||||
if (!isSaving.value && value !== lastSavedContent.value) {
|
||||
save(value);
|
||||
}
|
||||
}, 3000);
|
||||
},
|
||||
upload: {
|
||||
accept: 'image/*',
|
||||
@@ -69,7 +77,6 @@ const initVditor = () => {
|
||||
|
||||
uploadImage(file).then(res => {
|
||||
const url = res.url;
|
||||
// 使用 file.name 替代 files.name 保证一致性
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL || '';
|
||||
vditor.value.insertValue(``);
|
||||
}).catch(() => {
|
||||
@@ -81,33 +88,72 @@ const initVditor = () => {
|
||||
};
|
||||
|
||||
const save = async (value) => {
|
||||
if (isSaving.value) return;
|
||||
|
||||
clearTimeout(saveTimeout);
|
||||
const content = typeof value === 'string' ? value : vditor.value.getValue();
|
||||
|
||||
if (content === lastSavedContent.value && currentId.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSaving.value = true;
|
||||
try {
|
||||
saveStatus.value = '正在保存...';
|
||||
// 发送完整的笔记对象,确保包含所有必要字段
|
||||
const response = await updateMarkdown({
|
||||
id: props.editData.id? props.editData.id : bijiId.value,
|
||||
|
||||
const payload = {
|
||||
id: currentId.value || props.editData.id || null,
|
||||
content: content,
|
||||
title: props.editData.title,
|
||||
groupingId: props.editData.groupingId,
|
||||
fileName: props.editData.fileName,
|
||||
isPrivate: props.editData.isPrivate
|
||||
});
|
||||
// 确保获取到后端返回的数据,包括可能的新ID
|
||||
|
||||
bijiId.value = response.id;
|
||||
// 保存成功,更新状态
|
||||
saveStatus.value = '已保存';
|
||||
// 发送更新后的笔记数据(包含可能的新ID)
|
||||
emit('update:editData', { ...props.editData, content: content });
|
||||
};
|
||||
|
||||
const response = await updateMarkdown(payload);
|
||||
|
||||
if (response && response.id) {
|
||||
currentId.value = response.id;
|
||||
lastSavedContent.value = content;
|
||||
|
||||
const updatedFile = {
|
||||
...props.editData,
|
||||
id: response.id,
|
||||
content: content,
|
||||
groupingId: response.groupingId,
|
||||
groupingName: response.groupingName,
|
||||
title: response.title,
|
||||
isPrivate: response.isPrivate
|
||||
};
|
||||
|
||||
emit('update:editData', updatedFile);
|
||||
saveStatus.value = '已保存';
|
||||
}
|
||||
} catch (error) {
|
||||
// 保存失败,更新状态并显示错误消息
|
||||
saveStatus.value = '保存失败';
|
||||
ElMessage.error('保存失败: ' + (error.message || '未知错误'));
|
||||
} finally {
|
||||
isSaving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleBack = async () => {
|
||||
const content = vditor.value ? vditor.value.getValue() : '';
|
||||
if (content !== lastSavedContent.value && !isSaving.value) {
|
||||
await save(content);
|
||||
}
|
||||
|
||||
const returnData = {
|
||||
...props.editData,
|
||||
id: currentId.value || props.editData.id,
|
||||
content: content,
|
||||
groupingId: props.editData.groupingId,
|
||||
groupingName: props.editData.groupingName
|
||||
};
|
||||
|
||||
emit('back', returnData);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initVditor();
|
||||
});
|
||||
@@ -116,27 +162,17 @@ onBeforeUnmount(() => {
|
||||
clearTimeout(saveTimeout);
|
||||
if (vditor.value) {
|
||||
vditor.value.destroy();
|
||||
vditor.value = null;
|
||||
}
|
||||
if (bijiId.value){
|
||||
// 发送完整的笔记对象,确保包含所有必要字段
|
||||
updateMarkdown({
|
||||
id: bijiId.value,
|
||||
content: vditor.value.getValue(),
|
||||
title: props.editData.title,
|
||||
groupingId: props.editData.groupingId,
|
||||
fileName: props.editData.fileName,
|
||||
isPrivate: props.editData.isPrivate
|
||||
});
|
||||
}
|
||||
// 离开页面后清空 bijiId.value 变量
|
||||
bijiId.value = null;
|
||||
currentId.value = null;
|
||||
isInitialized.value = false;
|
||||
});
|
||||
|
||||
watch(() => props.editData, (newVal, oldVal) => {
|
||||
if (vditor.value && newVal && newVal.id !== oldVal?.id) {
|
||||
isProgrammaticChange.value = true;
|
||||
if (vditor.value && isInitialized.value && newVal && newVal.id !== oldVal?.id) {
|
||||
vditor.value.setValue(newVal.content || '');
|
||||
isProgrammaticChange.value = false;
|
||||
lastSavedContent.value = newVal.content || '';
|
||||
currentId.value = newVal.id;
|
||||
saveStatus.value = '';
|
||||
}
|
||||
}, { deep: true });
|
||||
|
||||
Reference in New Issue
Block a user