{{ title }}
diff --git a/src/components/LLocalePicker/index.vue b/src/components/LLocalePicker/index.vue
new file mode 100644
index 0000000..a4c9180
--- /dev/null
+++ b/src/components/LLocalePicker/index.vue
@@ -0,0 +1,64 @@
+
+
+
+  
+    
+      
+        
+        {{ getLocaleText }}
+      
+    
+  
 
+
diff --git a/src/components/form/bg.vue b/src/components/form/bg.vue
deleted file mode 100644
index 3c8bdd1..0000000
--- a/src/components/form/bg.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
-  222
-
diff --git a/src/composables/config/app-config.ts b/src/composables/config/app-config.ts
index a01df1c..ea6555b 100644
--- a/src/composables/config/app-config.ts
+++ b/src/composables/config/app-config.ts
@@ -1,6 +1,6 @@
 import { storeToRefs } from 'pinia'
-import { HandlerSettingEnum, ThemeEnum } from '~/constants'
-import { _omit } from '~/utils'
+import { HandlerSettingEnum, NavBarModeEnum } from '~/constants'
+import { _merge, _omit } from '~/utils'
 
 export function useAppConfig() {
   const configStore = useAppConfigStore()
@@ -14,7 +14,19 @@ export function useAppConfig() {
   } = appConfigOptions
 
   function setAppConfig(configs: DeepPartial
) {
-    configStore.setAppConfig(configs)
+    configStore.$patch((state) => {
+      _merge(state, preDealConfig(configs))
+    })
+  }
+
+  function preDealConfig(configs: DeepPartial) {
+    if (
+      configs.navBarMode
+      && configs.navBarMode === NavBarModeEnum.MIX_SIDEBAR
+    ) {
+      configs.logo = { show: true, visible: true }
+    }
+    return configs
   }
 
   function toggleOpenSettingDrawer() {
@@ -82,12 +94,11 @@ function handlerResults(
   value: any,
   configOptions: DefineAppConfigOptions,
 ): DeepPartial {
-  const { themeColor, theme, sidebar, header } = configOptions
+  const { themeColor, sidebar, header } = configOptions
   switch (event) {
     case HandlerSettingEnum.CHANGE_LAYOUT:
-      // eslint-disable-next-line no-case-declarations
+    {
       const { mode, type, split } = value
-      // eslint-disable-next-line no-case-declarations
       const splitOpt = split === undefined ? { split } : {}
       return {
         navBarMode: type,
@@ -97,19 +108,17 @@ function handlerResults(
         },
         sidebar: { collapsed: false },
       }
-
+    }
+    // TODO remove it
     case HandlerSettingEnum.CHANGE_THEME_COLOR:
       if (unref(themeColor) === value)
         return {}
 
       // changeTheme(value);
       return { themeColor: value }
-
+      // TODO remove it
     case HandlerSettingEnum.CHANGE_THEME:
-      if (unref(theme) === value)
-        return {}
-
-      return { theme: value ? ThemeEnum.DARK : ThemeEnum.LIGHT }
+      return { theme: value }
 
     case HandlerSettingEnum.MENU_HAS_DRAG:
       return { menu: { canDrag: value } }
@@ -137,8 +146,9 @@ function handlerResults(
 
     case HandlerSettingEnum.MENU_THEME:
       // updateSidebarBgColor(value);
-      if (unref(sidebar).bgColor === value)
+      if (unref(sidebar).bgColor === value) {
         return {}
+      }
       return { sidebar: { bgColor: value } }
 
     case HandlerSettingEnum.MENU_SPLIT:
@@ -219,8 +229,9 @@ function handlerResults(
       // ============header==================
     case HandlerSettingEnum.HEADER_THEME:
       // updateHeaderBgColor(value);
-      if (unref(header).bgColor === value)
+      if (unref(header).bgColor === value) {
         return {}
+      }
       return { header: { bgColor: value } }
 
     case HandlerSettingEnum.HEADER_SEARCH:
diff --git a/src/composables/dark.ts b/src/composables/dark.ts
deleted file mode 100644
index 5cd66bc..0000000
--- a/src/composables/dark.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// these APIs are auto-imported from @vueuse/core
-export const isDark = useDark({
-  selector: 'html',
-  attribute: 'theme-mode',
-  valueDark: 'dark',
-  valueLight: '',
-})
-export const toggleDark = useToggle(isDark)
-export const preferredDark = usePreferredDark()
diff --git a/src/composables/i18n.ts b/src/composables/locale/i18n.ts
similarity index 59%
rename from src/composables/i18n.ts
rename to src/composables/locale/i18n.ts
index da26d7a..4e5a077 100644
--- a/src/composables/i18n.ts
+++ b/src/composables/locale/i18n.ts
@@ -1,4 +1,13 @@
-import { i18n } from '~/modules/i18n'
+import { i18n } from '~/modules/i18n/index'
+
+export interface I18nGlobalTranslation {
+  (key: string): string
+  (key: string, locale: string): string
+  (key: string, locale: string, list: unknown[]): string
+  (key: string, locale: string, named: Record): string
+  (key: string, list: unknown[]): string
+  (key: string, named: Record): string
+}
 
 type I18nTranslationRestParameters = [string, any]
 
@@ -22,13 +31,14 @@ export function useI18n(namespace?: string) {
     return normalFn
   }
   const { t, ...other } = i18n.global
-  const tFn = (key: string, ...arg: any[]) => {
+  const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => {
     if (!key) {
       return ''
     }
     if (!key.includes('.') && !namespace) {
       return key
     }
+    // @ts-expect-error no-err
     return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters))
   }
   return {
diff --git a/src/composables/locale/locale.ts b/src/composables/locale/locale.ts
new file mode 100644
index 0000000..1b8769d
--- /dev/null
+++ b/src/composables/locale/locale.ts
@@ -0,0 +1,26 @@
+import { storeToRefs } from 'pinia'
+import { i18n, loadLanguageAsync } from '~/modules/i18n'
+
+export function useLocale() {
+  const localeStore = useLocaleStore()
+  const { setLocale } = localeStore
+  const { getLocale } = storeToRefs(localeStore)
+
+  const changeLocale = async (locale: string) => {
+    const currentLocale = unref(i18n.global.locale) as string
+    if (locale === currentLocale) {
+      return locale
+    }
+
+    await loadLanguageAsync(locale)
+
+    setLocale(locale)
+
+    return locale
+  }
+
+  return {
+    getLocale,
+    changeLocale,
+  }
+}
diff --git a/src/composables/promise.ts b/src/composables/promise.ts
new file mode 100644
index 0000000..ee9e1aa
--- /dev/null
+++ b/src/composables/promise.ts
@@ -0,0 +1,166 @@
+import type { ShallowRef, UnwrapRef } from 'vue'
+import { containsProp, isEqual } from '~/utils'
+
+export interface UsePromiseOption {
+  /**
+   * 是否立即执行
+   * default: true
+   */
+  immediate?: boolean
+  /**
+   * 参数变化时是否自动执行函数
+   * default: false
+   */
+  redo?: boolean
+  /**
+   * 仅当redo = true时生效, 防抖时间. 单位 ms
+   * default: 0(ms)
+   */
+  debounce?: number
+  /**
+   * 再次请求时是否忽略当前的loading状态(能否重复执行)
+   * default: false
+   */
+  ignoreLoading?: boolean
+  /**
+   * 函数执行失败时是否对外抛出异常
+   * default: false
+   */
+  throwOnFailed?: boolean
+}
+
+export interface UsePromiseReturnType {
+  data: ShallowRef
+  loading: Ref>
+  handleFn: () => Promise
+  error: ShallowRef
+  finished: Ref>
+}
+
+function isUsePromiseOption(obj: object): obj is UsePromiseOption {
+  return containsProp(
+    obj,
+    'immediate',
+    'redo',
+    'debounce',
+    'ignoreLoading',
+    'throwOnFailed',
+  )
+}
+
+export function usePromise(
+  fn: (...args: any[]) => Promise,
+): UsePromiseReturnType
+
+export function usePromise(
+  fn: (...args: any[]) => Promise,
+  opt: UsePromiseOption,
+): UsePromiseReturnType
+
+export function usePromise(
+  fn: (...args: any[]) => Promise,
+  fnArgs: unknown,
+  opt?: UsePromiseOption,
+): UsePromiseReturnType
+
+export function usePromise(
+  fn: (...args: any[]) => Promise,
+  ...args: any[]
+): UsePromiseReturnType {
+  const data = shallowRef(null)
+  const loading = ref(false)
+  const finished = ref(false)
+  const error = shallowRef(null)
+
+  // fn params
+  const fnArgs = ref()
+
+  // config
+  let config: UsePromiseOption = {
+    immediate: true,
+    redo: false,
+    debounce: 0,
+    ignoreLoading: false,
+    throwOnFailed: false,
+  }
+
+  function handleFn(): Promise {
+    return new Promise((resolve, reject) => {
+      const { ignoreLoading, throwOnFailed } = config
+      if (!ignoreLoading && loading.value) {
+        return
+      }
+      loading.value = true
+      finished.value = false
+      fn(undefined, unref(fnArgs))
+        .then((res) => {
+          data.value = res
+          error.value = null
+          return resolve(res)
+        })
+        .catch((e) => {
+          data.value = null
+          error.value = e
+          if (throwOnFailed) {
+            return reject(e)
+          }
+          return resolve(null)
+        })
+        .finally(() => {
+          loading.value = false
+          finished.value = true
+        })
+    })
+  }
+
+  // 存在不在vue3 setup中执行.
+  const scoped = effectScope()
+
+  scoped.run(() => {
+    if (args.length > 0) {
+      if (isUsePromiseOption(args[0])) {
+        config = { ...config, ...args[0] }
+      }
+      else { fnArgs.value = args[0] }
+    }
+
+    if (args.length > 1) {
+      if (isUsePromiseOption(args[1])) {
+        config = { ...config, ...args[1] }
+      }
+    }
+
+    const { debounce, immediate, redo } = config
+    const debounceFn = useDebounceFn(() => {
+      return handleFn()
+    }, debounce)
+
+    if (immediate) {
+      debounceFn().then()
+    }
+
+    if (redo) {
+      watch(
+        fnArgs,
+        (newArgs, oldArgs) => {
+          if (!isEqual(newArgs, oldArgs)) {
+            debounceFn().then()
+          }
+        },
+        { deep: true },
+      )
+    }
+  })
+
+  tryOnBeforeUnmount(() => {
+    scoped.stop()
+  })
+
+  return {
+    data,
+    loading,
+    finished,
+    error,
+    handleFn,
+  }
+}
diff --git a/src/composables/router/menu.ts b/src/composables/router/menu.ts
index d117e2d..8f99c40 100644
--- a/src/composables/router/menu.ts
+++ b/src/composables/router/menu.ts
@@ -1,66 +1,54 @@
-import type { RouteRecordRaw } from 'vue-router'
+import { storeToRefs } from 'pinia'
+import { filterTree, getAllParentPath, hideFilter } from '~/utils'
 
 export function useMenu() {
-  const menuStore = useMenuStore()
-  const userStore = useUserStore()
-  const { resolve } = useRouter()
+  const routeStore = useRouteStore()
+  const { initMenu, menuListRef } = storeToRefs(routeStore)
 
-  /** 判断给定的route中是否具有给定perms列表的权限 */
-  function hasPermission(route: RouteRecordRaw, perms: any[] = []) {
-    if (!route.meta?.perm) {
-      return true
+  const getMenuList = async () => {
+    if (!unref(initMenu)) {
+      await routeStore.buildMenu()
     }
-
-    // 递归寻找子节点perm
-    if (route.meta?.perm === true && route.children?.length) {
-      return filterAsyncRoutes(route.children, perms).length
-    }
-
-    // 否则直接通过perm进行判断
-    return perms.includes(
-      Array.isArray(route.meta?.perm)
-        ? route.meta.perm[0]?.perm
-        : route.meta?.perm,
-    )
+    const menuList = unref(menuListRef)
+    return filterTree(menuList, hideFilter)
   }
 
-  // 过滤掉所有perm不匹配的路由
-  function filterAsyncRoutes(routes: RouteRecordRaw[], perms: any[]) {
-    return routes.reduce((rs: RouteRecordRaw[], route) => {
-      if (hasPermission(route, perms)) {
-        rs.push({
-          ...route,
-          children: route.children ? filterAsyncRoutes(route.children, perms) : [],
-        })
-      }
-      return rs
-    }, [])
+  const getCurrentParentPath = async (currentPath: string) => {
+    const menus = await getMenuList()
+    const allParentPath = await getAllParentPath(menus, currentPath)
+    return allParentPath?.[0]
   }
 
-  async function generateRoutes() {
-    const perms = userStore.userInfo?.perms ?? ['role', 'role/post']
-    menuStore.menuList = filterAsyncRoutes(getRoutes(), perms)
+  const getShallowMenus = async () => {
+    const menus = await getMenuList()
+    const shallowMenuList = menus.map(item => ({
+      ...item,
+      children: undefined,
+    }))
+    // if (isRoleMode()) {
+    //   const routes = router.getRoutes()
+    //   return shallowMenuList.filter(basicFilter(routes))
+    // }
+    return shallowMenuList
   }
 
-  function getMenuList(routes: RouteRecordRaw[]) {
-    return routes.reduce((rs: RouteRecordRaw[], route) => {
-      if (!route.meta?.hidden && !route.meta?.hideInMenu) {
-        rs.push({
-          ...route,
-          path: resolve(route.redirect || route).path,
-          children: route.children ? getMenuList(route.children) : [],
-        })
-      }
-      return rs
-    }, []).sort((a, b) => {
-      return (a.meta?.order || 999) - (b.meta?.order || 999)
-    })
+  const getChildrenMenus = async (parentPath: string) => {
+    const menus = await getMenuList()
+    const parent = menus.find(item => item.path === parentPath)
+    if (!parent || !parent.children || !!parent?.meta?.hideChildrenInMenu) {
+      return [] as Menu[]
+    }
+    // if (isRoleMode()) {
+    //   const routes = router.getRoutes()
+    //   return filterTree(parent.children, basicFilter(routes))
+    // }
+    return parent.children
   }
 
-  const menuList = computed(() => getMenuList(menuStore.menuList))
-
   return {
-    menuList,
-    generateRoutes,
+    getMenuList,
+    getShallowMenus,
+    getChildrenMenus,
+    getCurrentParentPath,
   }
 }
diff --git a/src/composables/router/routes.ts b/src/composables/router/routes.ts
deleted file mode 100644
index 836138c..0000000
--- a/src/composables/router/routes.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import type { RouteRecordRaw } from 'vue-router'
-import routes from '~pages'
-
-/**
- * 过滤所有hidden的路由
- */
-function filterHiddenRoutes(routes: RouteRecordRaw[]) {
-  return routes.filter((i) => {
-    if (i.children) {
-      i.children = filterHiddenRoutes(i.children)
-    }
-    return !i.meta?.hidden
-  })
-}
-
-/**
- * 获取真实存在的路由
- */
-export function getRoutes() {
-  return filterHiddenRoutes(routes)
-}
diff --git a/src/composables/setting/menu-setting.ts b/src/composables/setting/menu-setting.ts
index 81f4dfb..b7e3283 100644
--- a/src/composables/setting/menu-setting.ts
+++ b/src/composables/setting/menu-setting.ts
@@ -1,4 +1,6 @@
 import {
+  MenuModeEnum,
+  NavBarModeEnum,
   SIDE_BAR_MINI_WIDTH,
   SIDE_BAR_SHOW_TIT_MINI_WIDTH,
   TriggerEnum,
@@ -8,59 +10,66 @@ const mixSideHasChildren = ref(false)
 
 export function useMenuSetting() {
   const { getFullContent: fullContent } = useFullContent()
-  const configStore = useAppConfigStore()
+  const appConfig = useAppConfig()
   const { getShowLogo } = useRootSetting()
 
-  const getCollapsed = computed(() => configStore.sidebar.collapsed)
-  const getMenuType = computed(() => configStore.navBarMode)
-  const getMenuMode = computed(() => configStore.menu.mode)
-  const getMenuFixed = computed(() => configStore.sidebar.fixed)
-  const getShowMenu = computed(() => configStore.menu.show)
-  const getMenuHidden = computed(() => !configStore.sidebar.visible)
-  const getMenuWidth = computed(() => configStore.sidebar.width)
-  const getTrigger = computed(() => configStore.sidebar.trigger)
-  const getMenuTheme = computed(() => configStore.sidebar.theme)
-  const getSplit = computed(() => configStore.menu.split)
-  const getMenuBgColor = computed(() => configStore.sidebar.bgColor)
-  const getMixSideTrigger = computed(() => configStore.menu.mixSideTrigger)
+  const getCollapsed = computed(() => appConfig.sidebar.value.collapsed)
+  const getMenuType = computed(() => unref(appConfig.navBarMode))
+  const getMenuMode = computed(() => appConfig.menu.value.mode)
+  const getMenuFixed = computed(() => appConfig.sidebar.value.fixed)
+  const getShowMenu = computed(() => appConfig.menu.value.show)
+  const getMenuHidden = computed(() => !appConfig.sidebar.value.visible)
+  const getMenuWidth = computed(() => appConfig.sidebar.value.width)
+  const getTrigger = computed(() => appConfig.sidebar.value.trigger)
+  const getMenuTheme = computed(() => appConfig.sidebar.value.theme)
+  const getSplit = computed(() => appConfig.menu.value.split)
+  const getMenuBgColor = computed(() => appConfig.sidebar.value.bgColor)
+  const getMixSideTrigger = computed(() => appConfig.menu.value.mixSideTrigger)
   const getShowSidebar = computed(() => {
     return (
       unref(getSplit)
       || (unref(getShowMenu)
-        && !unref(configStore.isHorizontal)
+        && !unref(appConfig.isHorizontal)
         && !unref(fullContent))
     )
   })
 
-  const getCanDrag = computed(() => configStore.menu.canDrag)
-  const getAccordion = computed(() => configStore.menu.accordion)
-  const getMixSideFixed = computed(() => configStore.menu.mixSideFixed)
-  const getTopMenuAlign = computed(() => configStore.menu.topMenuAlign)
-  const getCloseMixSidebarOnChange = computed(() => configStore.closeMixSidebarOnChange)
-  const getIsSidebarType = computed(() => configStore.isSidebar)
-  const getIsTopMenu = computed(() => configStore.isTopMenu)
+  const getCanDrag = computed(() => appConfig.menu.value.canDrag)
+  const getAccordion = computed(() => appConfig.menu.value.accordion)
+  const getMixSideFixed = computed(() => appConfig.menu.value.mixSideFixed)
+  const getTopMenuAlign = computed(() => appConfig.menu.value.topMenuAlign)
+  const getCloseMixSidebarOnChange = computed(() => appConfig.closeMixSidebarOnChange.value)
+  const getIsSidebarType = computed(() => unref(getMenuType) === NavBarModeEnum.SIDEBAR)
+  const getIsTopMenu = computed(() => unref(getMenuType) === NavBarModeEnum.TOP_MENU)
   const getMenuShowLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType))
-  const getCollapsedShowTitle = computed(() => configStore.isCollapsedShowTitle)
-  const getShowTopMenu = computed(() => unref(configStore.isHorizontal) || unref(getSplit))
+  const getCollapsedShowTitle = computed(() => appConfig.menu.value.collapsedShowTitle)
+  const getShowTopMenu = computed(() => unref(getMenuMode) === MenuModeEnum.HORIZONTAL || unref(getSplit))
+
   const getShowHeaderTrigger = computed(() => {
     if (
-      configStore.isTopMenu
+      unref(getMenuType) === NavBarModeEnum.TOP_MENU
       || !unref(getShowMenu)
       || unref(getMenuHidden)
-    )
+    ) {
       return false
+    }
 
     return unref(getTrigger) === TriggerEnum.HEADER
   })
 
   const getShowCenterTrigger = computed(() => unref(getTrigger) === TriggerEnum.CENTER)
   const getShowFooterTrigger = computed(() => unref(getTrigger) === TriggerEnum.FOOTER)
-  const getIsHorizontal = computed(() => configStore.isHorizontal)
-  const getIsMixSidebar = computed(() => configStore.isMixSidebar)
-  const getIsMixMode = computed(() => configStore.isMixMode)
+  const getIsHorizontal = computed(() => unref(getMenuMode) === MenuModeEnum.HORIZONTAL)
+  const getIsMixSidebar = computed(() => unref(getMenuType) === NavBarModeEnum.MIX_SIDEBAR)
+  const getIsMixMode = computed(() => {
+    return (
+      unref(getMenuMode) === MenuModeEnum.INLINE
+      && unref(getMenuType) === NavBarModeEnum.MIX
+    )
+  })
 
   const getMiniWidthNumber = computed(() => {
-    const { collapsedShowTitle } = configStore.menu
+    const { collapsedShowTitle } = appConfig.menu.value
     return collapsedShowTitle
       ? SIDE_BAR_SHOW_TIT_MINI_WIDTH
       : SIDE_BAR_MINI_WIDTH
@@ -93,14 +102,18 @@ export function useMenuSetting() {
     return `calc(100% - ${unref(width)}px)`
   })
 
+  const getIsFixed = computed(() => {
+    return unref(getMixSideFixed) && unref(mixSideHasChildren)
+  })
+
   function setMenuSetting(menuSetting: Partial): void {
-    configStore.setMenu(menuSetting)
+    appConfig.setAppConfig({ menu: menuSetting })
   }
 
   function setSidebarSetting(
     sidebarSetting: Partial,
   ): void {
-    configStore.setSidebar(sidebarSetting)
+    appConfig.setAppConfig({ sidebar: sidebarSetting })
   }
 
   function setSiderWidth(width: number) {
@@ -148,6 +161,7 @@ export function useMenuSetting() {
     getCloseMixSidebarOnChange,
     getMixSideTrigger,
     getMixSideFixed,
+    getIsFixed,
     mixSideHasChildren,
     getMenuShowLogo,
   }
diff --git a/src/composables/web/theme.ts b/src/composables/web/theme.ts
new file mode 100644
index 0000000..2bf5adc
--- /dev/null
+++ b/src/composables/web/theme.ts
@@ -0,0 +1,343 @@
+import { storeToRefs } from 'pinia'
+import { ThemeEnum } from '~/constants'
+import type { MaybeElementRef } from '~/utils'
+import { darken, generateColors, lighten, pickTextColorBasedOnBgColor, setCssVar, toggleClass } from '~/utils'
+
+const HEADER_HEIGHT = '--header-height'
+const HEADER_BG_COLOR_VAR = '--header-background-color'
+const HEADER_TEXT_COLOR_VAR = '--header-text-color'
+const HEADER_ACTION_HOVER_BG_COLOR_VAR = '--header-action-hover-bg-color'
+
+const ASIDE_WIDTH = '--aside-width'
+const ASIDE_DARK_BG_COLOR = '--aside-background-color'
+const ASIDE_TEXT_COLOR_VAR = '--aside-text-color'
+
+const TRIGGER_BG_COLOR_VAR = '--trigger-background-color'
+
+const TAB_BAR_HEIGHT = '--tab-bar-height'
+
+const FOOTER_HEIGHT = '--footer-height'
+
+const LIGHT_TEXT_COLOR = 'rgba(0,0,0,.85)'
+const DARK_TEXT_COLOR = '#fff'
+
+export function createMediaPrefersColorSchemeListen() {
+  const { setAppConfig } = useAppConfig()
+
+  // 监听系统主题变更
+  useEventListener(
+    window.matchMedia('(prefers-color-scheme: dark)'),
+    'change',
+    (e: MediaQueryListEvent) => setAppConfig({ theme: e.matches ? ThemeEnum.DARK : ThemeEnum.LIGHT }),
+  )
+}
+
+function toggleGrayMode(val: boolean) {
+  toggleClass(val, 'gray-mode', document.documentElement)
+}
+
+function toggleColorWeak(val: boolean) {
+  toggleClass(val, 'color-weak', document.documentElement)
+}
+
+export function createGridLayoutListen(el: MaybeElementRef | null) {
+  const {
+    isTopMenu,
+    sidebar,
+    header,
+    footer,
+    tabTar,
+    getCollapsedShowTitle,
+    menu,
+    isMixSidebar,
+  } = useAppConfig()
+  const asideWidth = useCssVar(ASIDE_WIDTH, el, {
+    initialValue: `${unref(sidebar).width}px`,
+  })
+  const headerHeight = useCssVar(HEADER_HEIGHT, el, {
+    initialValue: `${unref(header).height}px`,
+  })
+  const tabBarHeight = useCssVar(TAB_BAR_HEIGHT, el, {
+    initialValue: `${unref(tabTar).height}px`,
+  })
+  const footerHeight = useCssVar(FOOTER_HEIGHT, el, {
+    initialValue: `${unref(footer).height}px`,
+  })
+
+  watchEffect(() => {
+    const getAsideWidth = () => {
+      if (unref(isTopMenu) || !unref(sidebar).visible)
+        return 0
+      if (unref(getCollapsedShowTitle)) {
+        return unref(menu).mixSideFixed && unref(isMixSidebar)
+          ? unref(sidebar).mixSidebarWidth + unref(menu).subMenuWidth
+          : unref(sidebar).mixSidebarWidth
+      }
+      if (unref(sidebar).collapsed) {
+        return unref(menu).mixSideFixed && unref(isMixSidebar)
+          ? unref(sidebar).collapsedWidth + unref(menu).subMenuWidth
+          : unref(sidebar).collapsedWidth
+      }
+      return unref(sidebar).width
+    }
+
+    const getHeaderHeight = () => {
+      if (!unref(header).visible)
+        return 0
+      return unref(header).height
+    }
+
+    const getTabBarHeight = () => {
+      if (!unref(tabTar).visible)
+        return 0
+      return unref(tabTar).height
+    }
+
+    const getFooterHeight = () => {
+      if (!unref(footer).visible)
+        return 0
+      return unref(footer).height
+    }
+    asideWidth.value = `${getAsideWidth()}px`
+    headerHeight.value = `${getHeaderHeight()}px`
+    tabBarHeight.value = `${getTabBarHeight()}px`
+    footerHeight.value = `${getFooterHeight()}px`
+  })
+}
+
+export function createThemeColorListen() {
+  const {
+    sidebar,
+    header,
+    grayMode,
+    colorWeak,
+    setAppConfig,
+  } = useAppConfig()
+
+  const headerBgColor = useCssVar(HEADER_BG_COLOR_VAR, null, {
+    initialValue: `${unref(header).bgColor}px`,
+  })
+
+  const headerTextColor = useCssVar(
+    HEADER_TEXT_COLOR_VAR,
+    null,
+    {
+      initialValue: LIGHT_TEXT_COLOR,
+    },
+  )
+  const headerActionHoverBgColor = useCssVar(
+    HEADER_ACTION_HOVER_BG_COLOR_VAR,
+    null,
+  )
+
+  const sidebarBgColor = useCssVar(
+    ASIDE_DARK_BG_COLOR,
+    null,
+    {
+      initialValue: `${unref(sidebar).bgColor}px`,
+    },
+  )
+
+  const asideTextColor = useCssVar(
+    ASIDE_TEXT_COLOR_VAR,
+    null,
+    {
+      initialValue: LIGHT_TEXT_COLOR,
+    },
+  )
+  const triggerBackgroundColor = useCssVar(
+    TRIGGER_BG_COLOR_VAR,
+    null,
+  )
+
+  watchEffect(() => {
+    headerBgColor.value = unref(header).bgColor
+    headerTextColor.value = pickTextColorBasedOnBgColor(
+      unref(header).bgColor,
+      LIGHT_TEXT_COLOR,
+      DARK_TEXT_COLOR,
+    )
+
+    if (['#fff', '#ffffff'].includes(unref(header).bgColor.toLowerCase())) {
+      headerActionHoverBgColor.value = darken(unref(header).bgColor, 6)
+      setAppConfig({ header: { theme: ThemeEnum.LIGHT } })
+    }
+    else {
+      headerActionHoverBgColor.value = lighten(unref(header).bgColor, 6)
+      setAppConfig({ header: { theme: ThemeEnum.DARK } })
+    }
+
+    sidebarBgColor.value = unref(sidebar).bgColor
+    asideTextColor.value = pickTextColorBasedOnBgColor(
+      unref(sidebar).bgColor,
+      LIGHT_TEXT_COLOR,
+      DARK_TEXT_COLOR,
+    )
+
+    if (['#fff', '#ffffff'].includes(unref(sidebar).bgColor.toLowerCase())) {
+      triggerBackgroundColor.value = darken(unref(sidebar).bgColor, 6)
+      setAppConfig({ sidebar: { theme: ThemeEnum.LIGHT } })
+    }
+    else {
+      triggerBackgroundColor.value = lighten(unref(sidebar).bgColor, 6)
+      setAppConfig({ sidebar: { theme: ThemeEnum.DARK } })
+    }
+
+    toggleGrayMode(unref(grayMode))
+    toggleColorWeak(unref(colorWeak))
+    // toggleClass(
+    //   ThemeEnum.DARK === unref(theme),
+    //   ThemeEnum.DARK,
+    //   document.documentElement,
+    // )
+  })
+}
+
+export function useAppTheme() {
+  const themeStore = useThemeStore()
+  const { setThemeConfig, setSidebarTheme, setHeaderTheme } = themeStore
+  const { themeConfig: getThemeConfig, theme, sidebar, header } = storeToRefs(themeStore)
+
+  const darkRef = useDark({
+    selector: 'html',
+    attribute: 'theme-mode',
+    valueDark: 'dark',
+    valueLight: '',
+  })
+  const toggleDark = useToggle(darkRef)
+
+  const isDark = computed(() => unref(darkRef) && unref(theme) === ThemeEnum.DARK)
+  const toggleTheme = (dark: boolean) => {
+    theme.value = dark ? ThemeEnum.DARK : ThemeEnum.LIGHT
+    toggleDark(dark)
+  }
+
+  // sidebar
+  const isSidebarDark = computed(() => (unref(theme) === ThemeEnum.DARK || unref(sidebar) === ThemeEnum.DARK))
+  const toggleSidebarTheme = (dark: boolean) => {
+    setSidebarTheme(dark ? ThemeEnum.DARK : ThemeEnum.LIGHT)
+  }
+
+  const isHeaderDark = computed(() => (unref(theme) === ThemeEnum.DARK || unref(header) === ThemeEnum.DARK))
+  const toggleHeaderTheme = (dark: boolean) => {
+    setHeaderTheme(dark ? ThemeEnum.DARK : ThemeEnum.LIGHT)
+  }
+
+  const primaryColor = computed(() => {
+    return getThemeConfig.value.primaryColor
+  })
+
+  const infoColor = computed(() => {
+    return getThemeConfig.value.infoColor
+  })
+
+  const successColor = computed(() => {
+    return getThemeConfig.value.successColor
+  })
+
+  const warningColor = computed(() => {
+    return getThemeConfig.value.warningColor
+  })
+
+  const errorColor = computed(() => {
+    return getThemeConfig.value.errorColor
+  })
+
+  const themeColors = computed(() => {
+    let colors: ThemeColors = {}
+    const themeConfig = getThemeConfig.value
+
+    if (themeConfig.primaryColor) {
+      const primaryColorList = generateColors(themeConfig.primaryColor)
+      colors = {
+        ...colors,
+        ...{
+          primaryColor: primaryColorList[5],
+          primaryColorHover: primaryColorList[4],
+          primaryColorPressed: primaryColorList[4],
+          primaryColorSuppl: primaryColorList[6],
+        },
+      }
+    }
+
+    if (themeConfig.infoColor) {
+      const infoColorList = generateColors(themeConfig.infoColor)
+      colors = {
+        ...colors,
+        ...{
+          infoColor: infoColorList[5],
+          infoColorHover: infoColorList[4],
+          infoColorPressed: infoColorList[4],
+          infoColorSuppl: infoColorList[6],
+        },
+      }
+    }
+
+    if (themeConfig.successColor) {
+      const successColorList = generateColors(themeConfig.successColor)
+      colors = {
+        ...colors,
+        ...{
+          successColor: successColorList[5],
+          successColorHover: successColorList[4],
+          successColorPressed: successColorList[4],
+          successColorSuppl: successColorList[6],
+        },
+      }
+    }
+
+    if (themeConfig.warningColor) {
+      const warningColorList = generateColors(themeConfig.warningColor)
+      colors = {
+        ...colors,
+        ...{
+          warningColor: warningColorList[5],
+          warningColorHover: warningColorList[4],
+          warningColorPressed: warningColorList[4],
+          warningColorSuppl: warningColorList[6],
+        },
+      }
+    }
+
+    if (themeConfig.errorColor) {
+      const errorColorList = generateColors(themeConfig.errorColor)
+      colors = {
+        ...colors,
+        ...{
+          errorColor: errorColorList[5],
+          errorColorHover: errorColorList[4],
+          errorColorPressed: errorColorList[4],
+          errorColorSuppl: errorColorList[6],
+        },
+      }
+    }
+    return colors
+  })
+
+  watch(
+    themeColors,
+    (val) => {
+      val.primaryColor && setCssVar('--primary-color', val.primaryColor)
+      val.successColor && setCssVar('--success-color', val.successColor)
+      val.errorColor && setCssVar('--error-color', val.errorColor)
+      val.warningColor && setCssVar('--warning-color', val.warningColor)
+    },
+    { deep: true },
+  )
+
+  return {
+    isDark,
+    isSidebarDark,
+    isHeaderDark,
+    toggleTheme,
+    primaryColor,
+    infoColor,
+    successColor,
+    warningColor,
+    errorColor,
+    themeColors,
+    setThemeConfig,
+    toggleSidebarTheme,
+    toggleHeaderTheme,
+  }
+}
diff --git a/src/constants/app.ts b/src/constants/app.ts
index eef50b2..97e927d 100644
--- a/src/constants/app.ts
+++ b/src/constants/app.ts
@@ -23,6 +23,16 @@ export enum NavBarModeEnum {
   TOP_MENU = 'top-menu',
 }
 
+/**
+ * 权限模式(功能权限)
+ */
+export enum PermissionModeEnum {
+  // 角色权限模式
+  ROLE = 'role',
+  // 权限代码模式
+  PERM = 'perm',
+}
+
 // Session过期处理方式
 export enum SessionTimeoutProcessingEnum {
   ROUTE_JUMP, // 路由跳转
@@ -54,3 +64,17 @@ export enum ErrorTypeEnum {
   AJAX = 'ajax',
   PROMISE = 'promise',
 }
+
+// 异常类型
+export enum ExceptionEnum {
+  // 无权访问
+  PAGE_NOT_ACCESS = 403,
+  // 页面未找到
+  PAGE_NOT_FOUND = 404,
+  // 服务器错误
+  ERROR = 500,
+  // 网络错误
+  NET_WORK_ERROR = 10000,
+  // 页面无数据
+  PAGE_NOT_DATA = 10100,
+}
diff --git a/src/constants/design.ts b/src/constants/design.ts
index d543a26..f4b94d0 100644
--- a/src/constants/design.ts
+++ b/src/constants/design.ts
@@ -40,6 +40,19 @@ export const SIDE_BAR_BG_COLOR_LIST: string[] = [
   '#383f45',
 ]
 
+export enum ThemeChangeEnum {
+  THEME_CHANGE,
+
+  THEME_PRIMARY_COLOR_CHANGE,
+  THEME_INFO_COLOR_CHANGE,
+  THEME_SUCCESS_COLOR_CHANGE,
+  THEME_WARNING_COLOR_CHANGE,
+  THEME_ERROR_COLOR_CHANGE,
+
+  THEME_HEADER_BG_COLOR_CHANGE,
+  THEME_SIDEBAR_BG_COLOR_CHANGE,
+}
+
 // 设置事件Enum
 export enum HandlerSettingEnum {
   CHANGE_LAYOUT,
diff --git a/src/layouts/404.vue b/src/layouts/404.vue
deleted file mode 100644
index ca5ada7..0000000
--- a/src/layouts/404.vue
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-