fix(biji): 优化笔记编辑器自动保存机制并修复相关问题

- 实现了更可靠的自动保存功能,仅在用户停止输入后触发保存操作
- 修复了切换笔记时意外触发自动保存的问题
- 优化了重命名文件后的预览更新逻辑
- 调整了保存成功后的状态清理策略,提高了用户体验
This commit is contained in:
2025-08-14 14:44:27 +08:00
parent 337645f27b
commit 56465ffa75
12 changed files with 198 additions and 82 deletions

View File

@@ -26,4 +26,132 @@ const props = defineProps({
},
});
const emit = defineEmits
const emit = defineEmits(['back', 'update:editData']);
const vditor = ref(null);
const saveStatus = ref('');
let saveTimeout = null;
const isProgrammaticChange = ref(false);
const initVditor = () => {
vditor.value = new Vditor('vditor-editor', {
height: 'calc(100vh - 120px)',
mode: 'ir',
cache: {
enable: false,
},
after: () => {
if (props.editData && props.editData.content) {
isProgrammaticChange.value = true;
vditor.value.setValue(props.editData.content);
isProgrammaticChange.value = false;
}
vditor.value.focus();
},
input: (value) => {
if (isProgrammaticChange.value) {
return;
}
// 启动定时器延迟5秒后执行保存
clearTimeout(saveTimeout);
saveStatus.value = '正在输入...';
saveTimeout = setTimeout(() => {
save(value);
}, 5000);
},
upload: {
accept: 'image/*',
handler(files) {
const file = files; // 必须是 File 对象,而不是 FileList
if (!file) return;
const formData = new FormData();
formData.append('file', file); // 字段名必须是 'file'
uploadImage(formData).then(res => {
if (res.code === 200) {
const url = res.data;
// 使用 file.name 替代 files.name 保证一致性
vditor.value.insertValue(`![${file.name}](${url})`);
} else {
ElMessage.error('图片上传失败');
}
}).catch(() => {
ElMessage.error('图片上传失败');
});
},
},
});
};
const save = async (value) => {
clearTimeout(saveTimeout);
const content = typeof value === 'string' ? value : vditor.value.getValue();
try {
saveStatus.value = '正在保存...';
const res = await updateMarkdown({ id: props.editData.id, content: content });
if (res.code === 200) {
saveStatus.value = '已保存';
emit('update:editData', { ...props.editData, content: content });
} else {
saveStatus.value = '保存失败';
ElMessage.error(res.message || '保存失败');
}
} catch (error) {
saveStatus.value = '保存失败';
ElMessage.error('保存失败');
}
};
onMounted(() => {
initVditor();
});
onBeforeUnmount(() => {
clearTimeout(saveTimeout);
if (vditor.value) {
vditor.value.destroy();
}
});
watch(() => props.editData, (newVal, oldVal) => {
if (vditor.value && newVal && newVal.id !== oldVal?.id) {
isProgrammaticChange.value = true;
vditor.value.setValue(newVal.content || '');
isProgrammaticChange.value = false;
saveStatus.value = '';
}
}, { deep: true });
</script>
<style scoped>
.note-editor-wrapper {
height: 100%;
display: flex;
flex-direction: column;
}
.editor-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
border-bottom: 1px solid #dcdfe6;
}
.editor-title {
margin: 0;
font-size: 20px;
}
.actions .save-status {
margin-left: 10px;
color: #909399;
}
.vditor {
flex-grow: 1;
border: none;
}
</style>