✨ Added tab functionality and optimized related configurations
This commit is contained in:
227
frontend/src/stores/tabStore.ts
Normal file
227
frontend/src/stores/tabStore.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
import {defineStore} from 'pinia';
|
||||
import {computed, readonly, ref} from 'vue';
|
||||
import {useConfigStore} from './configStore';
|
||||
import {useDocumentStore} from './documentStore';
|
||||
import type {Document} from '@/../bindings/voidraft/internal/models/models';
|
||||
|
||||
export interface Tab {
|
||||
documentId: number; // 直接使用文档ID作为唯一标识
|
||||
title: string; // 标签页标题
|
||||
}
|
||||
|
||||
export const useTabStore = defineStore('tab', () => {
|
||||
// === 依赖store ===
|
||||
const configStore = useConfigStore();
|
||||
const documentStore = useDocumentStore();
|
||||
|
||||
// === 核心状态 ===
|
||||
const tabsMap = ref<Map<number, Tab>>(new Map());
|
||||
const tabOrder = ref<number[]>([]); // 维护标签页顺序
|
||||
const draggedTabId = ref<number | null>(null);
|
||||
|
||||
// === 计算属性 ===
|
||||
|
||||
const isTabsEnabled = computed(() => configStore.config.general.enableTabs);
|
||||
const canCloseTab = computed(() => tabOrder.value.length > 1);
|
||||
const currentDocumentId = computed(() => documentStore.currentDocumentId);
|
||||
|
||||
// 按顺序返回标签页数组(用于UI渲染)
|
||||
const tabs = computed(() => {
|
||||
return tabOrder.value
|
||||
.map(documentId => tabsMap.value.get(documentId))
|
||||
.filter(tab => tab !== undefined) as Tab[];
|
||||
});
|
||||
|
||||
// === 私有方法 ===
|
||||
const hasTab = (documentId: number): boolean => {
|
||||
return tabsMap.value.has(documentId);
|
||||
};
|
||||
|
||||
const getTab = (documentId: number): Tab | undefined => {
|
||||
return tabsMap.value.get(documentId);
|
||||
};
|
||||
|
||||
const updateTabTitle = (documentId: number, title: string) => {
|
||||
const tab = tabsMap.value.get(documentId);
|
||||
if (tab) {
|
||||
tab.title = title;
|
||||
}
|
||||
};
|
||||
|
||||
// === 公共方法 ===
|
||||
|
||||
/**
|
||||
* 添加或激活标签页
|
||||
*/
|
||||
const addOrActivateTab = (document: Document) => {
|
||||
const documentId = document.id;
|
||||
|
||||
if (hasTab(documentId)) {
|
||||
// 标签页已存在,无需重复添加
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建新标签页
|
||||
const newTab: Tab = {
|
||||
documentId,
|
||||
title: document.title
|
||||
};
|
||||
|
||||
tabsMap.value.set(documentId, newTab);
|
||||
tabOrder.value.push(documentId);
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭标签页
|
||||
*/
|
||||
const closeTab = (documentId: number) => {
|
||||
if (!hasTab(documentId)) return;
|
||||
|
||||
const tabIndex = tabOrder.value.indexOf(documentId);
|
||||
if (tabIndex === -1) return;
|
||||
|
||||
// 从映射和顺序数组中移除
|
||||
tabsMap.value.delete(documentId);
|
||||
tabOrder.value.splice(tabIndex, 1);
|
||||
|
||||
// 如果关闭的是当前文档,需要切换到其他文档
|
||||
if (documentStore.currentDocument?.id === documentId) {
|
||||
// 优先选择下一个标签页,如果没有则选择上一个
|
||||
let nextIndex = tabIndex;
|
||||
if (nextIndex >= tabOrder.value.length) {
|
||||
nextIndex = tabOrder.value.length - 1;
|
||||
}
|
||||
|
||||
if (nextIndex >= 0 && tabOrder.value[nextIndex]) {
|
||||
const nextDocumentId = tabOrder.value[nextIndex];
|
||||
switchToTabAndDocument(nextDocumentId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 切换到指定标签页并打开对应文档
|
||||
*/
|
||||
const switchToTabAndDocument = (documentId: number) => {
|
||||
if (!hasTab(documentId)) return;
|
||||
|
||||
// 如果点击的是当前已激活的文档,不需要重复请求
|
||||
if (documentStore.currentDocumentId === documentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
documentStore.openDocument(documentId);
|
||||
};
|
||||
|
||||
/**
|
||||
* 移动标签页位置
|
||||
*/
|
||||
const moveTab = (fromIndex: number, toIndex: number) => {
|
||||
if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 ||
|
||||
fromIndex >= tabOrder.value.length || toIndex >= tabOrder.value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [movedTab] = tabOrder.value.splice(fromIndex, 1);
|
||||
tabOrder.value.splice(toIndex, 0, movedTab);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取标签页在顺序中的索引
|
||||
*/
|
||||
const getTabIndex = (documentId: number): number => {
|
||||
return tabOrder.value.indexOf(documentId);
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化标签页(当前文档)
|
||||
*/
|
||||
const initializeTab = () => {
|
||||
if (isTabsEnabled.value) {
|
||||
const currentDoc = documentStore.currentDocument;
|
||||
if (currentDoc) {
|
||||
addOrActivateTab(currentDoc);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// === 公共方法 ===
|
||||
|
||||
/**
|
||||
* 关闭其他标签页(除了指定的标签页)
|
||||
*/
|
||||
const closeOtherTabs = (keepDocumentId: number) => {
|
||||
if (!hasTab(keepDocumentId)) return;
|
||||
|
||||
// 获取所有其他标签页的ID
|
||||
const otherTabIds = tabOrder.value.filter(id => id !== keepDocumentId);
|
||||
|
||||
// 关闭其他标签页
|
||||
otherTabIds.forEach(id => closeTab(id));
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭指定标签页右侧的所有标签页
|
||||
*/
|
||||
const closeTabsToRight = (documentId: number) => {
|
||||
const index = getTabIndex(documentId);
|
||||
if (index === -1) return;
|
||||
|
||||
// 获取右侧所有标签页的ID
|
||||
const rightTabIds = tabOrder.value.slice(index + 1);
|
||||
|
||||
// 关闭右侧标签页
|
||||
rightTabIds.forEach(id => closeTab(id));
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭指定标签页左侧的所有标签页
|
||||
*/
|
||||
const closeTabsToLeft = (documentId: number) => {
|
||||
const index = getTabIndex(documentId);
|
||||
if (index <= 0) return;
|
||||
|
||||
// 获取左侧所有标签页的ID
|
||||
const leftTabIds = tabOrder.value.slice(0, index);
|
||||
|
||||
// 关闭左侧标签页
|
||||
leftTabIds.forEach(id => closeTab(id));
|
||||
};
|
||||
|
||||
/**
|
||||
* 清空所有标签页
|
||||
*/
|
||||
const clearAllTabs = () => {
|
||||
tabsMap.value.clear();
|
||||
tabOrder.value = [];
|
||||
};
|
||||
|
||||
// === 公共API ===
|
||||
return {
|
||||
// 状态
|
||||
tabs: readonly(tabs),
|
||||
draggedTabId,
|
||||
|
||||
// 计算属性
|
||||
isTabsEnabled,
|
||||
canCloseTab,
|
||||
currentDocumentId,
|
||||
|
||||
// 方法
|
||||
addOrActivateTab,
|
||||
closeTab,
|
||||
closeOtherTabs,
|
||||
closeTabsToLeft,
|
||||
closeTabsToRight,
|
||||
switchToTabAndDocument,
|
||||
moveTab,
|
||||
getTabIndex,
|
||||
initializeTab,
|
||||
clearAllTabs,
|
||||
updateTabTitle,
|
||||
|
||||
// 工具方法
|
||||
hasTab,
|
||||
getTab
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user