All files / src/stores app.js

83.92% Statements 47/56
81.81% Branches 18/22
84.37% Functions 27/32
80% Lines 32/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          3x 16x                                   4x 1x           1x     1x     1x     1x     1x     1x     52x 24x     2x 1x   1x       4x 1x       4x 2x 4x 1x       3x 1x 4x 1x       4x 1x       9x 4x 8x       2x     26x                                                          
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)
      Eif (index >= 0) this.visitedTabs.splice(index, 1)
      // 同步清理该页签的刷新计数(对齐 old1:关闭即释放,避免 key 无限累积)
      Eif (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)
      Iif (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
}