完善编辑功能

This commit is contained in:
2026-07-20 17:47:24 +08:00
parent f725261370
commit 05898a14e0
4 changed files with 41 additions and 104 deletions

View File

@@ -60,9 +60,9 @@
"updatedAt": 1783674217586 "updatedAt": 1783674217586
}, },
"topic_20260710-090003_60717bd90da5ec68": { "topic_20260710-090003_60717bd90da5ec68": {
"stage": 1, "stage": 3,
"userTurns": 1, "userTurns": 3,
"basisHash": "cd60850543a6985b", "basisHash": "de6e56c00454cedd",
"updatedAt": 1783700550206 "updatedAt": 1784540365461
} }
} }

View File

@@ -50,5 +50,5 @@
"topic_20260710-034724_3c6fb29d8d0a4119": "目录树中鼠标悬浮弹框dropmenu…", "topic_20260710-034724_3c6fb29d8d0a4119": "目录树中鼠标悬浮弹框dropmenu…",
"topic_20260710-054543_5e7c427932aecd7b": "现在在markdown的编辑区域中…", "topic_20260710-054543_5e7c427932aecd7b": "现在在markdown的编辑区域中…",
"topic_20260710-063053_353d579222d3b70b": "这个应用里有一个全局搜索的功能,帮我…", "topic_20260710-063053_353d579222d3b70b": "这个应用里有一个全局搜索的功能,帮我…",
"topic_20260710-090003_60717bd90da5ec68": "当用户在markdownEditor…" "topic_20260710-090003_60717bd90da5ec68": "D:\\code\\yurou\\src…"
} }

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref, watch, onMounted, onUnmounted, nextTick} from "vue"; import {computed, ref, onMounted, onUnmounted, nextTick} from "vue";
import {listen, type EventCallback, type EventName} from "@tauri-apps/api/event"; import {listen, type EventCallback, type EventName} from "@tauri-apps/api/event";
import {convertFileSrc, invoke, isTauri} from "@tauri-apps/api/core"; import {convertFileSrc, invoke, isTauri} from "@tauri-apps/api/core";
import {getCurrentWindow, type CloseRequestedEvent} from "@tauri-apps/api/window"; import {getCurrentWindow, type CloseRequestedEvent} from "@tauri-apps/api/window";
@@ -186,7 +186,7 @@ const WORKSPACE_REFRESH_DELAY_MS = 140;
let workspaceRefreshTimer: ReturnType<typeof setTimeout> | null = null; let workspaceRefreshTimer: ReturnType<typeof setTimeout> | null = null;
let workspaceRefreshInFlight = false; let workspaceRefreshInFlight = false;
let workspaceRefreshQueued = false; let workspaceRefreshQueued = false;
let syncInFlight = false; const syncInFlight = ref(false);
let fileOpenRequest = 0; let fileOpenRequest = 0;
let saveNoticeTimer: ReturnType<typeof setTimeout> | null = null; let saveNoticeTimer: ReturnType<typeof setTimeout> | null = null;
let closeSaveInFlight = false; let closeSaveInFlight = false;
@@ -247,7 +247,6 @@ const documentMode = ref<DocumentMode>(defaultDocumentMode.value);
const appTheme = ref<AppTheme>(loadAppTheme()); const appTheme = ref<AppTheme>(loadAppTheme());
const renderSingleLineBreaks = ref(loadRenderSingleLineBreaks()); const renderSingleLineBreaks = ref(loadRenderSingleLineBreaks());
const paragraphIndent = ref(loadParagraphIndent()); const paragraphIndent = ref(loadParagraphIndent());
const showMoreMenu = ref(false);
const settingsRequest = ref(0); const settingsRequest = ref(0);
const settingsInitialPage = ref<"about" | "editor" | "sync">("about"); const settingsInitialPage = ref<"about" | "editor" | "sync">("about");
const markdownEditorRef = ref<{ flushPendingChanges: () => string | undefined; scrollToLine: (line: number) => void } | null>(null); const markdownEditorRef = ref<{ flushPendingChanges: () => string | undefined; scrollToLine: (line: number) => void } | null>(null);
@@ -398,23 +397,6 @@ function updateAppTheme(theme: AppTheme) {
saveAppTheme(theme); saveAppTheme(theme);
} }
function handleExportPdf() {
showMoreMenu.value = false;
// TODO: implement PDF export
}
function closeMoreMenu() {
showMoreMenu.value = false;
}
watch(showMoreMenu, (val) => {
if (val) {
setTimeout(() => {
document.addEventListener("click", closeMoreMenu, {once: true});
}, 0);
}
});
onMounted(() => { onMounted(() => {
applyAppTheme(appTheme.value); applyAppTheme(appTheme.value);
applyZoomCssVars(); applyZoomCssVars();
@@ -503,7 +485,6 @@ function resetMarkdownEditorState() {
saveStatus.value = ""; saveStatus.value = "";
showOutline.value = false; showOutline.value = false;
documentMode.value = defaultDocumentMode.value; documentMode.value = defaultDocumentMode.value;
showMoreMenu.value = false;
editorResetKey.value += 1; editorResetKey.value += 1;
} }
@@ -798,36 +779,41 @@ async function refreshCurrentOpenFileFromDisk() {
} }
async function runConfiguredSync(trigger: "startup" | "save" | "manual") { async function runConfiguredSync(trigger: "startup" | "save" | "manual") {
if (!folderPath.value || syncInFlight) return false; if (!folderPath.value || syncInFlight.value) return false;
const config = await loadSyncConfig(); const config = await loadSyncConfig();
if (trigger !== "manual" && !shouldRunSync(config, trigger)) return false; if (trigger !== "manual" && !shouldRunSync(config, trigger)) return false;
if (!config || config.provider === "none") return false; if (!config || config.provider === "none") {
if (trigger === "manual") {
showSaveNotice("请先在设置中配置同步方式", "error");
}
return false;
}
if (trigger !== "save") { if (trigger !== "save") {
const saved = await saveCurrentWorkspaceDocumentBeforeSwitch(); const saved = await saveCurrentWorkspaceDocumentBeforeSwitch();
if (!saved) return false; if (!saved) return false;
} }
syncInFlight = true; syncInFlight.value = true;
showSaveNotice("正在同步...", "success");
try { try {
const result = await invoke<SyncResult>("sync_now", { const result = await invoke<SyncResult>("sync_now", {
workspace: folderPath.value, workspace: folderPath.value,
config, config,
}); });
saveStatus.value = describeSyncResult(result); saveStatus.value = describeSyncResult(result);
setTimeout(() => { showSaveNotice(describeSyncResult(result), "success");
saveStatus.value = "";
}, 3600);
await refreshWorkspaceTree(); await refreshWorkspaceTree();
await refreshCurrentOpenFileFromDisk(); await refreshCurrentOpenFileFromDisk();
return true; return true;
} catch (e) { } catch (e) {
console.error("Sync failed:", e); console.error("Sync failed:", e);
saveStatus.value = `同步失败: ${e}`; saveStatus.value = `同步失败: ${e}`;
showSaveNotice(`同步失败: ${e}`, "error");
return false; return false;
} finally { } finally {
syncInFlight = false; syncInFlight.value = false;
} }
} }
@@ -915,7 +901,6 @@ function openMediaPreview(path: string, fileType: Extract<WorkspaceFileType, "im
hasDraftDocument.value = false; hasDraftDocument.value = false;
saveStatus.value = ""; saveStatus.value = "";
showOutline.value = false; showOutline.value = false;
showMoreMenu.value = false;
editorResetKey.value += 1; editorResetKey.value += 1;
} }
@@ -1380,34 +1365,16 @@ async function handleSearchResultClick(filePath: string, lineNumber: number) {
</button> </button>
</div> </div>
<!-- More menu --> <!-- Sync button -->
<div v-if="isMarkdownDocument" class="relative"> <button
<button v-if="folderPath"
class="toolbar-btn" class="toolbar-btn"
title="更多" :title="syncInFlight ? '正在同步...' : '同步工作目录'"
@click="showMoreMenu = !showMoreMenu" :disabled="syncInFlight"
> @click="runConfiguredSync('manual')"
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"> >
<circle cx="3" cy="8" r="1.5"/> <i :class="syncInFlight ? 'ri-loader-4-line sync-spinning' : 'ri-loop-left-line'"></i>
<circle cx="8" cy="8" r="1.5"/> </button>
<circle cx="13" cy="8" r="1.5"/>
</svg>
</button>
<div
v-if="showMoreMenu"
class="dropdown-menu"
@click.stop
>
<button class="dropdown-item" @click="handleExportPdf">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M5 1h6l4 4v9a1 1 0 01-1 1H2a1 1 0 01-1-1V2a1 1 0 011-1z"/>
<path d="M5 1v4h4"/>
<path d="M5 9h6M5 12h4"/>
</svg>
导出 PDF
</button>
</div>
</div>
</div> </div>
<!-- Window controls --> <!-- Window controls -->
@@ -1872,38 +1839,6 @@ async function handleSearchResultClick(filePath: string, lineNumber: number) {
box-shadow: 0 1px 3px var(--app-shadow); box-shadow: 0 1px 3px var(--app-shadow);
} }
.dropdown-menu {
position: absolute;
right: 0;
top: 100%;
margin-top: 4px;
background: var(--app-surface);
border: 1px solid var(--app-border-strong);
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
min-width: 140px;
padding: 4px;
z-index: 50;
}
.dropdown-item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 6px 10px;
border-radius: 6px;
font-size: 13px;
color: var(--app-text);
background: transparent;
border: none;
cursor: pointer;
text-align: left;
}
.dropdown-item:hover {
background-color: var(--app-surface-muted);
}
.start-actions { .start-actions {
background: background:
@@ -1934,4 +1869,15 @@ async function handleSearchResultClick(filePath: string, lineNumber: number) {
text-decoration-color: currentColor; text-decoration-color: currentColor;
outline: none; outline: none;
} }
/* Sync button spin animation */
@keyframes sync-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.sync-spinning {
display: inline-block;
animation: sync-spin 1s linear infinite;
}
</style> </style>

View File

@@ -1148,16 +1148,7 @@ defineExpose({
.markdown-preview h3 { font-size: 1.125em; } .markdown-preview h3 { font-size: 1.125em; }
.markdown-preview h4 { font-size: 1em; } .markdown-preview h4 { font-size: 1em; }
.markdown-preview p { margin: 0; text-indent: var(--paragraph-indent, 0); } .markdown-preview p { margin: 0.8em 0; text-indent: var(--paragraph-indent, 0); }
.markdown-preview:not(.markdown-mixed) > p + p,
.markdown-mixed
> .markdown-block-paragraph
+ .markdown-block-paragraph
> .markdown-block-preview
> p {
margin-top: 0;
}
.markdown-preview strong { font-weight: 700; color: var(--app-text-strong); } .markdown-preview strong { font-weight: 700; color: var(--app-text-strong); }
.markdown-preview em { font-style: italic; } .markdown-preview em { font-style: italic; }