mirror of
				https://github.com/Tencent/tmagic-editor.git
				synced 2025-11-04 02:28:04 +08:00 
			
		
		
		
	feat(editor): 完善storageService功能
新增namespace,setItem/getItem时自动key自动加上namespace;setItem可以指定协议;getItem时根据协议自动解析 fix #224
This commit is contained in:
		
							parent
							
								
									6e199897ac
								
							
						
					
					
						commit
						574e03f685
					
				@ -18,7 +18,6 @@
 | 
			
		||||
 | 
			
		||||
import { reactive, toRaw } from 'vue';
 | 
			
		||||
import { cloneDeep, mergeWith, uniq } from 'lodash-es';
 | 
			
		||||
import serialize from 'serialize-javascript';
 | 
			
		||||
 | 
			
		||||
import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
 | 
			
		||||
import { NodeType } from '@tmagic/schema';
 | 
			
		||||
@ -26,7 +25,7 @@ import StageCore from '@tmagic/stage';
 | 
			
		||||
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
 | 
			
		||||
 | 
			
		||||
import historyService, { StepValue } from '@editor/services/history';
 | 
			
		||||
import storageService from '@editor/services/storage';
 | 
			
		||||
import storageService, { Protocol } from '@editor/services/storage';
 | 
			
		||||
import type { AddMNode, EditorNodeInfo, PastePosition, StoreState } from '@editor/type';
 | 
			
		||||
import { LayerOffset, Layout } from '@editor/type';
 | 
			
		||||
