工匠基地官方网站
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

707 lines
19 KiB

/**
* 主题状态管理
*/
import { defineStore } from 'pinia';
import {
changeColor,
screenWidth,
screenHeight,
contentWidth,
contentHeight,
WEAK_CLASS,
BODY_LIMIT_CLASS,
DISABLES_CLASS
} from 'ele-admin-pro/es';
import type {
TabItem,
HeadStyleType,
SideStyleType,
LayoutStyleType,
SideMenuStyleType,
TabStyleType,
TabRemoveOption
} from 'ele-admin-pro/es';
import {
TAB_KEEP_ALIVE,
KEEP_ALIVE_EXCLUDES,
THEME_STORE_NAME
} from '@/config/setting';
// import { getCache } from '@/api/system/cache';
/**
* state 默认值
*/
const DEFAULT_STATE: ThemeState = Object.freeze({
// 页签数据
tabs: [],
// 是否折叠侧栏
collapse: false,
// 是否折叠一级侧栏
sideNavCollapse: true,
// 内容区域是否全屏
bodyFullscreen: false,
// 是否开启页签栏
showTabs: true,
// 是否开启页脚
showFooter: true,
// 顶栏风格: light(亮色), dark(暗色), primary(主色)
headStyle: 'light',
// 侧栏风格: light(亮色), dark(暗色)
sideStyle: 'dark',
// 布局风格: side(默认), top(顶栏导航), mix(混合导航)
layoutStyle: 'side',
// 侧栏菜单风格: default(默认), mix(双排侧栏)
sideMenuStyle: 'default',
// 页签风格: default(默认), dot(圆点), card(卡片)
tabStyle: 'default',
// 路由切换动画
transitionName: 'fade',
// 是否固定顶栏
fixedHeader: true,
// 是否固定侧栏
fixedSidebar: true,
// 是否固定主体
fixedBody: true,
// 内容区域宽度铺满
bodyFull: true,
// logo 是否自适应宽度
logoAutoSize: false,
// 侧栏是否彩色图标
colorfulIcon: false,
// 侧栏是否只保持一个子菜单展开
sideUniqueOpen: false,
// 是否是色弱模式
weakMode: false,
// 是否是暗黑模式
darkMode: false,
// 主题色
color: '',
// 主页的组件名称
homeComponents: [],
// 刷新路由时的参数
routeReload: null,
// 屏幕宽度
screenWidth: screenWidth(),
// 屏幕高度
screenHeight: screenHeight(),
// 内容区域宽度
contentWidth: contentWidth(),
// 内容区域高度
contentHeight: contentHeight(),
// 是否开启响应式
styleResponsive: true
});
// 延时操作定时器
let disableTransitionTimer: number, updateContentSizeTimer: number;
/**
* 读取缓存配置
*/
function getCacheSetting(): any {
try {
const value = localStorage.getItem(THEME_STORE_NAME);
// 加载redis缓存
// getCache('theme').then((data) => {
// if (typeof data === 'object') {
// // 写入本地缓存
// localStorage.setItem(THEME_STORE_NAME, JSON.stringify(data));
// return data;
// }
// });
if (value) {
const cache = JSON.parse(value);
// 加载本地缓存
if (typeof cache === 'object') {
return cache;
}
}
} catch (e) {
console.error(e);
}
return {};
}
/**
* 缓存配置
*/
function cacheSetting(key: string, value: any) {
const cache = getCacheSetting();
if (cache[key] !== value) {
cache[key] = value;
// console.log(value);
// localStorage.setItem(THEME_STORE_NAME, JSON.stringify(cache));
// updateCacheTheme({
// key: 'theme',
// content: JSON.stringify(cache)
// }).then((res) => {
// console.log(res);
// });
}
}
/**
* 开关响应式布局
*/
function changeStyleResponsive(styleResponsive: boolean) {
if (styleResponsive) {
document.body.classList.remove(BODY_LIMIT_CLASS);
} else {
document.body.classList.add(BODY_LIMIT_CLASS);
}
}
/**
* 切换色弱模式
*/
function changeWeakMode(weakMode: boolean) {
if (weakMode) {
document.body.classList.add(WEAK_CLASS);
} else {
document.body.classList.remove(WEAK_CLASS);
}
}
/**
* 切换主题
*/
function changeTheme(value?: string | null, dark?: boolean) {
return new Promise<void>((resolve, reject) => {
try {
changeColor(value, dark);
resolve();
} catch (e) {
reject(e);
}
});
}
/**
* 切换布局时禁用过渡动画
*/
function disableTransition() {
disableTransitionTimer && clearTimeout(disableTransitionTimer);
document.body.classList.add(DISABLES_CLASS);
disableTransitionTimer = setTimeout(() => {
document.body.classList.remove(DISABLES_CLASS);
}, 100) as unknown as number;
}
export const useThemeStore = defineStore({
id: 'theme',
state: (): ThemeState => {
const state = { ...DEFAULT_STATE };
// 读取本地缓存
const cache = getCacheSetting();
Object.keys(state).forEach((key) => {
if (typeof cache[key] !== 'undefined') {
state[key] = cache[key];
}
});
return state;
},
getters: {
// 需要 keep-alive 的组件
keepAliveInclude(): string[] {
if (!TAB_KEEP_ALIVE || !this.showTabs) {
return [];
}
const components = new Set<string>();
const { reloadPath, reloadHome } = this.routeReload || {};
this.tabs?.forEach((t) => {
const isAlive = t.meta?.keepAlive !== false;
const isExclude = KEEP_ALIVE_EXCLUDES.includes(t.path as string);
const isReload = reloadPath && reloadPath === t.fullPath;
if (isAlive && !isExclude && !isReload && t.components) {
t.components.forEach((c) => {
if (typeof c === 'string' && c) {
components.add(c);
}
});
}
});
if (!reloadHome) {
this.homeComponents?.forEach((c) => {
if (typeof c === 'string' && c) {
components.add(c);
}
});
}
return Array.from(components);
}
},
actions: {
setTabs(value: TabItem[]) {
this.tabs = value;
//cacheSetting('tabs', value);
},
setCollapse(value: boolean) {
this.collapse = value;
this.delayUpdateContentSize(800);
},
setSideNavCollapse(value: boolean) {
this.sideNavCollapse = value;
this.delayUpdateContentSize(800);
},
setBodyFullscreen(value: boolean) {
disableTransition();
this.bodyFullscreen = value;
this.delayUpdateContentSize(800);
},
setShowTabs(value: boolean) {
this.showTabs = value;
cacheSetting('showTabs', value);
this.delayUpdateContentSize();
},
setShowFooter(value: boolean) {
this.showFooter = value;
cacheSetting('showFooter', value);
this.delayUpdateContentSize();
},
setHeadStyle(value: HeadStyleType) {
this.headStyle = value;
cacheSetting('headStyle', value);
},
setSideStyle(value: SideStyleType) {
this.sideStyle = value;
cacheSetting('sideStyle', value);
},
setLayoutStyle(value: LayoutStyleType) {
disableTransition();
this.layoutStyle = value;
cacheSetting('layoutStyle', value);
this.delayUpdateContentSize();
},
setSideMenuStyle(value: SideMenuStyleType) {
disableTransition();
this.sideMenuStyle = value;
cacheSetting('sideMenuStyle', value);
this.delayUpdateContentSize();
},
setTabStyle(value: TabStyleType) {
this.tabStyle = value;
cacheSetting('tabStyle', value);
},
setTransitionName(value: string) {
this.transitionName = value;
cacheSetting('transitionName', value);
},
setFixedHeader(value: boolean) {
disableTransition();
this.fixedHeader = value;
cacheSetting('fixedHeader', value);
},
setFixedSidebar(value: boolean) {
disableTransition();
this.fixedSidebar = value;
cacheSetting('fixedSidebar', value);
},
setFixedBody(value: boolean) {
disableTransition();
this.fixedBody = value;
cacheSetting('fixedBody', value);
},
setBodyFull(value: boolean) {
this.bodyFull = value;
cacheSetting('bodyFull', value);
this.delayUpdateContentSize();
},
setLogoAutoSize(value: boolean) {
disableTransition();
this.logoAutoSize = value;
cacheSetting('logoAutoSize', value);
},
setColorfulIcon(value: boolean) {
this.colorfulIcon = value;
cacheSetting('colorfulIcon', value);
},
setSideUniqueOpen(value: boolean) {
this.sideUniqueOpen = value;
cacheSetting('sideUniqueOpen', value);
},
setStyleResponsive(value: boolean) {
changeStyleResponsive(value);
this.styleResponsive = value;
cacheSetting('styleResponsive', value);
},
/**
* 切换色弱模式
* @param value 是否是色弱模式
*/
setWeakMode(value: boolean) {
return new Promise<void>((resolve) => {
changeWeakMode(value);
this.weakMode = value;
cacheSetting('weakMode', value);
resolve();
});
},
/**
* 切换暗黑模式
* @param value 是否是暗黑模式
*/
setDarkMode(value: boolean) {
return new Promise<void>((resolve, reject) => {
changeTheme(this.color, value)
.then(() => {
this.darkMode = value;
cacheSetting('darkMode', value);
resolve();
})
.catch((e) => {
reject(e);
});
});
},
/**
* 切换主题色
* @param value 主题色
*/
setColor(value?: string) {
return new Promise<void>((resolve, reject) => {
changeTheme(value, this.darkMode)
.then(() => {
this.color = value;
cacheSetting('color', value);
resolve();
})
.catch((e) => {
reject(e);
});
});
},
/**
* 设置主页路由对应的组件名称
* @param components 组件名称
*/
setHomeComponents(components: string[]) {
this.homeComponents = components;
},
/**
* 设置刷新路由信息
* @param option 路由刷新参数
*/
setRouteReload(option: RouteReloadOption | null) {
this.routeReload = option;
},
/**
* 更新屏幕尺寸
*/
updateScreenSize() {
this.screenWidth = screenWidth();
this.screenHeight = screenHeight();
this.updateContentSize();
},
/**
* 更新内容区域尺寸
*/
updateContentSize() {
this.contentWidth = contentWidth();
this.contentHeight = contentHeight();
},
/**
* 延时更新内容区域尺寸
* @param delay 延迟时间
*/
delayUpdateContentSize(delay?: number) {
updateContentSizeTimer && clearTimeout(updateContentSizeTimer);
updateContentSizeTimer = setTimeout(() => {
this.updateContentSize();
}, delay ?? 100) as unknown as number;
},
/**
* 重置设置
*/
resetSetting() {
return new Promise<void>((resolve, reject) => {
disableTransition();
this.showTabs = DEFAULT_STATE.showTabs;
this.showFooter = DEFAULT_STATE.showFooter;
this.headStyle = DEFAULT_STATE.headStyle;
this.sideStyle = DEFAULT_STATE.sideStyle;
this.layoutStyle = DEFAULT_STATE.layoutStyle;
this.sideMenuStyle = DEFAULT_STATE.sideMenuStyle;
this.tabStyle = DEFAULT_STATE.tabStyle;
this.transitionName = DEFAULT_STATE.transitionName;
this.fixedHeader = DEFAULT_STATE.fixedHeader;
this.fixedSidebar = DEFAULT_STATE.fixedSidebar;
this.fixedBody = DEFAULT_STATE.fixedBody;
this.bodyFull = DEFAULT_STATE.bodyFull;
this.logoAutoSize = DEFAULT_STATE.logoAutoSize;
this.colorfulIcon = DEFAULT_STATE.colorfulIcon;
this.sideUniqueOpen = DEFAULT_STATE.sideUniqueOpen;
this.styleResponsive = DEFAULT_STATE.styleResponsive;
this.weakMode = DEFAULT_STATE.weakMode;
this.darkMode = DEFAULT_STATE.darkMode;
this.color = DEFAULT_STATE.color;
localStorage.removeItem(THEME_STORE_NAME);
Promise.all([
changeStyleResponsive(this.styleResponsive),
changeWeakMode(this.weakMode),
changeTheme(this.color, this.darkMode)
])
.then(() => {
resolve();
})
.catch((e) => {
reject(e);
});
});
},
/**
* 恢复主题
*/
recoverTheme() {
// 关闭响应式布局
if (!this.styleResponsive) {
changeStyleResponsive(false);
}
// 恢复色弱模式
if (this.weakMode) {
changeWeakMode(true);
}
// 恢复主题色
if (this.color || this.darkMode) {
changeTheme(this.color, this.darkMode).catch((e) => {
console.error(e);
});
}
},
/**
* 添加页签或更新相同 key 的页签数据
* @param data 页签数据
*/
tabAdd(data: TabItem | TabItem[]) {
if (Array.isArray(data)) {
data.forEach((d) => {
this.tabAdd(d);
});
return;
}
const i = this.tabs.findIndex((d) => d.key === data.key);
if (i === -1) {
this.setTabs(this.tabs.concat([data]));
} else if (data.fullPath !== this.tabs[i].fullPath) {
this.setTabs(
this.tabs
.slice(0, i)
.concat([data])
.concat(this.tabs.slice(i + 1))
);
}
},
/**
* 关闭页签
* @param key 页签 key
*/
async tabRemove({
key,
active
}: TabRemoveOption): Promise<TabRemoveResult> {
const i = this.tabs.findIndex((t) => t.key === key || t.fullPath === key);
if (i === -1) {
return {};
}
const t = this.tabs[i];
if (!t.closable) {
return Promise.reject();
}
const path = this.tabs[i - 1]?.fullPath;
this.setTabs(this.tabs.filter((_d, j) => j !== i));
return t.key === active ? { path, home: !path } : {};
},
/**
* 关闭左侧页签
*/
async tabRemoveLeft({
key,
active
}: TabRemoveOption): Promise<TabRemoveResult> {
let index = -1; // 选中页签的 index
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].key === active) {
index = i;
}
if (this.tabs[i].key === key) {
if (i === 0) {
break;
}
const temp = this.tabs.filter((d, j) => !d.closable && j < i);
if (temp.length === i + 1) {
break;
}
const path = index === -1 ? void 0 : this.tabs[i].fullPath;
this.setTabs(temp.concat(this.tabs.slice(i)));
return { path };
}
}
return Promise.reject();
},
/**
* 关闭右侧页签
*/
async tabRemoveRight({
key,
active
}: TabRemoveOption): Promise<TabRemoveResult> {
if (this.tabs.length) {
let index = -1; // 选中页签的 index
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].key === active) {
index = i;
}
if (this.tabs[i].key === key) {
if (i === this.tabs.length - 1) {
return Promise.reject();
}
const temp = this.tabs.filter((d, j) => !d.closable && j > i);
if (temp.length === this.tabs.length - i - 1) {
return Promise.reject();
}
const path = index === -1 ? this.tabs[i].fullPath : void 0;
this.setTabs(
this.tabs
.slice(0, i + 1)
.concat(this.tabs.filter((d, j) => !d.closable && j > i))
);
return { path };
}
}
// 主页时关闭全部
const temp = this.tabs.filter((d) => !d.closable);
if (temp.length !== this.tabs.length) {
this.setTabs(temp);
return { home: index !== -1 };
}
}
return Promise.reject();
},
/**
* 关闭其它页签
*/
async tabRemoveOther({
key,
active
}: TabRemoveOption): Promise<TabRemoveResult> {
let index = -1; // 选中页签的 index
let path: string | undefined; // 关闭后跳转的 path
const temp = this.tabs.filter((d, i) => {
if (d.key === active) {
index = i;
}
if (d.key === key) {
path = d.fullPath;
}
return !d.closable || d.key === key;
});
if (temp.length === this.tabs.length) {
return Promise.reject();
}
this.setTabs(temp);
if (index === -1) {
return {};
}
return key === active ? {} : { path, home: !path };
},
/**
* 关闭全部页签
* @param active 选中页签的 key
*/
async tabRemoveAll(active: string): Promise<TabRemoveResult> {
const t = this.tabs.find((d) => d.key === active);
const home = typeof t !== 'undefined' && t.closable === true; // 是否跳转主页
const temp = this.tabs.filter((d) => !d.closable);
if (temp.length === this.tabs.length) {
return Promise.reject();
}
this.setTabs(temp);
return { home };
},
/**
* 修改页签
* @param data 页签数据
*/
tabSetItem(data: TabItem) {
let i = -1;
if (data.key) {
i = this.tabs.findIndex((d) => d.key === data.key);
} else if (data.fullPath) {
i = this.tabs.findIndex((d) => d.fullPath === data.fullPath);
} else if (data.path) {
i = this.tabs.findIndex((d) => d.path === data.path);
}
if (i !== -1) {
const item = { ...this.tabs[i] };
if (data.title) {
item.title = data.title;
}
if (typeof data.closable === 'boolean') {
item.closable = data.closable;
}
if (data.components) {
item.components = data.components;
}
this.setTabs(
this.tabs
.slice(0, i)
.concat([item])
.concat(this.tabs.slice(i + 1))
);
}
}
}
});
/**
* 主题 State 类型
*/
export interface ThemeState {
tabs: TabItem[];
collapse: boolean;
sideNavCollapse: boolean;
bodyFullscreen: boolean;
showTabs: boolean;
showFooter: boolean;
headStyle: HeadStyleType;
sideStyle: SideStyleType;
layoutStyle: LayoutStyleType;
sideMenuStyle: SideMenuStyleType;
tabStyle: TabStyleType;
transitionName: string;
fixedHeader: boolean;
fixedSidebar: boolean;
fixedBody: boolean;
bodyFull: boolean;
logoAutoSize: boolean;
colorfulIcon: boolean;
sideUniqueOpen: boolean;
weakMode: boolean;
darkMode: boolean;
color?: string | null;
homeComponents: string[];
routeReload: RouteReloadOption | null;
screenWidth: number;
screenHeight: number;
contentWidth: number;
contentHeight: number;
styleResponsive: boolean;
}
/**
* 设置路由刷新方法的参数
*/
export interface RouteReloadOption {
// 是否是刷新主页
reloadHome?: boolean;
// 要刷新的页签路由地址
reloadPath?: string;
}
/**
* 关闭页签返回类型
*/
export interface TabRemoveResult {
// 关闭后要跳转的地址
path?: string;
// 关闭后是否跳转到主页
home?: boolean;
}