All files / src/stores app.js

1.78% Statements 1/56
0% Branches 0/22
0% Functions 0/32
2.5% Lines 1/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123          1x                                                                                                                                                                                                                                          
import { defineStore } from 'pinia'
import { watch } from 'vue'
import { DEFAULT_PRIMARY } from '@/theme/presets'
import { applyPrimaryColor, applyDarkMode } from '@/theme/index'
 
export const useAppStore = defineStore('app', {
  state: () => ({
    darkMode: false,
    primaryColor: DEFAULT_PRIMARY,
    /** 侧边栏是否折叠为图标栏 */
    sidebarCollapsed: false,
    /** 界面语言(占位:项目暂无 i18n 文案,仅保留切换状态) */
    locale: 'zh-CN',
    /** 已访问页签列表;工作台固定不可关闭 */
    visitedTabs: [{ key: '/dashboard', label: '工作台', closable: false, name: 'Dashboard' }],
    /** 路由刷新计数表:key 为路由 fullPath,值变化即强制重渲染该路由 */
    routeRefreshKeys: {},
  }),
 
  getters: {
    /** keep-alive 的 include 列表:当前打开页签对应的组件名(去重)。
     *  页签关闭时该集合随之缩小,keep-alive 据此销毁对应缓存实例,
     *  实现"关闭页签即销毁缓存" */
    cachedViews: (state) => {
      const names = state.visitedTabs.map((t) => t.name).filter(Boolean)
      return [...new Set(names)]
    },
  },
 
  actions: {
    setDark(value) {
      this.darkMode = !!value
    },
    toggleDark() {
      this.darkMode = !this.darkMode
    },
    setPrimary(color) {
      this.primaryColor = color
    },
    toggleSidebar() {
      this.sidebarCollapsed = !this.sidebarCollapsed
    },
    setSidebar(collapsed) {
      this.sidebarCollapsed = !!collapsed
    },
    toggleLocale() {
      this.locale = this.locale === 'zh-CN' ? 'en-US' : 'zh-CN'
    },
    addVisitedTab(tab) {
      if (this.visitedTabs.some((t) => t.key === tab.key)) return
      this.visitedTabs.push(tab)
    },
    removeVisitedTab(key) {
      const index = this.visitedTabs.findIndex((t) => t.key === key)
      if (index >= 0) this.visitedTabs.splice(index, 1)
      // 同步清理该页签的刷新计数(对齐 old1:关闭即释放,避免 key 无限累积)
      if (key in this.routeRefreshKeys) delete this.routeRefreshKeys[key]
    },
    /** 关闭其他:保留工作台(不可关)与指定 key */
    closeOthers(key) {
      this.visitedTabs = this.visitedTabs.filter((t) => t.key === '/dashboard' || t.key === key)
      this.pruneOrphanRefreshKeys()
    },
    /** 关闭左侧:保留工作台与指定 key 及其右侧 */
    closeLeft(key) {
      const idx = this.visitedTabs.findIndex((t) => t.key === key)
      if (idx <= 0) return
      this.visitedTabs = this.visitedTabs.filter((t, i) => i >= idx || t.key === '/dashboard')
      this.pruneOrphanRefreshKeys()
    },
    /** 关闭右侧:保留指定 key 及其左侧(含工作台) */
    closeRight(key) {
      const idx = this.visitedTabs.findIndex((t) => t.key === key)
      if (idx < 0) return
      this.visitedTabs = this.visitedTabs.filter((t, i) => i <= idx)
      this.pruneOrphanRefreshKeys()
    },
    /** 关闭全部:仅保留工作台 */
    closeAll() {
      this.visitedTabs = this.visitedTabs.filter((t) => t.key === '/dashboard')
      this.pruneOrphanRefreshKeys()
    },
    /** 清理已关闭页签残留的刷新计数,避免 routeRefreshKeys 无限增长 */
    pruneOrphanRefreshKeys() {
      const keep = new Set(this.visitedTabs.map((t) => t.key))
      for (const key of Object.keys(this.routeRefreshKeys)) {
        if (!keep.has(key)) delete this.routeRefreshKeys[key]
      }
    },
    getRouteRefreshKey(key) {
      return this.routeRefreshKeys[key] ?? 0
    },
    refreshRoute(key) {
      this.routeRefreshKeys[key] = (this.routeRefreshKeys[key] ?? 0) + 1
    },
  },
 
  // 持久化状态:仅持久化主题与语言;sidebarCollapsed / visitedTabs / routeRefreshKeys 不持久化(本次会话有效,刷新恢复默认)
  persist: {
    pick: ['darkMode', 'primaryColor', 'locale'],
  },
})
 
// 在 store 定义后立即建立 watch,将状态同步到 DOM
// 需在 pinia 实例激活后才能调用,故封装成初始化函数
export function initThemeSync(pinia) {
  const store = useAppStore(pinia)
 
  watch(
    () => store.darkMode,
    (val) => applyDarkMode(val),
    { immediate: true },
  )
 
  watch(
    () => store.primaryColor,
    (val) => applyPrimaryColor(val),
    { immediate: true },
  )
 
  return store
}