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