feat(LivePreviewEditor): 为目录侧边栏添加宽度拖拽调整功能
添加目录侧边栏的宽度拖拽调整支持,提升用户自定义布局的灵活性。 - 新增拖拽手柄组件,用户可调整侧边栏宽度(150px-600px范围) - 拖拽时提供视觉反馈(光标变化、手柄高亮) - 移除侧边栏固定宽度样式,改为JS动态控制 - 拖拽期间禁用过渡动画以避免视觉延迟
This commit is contained in:
parent
e1c97dd8f0
commit
2c6b978d8c
|
|
@ -4,8 +4,12 @@
|
||||||
|
|
||||||
<div class="editor-main">
|
<div class="editor-main">
|
||||||
<!-- 目录大纲侧边栏 -->
|
<!-- 目录大纲侧边栏 -->
|
||||||
<div class="toc-sidebar" :class="{ collapsed: !showToc }">
|
<div
|
||||||
<div class="toc-content">
|
class="toc-sidebar"
|
||||||
|
:class="{ collapsed: !showToc, resizing: isResizing }"
|
||||||
|
:style="{ width: showToc ? `${tocWidth}px` : '0px' }"
|
||||||
|
>
|
||||||
|
<div class="toc-content" :style="{ width: `${tocWidth}px` }">
|
||||||
<div v-if="toc.length === 0" class="toc-empty">暂无大纲</div>
|
<div v-if="toc.length === 0" class="toc-empty">暂无大纲</div>
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in toc"
|
v-for="(item, index) in toc"
|
||||||
|
|
@ -20,6 +24,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 拖拽手柄 -->
|
||||||
|
<div
|
||||||
|
v-if="showToc"
|
||||||
|
class="toc-resizer"
|
||||||
|
@mousedown.prevent="startResize"
|
||||||
|
></div>
|
||||||
|
|
||||||
<codemirror
|
<codemirror
|
||||||
v-model="code"
|
v-model="code"
|
||||||
placeholder="请输入 Markdown 内容..."
|
placeholder="请输入 Markdown 内容..."
|
||||||
|
|
@ -84,6 +95,36 @@ const { applyHeading, wrapSelection, prefixLines, insertLink } = useMarkdownActi
|
||||||
// 目录相关
|
// 目录相关
|
||||||
const showToc = ref(true);
|
const showToc = ref(true);
|
||||||
const toc = shallowRef([]);
|
const toc = shallowRef([]);
|
||||||
|
const tocWidth = ref(220);
|
||||||
|
const isResizing = ref(false);
|
||||||
|
let startX = 0;
|
||||||
|
let startWidth = 0;
|
||||||
|
|
||||||
|
const startResize = (e) => {
|
||||||
|
isResizing.value = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startWidth = tocWidth.value;
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', handleResize);
|
||||||
|
document.addEventListener('mouseup', stopResize);
|
||||||
|
document.body.style.cursor = 'col-resize';
|
||||||
|
document.body.style.userSelect = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResize = (e) => {
|
||||||
|
if (!isResizing.value) return;
|
||||||
|
const diff = e.clientX - startX;
|
||||||
|
const newWidth = Math.max(150, Math.min(600, startWidth + diff));
|
||||||
|
tocWidth.value = newWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopResize = () => {
|
||||||
|
isResizing.value = false;
|
||||||
|
document.removeEventListener('mousemove', handleResize);
|
||||||
|
document.removeEventListener('mouseup', stopResize);
|
||||||
|
document.body.style.cursor = '';
|
||||||
|
document.body.style.userSelect = '';
|
||||||
|
};
|
||||||
|
|
||||||
const debounce = (fn, delay) => {
|
const debounce = (fn, delay) => {
|
||||||
let timer = null;
|
let timer = null;
|
||||||
|
|
@ -480,16 +521,20 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc-sidebar {
|
.toc-sidebar {
|
||||||
width: 220px;
|
/* width: 220px; 由 JS 控制 */
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-right: 1px solid var(--border-color);
|
border-right: 1px solid var(--border-color);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
background-color: var(--card-background);
|
background-color: var(--card-background);
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toc-sidebar.resizing {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
.toc-sidebar.collapsed {
|
.toc-sidebar.collapsed {
|
||||||
width: 0;
|
width: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
@ -497,10 +542,25 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc-content {
|
.toc-content {
|
||||||
width: 220px; /* 固定宽度,防止内容挤压 */
|
/* width: 220px; 由 JS 控制 */
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toc-resizer {
|
||||||
|
width: 4px;
|
||||||
|
cursor: col-resize;
|
||||||
|
background-color: transparent;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: -1px; /* 重叠边框 */
|
||||||
|
z-index: 10;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-resizer:hover, .toc-sidebar.resizing + .toc-resizer {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
.toc-empty {
|
.toc-empty {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue