feat(editor): 完善storageService功能

新增namespace,setItem/getItem时自动key自动加上namespace;setItem可以指定协议;getItem时根据协议自动解析

fix #224
This commit is contained in:
roymondchen 2022-08-04 18:44:38 +08:00 committed by jia000
parent 6e199897ac
commit 574e03f685
3 changed files with 100 additions and 36 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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();
});