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 { reactive, toRaw } from 'vue';
 | 
				
			||||||
import { cloneDeep, mergeWith, uniq } from 'lodash-es';
 | 
					import { cloneDeep, mergeWith, uniq } from 'lodash-es';
 | 
				
			||||||
import serialize from 'serialize-javascript';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
 | 
					import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
 | 
				
			||||||
import { NodeType } 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 { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import historyService, { StepValue } from '@editor/services/history';
 | 
					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 type { AddMNode, EditorNodeInfo, PastePosition, StoreState } from '@editor/type';
 | 
				
			||||||
import { LayerOffset, Layout } from '@editor/type';
 | 
					import { LayerOffset, Layout } from '@editor/type';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -483,7 +482,9 @@ class Editor extends BaseService {
 | 
				
			|||||||
   * @returns 组件节点配置
 | 
					   * @returns 组件节点配置
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async copy(config: MNode | MNode[]): Promise<void> {
 | 
					  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 添加后的组件节点配置
 | 
					   * @returns 添加后的组件节点配置
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async paste(position: PastePosition = {}): Promise<MNode[] | void> {
 | 
					  public async paste(position: PastePosition = {}): Promise<MNode[] | void> {
 | 
				
			||||||
    const configStr = await storageService.getItem(COPY_STORAGE_KEY);
 | 
					    const config = await storageService.getItem(COPY_STORAGE_KEY);
 | 
				
			||||||
    // eslint-disable-next-line prefer-const
 | 
					
 | 
				
			||||||
    let config: any = {};
 | 
					    if (!config) return;
 | 
				
			||||||
    if (!configStr) {
 | 
					
 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      // eslint-disable-next-line no-eval
 | 
					 | 
				
			||||||
      eval(`config = ${configStr}`);
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      console.error(e);
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const pasteConfigs = await beforePaste(position, config);
 | 
					    const pasteConfigs = await beforePaste(position, config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return await this.multiAdd(pasteConfigs);
 | 
					    return await this.multiAdd(pasteConfigs);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,39 +1,80 @@
 | 
				
			|||||||
 | 
					import serialize from 'serialize-javascript';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseService from './BaseService';
 | 
					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 {
 | 
					export class WebStorage extends BaseService {
 | 
				
			||||||
 | 
					  private storage: Storage = globalThis.localStorage;
 | 
				
			||||||
 | 
					  private namespace = 'tmagic';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    super(['getStorage', 'clear', 'getItem', 'removeItem', 'setItem']);
 | 
					    super(['getStorage', 'getNamespace', 'clear', 'getItem', 'removeItem', 'setItem']);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * 获取数据存储对象,可以通过
 | 
					   * 获取数据存储对象,可以通过
 | 
				
			||||||
   * const storageService = new StorageService();
 | 
					   * const storageService = new StorageService();
 | 
				
			||||||
   * storageService.use({
 | 
					   * storageService.usePlugin({
 | 
				
			||||||
   *    // 替换存储对象为 sessionStorage
 | 
					   *    // 替换存储对象为 sessionStorage
 | 
				
			||||||
   *    async getStorage(): Promise<Storage> {
 | 
					   *    async afterGetStorage(): Promise<Storage> {
 | 
				
			||||||
   *    return window.sessionStorage;
 | 
					   *      return window.sessionStorage;
 | 
				
			||||||
   *    },
 | 
					   *    },
 | 
				
			||||||
   * });
 | 
					   * });
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async getStorage(): Promise<Storage> {
 | 
					  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> {
 | 
					  public async clear(): Promise<void> {
 | 
				
			||||||
    const storage = await this.getStorage();
 | 
					    const storage = await this.getStorage();
 | 
				
			||||||
    storage.clear();
 | 
					    storage.clear();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * 获取存储项,支持storageService.use
 | 
					   * 获取存储项,支持storageService.usePlugin
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async getItem(key: string): Promise<string | null> {
 | 
					  public async getItem(key: string, options: Options = {}): Promise<any> {
 | 
				
			||||||
    const storage = await this.getStorage();
 | 
					    const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
 | 
				
			||||||
    return storage.getItem(key);
 | 
					    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
 | 
					   * 获取指定索引位置的key
 | 
				
			||||||
@ -42,20 +83,51 @@ export class WebStorage extends BaseService {
 | 
				
			|||||||
    const storage = await this.getStorage();
 | 
					    const storage = await this.getStorage();
 | 
				
			||||||
    return storage.key(index);
 | 
					    return storage.key(index);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * 移除存储项,支持storageService.use
 | 
					   * 移除存储项,支持storageService.usePlugin
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async removeItem(key: string): Promise<void> {
 | 
					  public async removeItem(key: string, options: Options = {}): Promise<void> {
 | 
				
			||||||
    const storage = await this.getStorage();
 | 
					    const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
 | 
				
			||||||
    storage.removeItem(key);
 | 
					    storage.removeItem(`${options.namespace || namespace}:${key}`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * 设置存储项,支持storageService.use
 | 
					   * 设置存储项,支持storageService.usePlugin
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async setItem(key: string, value: string): Promise<void> {
 | 
					  public async setItem(key: string, value: any, options: Options = {}): Promise<void> {
 | 
				
			||||||
    const storage = await this.getStorage();
 | 
					    const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
 | 
				
			||||||
    storage.setItem(key, value);
 | 
					    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 type StorageService = WebStorage;
 | 
				
			||||||
export default new WebStorage();
 | 
					export default new WebStorage();
 | 
				
			||||||
 | 
				
			|||||||
@ -354,7 +354,7 @@ describe('copy', () => {
 | 
				
			|||||||
    const node = editorService.getNodeById(NodeId.NODE_ID2);
 | 
					    const node = editorService.getNodeById(NodeId.NODE_ID2);
 | 
				
			||||||
    await editorService.copy(node!);
 | 
					    await editorService.copy(node!);
 | 
				
			||||||
    const str = await storageService.getItem(COPY_STORAGE_KEY);
 | 
					    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 () => {
 | 
					  test('空', async () => {
 | 
				
			||||||
    globalThis.localStorage.clear();
 | 
					    await storageService.clear();
 | 
				
			||||||
    const newNode = await editorService.paste({ left: 0, top: 0 });
 | 
					    const newNode = await editorService.paste({ left: 0, top: 0 });
 | 
				
			||||||
    expect(newNode).toBeUndefined();
 | 
					    expect(newNode).toBeUndefined();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user