添加项目基础文件结构、路由配置、API接口和核心组件 实现登录认证、权限控制、WebSocket通信等基础功能 引入antd-mobile UI组件库和Vite构建工具 配置TypeScript、ESLint、Prettier等开发环境 添加移动端适配方案和全局样式 完成首页、工作台、个人中心等基础页面框架
262 lines
6.5 KiB
TypeScript
262 lines
6.5 KiB
TypeScript
// src/store/createPersistStore.ts
|
||
import { create } from "zustand";
|
||
import { persist, PersistOptions } from "zustand/middleware";
|
||
|
||
export interface PersistConfig {
|
||
name: string;
|
||
partialize?: (state: any) => any;
|
||
storage?: Storage;
|
||
version?: number;
|
||
migrate?: (persistedState: any, version: number) => any;
|
||
onRehydrateStorage?: (state: any) => void;
|
||
skipHydration?: boolean;
|
||
compress?: boolean;
|
||
encrypt?: boolean;
|
||
ttl?: number; // 生存时间(毫秒)
|
||
encryptionKey?: string;
|
||
}
|
||
|
||
// 默认配置
|
||
const DEFAULT_CONFIG = {
|
||
storage: localStorage,
|
||
version: 1,
|
||
skipHydration: false,
|
||
compress: false,
|
||
encrypt: false,
|
||
};
|
||
|
||
// 简单的数据压缩
|
||
function compressData(data: any): string {
|
||
try {
|
||
const jsonString = JSON.stringify(data);
|
||
return btoa(encodeURIComponent(jsonString));
|
||
} catch {
|
||
return JSON.stringify(data);
|
||
}
|
||
}
|
||
|
||
// 简单的数据解压
|
||
function decompressData(compressedData: string): any {
|
||
try {
|
||
const jsonString = decodeURIComponent(atob(compressedData));
|
||
return JSON.parse(jsonString);
|
||
} catch {
|
||
return JSON.parse(compressedData);
|
||
}
|
||
}
|
||
|
||
// 简单的数据加密
|
||
function encryptData(data: string, key: string = "default-key"): string {
|
||
let result = "";
|
||
for (let i = 0; i < data.length; i++) {
|
||
result += String.fromCharCode(
|
||
data.charCodeAt(i) ^ key.charCodeAt(i % key.length),
|
||
);
|
||
}
|
||
return btoa(result);
|
||
}
|
||
|
||
// 简单的数据解密
|
||
function decryptData(
|
||
encryptedData: string,
|
||
key: string = "default-key",
|
||
): string {
|
||
try {
|
||
const data = atob(encryptedData);
|
||
let result = "";
|
||
for (let i = 0; i < data.length; i++) {
|
||
result += String.fromCharCode(
|
||
data.charCodeAt(i) ^ key.charCodeAt(i % key.length),
|
||
);
|
||
}
|
||
return result;
|
||
} catch {
|
||
return encryptedData;
|
||
}
|
||
}
|
||
|
||
// 检查数据是否过期
|
||
function isDataExpired(timestamp: number, ttl: number): boolean {
|
||
return Date.now() - timestamp > ttl;
|
||
}
|
||
|
||
export function createPersistStore<T>(
|
||
createState: (set: any, get: any) => T,
|
||
config: PersistConfig,
|
||
) {
|
||
const {
|
||
name,
|
||
partialize,
|
||
storage = DEFAULT_CONFIG.storage,
|
||
version = DEFAULT_CONFIG.version,
|
||
migrate,
|
||
onRehydrateStorage,
|
||
skipHydration = DEFAULT_CONFIG.skipHydration,
|
||
compress = DEFAULT_CONFIG.compress,
|
||
encrypt = DEFAULT_CONFIG.encrypt,
|
||
ttl,
|
||
encryptionKey = "default-key",
|
||
} = config;
|
||
|
||
return create<T>()(
|
||
persist(createState, {
|
||
name,
|
||
partialize,
|
||
storage: {
|
||
getItem: (name: string) => {
|
||
try {
|
||
const item = storage.getItem(name);
|
||
if (!item) return null;
|
||
|
||
let data: any;
|
||
try {
|
||
data = JSON.parse(item);
|
||
} catch {
|
||
return null;
|
||
}
|
||
|
||
// 检查TTL
|
||
if (data.timestamp && ttl && isDataExpired(data.timestamp, ttl)) {
|
||
storage.removeItem(name);
|
||
return null;
|
||
}
|
||
|
||
let value = data.value;
|
||
|
||
// 解密
|
||
if (encrypt && typeof value === "string") {
|
||
value = decryptData(value, encryptionKey);
|
||
}
|
||
|
||
// 解压
|
||
if (compress && typeof value === "string") {
|
||
value = decompressData(value);
|
||
}
|
||
|
||
return value;
|
||
} catch (error) {
|
||
console.warn(`Failed to get item ${name} from storage:`, error);
|
||
return null;
|
||
}
|
||
},
|
||
setItem: (name: string, value: any) => {
|
||
try {
|
||
let processedValue = value;
|
||
|
||
// 压缩
|
||
if (compress) {
|
||
processedValue = compressData(processedValue);
|
||
}
|
||
|
||
// 加密
|
||
if (encrypt && typeof processedValue === "string") {
|
||
processedValue = encryptData(processedValue, encryptionKey);
|
||
}
|
||
|
||
const storageData = {
|
||
value: processedValue,
|
||
timestamp: Date.now(),
|
||
config: { compress, encrypt, ttl },
|
||
};
|
||
|
||
storage.setItem(name, JSON.stringify(storageData));
|
||
} catch (error) {
|
||
console.warn(`Failed to set item ${name} to storage:`, error);
|
||
}
|
||
},
|
||
removeItem: (name: string) => {
|
||
try {
|
||
storage.removeItem(name);
|
||
} catch (error) {
|
||
console.warn(`Failed to remove item ${name} from storage:`, error);
|
||
}
|
||
},
|
||
},
|
||
version,
|
||
migrate,
|
||
onRehydrateStorage,
|
||
skipHydration,
|
||
} as PersistOptions<T>),
|
||
);
|
||
}
|
||
|
||
// 便利函数:创建localStorage持久化store
|
||
export function createLocalStorageStore<T>(
|
||
createState: (set: any, get: any) => T,
|
||
name: string,
|
||
partialize?: (state: T) => Partial<T>,
|
||
options?: Partial<Omit<PersistConfig, "name" | "partialize" | "storage">>,
|
||
) {
|
||
return createPersistStore(createState, {
|
||
name,
|
||
partialize,
|
||
storage: localStorage,
|
||
...options,
|
||
});
|
||
}
|
||
|
||
// 便利函数:创建sessionStorage持久化store
|
||
export function createSessionStorageStore<T>(
|
||
createState: (set: any, get: any) => T,
|
||
name: string,
|
||
partialize?: (state: T) => Partial<T>,
|
||
options?: Partial<Omit<PersistConfig, "name" | "partialize" | "storage">>,
|
||
) {
|
||
return createPersistStore(createState, {
|
||
name,
|
||
partialize,
|
||
storage: sessionStorage,
|
||
...options,
|
||
});
|
||
}
|
||
|
||
// 便利函数:创建加密持久化store
|
||
export function createEncryptedStore<T>(
|
||
createState: (set: any, get: any) => T,
|
||
name: string,
|
||
encryptionKey: string,
|
||
partialize?: (state: T) => Partial<T>,
|
||
options?: Partial<
|
||
Omit<PersistConfig, "name" | "partialize" | "encrypt" | "encryptionKey">
|
||
>,
|
||
) {
|
||
return createPersistStore(createState, {
|
||
name,
|
||
partialize,
|
||
encrypt: true,
|
||
encryptionKey,
|
||
...options,
|
||
});
|
||
}
|
||
|
||
// 便利函数:创建压缩持久化store
|
||
export function createCompressedStore<T>(
|
||
createState: (set: any, get: any) => T,
|
||
name: string,
|
||
partialize?: (state: T) => Partial<T>,
|
||
options?: Partial<Omit<PersistConfig, "name" | "partialize" | "compress">>,
|
||
) {
|
||
return createPersistStore(createState, {
|
||
name,
|
||
partialize,
|
||
compress: true,
|
||
...options,
|
||
});
|
||
}
|
||
|
||
// 便利函数:创建带TTL的持久化store
|
||
export function createTTLStore<T>(
|
||
createState: (set: any, get: any) => T,
|
||
name: string,
|
||
ttl: number, // 生存时间(毫秒)
|
||
partialize?: (state: T) => Partial<T>,
|
||
options?: Partial<Omit<PersistConfig, "name" | "partialize" | "ttl">>,
|
||
) {
|
||
return createPersistStore(createState, {
|
||
name,
|
||
partialize,
|
||
ttl,
|
||
...options,
|
||
});
|
||
}
|