generated from template/astro-template
	
		
			
				
	
	
		
			216 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			216 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
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<void>;
 | 
						|
  list: any[];
 | 
						|
  setList: (list: any[]) => void;
 | 
						|
  getMark: (markId: string) => Promise<void>;
 | 
						|
  updateMark: () => Promise<void>;
 | 
						|
  getCache: (
 | 
						|
    id: string,
 | 
						|
    updateApiData?: boolean,
 | 
						|
  ) => Promise<
 | 
						|
    | {
 | 
						|
        elements: OrderedExcalidrawElement[];
 | 
						|
        filesObject: Record<string, any>;
 | 
						|
      }
 | 
						|
    | undefined
 | 
						|
  >;
 | 
						|
  setCache: (cache: any, version?: number) => Promise<void>;
 | 
						|
  loading: boolean;
 | 
						|
  setLoading: (loading: boolean) => void;
 | 
						|
  // excalidraw
 | 
						|
 | 
						|
  api: ExcalidrawImperativeAPI | null;
 | 
						|
  setApi: (api: ExcalidrawImperativeAPI) => void;
 | 
						|
};
 | 
						|
export const createMarkStore: StateCreator<MarkStore, [], [], MarkStore> = (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<MarkStore>;
 | 
						|
 | 
						|
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,
 | 
						|
  },
 | 
						|
];
 |