mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-18 09:46:36 +08:00
refactor(hooks): optimize useContext and update useMixMenuContext
This commit is contained in:
parent
32b8f99071
commit
c965140b87
@ -1,5 +1,4 @@
|
|||||||
import { inject, provide } from 'vue';
|
import { inject, provide } from 'vue';
|
||||||
import type { InjectionKey } from 'vue';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use context
|
* Use context
|
||||||
@ -12,7 +11,7 @@ import type { InjectionKey } from 'vue';
|
|||||||
* import { ref } from 'vue';
|
* import { ref } from 'vue';
|
||||||
* import { useContext } from '@sa/hooks';
|
* import { useContext } from '@sa/hooks';
|
||||||
*
|
*
|
||||||
* export const { setupStore, useStore } = useContext('demo', () => {
|
* export const [provideDemoContext, useDemoContext] = useContext('demo', () => {
|
||||||
* const count = ref(0);
|
* const count = ref(0);
|
||||||
*
|
*
|
||||||
* function increment() {
|
* function increment() {
|
||||||
@ -35,10 +34,10 @@ import type { InjectionKey } from 'vue';
|
|||||||
* <div>A</div>
|
* <div>A</div>
|
||||||
* </template>
|
* </template>
|
||||||
* <script setup lang="ts">
|
* <script setup lang="ts">
|
||||||
* import { setupStore } from './context';
|
* import { provideDemoContext } from './context';
|
||||||
*
|
*
|
||||||
* setupStore();
|
* provideDemoContext();
|
||||||
* // const { increment } = setupStore(); // also can control the store in the parent component
|
* // const { increment } = provideDemoContext(); // also can control the store in the parent component
|
||||||
* </script>
|
* </script>
|
||||||
* ``` // B.vue
|
* ``` // B.vue
|
||||||
* ```vue
|
* ```vue
|
||||||
@ -46,9 +45,9 @@ import type { InjectionKey } from 'vue';
|
|||||||
* <div>B</div>
|
* <div>B</div>
|
||||||
* </template>
|
* </template>
|
||||||
* <script setup lang="ts">
|
* <script setup lang="ts">
|
||||||
* import { useStore } from './context';
|
* import { useDemoContext } from './context';
|
||||||
*
|
*
|
||||||
* const { count, increment } = useStore();
|
* const { count, increment } = useDemoContext();
|
||||||
* </script>
|
* </script>
|
||||||
* ```;
|
* ```;
|
||||||
*
|
*
|
||||||
@ -57,40 +56,41 @@ import type { InjectionKey } from 'vue';
|
|||||||
* @param contextName Context name
|
* @param contextName Context name
|
||||||
* @param fn Context function
|
* @param fn Context function
|
||||||
*/
|
*/
|
||||||
export default function useContext<T extends (...args: any[]) => any>(contextName: string, fn: T) {
|
export default function useContext<Arguments extends Array<any>, T>(
|
||||||
type Context = ReturnType<T>;
|
contextName: string,
|
||||||
|
composable: (...args: Arguments) => T
|
||||||
|
) {
|
||||||
|
const key = Symbol(contextName);
|
||||||
|
|
||||||
const { useProvide, useInject: useStore } = createContext<Context>(contextName);
|
/**
|
||||||
|
* Injects the context value.
|
||||||
|
*
|
||||||
|
* @param consumerName - The name of the component that is consuming the context. If provided, the component must be
|
||||||
|
* used within the context provider.
|
||||||
|
* @param defaultValue - The default value to return if the context is not provided.
|
||||||
|
* @returns The context value.
|
||||||
|
*/
|
||||||
|
const useInject = <N extends string | null | undefined = undefined>(
|
||||||
|
consumerName?: N,
|
||||||
|
defaultValue?: T
|
||||||
|
): N extends null | undefined ? T | null : T => {
|
||||||
|
const value = inject(key, defaultValue);
|
||||||
|
|
||||||
function setupStore(...args: Parameters<T>) {
|
if (consumerName && !value) {
|
||||||
const context: Context = fn(...args);
|
throw new Error(`\`${consumerName}\` must be used within \`${contextName}\``);
|
||||||
return useProvide(context);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
// @ts-expect-error - we want to return null if the value is undefined or null
|
||||||
/** Setup store in the parent component */
|
return value || null;
|
||||||
setupStore,
|
|
||||||
/** Use store in the child component */
|
|
||||||
useStore
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/** Create context */
|
const useProvide = (...args: Arguments) => {
|
||||||
function createContext<T>(contextName: string) {
|
const value = composable(...args);
|
||||||
const injectKey: InjectionKey<T> = Symbol(contextName);
|
|
||||||
|
|
||||||
function useProvide(context: T) {
|
provide(key, value);
|
||||||
provide(injectKey, context);
|
|
||||||
|
|
||||||
return context;
|
return value;
|
||||||
}
|
|
||||||
|
|
||||||
function useInject() {
|
|
||||||
return inject(injectKey) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
useProvide,
|
|
||||||
useInject
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return [useProvide, useInject] as const;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import GlobalTab from '../modules/global-tab/index.vue';
|
|||||||
import GlobalContent from '../modules/global-content/index.vue';
|
import GlobalContent from '../modules/global-content/index.vue';
|
||||||
import GlobalFooter from '../modules/global-footer/index.vue';
|
import GlobalFooter from '../modules/global-footer/index.vue';
|
||||||
import ThemeDrawer from '../modules/theme-drawer/index.vue';
|
import ThemeDrawer from '../modules/theme-drawer/index.vue';
|
||||||
import { setupMixMenuContext } from '../context';
|
import { provideMixMenuContext } from '../modules/global-menu/context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'BaseLayout'
|
name: 'BaseLayout'
|
||||||
@ -18,7 +18,7 @@ defineOptions({
|
|||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = setupMixMenuContext();
|
const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext();
|
||||||
|
|
||||||
const GlobalMenu = defineAsyncComponent(() => import('../modules/global-menu/index.vue'));
|
const GlobalMenu = defineAsyncComponent(() => import('../modules/global-menu/index.vue'));
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import type { RouteKey } from '@elegant-router/types';
|
|||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
|
|
||||||
export const { setupStore: setupMixMenuContext, useStore: useMixMenuContext } = useContext('mix-menu', useMixMenu);
|
export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu', useMixMenu);
|
||||||
|
|
||||||
function useMixMenu() {
|
function useMixMenu() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
@ -2,7 +2,7 @@
|
|||||||
import { GLOBAL_HEADER_MENU_ID } from '@/constants/app';
|
import { GLOBAL_HEADER_MENU_ID } from '@/constants/app';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useMenu } from '../../../context';
|
import { useMenu } from '../context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'HorizontalMenu'
|
name: 'HorizontalMenu'
|
||||||
|
@ -7,7 +7,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useMenu, useMixMenuContext } from '../../../context';
|
import { useMenu, useMixMenuContext } from '../context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'TopHybridHeaderFirst'
|
name: 'TopHybridHeaderFirst'
|
||||||
@ -18,7 +18,8 @@ const appStore = useAppStore();
|
|||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore();
|
||||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||||
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } = useMixMenuContext();
|
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||||
|
useMixMenuContext('TopHybridHeaderFirst');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
const expandedKeys = ref<string[]>([]);
|
const expandedKeys = ref<string[]>([]);
|
||||||
|
@ -4,7 +4,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import FirstLevelMenu from '../components/first-level-menu.vue';
|
import FirstLevelMenu from '../components/first-level-menu.vue';
|
||||||
import { useMenu, useMixMenuContext } from '../../../context';
|
import { useMenu, useMixMenuContext } from '../context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'TopHybridSidebarFirst'
|
name: 'TopHybridSidebarFirst'
|
||||||
@ -13,7 +13,8 @@ defineOptions({
|
|||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||||
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } = useMixMenuContext();
|
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||||
|
useMixMenuContext('TopHybridSidebarFirst');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useMenu, useMixMenuContext } from '../../../context';
|
import { useMenu, useMixMenuContext } from '../context';
|
||||||
import FirstLevelMenu from '../components/first-level-menu.vue';
|
import FirstLevelMenu from '../components/first-level-menu.vue';
|
||||||
import GlobalLogo from '../../global-logo/index.vue';
|
import GlobalLogo from '../../global-logo/index.vue';
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ const {
|
|||||||
handleSelectSecondLevelMenu,
|
handleSelectSecondLevelMenu,
|
||||||
getActiveSecondLevelMenuKey,
|
getActiveSecondLevelMenuKey,
|
||||||
childLevelMenus
|
childLevelMenus
|
||||||
} = useMixMenuContext();
|
} = useMixMenuContext('VerticalHybridHeaderFirst');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
||||||
|
@ -7,7 +7,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useMenu } from '../../../context';
|
import { useMenu } from '../context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'VerticalMenu'
|
name: 'VerticalMenu'
|
||||||
|
@ -10,7 +10,7 @@ import { useThemeStore } from '@/store/modules/theme';
|
|||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import { useMenu, useMixMenuContext } from '../../../context';
|
import { useMenu, useMixMenuContext } from '../context';
|
||||||
import FirstLevelMenu from '../components/first-level-menu.vue';
|
import FirstLevelMenu from '../components/first-level-menu.vue';
|
||||||
import GlobalLogo from '../../global-logo/index.vue';
|
import GlobalLogo from '../../global-logo/index.vue';
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ const {
|
|||||||
isActiveFirstLevelMenuHasChildren,
|
isActiveFirstLevelMenuHasChildren,
|
||||||
getActiveFirstLevelMenuKey,
|
getActiveFirstLevelMenuKey,
|
||||||
handleSelectFirstLevelMenu
|
handleSelectFirstLevelMenu
|
||||||
} = useMixMenuContext();
|
} = useMixMenuContext('VerticalMixMenu');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
||||||
|
Loading…
Reference in New Issue
Block a user