feat(projects): 添加生产的主题配置缓存

This commit is contained in:
Soybean 2022-06-20 23:23:55 +08:00
parent 5c1b086cb4
commit 718c36263e
16 changed files with 137 additions and 48 deletions

View File

@ -76,7 +76,7 @@ Soybean Admin 是一个基于 Vue3、Vite、TypeScript、Naive UI 的免费中
- [x] 引入ECharts替换AntV G2Plot - [x] 引入ECharts替换AntV G2Plot
- [x] 图表示例ECharts、AntV G2 - [x] 图表示例ECharts、AntV G2
- [x] 多页签支持query、hash等参数同一页面支持多个Tab - [x] 多页签支持query、hash等参数同一页面支持多个Tab
- [ ] 缓存主题配置 - [x] 缓存主题配置
- [ ] 添加锁屏组件、全局Iframe组件 - [ ] 添加锁屏组件、全局Iframe组件
- [ ] 示例页面完善 - [ ] 示例页面完善
- [ ] 表单、表格示例 - [ ] 表单、表格示例

View File

@ -15,10 +15,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { zhCN, dateZhCN } from 'naive-ui'; import { zhCN, dateZhCN } from 'naive-ui';
import { useThemeStore, subscribeStore } from '@/store'; import { useThemeStore, subscribeStore } from '@/store';
import { useGlobalEvents } from '@/composables';
const theme = useThemeStore(); const theme = useThemeStore();
subscribeStore(); subscribeStore();
useGlobalEvents();
</script> </script>
<style scoped></style> <style scoped></style>

14
src/composables/events.ts Normal file
View 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();
});
}

View File

@ -1,4 +1,5 @@
export * from './system'; export * from './system';
export * from './router'; export * from './router';
export * from './layout'; export * from './layout';
export * from './events';
export * from './echarts'; export * from './echarts';

View File

@ -15,6 +15,8 @@ export enum EnumStorageKey {
'refresh-token' = '__REFRESH_TOKEN__', 'refresh-token' = '__REFRESH_TOKEN__',
/** 用户信息 */ /** 用户信息 */
'user-info' = '__USER_INFO__', 'user-info' = '__USER_INFO__',
/** 主题配置 */
'theme-settings' = '__THEME_SETTINGS__',
/** 多页签路由信息 */ /** 多页签路由信息 */
'multi-tab-routes' = '__MULTI_TAB_ROUTES__' 'multi-tab-routes' = '__MULTI_TAB_ROUTES__'
} }

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

View File

@ -6,5 +6,16 @@ import FullScreen from './FullScreen.vue';
import ThemeMode from './ThemeMode.vue'; import ThemeMode from './ThemeMode.vue';
import UserAvatar from './UserAvatar.vue'; import UserAvatar from './UserAvatar.vue';
import SystemMessage from './SystemMessage.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
};

View File

