'use client'; import { create } from 'zustand'; export type AppTab = { id: string; title: string; path: string; pinned?: boolean; }; type TabStoreState = { tabs: AppTab[]; activeId: string; open: (tab: Omit & { id?: string }) => void; /** 同 path 则仅激活,否则新开 */ openOrActivate: (tab: { path: string; title: string }) => void; /** 浏览器地址变化时同步当前页签 */ syncFromPath: (path: string) => void; /** 切换租户:保留固定「概览」并重置页签 */ resetForTenantSwitch: () => void; close: (id: string) => string | null; activate: (id: string) => void; }; let seq = 0; const overview: AppTab = { id: 'overview', title: '概览', path: '/dashboard', pinned: true, }; function titleFromPath(path: string): string { const parts = path.split('/').filter(Boolean); const last = parts[parts.length - 1]; return last ? decodeURIComponent(last) : path; } export const useTabStore = create((set, get) => ({ tabs: [overview], activeId: 'overview', open: (tab) => { const id = tab.id ?? `t-${++seq}`; set((s) => ({ tabs: [...s.tabs, { ...tab, id }], activeId: id, })); }, openOrActivate: ({ path, title }) => { const normalized = path.startsWith('/') ? path : `/${path}`; set((s) => { const hit = s.tabs.find((t) => t.path === normalized); if (hit) { return { activeId: hit.id }; } const id = `t-${++seq}`; return { tabs: [...s.tabs, { id, path: normalized, title: title || titleFromPath(normalized) }], activeId: id, }; }); }, syncFromPath: (path) => { const normalized = path.startsWith('/') ? path : `/${path}`; if (!normalized.startsWith('/dashboard')) { return; } set((s) => { const hit = s.tabs.find((t) => t.path === normalized); if (hit) { return { activeId: hit.id }; } const id = `t-${++seq}`; return { tabs: [ ...s.tabs, { id, path: normalized, title: titleFromPath(normalized), }, ], activeId: id, }; }); }, resetForTenantSwitch: () => { set({ tabs: [overview], activeId: 'overview', }); }, close: (id) => { const { tabs, activeId } = get(); const t = tabs.find((x) => x.id === id); if (!t || t.pinned) { return null; } const nextTabs = tabs.filter((x) => x.id !== id); let nextActive = activeId; if (activeId === id) { const idx = tabs.findIndex((x) => x.id === id); const neighbor = tabs[idx - 1] ?? tabs[idx + 1]; nextActive = neighbor?.id ?? 'overview'; } set({ tabs: nextTabs, activeId: nextActive }); const activeTab = get().tabs.find((x) => x.id === nextActive); return activeTab?.path ?? '/dashboard'; }, activate: (id) => set({ activeId: id }), }));