Browse Source

feat: introduced CollapseTransition component in the layout, optimized responsive design, and adjusted the display logic of navigation and breadcrumbs

nian 1 month ago
parent
commit
35ac5b1aa2

+ 45 - 47
src/layouts/header/Breadcrumb.vue

@@ -34,55 +34,53 @@ function isCurrentRoute(name: RouteRecordNameGeneric) {
 }
 </script>
 <template>
-  <nav class="min-w-0">
-    <TransitionGroup
-      :duration="300"
-      tag="ul"
-      class="flex"
-      type="transition"
-      enter-active-class="transition-[grid-template-columns]"
-      leave-active-class="transition-[grid-template-columns]"
-      enter-from-class="grid-cols-[0fr]"
-      leave-to-class="grid-cols-[0fr]"
-      enter-to-class="grid-cols-[1fr]"
-      leave-from-class="grid-cols-[1fr]"
+  <TransitionGroup
+    :duration="300"
+    tag="ul"
+    class="flex"
+    type="transition"
+    enter-active-class="transition-[grid-template-columns]"
+    leave-active-class="transition-[grid-template-columns]"
+    enter-from-class="grid-cols-[0fr]"
+    leave-to-class="grid-cols-[0fr]"
+    enter-to-class="grid-cols-[1fr]"
+    leave-from-class="grid-cols-[1fr]"
+  >
+    <li
+      v-for="item in routerBreadcrumb"
+      :key="item.path"
+      class="grid shrink-0 justify-start overflow-hidden"
     >
-      <li
-        v-for="item in routerBreadcrumb"
-        :key="item.path"
-        class="grid shrink-0 justify-start overflow-hidden"
+      <div
+        class="flex min-w-0 items-center"
+        :class="{
+          'not-hover:text-[var(--text-color-3)]': !isCurrentRoute(item.name),
+        }"
       >
-        <div
-          class="flex min-w-0 items-center"
-          :class="{
-            'not-hover:text-[var(--text-color-3)]': !isCurrentRoute(item.name),
-          }"
+        <NDropdown
+          :options="resolveDropdownOptions(item.children)"
+          placement="bottom-start"
+          @select="onDropdownSelected"
         >
-          <NDropdown
-            :options="resolveDropdownOptions(item.children)"
-            placement="bottom-start"
-            @select="onDropdownSelected"
+          <div
+            class="flex shrink-0 items-center gap-x-1.5 rounded px-1.5 py-1 transition-[background-color,color]"
+            :class="{
+              'cursor-pointer hover:bg-[var(--button-color-2-hover)]': !isCurrentRoute(item.name),
+            }"
           >
-            <div
-              class="flex shrink-0 items-center gap-x-1.5 rounded px-1.5 py-1 transition-[background-color,color]"
-              :class="{
-                'cursor-pointer hover:bg-[var(--button-color-2-hover)]': !isCurrentRoute(item.name),
-              }"
-            >
-              <span
-                v-if="item.meta?.icon"
-                class="size-5"
-                :class="item.meta?.icon"
-              />
-              {{ item.meta?.title }}
-            </div>
-          </NDropdown>
-          <span
-            class="iconify-[fluent--slash-forward-20-regular] w-3.5 text-[var(--text-color-3)]"
-            v-if="!isCurrentRoute(item.name)"
-          />
-        </div>
-      </li>
-    </TransitionGroup>
-  </nav>
+            <span
+              v-if="item.meta?.icon"
+              class="size-5"
+              :class="item.meta?.icon"
+            />
+            {{ item.meta?.title }}
+          </div>
+        </NDropdown>
+        <span
+          class="iconify-[fluent--slash-forward-20-regular] w-3.5 text-[var(--text-color-3)]"
+          v-if="!isCurrentRoute(item.name)"
+        />
+      </div>
+    </li>
+  </TransitionGroup>
 </template>

+ 17 - 19
src/layouts/header/Navigation.vue

