Files
cn-rdms-web/src/views/workbench/composables/use-workbench-layout.ts

168 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { computed, ref } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import { type WorkbenchColumnId, type WorkbenchModuleKey, useWorkbenchModules } from './use-workbench-modules';
import { buildDefaultLayout } from './workbench-layout-default';
import type { LayoutStorage } from './layout-storage';
import { LocalStorageAdapter } from './layout-storage-local';
import { reconcileLayout } from './workbench-layout-reconcile';
import { WORKBENCH_LAYOUT_VERSION, type WorkbenchLayout } from './workbench-layout-types';
export type WorkbenchMode = 'normal' | 'editing';
interface UseWorkbenchLayoutOptions {
userId: string;
storage?: LayoutStorage;
}
export function useWorkbenchLayout(options: UseWorkbenchLayoutOptions) {
const { getAllModules } = useWorkbenchModules();
const storage = options.storage ?? new LocalStorageAdapter();
const layout = ref<WorkbenchLayout>(buildDefaultLayout(getAllModules()));
const mode = ref<WorkbenchMode>('normal');
const dirty = ref(false);
const saving = ref(false);
const error = ref<Error | null>(null);
let snapshotBeforeEdit: WorkbenchLayout | null = null;
async function load() {
const fromStorage = await storage.load(options.userId);
if (fromStorage && fromStorage.version === WORKBENCH_LAYOUT_VERSION) {
layout.value = reconcileLayout(fromStorage, getAllModules());
return;
}
// 版本不匹配 / 无存储:走新默认布局;旧 settings 迁移过来,避免用户偏好(如 shortcut.menuKeys被 version bump 清空
const fresh = buildDefaultLayout(getAllModules());
if (fromStorage?.settings) {
fresh.settings = { ...fromStorage.settings };
}
layout.value = fresh;
}
const persist = useDebounceFn(async () => {
saving.value = true;
error.value = null;
try {
await storage.save(options.userId, layout.value);
} catch (err) {
error.value = err as Error;
} finally {
saving.value = false;
}
}, 500);
function markDirty() {
if (mode.value === 'editing') {
dirty.value = true;
} else {
// 非编辑态写(如折叠)直接落盘
persist();
}
}
function enterEditing() {
snapshotBeforeEdit = JSON.parse(JSON.stringify(layout.value));
mode.value = 'editing';
dirty.value = false;
}
async function saveEditing() {
saving.value = true;
try {
await storage.save(options.userId, layout.value);
mode.value = 'normal';
dirty.value = false;
snapshotBeforeEdit = null;
} catch (err) {
error.value = err as Error;
} finally {
saving.value = false;
}
}
function cancelEditing() {
if (snapshotBeforeEdit) {
layout.value = snapshotBeforeEdit;
}
mode.value = 'normal';
dirty.value = false;
snapshotBeforeEdit = null;
}
function hideModule(key: WorkbenchModuleKey) {
for (const col of layout.value.columns) {
col.modules = col.modules.filter(k => k !== key);
}
if (!layout.value.hidden.includes(key)) layout.value.hidden.push(key);
markDirty();
}
function showModule(key: WorkbenchModuleKey, columnId: WorkbenchColumnId = 'left') {
layout.value.hidden = layout.value.hidden.filter(k => k !== key);
const target = layout.value.columns.find(c => c.id === columnId);
if (target && !target.modules.includes(key)) target.modules.push(key);
markDirty();
}
function setColumnModules(columnId: WorkbenchColumnId, modules: WorkbenchModuleKey[]) {
const target = layout.value.columns.find(c => c.id === columnId);
if (target) target.modules = modules;
markDirty();
}
function toggleCollapse(key: WorkbenchModuleKey) {
if (layout.value.collapsed.includes(key)) {
layout.value.collapsed = layout.value.collapsed.filter(k => k !== key);
} else {
layout.value.collapsed.push(key);
}
markDirty();
}
function updateModuleSettings<K extends keyof WorkbenchLayout['settings']>(
key: K,
value: WorkbenchLayout['settings'][K]
) {
layout.value.settings = { ...layout.value.settings, [key]: value };
markDirty();
}
async function resetToDefault() {
layout.value = buildDefaultLayout(getAllModules());
mode.value = 'normal';
dirty.value = false;
snapshotBeforeEdit = null;
await storage.save(options.userId, layout.value);
}
const isCollapsed = (key: WorkbenchModuleKey) => layout.value.collapsed.includes(key);
const hiddenMetas = computed(() => {
const allMeta = getAllModules();
return layout.value.hidden
.map(k => allMeta.find(m => m.key === k))
.filter((m): m is NonNullable<typeof m> => Boolean(m));
});
return {
layout,
mode,
dirty,
saving,
error,
hiddenMetas,
isCollapsed,
load,
enterEditing,
saveEditing,
cancelEditing,
hideModule,
showModule,
setColumnModules,
toggleCollapse,
updateModuleSettings,
resetToDefault
};
}