将 web-admin 从子模块改为仓库内普通目录,便于单仓克隆与维护。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
2e9d020d50
commit
c87732fcd5
515 files changed
+74372
-1
No files matched your search
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 存储相关工具函数统一导出
|
||||
*/
|
||||
|
||||
export * from './storage'
|
||||
export * from './storage-config'
|
||||
export * from './storage-key-manager'
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 存储配置管理模块
|
||||
*
|
||||
* 提供统一的本地存储配置和工具方法
|
||||
*
|
||||
* ## 主要功能
|
||||
*
|
||||
* - 版本化存储键管理,支持多版本数据隔离
|
||||
* - 存储键名生成和解析(带版本前缀)
|
||||
* - 版本号提取和验证
|
||||
* - 存储键匹配的正则表达式生成
|
||||
* - 旧版本存储键兼容处理
|
||||
* - 升级和登出延迟配置
|
||||
* - 主题存储键配置
|
||||
*
|
||||
* ## 使用场景
|
||||
*
|
||||
* - Pinia Store 持久化存储
|
||||
* - 应用版本升级时的数据迁移
|
||||
* - 多版本数据清理
|
||||
* - 存储键的统一管理和规范
|
||||
*
|
||||
* 存储键格式:sys-v{version}-{storeId}
|
||||
* 例如:sys-v1.0.0-user, sys-v1.0.0-setting
|
||||
*
|
||||
* @module utils/storage/storage-config
|
||||
* @author Art Design Pro Team
|
||||
*/
|
||||
export class StorageConfig {
|
||||
/** 当前应用版本 */
|
||||
static readonly CURRENT_VERSION = __APP_VERSION__
|
||||
|
||||
/** 存储键前缀 */
|
||||
static readonly STORAGE_PREFIX = 'sys-v'
|
||||
|
||||
/** 版本键名 */
|
||||
static readonly VERSION_KEY = 'sys-version'
|
||||
|
||||
/** 主题键名(index.html中使用了,如果修改,需要同步修改) */
|
||||
static readonly THEME_KEY = 'sys-theme'
|
||||
|
||||
/** 上次登录用户ID键名(用于判断是否为同一用户登录) */
|
||||
static readonly LAST_USER_ID_KEY = 'sys-last-user-id'
|
||||
|
||||
/** 响应式布局切换时暂存桌面端菜单类型 */
|
||||
static readonly RESPONSIVE_MENU_TYPE_KEY = 'sys-responsive-menu-type'
|
||||
|
||||
/** 跳过升级检查的版本 */
|
||||
static readonly SKIP_UPGRADE_VERSION = '1.0.0'
|
||||
|
||||
/** 升级处理延迟时间(毫秒) */
|
||||
static readonly UPGRADE_DELAY = 1000
|
||||
|
||||
/** 登出延迟时间(毫秒) */
|
||||
static readonly LOGOUT_DELAY = 1000
|
||||
|
||||
/**
|
||||
* 生成版本化的存储键名
|
||||
* @param storeId 存储ID
|
||||
* @param version 版本号,默认使用当前版本
|
||||
*/
|
||||
static generateStorageKey(storeId: string, version: string = this.CURRENT_VERSION): string {
|
||||
return `${this.STORAGE_PREFIX}${version}-${storeId}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成旧版本的存储键名(不带分隔符)
|
||||
* @param version 版本号,默认使用当前版本
|
||||
*/
|
||||
static generateLegacyKey(version: string = this.CURRENT_VERSION): string {
|
||||
return `${this.STORAGE_PREFIX}${version}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建存储键匹配的正则表达式
|
||||
* @param storeId 存储ID
|
||||
*/
|
||||
static createKeyPattern(storeId: string): RegExp {
|
||||
return new RegExp(`^${this.STORAGE_PREFIX}[^-]+-${storeId}$`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建当前版本存储键匹配的正则表达式
|
||||
*/
|
||||
static createCurrentVersionPattern(): RegExp {
|
||||
return new RegExp(`^${this.STORAGE_PREFIX}${this.CURRENT_VERSION}-`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建任意版本存储键匹配的正则表达式
|
||||
*/
|
||||
static createVersionPattern(): RegExp {
|
||||
return new RegExp(`^${this.STORAGE_PREFIX}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为当前版本的键
|
||||
*/
|
||||
static isCurrentVersionKey(key: string): boolean {
|
||||
return key.startsWith(`${this.STORAGE_PREFIX}${this.CURRENT_VERSION}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为版本化的键
|
||||
*/
|
||||
static isVersionedKey(key: string): boolean {
|
||||
return key.startsWith(this.STORAGE_PREFIX)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从存储键中提取版本号
|
||||
*/
|
||||
static extractVersionFromKey(key: string): string | null {
|
||||
const match = key.match(new RegExp(`^${this.STORAGE_PREFIX}([^-]+)`))
|
||||
return match ? match[1] : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 从存储键中提取存储ID
|
||||
*/
|
||||
static extractStoreIdFromKey(key: string): string | null {
|
||||
const match = key.match(new RegExp(`^${this.STORAGE_PREFIX}[^-]+-(.+)$`))
|
||||
return match ? match[1] : null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 存储键名管理器模块
|
||||
*
|
||||
* 提供智能的版本化存储键管理和数据迁移功能
|
||||
*
|
||||
* ## 主要功能
|
||||
*
|
||||
* - 自动生成当前版本的存储键名
|
||||
* - 检测当前版本数据是否存在
|
||||
* - 查找其他版本的同名存储数据
|
||||
* - 自动将旧版本数据迁移到当前版本
|
||||
* - 数据迁移日志记录
|
||||
* - 迁移失败的错误处理
|
||||
*
|
||||
* ## 使用场景
|
||||
*
|
||||
* - Pinia Store 持久化插件中获取存储键
|
||||
* - 应用版本升级时自动迁移用户数据
|
||||
* - 避免版本升级导致的数据丢失
|
||||
* - 实现平滑的版本过渡
|
||||
*
|
||||
* ## 工作流程
|
||||
*
|
||||
* 1. 优先使用当前版本的存储键
|
||||
* 2. 如果当前版本无数据,查找其他版本的同名数据
|
||||
* 3. 找到旧版本数据后自动迁移到当前版本
|
||||
* 4. 返回当前版本的存储键供使用
|
||||
*
|
||||
* @module utils/storage/storage-key-manager
|
||||
* @author Art Design Pro Team
|
||||
*/
|
||||
import { StorageConfig } from '@/utils/storage'
|
||||
|
||||
/**
|
||||
* 存储键名管理器
|
||||
* 负责处理版本化的存储键名生成和数据迁移
|
||||
*/
|
||||
export class StorageKeyManager {
|
||||
/**
|
||||
* 获取当前版本的存储键名
|
||||
*/
|
||||
private getCurrentVersionKey(storeId: string): string {
|
||||
return StorageConfig.generateStorageKey(storeId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前版本的数据是否存在
|
||||
*/
|
||||
private hasCurrentVersionData(key: string): boolean {
|
||||
return localStorage.getItem(key) !== null
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找其他版本的同名存储键
|
||||
*/
|
||||
private findExistingKey(storeId: string): string | null {
|
||||
const storageKeys = Object.keys(localStorage)
|
||||
const pattern = StorageConfig.createKeyPattern(storeId)
|
||||
|
||||
return storageKeys.find((key) => pattern.test(key) && localStorage.getItem(key)) || null
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据从旧版本迁移到当前版本
|
||||
*/
|
||||
private migrateData(fromKey: string, toKey: string): void {
|
||||
try {
|
||||
const existingData = localStorage.getItem(fromKey)
|
||||
if (existingData) {
|
||||
localStorage.setItem(toKey, existingData)
|
||||
console.info(`[Storage] 已迁移数据: ${fromKey} → ${toKey}`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[Storage] 数据迁移失败: ${fromKey}`, error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取持久化存储的键名(支持自动数据迁移)
|
||||
*/
|
||||
getStorageKey(storeId: string): string {
|
||||
const currentKey = this.getCurrentVersionKey(storeId)
|
||||
|
||||
// 优先使用当前版本的数据
|
||||
if (this.hasCurrentVersionData(currentKey)) {
|
||||
return currentKey
|
||||
}
|
||||
|
||||
// 查找并迁移其他版本的数据
|
||||
const existingKey = this.findExistingKey(storeId)
|
||||
if (existingKey) {
|
||||
this.migrateData(existingKey, currentKey)
|
||||
}
|
||||
|
||||
return currentKey
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/**
|
||||
* 存储兼容性管理模块
|
||||
*
|
||||
* 提供完整的本地存储兼容性检查和数据验证功能
|
||||
*
|
||||
* 主要功能
|
||||
*
|
||||
* - 多版本存储数据检测和验证
|
||||
* - 新旧存储格式兼容处理
|
||||
* - 存储数据完整性校验
|
||||
* - 存储异常自动恢复(清理+登出)
|
||||
* - 登录状态验证
|
||||
* - 存储为空检测
|
||||
* - 版本号管理
|
||||
*
|
||||
* ## 使用场景
|
||||
*
|
||||
* - 应用启动时检查存储数据有效性
|
||||
* - 路由守卫中验证登录状态
|
||||
* - 版本升级时的数据兼容性检查
|
||||
* - 存储异常时的自动恢复
|
||||
* - 防止因存储数据损坏导致的系统异常
|
||||
*
|
||||
* ## 工作流程
|
||||
*
|
||||
* 1. 优先检查当前版本的存储数据
|
||||
* 2. 检查其他版本的存储数据
|
||||
* 3. 兼容旧格式的存储数据
|
||||
* 4. 验证数据完整性
|
||||
* 5. 异常时提示用户并执行登出
|
||||
*
|
||||
* @module utils/storage/storage
|
||||
* @author Art Design Pro Team
|
||||
*/
|
||||
import { router } from '@/router'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { StorageConfig } from '@/utils/storage/storage-config'
|
||||
|
||||
/**
|
||||
* 存储兼容性管理器
|
||||
* 负责处理不同版本间的存储兼容性检查和数据验证
|
||||
*/
|
||||
class StorageCompatibilityManager {
|
||||
/**
|
||||
* 获取系统版本号
|
||||
*/
|
||||
getSystemVersion(): string | null {
|
||||
return localStorage.getItem(StorageConfig.VERSION_KEY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统存储数据(兼容旧格式)
|
||||
*/
|
||||
getSystemStorage(): any {
|
||||
const version = this.getSystemVersion() || StorageConfig.CURRENT_VERSION
|
||||
const legacyKey = StorageConfig.generateLegacyKey(version)
|
||||
const data = localStorage.getItem(legacyKey)
|
||||
return data ? JSON.parse(data) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前版本是否有存储数据
|
||||
*/
|
||||
private hasCurrentVersionStorage(): boolean {
|
||||
const storageKeys = Object.keys(localStorage)
|
||||
const currentVersionPattern = StorageConfig.createCurrentVersionPattern()
|
||||
|
||||
return storageKeys.some(
|
||||
(key) => currentVersionPattern.test(key) && localStorage.getItem(key) !== null
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在任何版本的存储数据
|
||||
*/
|
||||
private hasAnyVersionStorage(): boolean {
|
||||
const storageKeys = Object.keys(localStorage)
|
||||
const versionPattern = StorageConfig.createVersionPattern()
|
||||
|
||||
return storageKeys.some((key) => versionPattern.test(key) && localStorage.getItem(key) !== null)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取旧格式的本地存储数据
|
||||
*/
|
||||
private getLegacyStorageData(): Record<string, any> {
|
||||
try {
|
||||
const systemStorage = this.getSystemStorage()
|
||||
return systemStorage || {}
|
||||
} catch (error) {
|
||||
console.warn('[Storage] 解析旧格式存储数据失败:', error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示存储错误消息
|
||||
*/
|
||||
private showStorageError(): void {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
offset: 40,
|
||||
duration: 5000,
|
||||
message: '系统检测到本地数据异常,请重新登录系统恢复使用!'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行系统登出
|
||||
*/
|
||||
private performSystemLogout(): void {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
localStorage.clear()
|
||||
useUserStore().logOut()
|
||||
router.push({ name: 'Login' })
|
||||
console.info('[Storage] 已执行系统登出')
|
||||
} catch (error) {
|
||||
console.error('[Storage] 系统登出失败:', error)
|
||||
}
|
||||
}, StorageConfig.LOGOUT_DELAY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理存储异常
|
||||
*/
|
||||
private handleStorageError(): void {
|
||||
this.showStorageError()
|
||||
this.performSystemLogout()
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证存储数据完整性
|
||||
* @param requireAuth 是否需要验证登录状态(默认 false)
|
||||
*/
|
||||
validateStorageData(requireAuth: boolean = false): boolean {
|
||||
try {
|
||||
// 优先检查新版本存储结构
|
||||
if (this.hasCurrentVersionStorage()) {
|
||||
// console.debug('[Storage] 发现当前版本存储数据')
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查是否有任何版本的存储数据
|
||||
if (this.hasAnyVersionStorage()) {
|
||||
// console.debug('[Storage] 发现其他版本存储数据,可能需要迁移')
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查旧版本存储结构
|
||||
const legacyData = this.getLegacyStorageData()
|
||||
if (Object.keys(legacyData).length === 0) {
|
||||
// 只有在需要验证登录状态时才执行登出操作
|
||||
if (requireAuth) {
|
||||
console.warn('[Storage] 未发现任何存储数据,需要重新登录')
|
||||
this.performSystemLogout()
|
||||
return false
|
||||
}
|
||||
// 首次访问或访问静态路由,不需要登出
|
||||
// console.debug('[Storage] 未发现存储数据,首次访问或访问静态路由')
|
||||
return true
|
||||
}
|
||||
|
||||
console.debug('[Storage] 发现旧版本存储数据')
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('[Storage] 存储数据验证失败:', error)
|
||||
// 只有在需要验证登录状态时才处理错误
|
||||
if (requireAuth) {
|
||||
this.handleStorageError()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查存储是否为空
|
||||
*/
|
||||
isStorageEmpty(): boolean {
|
||||
// 检查新版本存储结构
|
||||
if (this.hasCurrentVersionStorage()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否有任何版本的存储数据
|
||||
if (this.hasAnyVersionStorage()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查旧版本存储结构
|
||||
const legacyData = this.getLegacyStorageData()
|
||||
return Object.keys(legacyData).length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查存储兼容性
|
||||
* @param requireAuth 是否需要验证登录状态(默认 false)
|
||||
*/
|
||||
checkCompatibility(requireAuth: boolean = false): boolean {
|
||||
try {
|
||||
const isValid = this.validateStorageData(requireAuth)
|
||||
const isEmpty = this.isStorageEmpty()
|
||||
|
||||
if (isValid || isEmpty) {
|
||||
// console.debug('[Storage] 存储兼容性检查通过')
|
||||
return true
|
||||
}
|
||||
|
||||
console.warn('[Storage] 存储兼容性检查失败')
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('[Storage] 兼容性检查异常:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建存储兼容性管理器实例
|
||||
const storageManager = new StorageCompatibilityManager()
|
||||
|
||||
/**
|
||||
* 获取系统存储数据
|
||||
*/
|
||||
export function getSystemStorage(): any {
|
||||
return storageManager.getSystemStorage()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统版本号
|
||||
*/
|
||||
export function getSysVersion(): string | null {
|
||||
return storageManager.getSystemVersion()
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证本地存储数据
|
||||
* @param requireAuth 是否需要验证登录状态(默认 false)
|
||||
*/
|
||||
export function validateStorageData(requireAuth: boolean = false): boolean {
|
||||
return storageManager.validateStorageData(requireAuth)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查存储兼容性
|
||||
* @param requireAuth 是否需要验证登录状态(默认 false)
|
||||
*/
|
||||
export function checkStorageCompatibility(requireAuth: boolean = false): boolean {
|
||||
return storageManager.checkCompatibility(requireAuth)
|
||||
}
|
||||
Reference in new issue
Block a user