feat(store): 重构应用状态管理,移除旧的 app-store 并引入 app-store-auto-imports
This commit is contained in:
10
auto-imports.d.ts
vendored
10
auto-imports.d.ts
vendored
@@ -6,7 +6,6 @@
|
|||||||
// biome-ignore lint: disable
|
// biome-ignore lint: disable
|
||||||
export {}
|
export {}
|
||||||
declare global {
|
declare global {
|
||||||
const APP_THEME_MODES: typeof import('./src/stores/app-store')['APP_THEME_MODES']
|
|
||||||
const ConfirmationService: typeof import('utils4u/primevue')['ConfirmationService']
|
const ConfirmationService: typeof import('utils4u/primevue')['ConfirmationService']
|
||||||
const DialogService: typeof import('utils4u/primevue')['DialogService']
|
const DialogService: typeof import('utils4u/primevue')['DialogService']
|
||||||
const EffectScope: typeof import('vue')['EffectScope']
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
@@ -141,7 +140,7 @@ declare global {
|
|||||||
const until: typeof import('@vueuse/core')['until']
|
const until: typeof import('@vueuse/core')['until']
|
||||||
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||||
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
||||||
const useAppStore: typeof import('./src/stores/app-store')['useAppStore']
|
const useAppStore: typeof import('./src/stores/app-store-auto-imports')['useAppStore']
|
||||||
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
|
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
|
||||||
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
|
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
|
||||||
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
|
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
|
||||||
@@ -340,8 +339,8 @@ declare global {
|
|||||||
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||||
import('vue')
|
import('vue')
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export type { AppThemeMode } from './src/stores/app-store'
|
export type { AppThemeMode } from './src/stores/app-store-auto-imports'
|
||||||
import('./src/stores/app-store')
|
import('./src/stores/app-store-auto-imports')
|
||||||
}
|
}
|
||||||
|
|
||||||
// for vue template auto import
|
// for vue template auto import
|
||||||
@@ -349,7 +348,6 @@ import { UnwrapRef } from 'vue'
|
|||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
interface GlobalComponents {}
|
interface GlobalComponents {}
|
||||||
interface ComponentCustomProperties {
|
interface ComponentCustomProperties {
|
||||||
readonly APP_THEME_MODES: UnwrapRef<typeof import('./src/stores/app-store')['APP_THEME_MODES']>
|
|
||||||
readonly ConfirmationService: UnwrapRef<typeof import('utils4u/primevue')['ConfirmationService']>
|
readonly ConfirmationService: UnwrapRef<typeof import('utils4u/primevue')['ConfirmationService']>
|
||||||
readonly DialogService: UnwrapRef<typeof import('utils4u/primevue')['DialogService']>
|
readonly DialogService: UnwrapRef<typeof import('utils4u/primevue')['DialogService']>
|
||||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||||
@@ -484,7 +482,7 @@ declare module 'vue' {
|
|||||||
readonly until: UnwrapRef<typeof import('@vueuse/core')['until']>
|
readonly until: UnwrapRef<typeof import('@vueuse/core')['until']>
|
||||||
readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']>
|
readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']>
|
||||||
readonly useAnimate: UnwrapRef<typeof import('@vueuse/core')['useAnimate']>
|
readonly useAnimate: UnwrapRef<typeof import('@vueuse/core')['useAnimate']>
|
||||||
readonly useAppStore: UnwrapRef<typeof import('./src/stores/app-store')['useAppStore']>
|
readonly useAppStore: UnwrapRef<typeof import('./src/stores/app-store-auto-imports')['useAppStore']>
|
||||||
readonly useArrayDifference: UnwrapRef<typeof import('@vueuse/core')['useArrayDifference']>
|
readonly useArrayDifference: UnwrapRef<typeof import('@vueuse/core')['useArrayDifference']>
|
||||||
readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']>
|
readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']>
|
||||||
readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']>
|
readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const appStore = useAppStore();
|
|||||||
const themeLabels: Record<AppThemeMode, string> = {
|
const themeLabels: Record<AppThemeMode, string> = {
|
||||||
light: '浅色',
|
light: '浅色',
|
||||||
dark: '深色',
|
dark: '深色',
|
||||||
system: '跟随系统',
|
auto: '跟随系统',
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ const themeLabels: Record<AppThemeMode, string> = {
|
|||||||
<NTooltip :disabled="appStore.isMobile" placement="bottom-end">
|
<NTooltip :disabled="appStore.isMobile" placement="bottom-end">
|
||||||
{{ themeLabels[appStore.themeMode] }}
|
{{ themeLabels[appStore.themeMode] }}
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<NButton quaternary @click="appStore.cycleTheme">
|
<NButton quaternary @click="appStore.cycleTheme()">
|
||||||
<icon-line-md-sunny-filled-loop-to-moon-filled-loop-transition
|
<icon-line-md-sunny-filled-loop-to-moon-filled-loop-transition
|
||||||
v-if="appStore.themeMode === 'light'"
|
v-if="appStore.themeMode === 'light'"
|
||||||
w-4.5
|
w-4.5
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { useAppStore } from '@/stores/app-store';
|
import { useAppStore } from '@/stores/app-store-auto-imports';
|
||||||
|
|
||||||
const menuInstRef = useTemplateRef('menuInstRef');
|
const menuInstRef = useTemplateRef('menuInstRef');
|
||||||
const { options, selectedKey } = useMetaLayoutsNMenuOptions({
|
const { options, selectedKey } = useMetaLayoutsNMenuOptions({
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ consola.level = LogLevels.verbose;
|
|||||||
/* `import.meta.glob(${g}, { eager: ${isSync} })`; */
|
/* `import.meta.glob(${g}, { eager: ${isSync} })`; */
|
||||||
const autoInstallModules = import.meta.glob('./plugins/!(index).ts', { eager: true });
|
const autoInstallModules = import.meta.glob('./plugins/!(index).ts', { eager: true });
|
||||||
|
|
||||||
setupPlugins(createApp(App), autoInstallModules).mount('#app');
|
const app = setupPlugins(createApp(App), autoInstallModules);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 280));
|
||||||
|
app.mount('#app');
|
||||||
|
|||||||
42
src/stores/app-store-auto-imports.ts
Normal file
42
src/stores/app-store-auto-imports.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { useLocalStorage, useMediaQuery } from '@vueuse/core';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
// >>>>>
|
||||||
|
// https://vueuse.org/core/useColorMode/#advanced-usage
|
||||||
|
const { system, store: themeMode } = useColorMode({
|
||||||
|
modes: { light: '', dark: 'app-dark', auto: '' },
|
||||||
|
disableTransition: false,
|
||||||
|
});
|
||||||
|
const { state, next: cycleTheme } = useCycleList(['light', 'dark', 'auto'] as const, {
|
||||||
|
initialValue: themeMode,
|
||||||
|
});
|
||||||
|
watchEffect(() => (themeMode.value = state.value));
|
||||||
|
export type AppThemeMode = typeof themeMode.value;
|
||||||
|
// <<<<<
|
||||||
|
|
||||||
|
export const useAppStore = defineStore('app', () => {
|
||||||
|
// 侧边栏展开/收起状态
|
||||||
|
const sidebarCollapsed = useLocalStorage<boolean>('app-sidebar-collapsed', false);
|
||||||
|
const toggleSidebar = useToggle(sidebarCollapsed);
|
||||||
|
|
||||||
|
// 主题模式
|
||||||
|
const actualTheme = computed(() => (themeMode.value === 'auto' ? system.value : themeMode.value));
|
||||||
|
const isDark = computed(() => actualTheme.value === 'dark');
|
||||||
|
|
||||||
|
// 是否是移动端
|
||||||
|
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||||
|
|
||||||
|
return {
|
||||||
|
themeMode,
|
||||||
|
isDark,
|
||||||
|
isMobile,
|
||||||
|
cycleTheme,
|
||||||
|
sidebarCollapsed,
|
||||||
|
toggleSidebar,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.accept(acceptHMRUpdate(useAppStore, import.meta.hot));
|
||||||
|
}
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
import { useLocalStorage, useMediaQuery, usePreferredColorScheme } from '@vueuse/core';
|
|
||||||
import { defineStore } from 'pinia';
|
|
||||||
import { computed, watch } from 'vue';
|
|
||||||
|
|
||||||
export const APP_THEME_MODES = ['light', 'dark', 'system'] as const;
|
|
||||||
export type AppThemeMode = (typeof APP_THEME_MODES)[number];
|
|
||||||
|
|
||||||
const DARK_CLASS = 'app-dark';
|
|
||||||
|
|
||||||
export const useAppStore = defineStore('app', () => {
|
|
||||||
const themeMode = useLocalStorage<AppThemeMode>('app-theme-mode', 'system');
|
|
||||||
const preferredColor = usePreferredColorScheme();
|
|
||||||
|
|
||||||
// 侧边栏展开/收起状态
|
|
||||||
const sidebarCollapsed = useLocalStorage<boolean>('app-sidebar-collapsed', false);
|
|
||||||
|
|
||||||
// 计算实际使用的主题
|
|
||||||
const actualTheme = computed(() =>
|
|
||||||
themeMode.value === 'system'
|
|
||||||
? preferredColor.value === 'dark'
|
|
||||||
? 'dark'
|
|
||||||
: 'light'
|
|
||||||
: themeMode.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 是否是暗色主题
|
|
||||||
const isDark = computed(() => actualTheme.value === 'dark');
|
|
||||||
|
|
||||||
// 是否是移动端
|
|
||||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
|
||||||
|
|
||||||
// 更新 DOM 类名
|
|
||||||
function updateDomClass() {
|
|
||||||
document.documentElement.classList.toggle(DARK_CLASS, isDark.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 循环切换主题
|
|
||||||
function cycleTheme() {
|
|
||||||
const currentIndex = APP_THEME_MODES.indexOf(themeMode.value);
|
|
||||||
const nextIndex = (currentIndex + 1) % APP_THEME_MODES.length;
|
|
||||||
themeMode.value = APP_THEME_MODES[nextIndex]!;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换侧边栏展开/收起
|
|
||||||
function toggleSidebar() {
|
|
||||||
sidebarCollapsed.value = !sidebarCollapsed.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听主题变化,更新 DOM
|
|
||||||
watch(isDark, updateDomClass, { immediate: true });
|
|
||||||
|
|
||||||
return {
|
|
||||||
themeMode,
|
|
||||||
isDark,
|
|
||||||
isMobile,
|
|
||||||
cycleTheme,
|
|
||||||
sidebarCollapsed,
|
|
||||||
toggleSidebar,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.accept(acceptHMRUpdate(useAppStore, import.meta.hot));
|
|
||||||
}
|
|
||||||
@@ -63,7 +63,7 @@ export function loadPlugin(_configEnv: ConfigEnv): PluginOption {
|
|||||||
dirs: [
|
dirs: [
|
||||||
// 'src/utils',
|
// 'src/utils',
|
||||||
'src/composables',
|
'src/composables',
|
||||||
'src/stores',
|
// 'src/stores',
|
||||||
// 匹配所有 -auto-imports.ts / -auto-imports.tsx 结尾的文件
|
// 匹配所有 -auto-imports.ts / -auto-imports.tsx 结尾的文件
|
||||||
'src/**/*-auto-imports.{ts,tsx}',
|
'src/**/*-auto-imports.{ts,tsx}',
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { PluginOption } from 'vite';
|
|
||||||
import { minify as minifyHtml } from 'html-minifier-terser';
|
import { minify as minifyHtml } from 'html-minifier-terser';
|
||||||
|
import { loadEnv } from 'vite';
|
||||||
|
import type { ConfigEnv, PluginOption } from 'vite';
|
||||||
|
|
||||||
function IndexHtmlPlugin(): PluginOption {
|
function IndexHtmlPlugin(): PluginOption {
|
||||||
return {
|
return {
|
||||||
@@ -25,4 +26,7 @@ function IndexHtmlPlugin(): PluginOption {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default [IndexHtmlPlugin()] satisfies PluginOption[];
|
export function loadPlugin(_configEnv: ConfigEnv): PluginOption {
|
||||||
|
const env = loadEnv(_configEnv.mode, process.cwd());
|
||||||
|
if (env.VITE_BUILD_MINIFY === 'true') return IndexHtmlPlugin();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import type { ConfigEnv, PluginOption } from 'vite';
|
import type { ConfigEnv, PluginOption } from 'vite';
|
||||||
|
import { loadEnv } from 'vite';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
// ...
|
// ...
|
||||||
] satisfies PluginOption;
|
] satisfies PluginOption;
|
||||||
|
|
||||||
export function loadPlugin(_configEnv: ConfigEnv): PluginOption {
|
export function loadPlugin(_configEnv: ConfigEnv): PluginOption {
|
||||||
return [];
|
const env = loadEnv(_configEnv.mode, process.cwd());
|
||||||
|
console.debug(`env :>> `, env);
|
||||||
|
// ...
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user