@@ -26,24 +26,22 @@ const stopWatch = watch(
 )
 </script>
 <template>
-  <div class="min-w-0">
-    <div class="flex gap-x-0.5 pr-2">
-      <ButtonAnimation
-        size="medium"
-        @click="router.back()"
-        title="上一页"
-        :disabled="!navigationState.canGoBack"
-      >
-        <span class="iconify size-4.5 ph--arrow-left" />
-      </ButtonAnimation>
-      <ButtonAnimation
-        size="medium"
-        @click="router.forward()"
-        title="下一页"
-        :disabled="!navigationState.canGoForward"
-      >
-        <span class="iconify size-4.5 ph--arrow-right" />
-      </ButtonAnimation>
-    </div>
+  <div class="flex gap-x-0.5 pr-2">
+    <ButtonAnimation
+      size="medium"
+      @click="router.back()"
+      title="上一页"
+      :disabled="!navigationState.canGoBack"
+    >
+      <span class="iconify size-4.5 ph--arrow-left" />
+    </ButtonAnimation>
+    <ButtonAnimation
+      size="medium"
+      @click="router.forward()"
+      title="下一页"
+      :disabled="!navigationState.canGoForward"
+    >
+      <span class="iconify size-4.5 ph--arrow-right" />
+    </ButtonAnimation>
   </div>
 </template>

+ 15 - 47
src/layouts/header/index.vue

@@ -1,12 +1,10 @@
 <script setup lang="ts">
-import { useInjection } from '@/composable/useInjection'
-import { mediaQueryInjectionKey } from '@/injection'
+import CollapseTransition from '@/components/CollapseTransition.vue'
 import { usePreferencesStore } from '@/stores'
 
 import Actions from './actions/index.vue'
 import Breadcrumb from './Breadcrumb.vue'
 import LogoArea from './LogoArea.vue'
