diff --git a/frontend/bindings/voidraft/internal/models/models.ts b/frontend/bindings/voidraft/internal/models/models.ts index 177ee6f..feea55a 100644 --- a/frontend/bindings/voidraft/internal/models/models.ts +++ b/frontend/bindings/voidraft/internal/models/models.ts @@ -486,6 +486,11 @@ export class GeneralConfig { */ "enableLoadingAnimation": boolean; + /** + * 是否启用标签页模式 + */ + "enableTabs": boolean; + /** Creates a new GeneralConfig instance. */ constructor($$source: Partial = {}) { if (!("alwaysOnTop" in $$source)) { @@ -512,6 +517,9 @@ export class GeneralConfig { if (!("enableLoadingAnimation" in $$source)) { this["enableLoadingAnimation"] = false; } + if (!("enableTabs" in $$source)) { + this["enableTabs"] = false; + } Object.assign(this, $$source); } diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 1a3bf4b..4d5a1fa 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -17,6 +17,7 @@ declare module 'vue' { RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] TabContainer: typeof import('./src/components/tabs/TabContainer.vue')['default'] + TabContextMenu: typeof import('./src/components/tabs/TabContextMenu.vue')['default'] TabItem: typeof import('./src/components/tabs/TabItem.vue')['default'] Toolbar: typeof import('./src/components/toolbar/Toolbar.vue')['default'] WindowsTitleBar: typeof import('./src/components/titlebar/WindowsTitleBar.vue')['default'] diff --git a/frontend/src/assets/styles/variables.css b/frontend/src/assets/styles/variables.css index 4ca9c89..eb7d3a0 100644 --- a/frontend/src/assets/styles/variables.css +++ b/frontend/src/assets/styles/variables.css @@ -8,6 +8,7 @@ --dark-toolbar-text: #ffffff; --dark-toolbar-text-secondary: #cccccc; --dark-toolbar-button-hover: #404040; + --dark-tab-active-line: linear-gradient(90deg, #007acc 0%, #0099ff 100%); --dark-bg-secondary: #0E1217; --dark-text-secondary: #a0aec0; --dark-text-muted: #666; @@ -40,6 +41,7 @@ --light-toolbar-text: #212529; --light-toolbar-text-secondary: #495057; --light-toolbar-button-hover: #e9ecef; + --light-tab-active-line: linear-gradient(90deg, #0066cc 0%, #0088ff 100%); --light-bg-secondary: #f7fef7; --light-text-secondary: #374151; --light-text-muted: #6b7280; @@ -73,6 +75,7 @@ --toolbar-text-secondary: var(--dark-toolbar-text-secondary); --toolbar-button-hover: var(--dark-toolbar-button-hover); --toolbar-separator: var(--dark-toolbar-button-hover); + --tab-active-line: var(--dark-tab-active-line); --bg-secondary: var(--dark-bg-secondary); --text-secondary: var(--dark-text-secondary); --text-muted: var(--dark-text-muted); @@ -112,6 +115,7 @@ --toolbar-text-secondary: var(--dark-toolbar-text-secondary); --toolbar-button-hover: var(--dark-toolbar-button-hover); --toolbar-separator: var(--dark-toolbar-button-hover); + --tab-active-line: var(--dark-tab-active-line); --bg-secondary: var(--dark-bg-secondary); --text-secondary: var(--dark-text-secondary); --text-muted: var(--dark-text-muted); @@ -149,6 +153,7 @@ --toolbar-text-secondary: var(--light-toolbar-text-secondary); --toolbar-button-hover: var(--light-toolbar-button-hover); --toolbar-separator: var(--light-toolbar-button-hover); + --tab-active-line: var(--light-tab-active-line); --bg-secondary: var(--light-bg-secondary); --text-secondary: var(--light-text-secondary); --text-muted: var(--light-text-muted); @@ -185,6 +190,7 @@ --toolbar-text-secondary: var(--light-toolbar-text-secondary); --toolbar-button-hover: var(--light-toolbar-button-hover); --toolbar-separator: var(--light-toolbar-button-hover); + --tab-active-line: var(--light-tab-active-line); --bg-secondary: var(--light-bg-secondary); --text-secondary: var(--light-text-secondary); --text-muted: var(--light-text-muted); @@ -220,6 +226,7 @@ --toolbar-text-secondary: var(--dark-toolbar-text-secondary); --toolbar-button-hover: var(--dark-toolbar-button-hover); --toolbar-separator: var(--dark-toolbar-button-hover); + --tab-active-line: var(--dark-tab-active-line); --bg-secondary: var(--dark-bg-secondary); --text-secondary: var(--dark-text-secondary); --text-muted: var(--dark-text-muted); diff --git a/frontend/src/common/constant/config.ts b/frontend/src/common/constant/config.ts index fae20ad..09806ea 100644 --- a/frontend/src/common/constant/config.ts +++ b/frontend/src/common/constant/config.ts @@ -46,6 +46,7 @@ export const GENERAL_CONFIG_KEY_MAP: GeneralConfigKeyMap = { globalHotkey: 'general.globalHotkey', enableWindowSnap: 'general.enableWindowSnap', enableLoadingAnimation: 'general.enableLoadingAnimation', + enableTabs: 'general.enableTabs', } as const; export const EDITING_CONFIG_KEY_MAP: EditingConfigKeyMap = { @@ -113,6 +114,7 @@ export const DEFAULT_CONFIG: AppConfig = { }, enableWindowSnap: true, enableLoadingAnimation: true, + enableTabs: false, }, editing: { fontSize: CONFIG_LIMITS.fontSize.default, diff --git a/frontend/src/common/constant/editor.ts b/frontend/src/common/constant/editor.ts index 3ee6455..1616f7e 100644 --- a/frontend/src/common/constant/editor.ts +++ b/frontend/src/common/constant/editor.ts @@ -9,5 +9,5 @@ export const EDITOR_CONFIG = { /** 语法树缓存过期时间(毫秒) */ SYNTAX_TREE_CACHE_TIMEOUT: 30000, /** 加载状态延迟时间(毫秒) */ - LOADING_DELAY: 800, + LOADING_DELAY: 500, } as const; \ No newline at end of file diff --git a/frontend/src/components/tabs/TabContainer.vue b/frontend/src/components/tabs/TabContainer.vue index 66846b7..363794b 100644 --- a/frontend/src/components/tabs/TabContainer.vue +++ b/frontend/src/components/tabs/TabContainer.vue @@ -1,127 +1,105 @@ + + \ No newline at end of file diff --git a/frontend/src/components/tabs/TabItem.vue b/frontend/src/components/tabs/TabItem.vue index ef8fe29..5d73d92 100644 --- a/frontend/src/components/tabs/TabItem.vue +++ b/frontend/src/components/tabs/TabItem.vue @@ -39,9 +39,10 @@
(); // 组件事件 const emit = defineEmits<{ - click: [tabId: number]; - close: [tabId: number]; - dragstart: [event: DragEvent, tabId: number]; + click: [documentId: number]; + close: [documentId: number]; + dragstart: [event: DragEvent, documentId: number]; dragover: [event: DragEvent]; - drop: [event: DragEvent, tabId: number]; - contextmenu: [event: MouseEvent, tabId: number]; + drop: [event: DragEvent, documentId: number]; + contextmenu: [event: MouseEvent, documentId: number]; }>(); // 组件状态 @@ -103,16 +102,16 @@ const displayTitle = computed(() => { // 事件处理 const handleClick = () => { - emit('click', props.tab.id); + emit('click', props.tab.documentId); }; const handleClose = () => { - emit('close', props.tab.id); + emit('close', props.tab.documentId); }; const handleDragStart = (event: DragEvent) => { isDragging.value = true; - emit('dragstart', event, props.tab.id); + emit('dragstart', event, props.tab.documentId); }; const handleDragOver = (event: DragEvent) => { @@ -121,7 +120,7 @@ const handleDragOver = (event: DragEvent) => { const handleDrop = (event: DragEvent) => { isDragging.value = false; - emit('drop', event, props.tab.id); + emit('drop', event, props.tab.documentId); }; const handleDragEnd = () => { @@ -130,7 +129,7 @@ const handleDragEnd = () => { const handleContextMenu = (event: MouseEvent) => { event.preventDefault(); - emit('contextmenu', event, props.tab.id); + emit('contextmenu', event, props.tab.documentId); }; @@ -159,14 +158,36 @@ const handleContextMenu = (event: MouseEvent) => { } &.active { - background-color: var(--toolbar-button-hover); - border-bottom: 2px solid var(--accent-color); + background-color: var(--toolbar-button-active, var(--toolbar-button-hover)); color: var(--toolbar-text); - + position: relative; .tab-title { color: var(--toolbar-text); - /* 不用加粗,避免抖动 */ + font-weight: 600; /* 字体加粗 */ + text-shadow: 0 0 1px rgba(0, 0, 0, 0.1); /* 轻微阴影增强可读性 */ } + + .tab-icon { + color: var(--accent-color); + filter: brightness(1.1); + } + } + + /* 底部活跃线条 */ + &::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: var(--tab-active-line); + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 10; + } + + &.active::after { + width: 100%; } &.dragging { diff --git a/frontend/src/components/titlebar/WindowsTitleBar.vue b/frontend/src/components/titlebar/WindowsTitleBar.vue index 6e00b7f..bf216b1 100644 --- a/frontend/src/components/titlebar/WindowsTitleBar.vue +++ b/frontend/src/components/titlebar/WindowsTitleBar.vue @@ -4,11 +4,13 @@
voidraft
- +
{{ titleText }}
-
+
+ +
{{ titleText }}
@@ -42,19 +44,29 @@