mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-17 17:26:38 +08:00
feat(projects): 添加生产的主题配置缓存
This commit is contained in:
parent
5c1b086cb4
commit
718c36263e
@ -76,7 +76,7 @@ Soybean Admin 是一个基于 Vue3、Vite、TypeScript、Naive UI 的免费中
|
||||
- [x] 引入ECharts替换AntV G2Plot
|
||||
- [x] 图表示例:ECharts、AntV G2
|
||||
- [x] 多页签:支持query、hash等参数,同一页面支持多个Tab
|
||||
- [ ] 缓存主题配置
|
||||
- [x] 缓存主题配置
|
||||
- [ ] 添加锁屏组件、全局Iframe组件
|
||||
- [ ] 示例页面完善
|
||||
- [ ] 表单、表格示例
|
||||
|
@ -15,10 +15,12 @@
|
||||
<script setup lang="ts">
|
||||
import { zhCN, dateZhCN } from 'naive-ui';
|
||||
import { useThemeStore, subscribeStore } from '@/store';
|
||||
import { useGlobalEvents } from '@/composables';
|
||||
|
||||
const theme = useThemeStore();
|
||||
|
||||
subscribeStore();
|
||||
useGlobalEvents();
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
14
src/composables/events.ts
Normal file
14
src/composables/events.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { useThemeStore, useTabStore } from '@/store';
|
||||
|
||||
/** 全局事件 */
|
||||
export function useGlobalEvents() {
|
||||
const theme = useThemeStore();
|
||||
const tab = useTabStore();
|
||||
|
||||
/** 页面离开时缓存多页签数据 */
|
||||
useEventListener(window, 'beforeunload', () => {
|
||||
theme.cacheThemeSettings();
|
||||
tab.cacheTabRoutes();
|
||||
});
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export * from './system';
|
||||
export * from './router';
|
||||
export * from './layout';
|
||||
export * from './events';
|
||||
export * from './echarts';
|
||||
|
@ -15,6 +15,8 @@ export enum EnumStorageKey {
|
||||
'refresh-token' = '__REFRESH_TOKEN__',
|
||||
/** 用户信息 */
|
||||
'user-info' = '__USER_INFO__',
|
||||
/** 主题配置 */
|
||||
'theme-settings' = '__THEME_SETTINGS__',
|
||||
/** 多页签路由信息 */
|
||||
'multi-tab-routes' = '__MULTI_TAB_ROUTES__'
|
||||
}
|
||||
|
19
src/layouts/common/GlobalHeader/components/SettingButton.vue
Normal file
19
src/layouts/common/GlobalHeader/components/SettingButton.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<hover-container
|
||||
class="w-40px h-full"
|
||||
tooltip-content="主题配置"
|
||||
:inverted="theme.header.inverted"
|
||||
@click="app.toggleSettingDrawerVisible"
|
||||
>
|
||||
<icon-ant-design-setting-outlined class="text-20px" />
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
|
||||
const app = useAppStore();
|
||||
const theme = useThemeStore();
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -6,5 +6,16 @@ import FullScreen from './FullScreen.vue';
|
||||
import ThemeMode from './ThemeMode.vue';
|
||||
import UserAvatar from './UserAvatar.vue';
|
||||
import SystemMessage from './SystemMessage.vue';
|
||||
import SettingButton from './SettingButton.vue';
|
||||
|
||||
export { MenuCollapse, GlobalBreadcrumb, HeaderMenu, GithubSite, FullScreen, ThemeMode, UserAvatar, SystemMessage };
|
||||
export {
|
||||
MenuCollapse,
|
||||
GlobalBreadcrumb,
|
||||
HeaderMenu,
|
||||
GithubSite,
|
||||
FullScreen,
|
||||
ThemeMode,
|
||||
UserAvatar,
|
||||
SystemMessage,
|
||||
SettingButton
|
||||
};
|
||||
|
@ -12,6 +12,7 @@
|
||||
<full-screen />
|
||||
<theme-mode />
|
||||
<system-message />
|
||||
<setting-button v-if="isProd" />
|
||||
<user-avatar />
|
||||
</div>
|
||||
</dark-mode-container>
|
||||
@ -29,7 +30,8 @@ import {
|
||||
FullScreen,
|
||||
ThemeMode,
|
||||
UserAvatar,
|
||||
SystemMessage
|
||||
SystemMessage,
|
||||
SettingButton
|
||||
} from './components';
|
||||
|
||||
interface Props {
|
||||
@ -44,6 +46,8 @@ interface Props {
|
||||
defineProps<Props>();
|
||||
|
||||
const theme = useThemeStore();
|
||||
|
||||
const isProd = import.meta.env.PROD;
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -27,11 +27,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, nextTick, watch } from 'vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { ChromeTab, ButtonTab } from '@soybeanjs/vue-admin-tab';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { useThemeStore, useTabStore } from '@/store';
|
||||
import { setTabRoutes } from '@/utils';
|
||||
import { ContextMenu } from './components';
|
||||
|
||||
interface Emits {
|
||||
@ -95,11 +93,6 @@ watch(
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
/** 页面离开时缓存多页签数据 */
|
||||
useEventListener(window, 'beforeunload', () => {
|
||||
setTabRoutes(tab.tabs);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<theme-config />
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
<drawer-button />
|
||||
<drawer-button v-if="isDev" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -17,6 +17,8 @@ import { useAppStore } from '@/store';
|
||||
import { DrawerButton, DarkMode, LayoutMode, ThemeColorSelect, PageFunc, PageView, ThemeConfig } from './components';
|
||||
|
||||
const app = useAppStore();
|
||||
|
||||
const isDev = import.meta.env.DEV;
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -1,4 +1,6 @@
|
||||
import type { RouteRecordNormalized, RouteLocationNormalizedLoaded } from 'vue-router';
|
||||
import { EnumStorageKey } from '@/enum';
|
||||
import { setLocal, getLocal } from '@/utils';
|
||||
|
||||
/**
|
||||
* 根据vue路由获取tab路由
|
||||
@ -55,3 +57,30 @@ function hasFullPath(
|
||||
): route is RouteLocationNormalizedLoaded {
|
||||
return Boolean((route as RouteLocationNormalizedLoaded).fullPath);
|
||||
}
|
||||
|
||||
/** 缓存多页签数据 */
|
||||
export function setTabRoutes(data: GlobalTabRoute[]) {
|
||||
setLocal(EnumStorageKey['multi-tab-routes'], data);
|
||||
}
|
||||
|
||||
/** 获取缓存的多页签数据 */
|
||||
export function getTabRoutes() {
|
||||
const routes: GlobalTabRoute[] = [];
|
||||
const data = getLocal<GlobalTabRoute[]>(EnumStorageKey['multi-tab-routes']);
|
||||
if (data) {
|
||||
const defaultTabRoutes = data.map(item => ({
|
||||
...item,
|
||||
scrollPosition: {
|
||||
left: 0,
|
||||
top: 0
|
||||
}
|
||||
}));
|
||||
routes.push(...defaultTabRoutes);
|
||||
}
|
||||
return routes;
|
||||
}
|
||||
|
||||
/** 清空多页签数据 */
|
||||
export function clearTabRoutes() {
|
||||
setTabRoutes([]);
|
||||
}
|
||||
|
@ -1,9 +1,16 @@
|
||||
import type { Router, RouteLocationNormalizedLoaded } from 'vue-router';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { getTabRoutes, clearTabRoutes } from '@/utils';
|
||||
import { useThemeStore } from '../theme';
|
||||
import { getTabRouteByVueRoute, isInTabRoutes, getIndexInTabRoutes, getIndexInTabRoutesByRouteName } from './helpers';
|
||||
import {
|
||||
getTabRouteByVueRoute,
|
||||
isInTabRoutes,
|
||||
getIndexInTabRoutes,
|
||||
getIndexInTabRoutesByRouteName,
|
||||
setTabRoutes,
|
||||
getTabRoutes,
|
||||
clearTabRoutes
|
||||
} from './helpers';
|
||||
|
||||
interface TabState {
|
||||
/** 多页签数据 */
|
||||
@ -43,6 +50,10 @@ export const useTabStore = defineStore('tab-store', {
|
||||
clearTabRoutes();
|
||||
this.$reset();
|
||||
},
|
||||
/** 缓存页签路由数据 */
|
||||
cacheTabRoutes() {
|
||||
setTabRoutes(this.tabs);
|
||||
},
|
||||
/**
|
||||
* 设置当前路由对应的页签为激活状态
|
||||
* @param fullPath - 路由fullPath
|
||||
|
@ -1,10 +1,18 @@
|
||||
import type { GlobalThemeOverrides } from 'naive-ui';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { themeSetting } from '@/settings';
|
||||
import { getThemeColor, getColorPalette, addColorAlpha } from '@/utils';
|
||||
import { EnumStorageKey } from '@/enum';
|
||||
import { getThemeColor, getColorPalette, addColorAlpha, setLocal, getLocal, removeLocal } from '@/utils';
|
||||
|
||||
/** 初始化主题配置 */
|
||||
export function initThemeSettings() {
|
||||
const isProd = import.meta.env.PROD;
|
||||
// 生产环境才缓存主题配置,本地开发实时调整配置更改配置的json
|
||||
const storageSettings = getThemeSettings();
|
||||
if (isProd && storageSettings) {
|
||||
return storageSettings;
|
||||
}
|
||||
|
||||
/** 获取主题配置 */
|
||||
export function getThemeSettings() {
|
||||
const themeColor = getThemeColor() || themeSetting.themeColor;
|
||||
const info = themeSetting.isCustomizeInfoColor ? themeSetting.otherColor.info : getColorPalette(themeColor, 7);
|
||||
const otherColor = { ...themeSetting.otherColor, info };
|
||||
@ -70,3 +78,18 @@ export function getNaiveThemeOverrides(colors: Record<ColorType, string>): Globa
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** 获取缓存中的主题配置 */
|
||||
function getThemeSettings() {
|
||||
return getLocal<Theme.Setting>(EnumStorageKey['theme-settings']);
|
||||
}
|
||||
|
||||
/** 获取缓存中的主题配置 */
|
||||
export function setThemeSettings(settings: Theme.Setting) {
|
||||
return setLocal(EnumStorageKey['theme-settings'], settings);
|
||||
}
|
||||
|
||||
/** 清除缓存配置 */
|
||||
export function clearThemeSettings() {
|
||||
removeLocal(EnumStorageKey['theme-settings']);
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { darkTheme } from 'naive-ui';
|
||||
import { getThemeSettings, getNaiveThemeOverrides } from './helpers';
|
||||
import { initThemeSettings, getNaiveThemeOverrides, setThemeSettings, clearThemeSettings } from './helpers';
|
||||
|
||||
type ThemeState = Theme.Setting;
|
||||
|
||||
export const useThemeStore = defineStore('theme-store', {
|
||||
state: (): ThemeState => getThemeSettings(),
|
||||
state: (): ThemeState => initThemeSettings(),
|
||||
getters: {
|
||||
/** naiveUI的主题配置 */
|
||||
naiveThemeOverrides(state) {
|
||||
@ -24,8 +24,16 @@ export const useThemeStore = defineStore('theme-store', {
|
||||
actions: {
|
||||
/** 重置theme状态 */
|
||||
resetThemeStore() {
|
||||
clearThemeSettings();
|
||||
this.$reset();
|
||||
},
|
||||
/** 缓存主题配置 */
|
||||
cacheThemeSettings() {
|
||||
const isProd = import.meta.env.PROD;
|
||||
if (isProd) {
|
||||
setThemeSettings(this.$state);
|
||||
}
|
||||
},
|
||||
/** 设置暗黑模式 */
|
||||
setDarkMode(darkMode: boolean) {
|
||||
this.darkMode = darkMode;
|
||||
|
@ -4,5 +4,4 @@ export * from './cache';
|
||||
export * from './auth';
|
||||
export * from './menu';
|
||||
export * from './breadcrumb';
|
||||
export * from './tab';
|
||||
export * from './regexp';
|
||||
|
@ -1,29 +0,0 @@
|
||||
import { EnumStorageKey } from '@/enum';
|
||||
import { setLocal, getLocal } from '../storage';
|
||||
|
||||
/** 缓存多页签数据 */
|
||||
export function setTabRoutes(data: GlobalTabRoute[]) {
|
||||
setLocal(EnumStorageKey['multi-tab-routes'], data);
|
||||
}
|
||||
|
||||
/** 获取缓存的多页签数据 */
|
||||
export function getTabRoutes() {
|
||||
const routes: GlobalTabRoute[] = [];
|
||||
const data = getLocal<GlobalTabRoute[]>(EnumStorageKey['multi-tab-routes']);
|
||||
if (data) {
|
||||
const defaultTabRoutes = data.map(item => ({
|
||||
...item,
|
||||
scrollPosition: {
|
||||
left: 0,
|
||||
top: 0
|
||||
}
|
||||
}));
|
||||
routes.push(...defaultTabRoutes);
|
||||
}
|
||||
return routes;
|
||||
}
|
||||
|
||||
/** 清空多页签数据 */
|
||||
export function clearTabRoutes() {
|
||||
setTabRoutes([]);
|
||||
}
|
Loading…
Reference in New Issue
Block a user