Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 121 additions & 28 deletions src/components/Editor/MdEditor.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
<template>
<div class="md-editor-container" :class="{ 'hide-toolbar': hideToolbar }">
<div class="toolbar-hover-area" @mouseenter="showToolbar = true" @mouseleave="showToolbar = false">
<MdEditor
v-model="content"
:theme="theme"
:toolbars="toolbars"
:preview="'live'"
:language="language"
@onSave="handleSave"
@onUploadImg="handleUploadImg"
@onChange="handleChange"
@onError="handleError"
ref="mdEditorRef"
/>
</div>
<div
class="toolbar-trigger-area"
@mouseenter="showToolbar = true"
@mouseleave="showToolbar = false"
:class="{ 'inactive': showToolbar }"
></div>
<div
class="catalog-trigger-area"
@mouseenter="showCatalog = true"
@mouseleave="showCatalog = false"
:class="{ 'inactive': showCatalog }"
></div>

<MdEditor
v-model="content"
:theme="theme"
:toolbars="toolbars"
:preview="'live'"
:language="language"
:style="editorStyle"
@onSave="handleSave"
@onUploadImg="handleUploadImg"
@onChange="handleChange"
@onError="handleError"
ref="mdEditorRef"
/>
</div>
</template>

<script>
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import { ref, onMounted, watch } from 'vue';
import { ref, onMounted, watch, computed } from 'vue';
import axios from 'axios';

