首次发布git

This commit is contained in:
2026-02-24 09:53:19 +08:00
commit dd97cf6a2c
71 changed files with 14875 additions and 0 deletions
+156
View File
@@ -0,0 +1,156 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
export interface Page {
id: string;
title: string;
content: string;
parentId: string | null;
type: "file" | "folder";
children?: Page[]; // For tree view structures (virtual field)
createdAt?: string;
updatedAt?: string;
icon?: string | null;
tags?: string[];
order?: number;
isLocked?: boolean;
}
interface EditorState {
pages: Page[];
activePageId: string | null;
isLoading: boolean;
// Actions
fetchPages: () => Promise<void>;
setActivePageId: (id: string | null) => void;
addPage: (parentId?: string | null, type?: "file" | "folder", initialData?: { title: string, content: string }, order?: number) => Promise<void>;
updatePage: (id: string, data: Partial<Page>) => Promise<void>;
deletePage: (id: string) => Promise<void>;
movePage: (id: string, parentId: string | null) => Promise<void>;
reorderPages: (data: { id: string, order: number }[]) => Promise<void>;
}
export const useEditorStore = create<EditorState>()(
persist(
(set, get) => ({
pages: [],
activePageId: null,
isLoading: false,
fetchPages: async () => {
set({ isLoading: true });
try {
const res = await fetch("/api/pages");
if (res.ok) {
const data = await res.json();
set({ pages: data });
// If no active page, maybe select the first one? Or leave null.
// Persistence will handle restoring activePageId if it exists.
}
} catch (e) {
console.error("Failed to fetch pages", e);
} finally {
set({ isLoading: false });
}
},
setActivePageId: (id) => set({ activePageId: id }),
addPage: async (parentId = null, type = "file", initialData, order) => {
try {
const res = await fetch("/api/pages", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: initialData?.title || "无标题",
content: initialData?.content || "",
tags: [],
parentId,
type,
order
}),
});
if (res.ok) {
const newPage = await res.json();
set((state) => ({
pages: [newPage, ...state.pages],
activePageId: newPage.id // Auto select new page
}));
}
} catch (e) {
console.error("Failed to create page", e);
}
},
updatePage: async (id, data) => {
// Optimistic update
set((state) => ({
pages: state.pages.map((p) => (p.id === id ? { ...p, ...data, updatedAt: new Date().toISOString() } : p)),
}));
// Debounce logic could be added here, but for now direct call
try {
await fetch(`/api/pages/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
} catch (e) {
console.error("Failed to update page", e);
}
},
movePage: async (id, parentId) => {
// Reuse updatePage logic
await get().updatePage(id, { parentId });
},
deletePage: async (id) => {
// Optimistic delete
const currentActive = get().activePageId;
set((state) => ({
pages: state.pages.filter((p) => p.id !== id),
activePageId: currentActive === id ? null : currentActive
}));
try {
await fetch(`/api/pages/${id}`, {
method: "DELETE",
});
} catch (e) {
console.error("Failed to delete page", e);
// Rollback could be added here
}
},
reorderPages: async (updates) => {
// Optimistic update
set((state) => {
const newPages = [...state.pages];
updates.forEach(({ id, order }) => {
const page = newPages.find(p => p.id === id);
if (page) page.order = order;
});
// Re-sort locally? Or just trust UI to sort based on updated order property
// It's safer if 'pages' remains the master list
return { pages: newPages };
});
try {
await fetch("/api/pages/reorder", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ updates }),
});
} catch (e) {
console.error("Failed to reorder pages", e);
}
},
}),
{
name: "editor-storage",
partialize: (state) => ({ activePageId: state.activePageId }),
}
)
);