-import MobileHeader from './mobile/MobileHeader.vue'
 import Navigation from './Navigation.vue'
 
 defineOptions({
@@ -14,55 +12,25 @@ defineOptions({
 })
 
 const preferencesStore = usePreferencesStore()
-const { sm } = useInjection(mediaQueryInjectionKey)
 </script>
 <template>
   <header
-    class="border-b border-naive-border bg-naive-card transition-[background-color,border-color]"
+    class="flex border-b border-naive-border bg-naive-card transition-[background-color,border-color]"
   >
-    <div
-      v-if="!sm"
-      class="flex"
-    >
-      <LogoArea />
-      <div class="flex flex-1 items-center p-4">
-        <div class="flex flex-1 items-center">
-          <Transition
-            type="transition"
-            enter-active-class="transition-[grid-template-columns]"
-            leave-active-class="transition-[grid-template-columns]"
-            enter-from-class="grid-cols-[0fr]"
-            leave-to-class="grid-cols-[0fr]"
-            enter-to-class="grid-cols-[1fr]"
-            leave-from-class="grid-cols-[1fr]"
-          >
-            <div
-              v-if="preferencesStore.preferences.showNavigation"
-              class="grid overflow-hidden"
-            >
-              <Navigation />
-            </div>
-          </Transition>
-          <Transition
-            type="transition"
-            enter-active-class="transition-[grid-template-columns]"
-            leave-active-class="transition-[grid-template-columns]"
-            enter-from-class="grid-cols-[0fr]"
-            leave-to-class="grid-cols-[0fr]"
-            enter-to-class="grid-cols-[1fr]"
-            leave-from-class="grid-cols-[1fr]"
-          >
-            <div
-              v-if="preferencesStore.preferences.showBreadcrumb"
-              class="grid overflow-hidden"
-            >
-              <Breadcrumb />
-            </div>
-          </Transition>
-        </div>
-        <Actions class="gap-x-3" />
+    <LogoArea />
+    <div class="flex flex-1 items-center p-4">
+      <div class="flex flex-1 items-center">
+        <CollapseTransition :display="preferencesStore.preferences.showNavigation">
+          <Navigation />
+        </CollapseTransition>
+        <CollapseTransition
+          :display="preferencesStore.preferences.showBreadcrumb"
+          contentTag="nav"
+        >
+          <Breadcrumb />
+        </CollapseTransition>
       </div>
+      <Actions class="gap-x-3" />
     </div>
-    <MobileHeader v-else />
   </header>
 </template>

+ 7 - 5
src/layouts/header/mobile/MobileHeader.vue

@@ -5,12 +5,14 @@ import { useInjection } from '@/composable/useInjection'
 import { layoutInjectionKey } from '@/injection'
 import router from '@/router'
 
-const { setLayoutSlideDirection } = useInjection(layoutInjectionKey)
+const { layoutSlideDirection, setLayoutSlideDirection } = useInjection(layoutInjectionKey)
 </script>
 <template>
-  <div
-    class="flex items-center justify-between px-4 py-2"
-    @click="setLayoutSlideDirection(null)"
+  <header
+    class="flex items-center justify-between border-b border-naive-border bg-naive-card px-4 py-2 transition-[background-color,border-color]"
+    :class="{
+      'rounded-t-xl': layoutSlideDirection,
+    }"
   >
     <div
       class="size-9"
@@ -33,5 +35,5 @@ const { setLayoutSlideDirection } = useInjection(layoutInjectionKey)
         <span class="iconify ph--list" />
       </ButtonAnimation>
     </div>
-  </div>
+  </header>
 </template>

+ 35 - 43
src/layouts/index.vue

@@ -6,6 +6,7 @@ import { computed, provide, watch, ref } from 'vue'
 
 import texturePng from '@/assets/texture.png'
 import { EmptyPlaceholder } from '@/components'
+import CollapseTransition from '@/components/CollapseTransition.vue'
 import { useComponentThemeOverrides } from '@/composable/useComponentThemeOverrides'
 import { mediaQueryInjectionKey, layoutInjectionKey } from '@/injection'
 import { usePreferencesStore } from '@/stores'
@@ -17,6 +18,7 @@ import MobileRightAside from './aside/mobile/MobileRightAside.vue'
 import Tabs from './component/Tabs.vue'
 import FooterLayout from './footer/index.vue'
 import HeaderLayout from './header/index.vue'
+import MobileHeader from './header/mobile/MobileHeader.vue'
 import MainLayout from './main/index.vue'
 
 import type { LayoutSlideDirection } from '@/injection'
@@ -45,7 +47,7 @@ const layoutTranslateOffset = computed(() => {
   return layoutSlideDirection.value === 'right'
     ? preferencesStore.preferences.menu.maxWidth || 0
     : layoutSlideDirection.value === 'left'
-      ? -62
+      ? -64
       : 0
 })
 
@@ -81,43 +83,36 @@ provide(layoutInjectionKey, {
     :style="{ backgroundImage: `url(${texturePng})` }"
   >
     <MobileLeftAside v-if="mediaQuery.sm.value" />
+
     <div
-      class="relative flex h-full w-full flex-col border-naive-border transition-[border-color,rounded,transform] max-sm:rounded-xl sm:transform-none!"
+      class="relative flex h-full w-full flex-col border-naive-border bg-naive-card/50 transition-[background-color,border-color,rounded,transform]"
       :class="{
-        'overflow-hidden rounded-xl border': mediaQuery.sm.value && layoutTranslateOffset,
+        'rounded-xl border': mediaQuery.sm.value && layoutTranslateOffset,
       }"
       :style="
-        mediaQuery.sm.value && layoutSlideDirection
-          ? {
-              transform: `translate(${layoutTranslateOffset}px) scale(0.88)`,
-            }
-          : null
+        mediaQuery.sm.value &&
+        layoutSlideDirection && {
+          transform: `translate(${layoutTranslateOffset}px) scale(0.88)`,
+        }
       "
     >
-      <HeaderLayout />
+      <HeaderLayout v-if="!mediaQuery.sm.value" />
+      <MobileHeader v-else />
       <div class="flex flex-1 overflow-hidden">
         <AsideLayout v-if="!mediaQuery.sm.value" />
         <div class="relative flex flex-1 flex-col overflow-x-hidden">
-          <Transition
-            type="transition"
-            enter-active-class="transition-[grid-template-rows]"
-            leave-active-class="transition-[grid-template-rows]"
-            enter-from-class="grid-rows-[0fr]"
-            leave-to-class="grid-rows-[0fr]"
-            enter-to-class="grid-rows-[1fr]"
-            leave-from-class="grid-rows-[1fr]"
+          <CollapseTransition
+            :display="
+              !mediaQuery.sm.value &&
+              !isEmpty(tabsStore.tabs) &&
+              preferencesStore.preferences.showTabs
+            "
+            direction="horizontal"
+            :render-content="false"
+            container-class="shrink-0 items-baseline"
           >
-            <div
-              v-if="
-                !mediaQuery.sm.value &&
-                !isEmpty(tabsStore.tabs) &&
-                preferencesStore.preferences.showTabs
-              "
-              class="grid shrink-0 items-baseline overflow-hidden"
-            >
-              <Tabs />
-            </div>
-          </Transition>
+            <Tabs />
+          </CollapseTransition>
           <NScrollbar
             class="transition-[padding]"
             :class="{
@@ -139,24 +134,21 @@ provide(layoutInjectionKey, {
               </div>
             </template>
           </EmptyPlaceholder>
-          <Transition
-            type="transition"
-            enter-active-class="transition-[grid-template-rows]"
-            leave-active-class="transition-[grid-template-rows]"
-            enter-from-class="grid-rows-[0fr]"
-            leave-to-class="grid-rows-[0fr]"
-            enter-to-class="grid-rows-[1fr]"
-            leave-from-class="grid-rows-[1fr]"
+          <CollapseTransition
+            :display="!mediaQuery.sm.value && preferencesStore.preferences.showFooter"
+            direction="horizontal"
+            :render-content="false"
+            container-class="shrink-0 items-baseline"
           >
-            <div
-              v-if="!mediaQuery.sm.value && preferencesStore.preferences.showFooter"
-              class="grid shrink-0 items-baseline overflow-hidden"
-            >
-              <FooterLayout />
-            </div>
-          </Transition>
+            <FooterLayout />
+          </CollapseTransition>
         </div>
       </div>
+      <div
+        v-if="mediaQuery.sm.value && layoutSlideDirection"
+        class="absolute inset-0"
+        @click="setLayoutSlideDirection(null)"
+      />
     </div>
     <MobileRightAside v-if="mediaQuery.sm.value" />
   </div>

+ 6 - 1
src/layouts/main/index.vue

@@ -17,7 +17,8 @@ defineOptions({
 })
 
 const { sm } = useInjection(mediaQueryInjectionKey)
-const { shouldRefreshRoute } = useInjection(layoutInjectionKey)
+const { shouldRefreshRoute, layoutSlideDirection, setLayoutSlideDirection } =
+  useInjection(layoutInjectionKey)
 
 const layoutsRouteRedirect = router.getRoutes().find((item) => item.name === 'layouts')?.redirect
 
@@ -118,6 +119,10 @@ watch(
 watch(
   () => router.currentRoute.value,
   (newRoute, oldRoute) => {
+    if (layoutSlideDirection.value) {
+      setLayoutSlideDirection(null)
+    }
+
     if (newRoute.fullPath !== oldRoute?.fullPath) {
       const { showTab, enableMultiTab } = newRoute.meta
       const targetPath = enableMultiTab ? newRoute.fullPath : newRoute.path