新增:文件系统导航面包屑
功能: - 新增 PathBreadcrumb 组件,支持路径快速跳转 - 新增 DropdownItem 通用下拉菜单组件 优化: - 版本升级流程优化(Pinia 状态管理、进度节流、完整下载验证) - 模块延迟初始化(数据库、文件系统按需启动) - API 数据格式统一(蛇形转驼峰) - CodeMirror 语言包按需动态加载 - Markdown 渲染增强(支持锚点跳转) 重构: - 迁移到 Pinia 状态管理(stores/config.ts、stores/theme.ts、stores/update.ts) - 简化 UpdatePanel、UpdateNotification、ThemeToggle 逻辑 - 优化表结构加载逻辑 清理: - 删除测试组件 index-simple.vue - 删除旧的 useTheme.ts
This commit is contained in:
@@ -1,78 +0,0 @@
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
type Theme = 'light' | 'dark'
|
||||
|
||||
const THEME_STORAGE_KEY = 'app-theme'
|
||||
|
||||
// 单例模式:全局共享主题状态
|
||||
const theme = ref<Theme>('light')
|
||||
let systemThemeListener: (() => void) | null = null
|
||||
|
||||
// 应用主题到 DOM
|
||||
const applyTheme = (newTheme: Theme) => {
|
||||
theme.value = newTheme
|
||||
if (newTheme === 'dark') {
|
||||
document.body.setAttribute('arco-theme', 'dark')
|
||||
} else {
|
||||
document.body.removeAttribute('arco-theme')
|
||||
}
|
||||
localStorage.setItem(THEME_STORAGE_KEY, newTheme)
|
||||
}
|
||||
|
||||
// 初始化主题(只调用一次)
|
||||
const initTheme = () => {
|
||||
const savedTheme = localStorage.getItem(THEME_STORAGE_KEY) as Theme
|
||||
if (savedTheme && (savedTheme === 'light' || savedTheme === 'dark')) {
|
||||
applyTheme(savedTheme)
|
||||
} else {
|
||||
// 检测系统偏好
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
applyTheme('dark')
|
||||
} else {
|
||||
applyTheme('light')
|
||||
}
|
||||
}
|
||||
|
||||
// 监听系统主题变化
|
||||
if (window.matchMedia) {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const handleChange = (e: MediaQueryListEvent) => {
|
||||
// 如果用户没有手动设置过主题,则跟随系统
|
||||
if (!localStorage.getItem(THEME_STORAGE_KEY)) {
|
||||
applyTheme(e.matches ? 'dark' : 'light')
|
||||
}
|
||||
}
|
||||
mediaQuery.addEventListener('change', handleChange)
|
||||
systemThemeListener = () => mediaQuery.removeEventListener('change', handleChange)
|
||||
}
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
// 切换主题
|
||||
const toggleTheme = () => {
|
||||
const newTheme: Theme = theme.value === 'light' ? 'dark' : 'light'
|
||||
applyTheme(newTheme)
|
||||
}
|
||||
|
||||
// 设置为亮色主题
|
||||
const setLightTheme = () => {
|
||||
applyTheme('light')
|
||||
}
|
||||
|
||||
// 设置为暗色主题
|
||||
const setDarkTheme = () => {
|
||||
applyTheme('dark')
|
||||
}
|
||||
|
||||
return {
|
||||
theme: computed(() => theme.value),
|
||||
isDark: computed(() => theme.value === 'dark'),
|
||||
toggleTheme,
|
||||
setLightTheme,
|
||||
setDarkTheme,
|
||||
initTheme
|
||||
}
|
||||
}
|
||||
|
||||
// 导出初始化函数(在 main.js 中使用)
|
||||
export { initTheme }
|
||||
117
web/src/composables/useTimeout.ts
Normal file
117
web/src/composables/useTimeout.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* 定时器管理 Hook
|
||||
* 自动管理定时器生命周期,防止内存泄漏
|
||||
*
|
||||
* @module composables/useTimeout
|
||||
* @description 提供类型安全的定时器管理,组件卸载时自动清理所有定时器
|
||||
*/
|
||||
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
|
||||
export interface TimeoutOptions {
|
||||
/**
|
||||
* 是否在组件卸载时自动清理所有定时器
|
||||
* @default true
|
||||
*/
|
||||
autoCleanup?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器管理 Hook
|
||||
*
|
||||
* @param options - 配置选项
|
||||
* @returns 定时器管理方法
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const { setTimeout, clearTimeout, clearAll } = useTimeout()
|
||||
*
|
||||
* // 设置延迟执行
|
||||
* const timer = setTimeout(() => {
|
||||
* console.log('延迟执行')
|
||||
* }, 1000)
|
||||
*
|
||||
* // 清除特定定时器
|
||||
* clearTimeout(timer)
|
||||
*
|
||||
* // 清除所有定时器
|
||||
* clearAll()
|
||||
* ```
|
||||
*/
|
||||
export function useTimeout(options: TimeoutOptions = {}) {
|
||||
const { autoCleanup = true } = options
|
||||
|
||||
// 使用 Set 存储所有定时器 ID
|
||||
const timers = ref<Set<NodeJS.Timeout>>(new Set())
|
||||
|
||||
/**
|
||||
* 设置定时器(自动管理生命周期)
|
||||
* @param callback - 要执行的回调函数
|
||||
* @param delay - 延迟时间(毫秒)
|
||||
* @returns 定时器 ID
|
||||
*/
|
||||
const setTimeout = <T = void>(
|
||||
callback: () => T,
|
||||
delay: number
|
||||
): NodeJS.Timeout => {
|
||||
const timer = window.setTimeout(() => {
|
||||
try {
|
||||
callback()
|
||||
} finally {
|
||||
// 执行完成后自动从集合中移除
|
||||
timers.value.delete(timer)
|
||||
}
|
||||
}, delay)
|
||||
|
||||
// 添加到集合中
|
||||
timers.value.add(timer)
|
||||
|
||||
return timer
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除特定定时器
|
||||
* @param timer - 要清除的定时器 ID
|
||||
*/
|
||||
const clearTimeout = (timer: NodeJS.Timeout) => {
|
||||
window.clearTimeout(timer)
|
||||
timers.value.delete(timer)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有定时器
|
||||
*/
|
||||
const clearAll = () => {
|
||||
timers.value.forEach((timer) => {
|
||||
window.clearTimeout(timer)
|
||||
})
|
||||
timers.value.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前活跃的定时器数量
|
||||
*/
|
||||
const getActiveCount = () => timers.value.size
|
||||
|
||||
// 组件卸载时自动清理
|
||||
if (autoCleanup) {
|
||||
onUnmounted(() => {
|
||||
clearAll()
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
clearAll,
|
||||
getActiveCount
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器管理 Hook 的别名
|
||||
* 便于语义化使用(如延迟执行、防抖等场景)
|
||||
*/
|
||||
export const useDelay = useTimeout
|
||||
|
||||
export default useTimeout
|
||||
Reference in New Issue
Block a user