import { StoreManager } from '@kevisual/store'; import { useContextKey } from '@kevisual/store/context'; import { StateCreator, StoreApi, UseBoundStore } from 'zustand'; import { queryMark } from '../modules/query'; import { useStore, BoundStore } from '@kevisual/store/react'; import { createStore, set as setCache, get as getCache } from 'idb-keyval'; import { OrderedExcalidrawElement } from '@excalidraw/excalidraw/element/types'; import { toast } from 'react-toastify'; import { BinaryFileData, ExcalidrawImperativeAPI } from '@excalidraw/excalidraw/types'; export const cacheStore = createStore('excalidraw-store', 'excalidraw'); export const store = useContextKey('store', () => { return new StoreManager(); }); type MarkStore = { id: string; setId: (id: string) => void; mark: any; setMark: (mark: any) => void; info: any; setInfo: (info: any) => void; getList: () => Promise; list: any[]; setList: (list: any[]) => void; getMark: (markId: string) => Promise; updateMark: () => Promise; getCache: ( id: string, updateApiData?: boolean, ) => Promise< | { elements: OrderedExcalidrawElement[]; filesObject: Record; } | undefined >; setCache: (cache: any, version?: number) => Promise; loading: boolean; setLoading: (loading: boolean) => void; // excalidraw api: ExcalidrawImperativeAPI | null; setApi: (api: ExcalidrawImperativeAPI) => void; }; export const createMarkStore: StateCreator = (set, get, store) => { return { id: '', setId: (id: string) => set(() => ({ id })), mark: null, setMark: (mark: any) => set(() => ({ mark })), loading: true, setLoading: (loading: boolean) => set(() => ({ loading })), info: null, setCache: async (cache: any, version?: number) => { const { id, mark } = get(); console.log('cacheData setCache ,id', cache, id); if (!id) { return; } const cacheData = (await getCache(`${id}`, cacheStore)) || {}; await setCache( `${id}`, { ...cacheData, ...cache, data: { ...cacheData?.data, ...cache?.data, }, version: version || mark?.version || 0, }, cacheStore, ); }, updateMark: async () => { const { id } = get(); if (!id) { return; } const cacheData = await getCache(id, cacheStore); let mark = cacheData || {}; if (!mark) { return; } const { data } = mark; const { elements, filesObject } = data; console.log('updateMark', elements, filesObject); const res = await queryMark.updateMark({ id, data }); if (res.code === 200) { set(() => ({ mark: res.data })); toast.success('更新成功'); get().setCache({}, res.data!.version); } }, getCache: async (id: string, updateApiData?: boolean) => { if (!id) { return; } // 获取缓存 let cacheData = (await getCache(`${id}`, cacheStore)) || { data: { elements: [], filesObject: {} } }; console.log('getCache', id, cacheData); if (cacheData) { if (updateApiData) { const api = get().api; if (api) { const files = Object.values(cacheData.data.filesObject || {}) as BinaryFileData[]; api.addFiles(files || []); api.updateScene({ elements: [...(cacheData.data?.elements || [])], appState: {}, }); } } } return { elements: cacheData.data.elements || [], filesObject: cacheData.data.filesObject || {}, }; }, setInfo: (info: any) => set(() => ({ info })), getList: async () => { const res = await queryMark.getMarkList({ page: 1, pageSize: 10 }); console.log(res); }, list: [], setList: (list: any[]) => set(() => ({ list })), getMark: async (markId: string) => { set(() => ({ loading: true, id: markId })); const toastId = toast.loading(`获取数据中...`); const now = new Date().getTime(); const cacheData = await getCache(markId, cacheStore); const checkVersion = await queryMark.checkVersion(markId, cacheData?.version); if (checkVersion) { const res = await queryMark.getMark(markId); if (res.code === 200) { set(() => ({ mark: res.data, id: markId, })); const mark = res.data!; const excalidrawData = mark.data || {}; await get().setCache({ data: { elements: excalidrawData.elements, filesObject: excalidrawData.filesObject, }, version: mark.version, }); get().getCache(markId, true); } else { toast.error(res.message || '获取数据失败'); } } const end = new Date().getTime(); const getTime = end - now; if (getTime < 2 * 1000) { await new Promise((resolve) => setTimeout(resolve, 2 * 1000 - getTime)); } toast.dismiss(toastId); set(() => ({ loading: false })); }, api: null, setApi: (api: ExcalidrawImperativeAPI) => set(() => ({ api })), }; }; export const useMarkStore = useStore as BoundStore; export const fileDemo = { abc: { dataURL: 'https://kevisual.xiongxiao.me/root/center/panda.png' as any, // @ts-ignore id: 'abc', name: 'test2.png', type: 'image/png', }, }; export const demoElements: OrderedExcalidrawElement[] = [ { id: '1', type: 'image', x: 100, y: 100, width: 100, height: 100, fileId: 'abc' as any, version: 2, versionNonce: 28180243, index: 'a0' as any, isDeleted: false, fillStyle: 'solid', strokeWidth: 2, strokeStyle: 'solid', roughness: 1, opacity: 100, angle: 0, strokeColor: '#1e1e1e', backgroundColor: 'transparent', seed: 1, groupIds: [], frameId: null, roundness: null, boundElements: [], updated: 1743219351869, link: null, locked: false, status: 'pending', scale: [1, 1], crop: null, }, ];