@ -12,6 +12,7 @@
<full-screen /> <full-screen />
<theme-mode /> <theme-mode />
<system-message /> <system-message />
<setting-button v-if="isProd" />
<user-avatar /> <user-avatar />
</div> </div>
</dark-mode-container> </dark-mode-container>
@ -29,7 +30,8 @@ import {
FullScreen, FullScreen,
ThemeMode, ThemeMode,
UserAvatar, UserAvatar,
SystemMessage SystemMessage,
SettingButton
} from './components'; } from './components';
interface Props { interface Props {
@ -44,6 +46,8 @@ interface Props {
defineProps<Props>(); defineProps<Props>();
const theme = useThemeStore(); const theme = useThemeStore();
const isProd = import.meta.env.PROD;
</script> </script>
<style scoped> <style scoped>

View File

@ -27,11 +27,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, nextTick, watch } from 'vue'; import { ref, reactive, computed, nextTick, watch } from 'vue';
import { useEventListener } from '@vueuse/core';
import { ChromeTab, ButtonTab } from '@soybeanjs/vue-admin-tab'; import { ChromeTab, ButtonTab } from '@soybeanjs/vue-admin-tab';
import { Icon } from '@iconify/vue'; import { Icon } from '@iconify/vue';
import { useThemeStore, useTabStore } from '@/store'; import { useThemeStore, useTabStore } from '@/store';
import { setTabRoutes } from '@/utils';
import { ContextMenu } from './components'; import { ContextMenu } from './components';
interface Emits { interface Emits {
@ -95,11 +93,6 @@ watch(
immediate: true immediate: true
} }
); );
/** 页面离开时缓存多页签数据 */
useEventListener(window, 'beforeunload', () => {
setTabRoutes(tab.tabs);
});
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -9,7 +9,7 @@
<theme-config /> <theme-config />
</n-drawer-content> </n-drawer-content>
</n-drawer> </n-drawer>
<drawer-button /> <drawer-button v-if="isDev" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -17,6 +17,8 @@ import { useAppStore } from '@/store';
import { DrawerButton, DarkMode, LayoutMode, ThemeColorSelect, PageFunc, PageView, ThemeConfig } from './components'; import { DrawerButton, DarkMode, LayoutMode, ThemeColorSelect, PageFunc, PageView, ThemeConfig } from './components';
const app = useAppStore(); const app = useAppStore();
const isDev = import.meta.env.DEV;
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -1,4 +1,6 @@
import type { RouteRecordNormalized, RouteLocationNormalizedLoaded } from 'vue-router'; import type { RouteRecordNormalized, RouteLocationNormalizedLoaded } from 'vue-router';
import { EnumStorageKey } from '@/enum';
import { setLocal, getLocal } from '@/utils';
/** /**
* vue路由获取tab路由 * vue路由获取tab路由
@ -55,3 +57,30 @@ function hasFullPath(
): route is RouteLocationNormalizedLoaded { ): route is RouteLocationNormalizedLoaded {
return Boolean((route as RouteLocationNormalizedLoaded).fullPath); 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([]);
}

View File

@ -1,9 +1,16 @@
import type { Router, RouteLocationNormalizedLoaded } from 'vue-router'; import type { Router, RouteLocationNormalizedLoaded } from 'vue-router';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { useRouterPush } from '@/composables'; import { useRouterPush } from '@/composables';
import { getTabRoutes, clearTabRoutes } from '@/utils';
import { useThemeStore } from '../theme'; import { useThemeStore } from '../theme';
import { getTabRouteByVueRoute, isInTabRoutes, getIndexInTabRoutes, getIndexInTabRoutesByRouteName } from './helpers'; import {
getTabRouteByVueRoute,
isInTabRoutes,
getIndexInTabRoutes,
getIndexInTabRoutesByRouteName,
setTabRoutes,
getTabRoutes,
clearTabRoutes
} from './helpers';
interface TabState { interface TabState {
/** 多页签数据 */ /** 多页签数据 */
@ -43,6 +50,10 @@ export const useTabStore = defineStore('tab-store', {
clearTabRoutes(); clearTabRoutes();
this.$reset(); this.$reset();
}, },
/** 缓存页签路由数据 */
cacheTabRoutes() {
setTabRoutes(this.tabs);
},
/** /**
* *
* @param fullPath - fullPath * @param fullPath - fullPath

View File

@ -1,10 +1,18 @@
import type { GlobalThemeOverrides } from 'naive-ui'; import type { GlobalThemeOverrides } from 'naive-ui';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { themeSetting } from '@/settings'; 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 themeColor = getThemeColor() || themeSetting.themeColor;
const info = themeSetting.isCustomizeInfoColor ? themeSetting.otherColor.info : getColorPalette(themeColor, 7); const info = themeSetting.isCustomizeInfoColor ? themeSetting.otherColor.info : getColorPalette(themeColor, 7);
const otherColor = { ...themeSetting.otherColor, info }; 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']);
}

View File

@ -1,11 +1,11 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { darkTheme } from 'naive-ui'; import { darkTheme } from 'naive-ui';
import { getThemeSettings, getNaiveThemeOverrides } from './helpers'; import { initThemeSettings, getNaiveThemeOverrides, setThemeSettings, clearThemeSettings } from './helpers';
type ThemeState = Theme.Setting; type ThemeState = Theme.Setting;
export const useThemeStore = defineStore('theme-store', { export const useThemeStore = defineStore('theme-store', {
state: (): ThemeState => getThemeSettings(), state: (): ThemeState => initThemeSettings(),
getters: { getters: {
/** naiveUI的主题配置 */ /** naiveUI的主题配置 */
naiveThemeOverrides(state) { naiveThemeOverrides(state) {
@ -24,8 +24,16 @@ export const useThemeStore = defineStore('theme-store', {
actions: { actions: {
/** 重置theme状态 */ /** 重置theme状态 */
resetThemeStore() { resetThemeStore() {
clearThemeSettings();
this.$reset(); this.$reset();
}, },
/** 缓存主题配置 */
cacheThemeSettings() {
const isProd = import.meta.env.PROD;
if (isProd) {
setThemeSettings(this.$state);
}
},
/** 设置暗黑模式 */ /** 设置暗黑模式 */
setDarkMode(darkMode: boolean) { setDarkMode(darkMode: boolean) {
this.darkMode = darkMode; this.darkMode = darkMode;

View File

@ -4,5 +4,4 @@ export * from './cache';
export * from './auth'; export * from './auth';
export * from './menu'; export * from './menu';
export * from './breadcrumb'; export * from './breadcrumb';
export * from './tab';
export * from './regexp'; export * from './regexp';

View File

@ -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([]);
}