2025-04-30 01:22:13 +08:00

178 lines
5.7 KiB
Vue

<template>
<Split v-model="split" min="5px">
<template #left>
<div class="split-left" :class="hiddenSplitLeft">
<LeftSidebar v-model:rootFolderPath="rootFolderPath" v-model:currentFilePath="currentFilePath">
</LeftSidebar>
</div>
</template>
<template #trigger>
<div ref="splitTrigger" class="split-trigger"></div>
</template>
<template #right>
<div ref="splitRight" class="split-right">
<MainEditor ref="mainEditor" v-model:markdownCode="markdownCode"
v-model:leftSidebarState="leftSidebarState" :save="writeFileContent">
</MainEditor>
</div>
</template>
</Split>
</template>
<script setup>
import LeftSidebar from './components/LeftSidebar.vue'
import MainEditor from './components/MainEditor.vue'
import { ref, watch, useTemplateRef, nextTick, onMounted, onUnmounted } from "vue";
import { invoke } from "@tauri-apps/api/core";
import { readTextFile, writeTextFile, exists } from '@tauri-apps/plugin-fs';
import { Message } from 'view-ui-plus'
// 原始示例数据,仅供参考
const greetMsg = ref("");
const name = ref("");
async function greet() {
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
greetMsg.value = await invoke("greet", { name: name.value });
}
// 使用 Split 组件动态控制页面左右布局
const defaultSplit = 0.15; // 默认左侧边栏和右侧区域的比例为 1.5:8.5
const split = ref(defaultSplit);
const splitTrigger = useTemplateRef('splitTrigger');
const splitRight = useTemplateRef('splitRight');
const splitRightWidth = ref(0);
const hiddenSplitLeft = ref('');
const leftSidebarState = ref('open');
watch(split, (newSplit) => {
splitRightWidth.value = splitRight.value.offsetWidth;
// 当 split 小于某个值时,隐藏左边布局
if (newSplit < 0.05) {
leftSidebarState.value = 'close';
if (hiddenSplitLeft.value == '') {
hiddenSplitLeft.value = 'hidden';
}
} else {
leftSidebarState.value = 'open';
if (hiddenSplitLeft.value == 'hidden') {
hiddenSplitLeft.value = '';
}
}
})
watch(leftSidebarState, (state) => {
if (state == 'open') {
split.value = defaultSplit;
} else {
split.value = 0;
}
})
// 监听根目录路径。如果之前已经选过目录,初始界面直接加载该目录
const rootFolderPath = ref(localStorage.getItem('rootPathOfFolderTree') || "");
watch(rootFolderPath, (newRootPath) => {
localStorage.setItem('rootPathOfFolderTree', newRootPath);
})
// <FolderTree> 完成文件树渲染后,点击文件得到 currentFilePath
const currentFilePath = ref(localStorage.getItem('currentSelectedFilePath') || "");
// 读取 currentFilePath 的文件内容,填充到 MarkdownEditor 之中
watch(currentFilePath, async (newFilePath) => {
localStorage.setItem('currentSelectedFilePath', newFilePath);
await readFileContent(newFilePath);
})
const mainEditor = ref(null);
const markdownCode = ref("# Hello Markdown");
watch(markdownCode, (newMarkdownCode) => {
console.log("code be updated");
})
async function readFileContent(filePath) {
try {
let fileContent = '';
if (window.__TAURI_INTERNALS__ === undefined) {
fileContent = "# this is " + filePath;
} else {
fileContent = await readTextFile(filePath);
}
markdownCode.value = fileContent;
currentFilePath.value = filePath;
Message.success('已读取文件内容,并加载到编辑器中:' + filePath);
} catch (err) {
Message.error('文件读取失败:' + err);
}
}
async function writeFileContent(markdownCode) {
// 如果未选择任何文件,则不触发写文件事件
if (currentFilePath.value.length == 0) {
Message.warning('当前编辑器暂未关联目标文件,请在左侧打开目录并选择一个文件')
return;
}
try {
if (window.__TAURI_INTERNALS__ === undefined) {
// TODO 调用接口更新 currentFilePath 文件内容
Message.warning('暂未实现接口');
} else {
const exist = await exists(currentFilePath.value);
if (!exist) {
throw new Error(currentFilePath.value + " 文件不存在.");
}
await writeTextFile(currentFilePath.value, markdownCode);
}
Message.success('文件更新成功:' + currentFilePath.value);
} catch (err) {
Message.error('文件更新失败:' + err);
}
}
const resizeHandler = () => {
// splitRight 的宽度为视口宽度右边百分比,再减去 splitTrigger 的宽度
splitRightWidth.value = window.innerWidth * (1 - split.value) - splitTrigger.value.offsetWidth;
}
onMounted(async () => {
// 使用 nextTick 确保页面组件已完成挂载和渲染
await nextTick();
resizeHandler();
// 监听页面宽度和高度,调整 markdown 编辑器的高宽度
window.addEventListener('resize', resizeHandler);
// 打开上一次选择的文件的内容
if (currentFilePath.value.length > 0) {
await readFileContent(currentFilePath.value);
}
});
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler);
});
</script>
<style>
body {
overflow: hidden;
/* 禁用所有方向滚动 */
}
.split-left {
height: 100vh;
border-right: 1px solid #dcdee2;
overflow-x: hidden;
overflow-y: auto;
scrollbar-width: none;
}
.split-trigger {
width: 2px;
height: 100vh;
background-color: #e6e6e6;
}
.split-trigger:hover {
cursor: ew-resize;
}
.split-right {
margin-left: 2px;
}
.hidden {
display: none;
}
</style>