export default {
Expand Down Expand Up @@ -56,6 +68,10 @@ export default {
hideToolbar: {
type: Boolean,
default: false,
},
fullHeight: {
type: Boolean,
default: true,
}
},
emits: ['update:modelValue', 'save', 'change', 'error'],
Expand All @@ -65,6 +81,19 @@ export default {
const updating = ref(false);
const preview = ref(true);
const showToolbar = ref(false);
const showCatalog = ref(false);

// 计算编辑器样式,设置高度
const editorStyle = computed(() => {
if (props.fullHeight) {
return {
height: 'calc(100vh - 100px)',
};
}
return {
height: `${props.height}px`,
};
});

// 配置工具栏
const toolbars = [
Expand Down Expand Up @@ -184,6 +213,8 @@ export default {
mdEditorRef,
updating,
showToolbar,
showCatalog,
editorStyle,
handleChange,
handleError,
handleSave,
Expand All @@ -198,63 +229,125 @@ export default {
<style scoped>
.md-editor-container {
width: 100%;
margin: 0 auto;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
}

/* 添加悬停感应区域 */
.toolbar-hover-area {
position: relative;
width: 100%;
/* 增大触发区域 */
.toolbar-trigger-area {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 25px; /* 从15px增加到25px */
z-index: 20; /* 提高z-index确保可以触发 */
cursor: pointer;
/* 当触发区域处于活动状态时改变指针样式,表示它现在只是显示而不可交互 */
pointer-events: auto;
transition: opacity 0.3s ease;
}

.catalog-trigger-area {
position: absolute;
top: 46px; /* 工具栏高度后 */
right: 0;
width: 30px;
bottom: 0;
z-index: 20; /* 提高z-index确保可以触发 */
cursor: pointer;
pointer-events: auto;
transition: opacity 0.3s ease;
}

/* 当工具栏或目录显示时,使触发区域不再捕获鼠标事件 */
.toolbar-trigger-area.inactive,
.catalog-trigger-area.inactive {
pointer-events: none;
opacity: 0;
}

/* 设置编辑区和预览区比例为5:3 */
:deep(.md-editor-content) {
display: flex;
height: 100%;
margin-top: 46px !important; /* 为工具栏预留空间 */
}

:deep(.md-editor-content .md-editor-input-wrapper) {
flex: 5;
height: 100%;
}

:deep(.md-editor-content .md-editor-preview-wrapper) {
flex: 3;
height: 100%;
}

/* 改进工具栏隐藏/显示机制,防止内容区域移动 */
/* 设置编辑器填满容器 */
:deep(.md-editor) {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
}

/* 重新设置工具栏的样式 */
:deep(.md-editor-toolbar) {
transition: opacity 0.3s ease, transform 0.3s ease;
position: absolute;
position: absolute !important; /* 改回绝对定位 */
top: 0;
left: 0;
right: 0;
z-index: 2;
height: 46px;
z-index: 10;
background-color: var(--md-bk-color, #fff);
}

/* 工具栏在悬停触发区域时显示 */
.hide-toolbar :deep(.md-editor-toolbar) {
opacity: 0;
visibility: hidden;
transform: translateY(-100%);
pointer-events: none;
}

.hide-toolbar .toolbar-hover-area:hover :deep(.md-editor-toolbar),
.hide-toolbar:hover .toolbar-trigger-area:not(.inactive) ~ :deep(.md-editor) .md-editor-toolbar,
.hide-toolbar .toolbar-trigger-area:hover ~ :deep(.md-editor) .md-editor-toolbar,
.hide-toolbar :deep(.md-editor-toolbar:hover) {
opacity: 1;
visibility: visible;
transform: translateY(0);
pointer-events: auto;
/* 确保工具栏在显示时的z-index高于触发区域 */
z-index: 30;
}

/* 确保内容区域不会移动 */
:deep(.md-editor-content) {
padding-top: 0 !important; /* 重要:覆盖原来可能的内边距 */
margin-top: 46px; /* 给工具栏预留空间,这个高度根据实际工具栏高度调整 */
/* 目录悬停显示 */
:deep(.md-editor-catalog) {
transition: opacity 0.3s ease, transform 0.3s ease;
opacity: 0;
visibility: hidden;
transform: translateX(100%);
}

:deep(.md-editor-catalog):hover,
.catalog-trigger-area:not(.inactive):hover ~ :deep(.md-editor) .md-editor-catalog {
opacity: 1;
visibility: visible;
transform: translateX(0);
/* 确保目录在显示时的z-index高于触发区域 */
z-index: 30;
}

/* 触发区域的视觉指示 */
.toolbar-trigger-area:hover {
background-color: rgba(0, 0, 0, 0.05);
}

.catalog-trigger-area:hover {
background-color: rgba(0, 0, 0, 0.05);
}

/* 修复列表样式 */
Expand Down
91 changes: 68 additions & 23 deletions src/views/admin/Note/index.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
<template>
<div>
<CustomMdEditor
v-model="noteContent"
:noteId="$route.params.note_id"
:autoSave="true"
:autoFocus="true"
:height="600"
:hideToolbar="true"
@save="updateNote"
/>
<div class="note-page flex flex-col h-screen">
<!-- 顶部导航栏 -->
<nav class="relative flex flex-wrap items-center justify-between px-2 py-3 navbar-expand-lg bg-emerald-500">
<div class="container px-4 mx-auto flex flex-wrap items-center justify-between">
<div class="w-full relative flex justify-between lg:w-auto px-4 lg:static lg:block lg:justify-start">
<a class="text-lg font-bold leading-relaxed inline-block mr-4 py-2 whitespace-no-wrap uppercase text-white" href="javascript:;">
笔记编辑
</a>
</div>
<div class="lg:flex flex-grow items-center">
<ul class="flex flex-col lg:flex-row list-none ml-auto">
<li class="nav-item">
<a class="px-3 py-2 flex items-center text-xs uppercase font-bold leading-snug text-white hover:opacity-75"
href="javascript:;"
@click="handleSave">
<i class="fas fa-save text-lg leading-lg text-white opacity-75"></i>
<span class="ml-2">保存</span>
</a>
</li>
<li class="nav-item">
<a class="px-3 py-2 flex items-center text-xs uppercase font-bold leading-snug text-white hover:opacity-75"
href="javascript:;"
@click="handleBack">
<i class="fas fa-arrow-left text-lg leading-lg text-white opacity-75"></i>
<span class="ml-2">返回</span>
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- 编辑器主体 -->
<div class="note-container flex-1">
<CustomMdEditor
v-model="noteContent"
:noteId="$route.params.note_id"
:autoSave="true"
:autoFocus="true"
:fullHeight="true"
:hideToolbar="true"
@save="updateNote"
/>
</div>
</div>
</template>

<script>
import { ref, onMounted } from "vue";
import { ref, onMounted, getCurrentInstance } from "vue";
import axios from "axios";
import CustomMdEditor from "@/components/Editor/MdEditor.vue";
import defaultText from "@/components/Editor/config/defaultText";
Expand All @@ -25,7 +58,8 @@ export default {
setup() {
const noteContent = ref("");
const isMobile = ref(false);

const { proxy } = getCurrentInstance();

const fetchNote = async (noteId) => {
try {
const response = await axios.get(`http://localhost:8000/notes/${noteId}`);
Expand All @@ -37,33 +71,35 @@ export default {

const updateNote = async (content) => {
try {
const noteId = this.$route.params.note_id;
const noteId = proxy.$route.params.note_id;
await axios.put(`http://localhost:8000/notes/${noteId}`, {
content,
});
this.$message.success("笔记已更新");
proxy.$message.success("笔记已更新");
} catch (e) {
this.$message.error("更新失败");
proxy.$message.error("更新失败");
console.error(e);
}
};

const handleSave = () => {
updateNote(noteContent.value);
};

const handleBack = () => {
proxy.$router.back();
};

onMounted(() => {
// 检测是否为移动设备
isMobile.value = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
);

// 获取路由参数中的笔记ID
const noteId = window.location.pathname.split("/").pop();
if (noteId) {
fetchNote(noteId);
} else {
// 如果没有笔记ID,则使用默认文本
noteContent.value = defaultText;
}

// 设置默认文本
if (!noteContent.value.trim()) {
noteContent.value = defaultText;
}
Expand All @@ -72,12 +108,21 @@ export default {
return {
noteContent,
isMobile,
updateNote
updateNote,
handleSave,
handleBack
};
},
};
</script>

<style scoped>
/* 添加页面样式 */
.note-container {
width: 100%;
height: calc(100vh - 60px); /* 减去顶部导航栏的高度 */
position: relative;
display: flex;
flex-direction: column;
overflow: hidden;
}
</style>