refactor(project): simplified part of the package, code optimization
This commit is contained in:
@@ -36,6 +36,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.1.0",
|
||||
"@ctrl/tinycolor": "^4.1.0",
|
||||
"@vue/shared": "^3.4.31",
|
||||
"clsx": "^2.1.1",
|
||||
"defu": "^6.1.4",
|
||||
|
1
packages/@core/shared/toolkit/src/cache/index.ts
vendored
Normal file
1
packages/@core/shared/toolkit/src/cache/index.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * from './storage-manager';
|
130
packages/@core/shared/toolkit/src/cache/storage-manager.test.ts
vendored
Normal file
130
packages/@core/shared/toolkit/src/cache/storage-manager.test.ts
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { StorageManager } from './storage-manager';
|
||||
|
||||
describe('storageManager', () => {
|
||||
let storageManager: StorageManager;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
localStorage.clear();
|
||||
storageManager = new StorageManager({
|
||||
prefix: 'test_',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set and get an item', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should return default value if item does not exist', () => {
|
||||
const user = storageManager.getItem('nonexistent', {
|
||||
age: 0,
|
||||
name: 'Default User',
|
||||
});
|
||||
expect(user).toEqual({ age: 0, name: 'Default User' });
|
||||
});
|
||||
|
||||
it('should remove an item', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
storageManager.removeItem('user');
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it('should clear all items with the prefix', () => {
|
||||
storageManager.setItem('user1', { age: 30, name: 'John Doe' });
|
||||
storageManager.setItem('user2', { age: 25, name: 'Jane Doe' });
|
||||
storageManager.clear();
|
||||
expect(storageManager.getItem('user1')).toBeNull();
|
||||
expect(storageManager.getItem('user2')).toBeNull();
|
||||
});
|
||||
|
||||
it('should clear expired items', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' }, 1000); // 1秒过期
|
||||
vi.advanceTimersByTime(1001); // 快进时间
|
||||
storageManager.clearExpiredItems();
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it('should not clear non-expired items', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' }, 10_000); // 10秒过期
|
||||
vi.advanceTimersByTime(5000); // 快进时间
|
||||
storageManager.clearExpiredItems();
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should handle JSON parse errors gracefully', () => {
|
||||
localStorage.setItem('test_user', '{ invalid JSON }');
|
||||
const user = storageManager.getItem('user', {
|
||||
age: 0,
|
||||
name: 'Default User',
|
||||
});
|
||||
expect(user).toEqual({ age: 0, name: 'Default User' });
|
||||
});
|
||||
it('should return null for non-existent items without default value', () => {
|
||||
const user = storageManager.getItem('nonexistent');
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it('should overwrite existing items', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
storageManager.setItem('user', { age: 25, name: 'Jane Doe' });
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 25, name: 'Jane Doe' });
|
||||
});
|
||||
|
||||
it('should handle items without expiry correctly', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
vi.advanceTimersByTime(5000);
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should remove expired items when accessed', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' }, 1000); // 1秒过期
|
||||
vi.advanceTimersByTime(1001); // 快进时间
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it('should not remove non-expired items when accessed', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' }, 10_000); // 10秒过期
|
||||
vi.advanceTimersByTime(5000); // 快进时间
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should handle multiple items with different expiry times', () => {
|
||||
storageManager.setItem('user1', { age: 30, name: 'John Doe' }, 1000); // 1秒过期
|
||||
storageManager.setItem('user2', { age: 25, name: 'Jane Doe' }, 2000); // 2秒过期
|
||||
vi.advanceTimersByTime(1500); // 快进时间
|
||||
storageManager.clearExpiredItems();
|
||||
const user1 = storageManager.getItem('user1');
|
||||
const user2 = storageManager.getItem('user2');
|
||||
expect(user1).toBeNull();
|
||||
expect(user2).toEqual({ age: 25, name: 'Jane Doe' });
|
||||
});
|
||||
|
||||
it('should handle items with no expiry', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
vi.advanceTimersByTime(10_000); // 快进时间
|
||||
storageManager.clearExpiredItems();
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should clear all items correctly', () => {
|
||||
storageManager.setItem('user1', { age: 30, name: 'John Doe' });
|
||||
storageManager.setItem('user2', { age: 25, name: 'Jane Doe' });
|
||||
storageManager.clear();
|
||||
const user1 = storageManager.getItem('user1');
|
||||
const user2 = storageManager.getItem('user2');
|
||||
expect(user1).toBeNull();
|
||||
expect(user2).toBeNull();
|
||||
});
|
||||
});
|
118
packages/@core/shared/toolkit/src/cache/storage-manager.ts
vendored
Normal file
118
packages/@core/shared/toolkit/src/cache/storage-manager.ts
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
type StorageType = 'localStorage' | 'sessionStorage';
|
||||
|
||||
interface StorageManagerOptions {
|
||||
prefix?: string;
|
||||
storageType?: StorageType;
|
||||
}
|
||||
|
||||
interface StorageItem<T> {
|
||||
expiry?: number;
|
||||
value: T;
|
||||
}
|
||||
|
||||
class StorageManager {
|
||||
private prefix: string;
|
||||
private storage: Storage;
|
||||
|
||||
constructor({
|
||||
prefix = '',
|
||||
storageType = 'localStorage',
|
||||
}: StorageManagerOptions = {}) {
|
||||
this.prefix = prefix;
|
||||
this.storage =
|
||||
storageType === 'localStorage'
|
||||
? window.localStorage
|
||||
: window.sessionStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的存储键
|
||||
* @param key 原始键
|
||||
* @returns 带前缀的完整键
|
||||
*/
|
||||
private getFullKey(key: string): string {
|
||||
return `${this.prefix}-${key}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有带前缀的存储项
|
||||
*/
|
||||
clear(): void {
|
||||
const keysToRemove: string[] = [];
|
||||
for (let i = 0; i < this.storage.length; i++) {
|
||||
const key = this.storage.key(i);
|
||||
if (key && key.startsWith(this.prefix)) {
|
||||
keysToRemove.push(key);
|
||||
}
|
||||
}
|
||||
keysToRemove.forEach((key) => this.storage.removeItem(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有过期的存储项
|
||||
*/
|
||||
clearExpiredItems(): void {
|
||||
for (let i = 0; i < this.storage.length; i++) {
|
||||
const key = this.storage.key(i);
|
||||
if (key && key.startsWith(this.prefix)) {
|
||||
const shortKey = key.replace(this.prefix, '');
|
||||
this.getItem(shortKey); // 调用 getItem 方法检查并移除过期项
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储项
|
||||
* @param key 键
|
||||
* @param defaultValue 当项不存在或已过期时返回的默认值
|
||||
* @returns 值,如果项已过期或解析错误则返回默认值
|
||||
*/
|
||||
getItem<T>(key: string, defaultValue: T | null = null): T | null {
|
||||
const fullKey = this.getFullKey(key);
|
||||
const itemStr = this.storage.getItem(fullKey);
|
||||
if (!itemStr) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
const item: StorageItem<T> = JSON.parse(itemStr);
|
||||
if (item.expiry && Date.now() > item.expiry) {
|
||||
this.storage.removeItem(fullKey);
|
||||
return defaultValue;
|
||||
}
|
||||
return item.value;
|
||||
} catch (error) {
|
||||
console.error(`Error parsing item with key "${fullKey}":`, error);
|
||||
this.storage.removeItem(fullKey); // 如果解析失败,删除该项
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除存储项
|
||||
* @param key 键
|
||||
*/
|
||||
removeItem(key: string): void {
|
||||
const fullKey = this.getFullKey(key);
|
||||
this.storage.removeItem(fullKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置存储项
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param ttl 存活时间(毫秒)
|
||||
*/
|
||||
setItem<T>(key: string, value: T, ttl?: number): void {
|
||||
const fullKey = this.getFullKey(key);
|
||||
const expiry = ttl ? Date.now() + ttl : undefined;
|
||||
const item: StorageItem<T> = { expiry, value };
|
||||
try {
|
||||
this.storage.setItem(fullKey, JSON.stringify(item));
|
||||
} catch (error) {
|
||||
console.error(`Error setting item with key "${fullKey}":`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { StorageManager };
|
17
packages/@core/shared/toolkit/src/cache/types.ts
vendored
Normal file
17
packages/@core/shared/toolkit/src/cache/types.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
type StorageType = 'localStorage' | 'sessionStorage';
|
||||
|
||||
interface StorageValue<T> {
|
||||
data: T;
|
||||
expiry: null | number;
|
||||
}
|
||||
|
||||
interface IStorageCache {
|
||||
clear(): void;
|
||||
getItem<T>(key: string): T | null;
|
||||
key(index: number): null | string;
|
||||
length(): number;
|
||||
removeItem(key: string): void;
|
||||
setItem<T>(key: string, value: T, expiryInMinutes?: number): void;
|
||||
}
|
||||
|
||||
export type { IStorageCache, StorageType, StorageValue };
|
41
packages/@core/shared/toolkit/src/colorful/convert.test.ts
Normal file
41
packages/@core/shared/toolkit/src/colorful/convert.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { convertToHsl, convertToHslCssVar, isValidColor } from './convert';
|
||||
|
||||
describe('color conversion functions', () => {
|
||||
it('should correctly convert color to HSL format', () => {
|
||||
const color = '#ff0000';
|
||||
const expectedHsl = 'hsl(0 100% 50%)';
|
||||
expect(convertToHsl(color)).toEqual(expectedHsl);
|
||||
});
|
||||
|
||||
it('should correctly convert color with alpha to HSL format', () => {
|
||||
const color = 'rgba(255, 0, 0, 0.5)';
|
||||
const expectedHsl = 'hsl(0 100% 50%) 0.5';
|
||||
expect(convertToHsl(color)).toEqual(expectedHsl);
|
||||
});
|
||||
|
||||
it('should correctly convert color to HSL CSS variable format', () => {
|
||||
const color = '#ff0000';
|
||||
const expectedHsl = '0 100% 50%';
|
||||
expect(convertToHslCssVar(color)).toEqual(expectedHsl);
|
||||
});
|
||||
|
||||
it('should correctly convert color with alpha to HSL CSS variable format', () => {
|
||||
const color = 'rgba(255, 0, 0, 0.5)';
|
||||
const expectedHsl = '0 100% 50% / 0.5';
|
||||
expect(convertToHslCssVar(color)).toEqual(expectedHsl);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValidColor', () => {
|
||||
it('isValidColor function', () => {
|
||||
// 测试有效颜色
|
||||
expect(isValidColor('blue')).toBe(true);
|
||||
expect(isValidColor('#000000')).toBe(true);
|
||||
|
||||
// 测试无效颜色
|
||||
expect(isValidColor('invalid color')).toBe(false);
|
||||
expect(isValidColor()).toBe(false);
|
||||
});
|
||||
});
|
44
packages/@core/shared/toolkit/src/colorful/convert.ts
Normal file
44
packages/@core/shared/toolkit/src/colorful/convert.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
/**
|
||||
* 将颜色转换为HSL格式。
|
||||
*
|
||||
* HSL是一种颜色模型,包括色相(Hue)、饱和度(Saturation)和亮度(Lightness)三个部分。
|
||||
* 这个函数使用TinyColor库将输入的颜色转换为HSL格式,并返回一个字符串。
|
||||
*
|
||||
* @param {string} color 输入的颜色,可以是任何TinyColor支持的颜色格式。
|
||||
* @returns {string} HSL格式的颜色字符串。
|
||||
*/
|
||||
function convertToHsl(color: string): string {
|
||||
const { a, h, l, s } = new TinyColor(color).toHsl();
|
||||
const hsl = `hsl(${Math.round(h)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%)`;
|
||||
return a < 1 ? `${hsl} ${a}` : hsl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将颜色转换为HSL CSS变量。
|
||||
*
|
||||
* 这个函数与convertToHsl函数类似,但是返回的字符串格式稍有不同,
|
||||
* 以便可以作为CSS变量使用。
|
||||
*
|
||||
* @param {string} color 输入的颜色,可以是任何TinyColor支持的颜色格式。
|
||||
* @returns {string} 可以作为CSS变量使用的HSL格式的颜色字符串。
|
||||
*/
|
||||
function convertToHslCssVar(color: string): string {
|
||||
const { a, h, l, s } = new TinyColor(color).toHsl();
|
||||
const hsl = `${Math.round(h)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
|
||||
return a < 1 ? `${hsl} / ${a}` : hsl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查颜色是否有效
|
||||
* @param {string} color - 待检查的颜色
|
||||
* 如果颜色有效返回true,否则返回false
|
||||
*/
|
||||
function isValidColor(color?: string) {
|
||||
if (!color) {
|
||||
return false;
|
||||
}
|
||||
return new TinyColor(color).isValid;
|
||||
}
|
||||
|
||||
export { TinyColor, convertToHsl, convertToHslCssVar, isValidColor };
|
45
packages/@core/shared/toolkit/src/colorful/generator.ts
Normal file
45
packages/@core/shared/toolkit/src/colorful/generator.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { generate } from '@ant-design/colors';
|
||||
|
||||
import { convertToHslCssVar } from './convert';
|
||||
|
||||
export * from '@ant-design/colors';
|
||||
|
||||
interface Opts {
|
||||
backgroundColor?: string;
|
||||
theme?: 'dark' | 'default';
|
||||
}
|
||||
|
||||
interface ColorItem {
|
||||
alias?: string;
|
||||
color: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function generatorColorVariables(colorItems: ColorItem[], opts?: Opts) {
|
||||
const colorVariables: Record<string, string> = {};
|
||||
|
||||
colorItems.forEach(({ alias, color, name }) => {
|
||||
if (color) {
|
||||
const colors = generate(color, opts);
|
||||
let mainColor = colors[5];
|
||||
colors.forEach((colorValue, colorIndex) => {
|
||||
const hslColor = convertToHslCssVar(colorValue);
|
||||
colorVariables[`--${name}-${colorIndex + 1}00`] = hslColor;
|
||||
if (alias) {
|
||||
colorVariables[`--${alias}-${colorIndex + 1}00`] = hslColor;
|
||||
}
|
||||
|
||||
if (colorIndex === 5) {
|
||||
mainColor = hslColor;
|
||||
}
|
||||
});
|
||||
if (alias) {
|
||||
colorVariables[`--${alias}`] = mainColor;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return colorVariables;
|
||||
}
|
||||
|
||||
export { generatorColorVariables };
|
2
packages/@core/shared/toolkit/src/colorful/index.ts
Normal file
2
packages/@core/shared/toolkit/src/colorful/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './convert';
|
||||
export * from './generator';
|
@@ -1,4 +1,6 @@
|
||||
export * from './cache';
|
||||
export * from './cn';
|
||||
export * from './colorful';
|
||||
export * from './diff';
|
||||
export * from './dom';
|
||||
export * from './inference';
|
||||
|
Reference in New Issue
Block a user