import {
 | 
			
		||||
@ -483,7 +482,9 @@ class Editor extends BaseService {
 | 
			
		||||
   * @returns 组件节点配置
 | 
			
		||||
   */
 | 
			
		||||
  public async copy(config: MNode | MNode[]): Promise<void> {
 | 
			
		||||
    await storageService.setItem(COPY_STORAGE_KEY, serialize(Array.isArray(config) ? config : [config]));
 | 
			
		||||
    await storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
 | 
			
		||||
      protocol: Protocol.OBJECT,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@ -492,19 +493,10 @@ class Editor extends BaseService {
 | 
			
		||||
   * @returns 添加后的组件节点配置
 | 
			
		||||
   */
 | 
			
		||||
  public async paste(position: PastePosition = {}): Promise<MNode[] | void> {
 | 
			
		||||
    const configStr = await storageService.getItem(COPY_STORAGE_KEY);
 | 
			
		||||
    // eslint-disable-next-line prefer-const
 | 
			
		||||
    let config: any = {};
 | 
			
		||||
    if (!configStr) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    try {
 | 
			
		||||
      // eslint-disable-next-line no-eval
 | 
			
		||||
      eval(`config = ${configStr}`);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error(e);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const config = await storageService.getItem(COPY_STORAGE_KEY);
 | 
			
		||||
 | 
			
		||||
    if (!config) return;
 | 
			
		||||
 | 
			
		||||
    const pasteConfigs = await beforePaste(position, config);
 | 
			
		||||
 | 
			
		||||
    return await this.multiAdd(pasteConfigs);
 | 
			
		||||
 | 
			
		||||
@ -1,39 +1,80 @@
 | 
			
		||||
import serialize from 'serialize-javascript';
 | 
			
		||||
 | 
			
		||||
import BaseService from './BaseService';
 | 
			
		||||
 | 
			
		||||
interface Options {
 | 
			
		||||
  namespace?: string;
 | 
			
		||||
  protocol?: Protocol;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum Protocol {
 | 
			
		||||
  OBJECT = 'object',
 | 
			
		||||
  JSON = 'json',
 | 
			
		||||
  STRING = 'string',
 | 
			
		||||
  NUMBER = 'number',
 | 
			
		||||
  BOOLEAN = 'boolean',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 数据存储服务
 | 
			
		||||
 */
 | 
			
		||||
export class WebStorage extends BaseService {
 | 
			
		||||
  private storage: Storage = globalThis.localStorage;
 | 
			
		||||
  private namespace = 'tmagic';
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super(['getStorage', 'clear', 'getItem', 'removeItem', 'setItem']);
 | 
			
		||||
    super(['getStorage', 'getNamespace', 'clear', 'getItem', 'removeItem', 'setItem']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 获取数据存储对象,可以通过
 | 
			
		||||
   * const storageService = new StorageService();
 | 
			
		||||
   * storageService.use({
 | 
			
		||||
   * storageService.usePlugin({
 | 
			
		||||
   *    // 替换存储对象为 sessionStorage
 | 
			
		||||
   *    async getStorage(): Promise<Storage> {
 | 
			
		||||
   *    return window.sessionStorage;
 | 
			
		||||
   *    async afterGetStorage(): Promise<Storage> {
 | 
			
		||||
   *      return window.sessionStorage;
 | 
			
		||||
   *    },
 | 
			
		||||
   * });
 | 
			
		||||
   */
 | 
			
		||||
  public async getStorage(): Promise<Storage> {
 | 
			
		||||
    return globalThis.localStorage;
 | 
			
		||||
    return this.storage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getNamespace(): Promise<string> {
 | 
			
		||||
    return this.namespace;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 清理,支持storageService.use
 | 
			
		||||
   * 清理,支持storageService.usePlugin
 | 
			
		||||
   */
 | 
			
		||||
  public async clear(): Promise<void> {
 | 
			
		||||
    const storage = await this.getStorage();
 | 
			
		||||
    storage.clear();
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
   * 获取存储项,支持storageService.use
 | 
			
		||||
   * 获取存储项,支持storageService.usePlugin
 | 
			
		||||
   */
 | 
			
		||||
  public async getItem(key: string): Promise<string | null> {
 | 
			
		||||
    const storage = await this.getStorage();
 | 
			
		||||
    return storage.getItem(key);
 | 
			
		||||
  public async getItem(key: string, options: Options = {}): Promise<any> {
 | 
			
		||||
    const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
 | 
			
		||||
    const { protocol = options.protocol, item } = this.getValueAndProtocol(
 | 
			
		||||
      storage.getItem(`${options.namespace || namespace}:${key}`),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (item === null) return null;
 | 
			
		||||
 | 
			
		||||
    switch (protocol) {
 | 
			
		||||
      case Protocol.OBJECT:
 | 
			
		||||
        // eslint-disable-next-line no-eval
 | 
			
		||||
        return eval(`(${item})`);
 | 
			
		||||
      case Protocol.JSON:
 | 
			
		||||
        return JSON.parse(item);
 | 
			
		||||
      case Protocol.NUMBER:
 | 
			
		||||
        return Number(item);
 | 
			
		||||
      case Protocol.BOOLEAN:
 | 
			
		||||
        return Boolean(item);
 | 
			
		||||
      default:
 | 
			
		||||
        return item;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
   * 获取指定索引位置的key
 | 
			
		||||
@ -42,20 +83,51 @@ export class WebStorage extends BaseService {
 | 
			
		||||
    const storage = await this.getStorage();
 | 
			
		||||
    return storage.key(index);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 移除存储项,支持storageService.use
 | 
			
		||||
   * 移除存储项,支持storageService.usePlugin
 | 
			
		||||
   */
 | 
			
		||||
  public async removeItem(key: string): Promise<void> {
 | 
			
		||||
    const storage = await this.getStorage();
 | 
			
		||||
    storage.removeItem(key);
 | 
			
		||||
  public async removeItem(key: string, options: Options = {}): Promise<void> {
 | 
			
		||||
    const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
 | 
			
		||||
    storage.removeItem(`${options.namespace || namespace}:${key}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 设置存储项,支持storageService.use
 | 
			
		||||
   * 设置存储项,支持storageService.usePlugin
 | 
			
		||||
   */
 | 
			
		||||
  public async setItem(key: string, value: string): Promise<void> {
 | 
			
		||||
    const storage = await this.getStorage();
 | 
			
		||||
    storage.setItem(key, value);
 | 
			
		||||
  public async setItem(key: string, value: any, options: Options = {}): Promise<void> {
 | 
			
		||||
    const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
 | 
			
		||||
    let item = value;
 | 
			
		||||
    const protocol = options.protocol ? `${options.protocol}:` : '';
 | 
			
		||||
    if (typeof value === Protocol.STRING || typeof value === Protocol.NUMBER) {
 | 
			
		||||
      item = `${protocol}${value}`;
 | 
			
		||||
    } else {
 | 
			
		||||
      item = `${protocol}${serialize(value)}`;
 | 
			
		||||
    }
 | 
			
		||||
    storage.setItem(`${options.namespace || namespace}:${key}`, item);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getValueAndProtocol(value: string | null) {
 | 
			
		||||
    let protocol = '';
 | 
			
		||||
 | 
			
		||||
    if (value === null) {
 | 
			
		||||
      return {
 | 
			
		||||
        item: value,
 | 
			
		||||
        protocol,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const item = value.replace(new RegExp(`^(${Object.values(Protocol).join('|')})(:)(.+)`), (_$0, $1, _$2, $3) => {
 | 
			
		||||
      protocol = $1;
 | 
			
		||||
      return $3;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      protocol,
 | 
			
		||||
      item,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type StorageService = WebStorage;
 | 
			
		||||
export default new WebStorage();
 | 
			
		||||
 | 
			
		||||
@ -354,7 +354,7 @@ describe('copy', () => {
 | 
			
		||||
    const node = editorService.getNodeById(NodeId.NODE_ID2);
 | 
			
		||||
    await editorService.copy(node!);
 | 
			
		||||
    const str = await storageService.getItem(COPY_STORAGE_KEY);
 | 
			
		||||
    expect(str).toBe(JSON.stringify([node]));
 | 
			
		||||
    expect(str).toHaveLength(1);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -372,7 +372,7 @@ describe('paste', () => {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('空', async () => {
 | 
			
		||||
    globalThis.localStorage.clear();
 | 
			
		||||
    await storageService.clear();
 | 
			
		||||
    const newNode = await editorService.paste({ left: 0, top: 0 });
 | 
			
		||||
    expect(newNode).toBeUndefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user