/** * 主题 / 页签状态(精简版,对齐 WT 布局所需能力) */ import { screenWidth, screenHeight, contentWidth, contentHeight, } from 'ele-admin'; import { TAB_KEEP_ALIVE, KEEP_ALIVE_EXCLUDES, getThemeStoreName, HOME_PATH, LAYOUT_PATH, isHomeAliasPath, } from '@/config/setting'; import Vue from 'vue'; const HOME_ROUTE = HOME_PATH || '/home/index'; function isHomeTabKey(key) { if (!key) return false; if (key === LAYOUT_PATH || key === '/') return true; const pathOnly = String(key).split('?')[0]; return isHomeAliasPath(pathOnly) || pathOnly === HOME_ROUTE; } function withoutHomeTabs(tabs = []) { return (tabs || []).filter( (tab) => tab && !isHomeTabKey(tab.key) && !isHomeTabKey(tab.path) && !isHomeTabKey(tab.fullPath), ); } const DEFAULT_STATE = Object.freeze({ tabs: [], collapse: false, sideNavCollapse: false, bodyFullscreen: false, showTabs: true, showFooter: false, headStyle: 'light', sideStyle: 'dark', layoutStyle: 'side', sideMenuStyle: 'default', tabStyle: 'card', transitionName: 'slide-right', fixedHeader: false, fixedSidebar: true, fixedBody: true, bodyFull: true, logoAutoSize: false, colorfulIcon: false, sideUniqueOpen: true, weakMode: false, darkMode: false, color: null, homeComponents: [], routeReload: null, screenWidth: screenWidth(), screenHeight: screenHeight(), contentWidth: contentWidth(), contentHeight: contentHeight(), styleResponsive: true, }); function getCacheSetting() { try { const value = localStorage.getItem(getThemeStoreName()); if (value) { const cache = JSON.parse(value); if (typeof cache === 'object' && cache !== null) return cache; } } catch (e) { console.error(e); } return {}; } function cacheSetting(key, value) { const cache = getCacheSetting(); if (cache[key] !== value) { cache[key] = value; localStorage.setItem(getThemeStoreName(), JSON.stringify(cache)); } } export default { namespaced: true, state: (() => { const state = { ...DEFAULT_STATE }; const cache = getCacheSetting(); Object.keys(state).forEach((key) => { // 不从本地恢复页签:刷新后容易残留旧路径面包屑(如资质证照/新增) if (key === 'tabs' || key === 'homeComponents' || key === 'routeReload') { return; } if (typeof cache[key] !== 'undefined') state[key] = cache[key]; }); state.tabs = []; return state; })(), getters: { keepAliveInclude(state) { if (!TAB_KEEP_ALIVE || !state.showTabs) return []; const components = new Set(); const { reloadPath } = state.routeReload || {}; state.tabs?.forEach((t) => { const isAlive = t.meta?.keepAlive !== false; const isExclude = KEEP_ALIVE_EXCLUDES.includes(t.path); const isReload = reloadPath && reloadPath === t.fullPath; if (isAlive && !isExclude && !isReload && t.components) { t.components.forEach((c) => { if (typeof c === 'string' && c) components.add(c); }); } }); state.homeComponents?.forEach((c) => { if (typeof c === 'string' && c) { if (!state.routeReload?.reloadHome) components.add(c); } }); return Array.from(components); }, }, mutations: { SET(state, { key, value }) { state[key] = value; }, }, actions: { setCollapse({ commit }, value) { commit('SET', { key: 'collapse', value }); cacheSetting('collapse', value); }, setSideNavCollapse({ commit }, value) { commit('SET', { key: 'sideNavCollapse', value }); cacheSetting('sideNavCollapse', value); }, setBodyFullscreen({ commit }, value) { commit('SET', { key: 'bodyFullscreen', value }); cacheSetting('bodyFullscreen', value); }, setTabs({ commit }, value) { commit('SET', { key: 'tabs', value: withoutHomeTabs(value) }); }, tabAdd({ state, commit }, data) { if (!data) return; if ( isHomeTabKey(data.key) || isHomeTabKey(data.path) || isHomeTabKey(data.fullPath) ) { return; } const tabs = withoutHomeTabs(state.tabs || []); const index = tabs.findIndex((t) => t.key === data.key); if (index === -1) { commit('SET', { key: 'tabs', value: [...tabs, data] }); } else { const next = tabs.slice(); next[index] = { ...next[index], ...data }; commit('SET', { key: 'tabs', value: next }); } }, tabRemove({ state, commit }, { key, active }) { // 与 EOM 一致:乾坤下页签由门户管理 if (window.__POWERED_BY_QIANKUN__) { return Vue.prototype.$portalStore?.dispatch('theme/tabRemove', { key, active, }); } if (isHomeTabKey(key)) return Promise.reject(); const tabs = withoutHomeTabs(state.tabs || []); const index = tabs.findIndex((t) => t.key === key); if (index === -1) return Promise.reject(); const target = tabs[index]; if (target?.closable === false) return Promise.reject(); const next = tabs.filter((t) => t.key !== key); commit('SET', { key: 'tabs', value: next }); if (active === key) { const fallback = next[index] || next[index - 1]; return Promise.resolve({ path: fallback?.fullPath || fallback?.key, home: !fallback, }); } return Promise.resolve({}); }, tabRemoveLeft({ state, commit }, { key, active }) { const tabs = withoutHomeTabs(state.tabs || []); const index = tabs.findIndex((t) => t.key === key); if (index <= 0) return Promise.reject(); const next = tabs.slice(index); commit('SET', { key: 'tabs', value: next }); if (!next.some((t) => t.key === active)) { return Promise.resolve({ path: next[0]?.fullPath || next[0]?.key }); } return Promise.resolve({}); }, tabRemoveRight({ state, commit }, { key, active }) { const tabs = withoutHomeTabs(state.tabs || []); const index = tabs.findIndex((t) => t.key === key); if (index === -1 || index >= tabs.length - 1) return Promise.reject(); const next = tabs.slice(0, index + 1); commit('SET', { key: 'tabs', value: next }); if (!next.some((t) => t.key === active)) { return Promise.resolve({ path: next[index]?.fullPath || next[index]?.key, home: !next[index], }); } return Promise.resolve({}); }, tabRemoveOther({ state, commit }, { key }) { const tabs = withoutHomeTabs(state.tabs || []); if (isHomeTabKey(key)) { commit('SET', { key: 'tabs', value: [] }); return Promise.resolve({ home: true }); } const next = tabs.filter((t) => t.key === key); if (!next.length) return Promise.reject(); commit('SET', { key: 'tabs', value: next }); return Promise.resolve({ path: next[0]?.fullPath || next[0]?.key, home: false, }); }, tabRemoveAll({ commit }) { commit('SET', { key: 'tabs', value: [] }); cacheSetting('tabs', []); return Promise.resolve({ home: true }); }, tabSetItem({ state, commit }, data) { if (!data?.key || isHomeTabKey(data.key)) return; const tabs = withoutHomeTabs(state.tabs || []); const index = tabs.findIndex((t) => t.key === data.key); if (index === -1) return; const next = tabs.slice(); next[index] = { ...next[index], ...data }; commit('SET', { key: 'tabs', value: next }); }, setHomeComponents({ commit }, value) { commit('SET', { key: 'homeComponents', value: value || [] }); }, setRouteReload({ commit }, value) { commit('SET', { key: 'routeReload', value }); return Promise.resolve(); }, updateScreenSize({ commit }) { commit('SET', { key: 'screenWidth', value: screenWidth() }); commit('SET', { key: 'screenHeight', value: screenHeight() }); commit('SET', { key: 'contentWidth', value: contentWidth() }); commit('SET', { key: 'contentHeight', value: contentHeight() }); }, setColor({ commit }, value) { commit('SET', { key: 'color', value }); cacheSetting('color', value); }, setWeakMode({ commit }, value) { commit('SET', { key: 'weakMode', value }); cacheSetting('weakMode', value); }, setStyleResponsive({ commit }, value) { commit('SET', { key: 'styleResponsive', value }); cacheSetting('styleResponsive', value); }, recoverTheme({ commit, state }) { // 清理与当前地址无关的脏页签,避免面包屑残留「资质证照/新增」等旧路径 const cache = getCacheSetting(); const tabs = withoutHomeTabs(cache.tabs || state.tabs || []); commit('SET', { key: 'tabs', value: tabs }); cacheSetting('tabs', tabs); }, }, };