Private
Public Access
1
0

新增:文件系统导航面包屑

功能:
- 新增 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:
2026-02-05 00:17:32 +08:00
parent ce2698f245
commit f7d648ea52
48 changed files with 3930 additions and 1380 deletions

View File

@@ -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 }

View 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