diff --git a/package.json b/package.json index e836c4a94c..fd2d6a7e16 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "intro.js": "^7.2.0", "js-cookie": "^3.0.5", "jsbarcode": "^3.11.5", + "localforage": "^1.10.0", "md-editor-v3": "2.7.2", "mint-filter": "^4.0.3", "mitt": "^3.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d98e6e5dc..bbb15d153e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,7 @@ specifiers: js-cookie: ^3.0.5 jsbarcode: ^3.11.5 lint-staged: ^14.0.1 + localforage: ^1.10.0 md-editor-v3: 2.7.2 mint-filter: ^4.0.3 mitt: ^3.0.1 @@ -142,6 +143,7 @@ dependencies: intro.js: 7.2.0 js-cookie: 3.0.5 jsbarcode: 3.11.5 + localforage: 1.10.0 md-editor-v3: 2.7.2 mint-filter: 4.0.3 mitt: 3.0.1 @@ -4790,6 +4792,10 @@ packages: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} + /immediate/3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + dev: false + /immer/9.0.21: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} dev: false @@ -5696,6 +5702,12 @@ packages: type-check: 0.4.0 dev: true + /lie/3.1.1: + resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} + dependencies: + immediate: 3.0.6 + dev: false + /lilconfig/2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -5747,6 +5759,12 @@ packages: dev: false optional: true + /localforage/1.10.0: + resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} + dependencies: + lie: 3.1.1 + dev: false + /locate-path/5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} diff --git a/src/utils/localforage/index.ts b/src/utils/localforage/index.ts new file mode 100644 index 0000000000..87d7672d53 --- /dev/null +++ b/src/utils/localforage/index.ts @@ -0,0 +1,94 @@ +import forage from "localforage"; +import { LocalForage, ProxyStorage, ExpiresData } from "./types.d"; + +class StorageProxy implements ProxyStorage { + protected storage: LocalForage; + constructor(storageModel) { + this.storage = storageModel; + this.storage.config({ + // driver: [forage.LOCALSTORAGE], + name: "pure-admin" + }); + } + + /** + * @description 将对应键名的数据保存到离线仓库 + * @param k 键名 + * @param v 键值 + * @param m 缓存时间(单位`分`,默认`0`分钟,永久缓存) + */ + public async setItem(k: string, v: T, m = 0): Promise { + return new Promise((resolve, reject) => { + this.storage + .setItem(k, { + data: v, + expires: new Date().getTime() + m * 60 * 1000 + }) + .then(value => { + resolve(value.data); + }) + .catch(err => { + reject(err); + }); + }); + } + + /** + * @description 从离线仓库中获取对应键名的值 + * @param k 键名 + */ + public async getItem(k: string): Promise { + return new Promise((resolve, reject) => { + this.storage + .getItem(k) + .then((value: ExpiresData) => { + const data = + value.expires > new Date().getTime() || value.expires === 0 + ? value.data + : null; + resolve(data); + }) + .catch(err => { + reject(err); + }); + }); + } + + /** + * @description 从离线仓库中删除对应键名的值 + * @param k 键名 + */ + public async removeItem(k: string) { + return new Promise((resolve, reject) => { + this.storage + .removeItem(k) + .then(() => { + resolve(); + }) + .catch(err => { + reject(err); + }); + }); + } + + /** + * @description 从离线仓库中删除所有的键名,重置数据库 + */ + public async clear() { + return new Promise((resolve, reject) => { + this.storage + .clear() + .then(() => { + resolve(); + }) + .catch(err => { + reject(err); + }); + }); + } +} + +/** + * 二次封装 [localforage](https://localforage.docschina.org/) 支持设置过期时间,提供完整的类型提示 + */ +export const localForage = () => new StorageProxy(forage); diff --git a/src/utils/localforage/types.d.ts b/src/utils/localforage/types.d.ts new file mode 100644 index 0000000000..b013c5b8b9 --- /dev/null +++ b/src/utils/localforage/types.d.ts @@ -0,0 +1,166 @@ +// https://github.com/localForage/localForage/blob/master/typings/localforage.d.ts + +interface LocalForageDbInstanceOptions { + name?: string; + + storeName?: string; +} + +interface LocalForageOptions extends LocalForageDbInstanceOptions { + driver?: string | string[]; + + size?: number; + + version?: number; + + description?: string; +} + +interface LocalForageDbMethodsCore { + getItem( + key: string, + callback?: (err: any, value: T | null) => void + ): Promise; + + setItem( + key: string, + value: T, + callback?: (err: any, value: T) => void + ): Promise; + + removeItem(key: string, callback?: (err: any) => void): Promise; + + clear(callback?: (err: any) => void): Promise; + + length(callback?: (err: any, numberOfKeys: number) => void): Promise; + + key( + keyIndex: number, + callback?: (err: any, key: string) => void + ): Promise; + + keys(callback?: (err: any, keys: string[]) => void): Promise; + + iterate( + iteratee: (value: T, key: string, iterationNumber: number) => U, + callback?: (err: any, result: U) => void + ): Promise; +} + +interface LocalForageDropInstanceFn { + ( + dbInstanceOptions?: LocalForageDbInstanceOptions, + callback?: (err: any) => void + ): Promise; +} + +interface LocalForageDriverMethodsOptional { + dropInstance?: LocalForageDropInstanceFn; +} + +// duplicating LocalForageDriverMethodsOptional to preserve TS v2.0 support, +// since Partial<> isn't supported there +interface LocalForageDbMethodsOptional { + dropInstance: LocalForageDropInstanceFn; +} + +interface LocalForageDriverDbMethods + extends LocalForageDbMethodsCore, + LocalForageDriverMethodsOptional {} + +interface LocalForageDriverSupportFunc { + (): Promise; +} + +interface LocalForageDriver extends LocalForageDriverDbMethods { + _driver: string; + + _initStorage(options: LocalForageOptions): void; + + _support?: boolean | LocalForageDriverSupportFunc; +} + +interface LocalForageSerializer { + serialize( + value: T | ArrayBuffer | Blob, + callback: (value: string, error: any) => void + ): void; + + deserialize(value: string): T | ArrayBuffer | Blob; + + stringToBuffer(serializedString: string): ArrayBuffer; + + bufferToString(buffer: ArrayBuffer): string; +} + +interface LocalForageDbMethods + extends LocalForageDbMethodsCore, + LocalForageDbMethodsOptional {} + +export interface LocalForage extends LocalForageDbMethods { + LOCALSTORAGE: string; + WEBSQL: string; + INDEXEDDB: string; + + /** + * Set and persist localForage options. This must be called before any other calls to localForage are made, but can be called after localForage is loaded. + * If you set any config values with this method they will persist after driver changes, so you can call config() then setDriver() + * @param {LocalForageOptions} options? + */ + config(options: LocalForageOptions): boolean; + config(options: string): any; + config(): LocalForageOptions; + + /** + * Create a new instance of localForage to point to a different store. + * All the configuration options used by config are supported. + * @param {LocalForageOptions} options + */ + createInstance(options: LocalForageOptions): LocalForage; + + driver(): string; + + /** + * Force usage of a particular driver or drivers, if available. + * @param {string} driver + */ + setDriver( + driver: string | string[], + callback?: () => void, + errorCallback?: (error: any) => void + ): Promise; + + defineDriver( + driver: LocalForageDriver, + callback?: () => void, + errorCallback?: (error: any) => void + ): Promise; + + /** + * Return a particular driver + * @param {string} driver + */ + getDriver(driver: string): Promise; + + getSerializer( + callback?: (serializer: LocalForageSerializer) => void + ): Promise; + + supports(driverName: string): boolean; + + ready(callback?: (error: any) => void): Promise; +} + +// Customize + +export interface ProxyStorage { + setItem(k: string, v: T, m: number): Promise; + getItem(k: string): Promise; + removeItem(k: string): Promise; + clear(): Promise; +} + +export interface ExpiresData { + data: T; + expires: number; +}