Преглед на файлове

refactor: enhance sidebar menu behavior and styling with new width properties

nian преди 1 месец
родител
ревизия
7eecb59dd0

+ 2 - 1
src/assets/base.css

@@ -26,7 +26,8 @@
   --color-primary: var(--primary-color);
   --color-naive-border: var(--border-color);
   --color-naive-card: var(--card-color);
-  --color-naive-button-hover: var(--button-color-2-hover);
+  --color-naive-text3: var(--text-color-3);
+  --color-naive-button2-hover: var(--button-color-2-hover);
   --radius-naive: var(--border-radius);
   --radius-naive-sm: calc(var(--border-radius-small));
 }

+ 1 - 1
src/layout/aside/SidebarMenu.vue

@@ -40,7 +40,7 @@ watch(
   <NScrollbar>
     <NMenu
       ref="menuRef"
-      :collapsed-width="sidebarMenu.width"
+      :collapsed-width="sidebarMenu.minWidth"
       :collapsed="sidebarMenu.collapsed"
       :collapsed-icon-size="20"
       :value="menuActiveKey"

+ 39 - 15
src/layout/aside/index.vue

@@ -13,18 +13,27 @@ defineOptions({
   name: 'AsideLayout',
 })
 
+const {
+  minWidth: defaultMinWidth,
+  width: defaultWidth,
+  maxWidth: defaultMaxWidth,
+} = DEFAULT_PREFERENCES_OPTIONS.sidebarMenu
+
 const { preferences, sidebarMenu } = toRefsPreferencesStore()
 
 const { isSidebarColResizing } = useInjection(layoutInjectionKey)
 
-const sidelineRef = useTemplateRef<HTMLDivElement>('sidebarLine')
+const sidebarLineRef = useTemplateRef<HTMLDivElement>('sidebarLine')
 
-const { x } = useDraggable(sidelineRef, {})
+const { x: sidebarLineX } = useDraggable(sidebarLineRef, {})
 
 const menuCollapseWidth = computed(() => {
-  return sidebarMenu.value.collapsed
-    ? sidebarMenu.value.width || DEFAULT_PREFERENCES_OPTIONS.sidebarMenu.width
-    : sidebarMenu.value.maxWidth || DEFAULT_PREFERENCES_OPTIONS.sidebarMenu.maxWidth
+  const { minWidth, width, collapsed } = sidebarMenu.value
+
+  const mergedMinWidth = minWidth || defaultMinWidth
+  const mergedWidth = width || defaultWidth
+
+  return collapsed ? mergedMinWidth : mergedWidth
 })
 
 function handleCollapseClick() {
@@ -46,14 +55,27 @@ const onSidelineMouseDown = () => {
   )
 }
 
-watch(x, (newX) => {
-  console.log(newX)
+watch(sidebarLineX, (newSidebarLineX) => {
+  const { minWidth, maxWidth } = sidebarMenu.value
+  const mergedMinWidth = minWidth || defaultMinWidth
+  const mergedMaxWidth = maxWidth || defaultMaxWidth
+
+  if (newSidebarLineX <= mergedMinWidth) {
+    preferences.value.sidebarMenu.width = mergedMinWidth
+    preferences.value.sidebarMenu.collapsed = true
+  } else if (newSidebarLineX >= mergedMaxWidth) {
+    preferences.value.sidebarMenu.width = mergedMaxWidth
+    preferences.value.sidebarMenu.collapsed = false
+  } else {
+    preferences.value.sidebarMenu.width = newSidebarLineX
+    preferences.value.sidebarMenu.collapsed = false
+  }
 })
 </script>
 <template>
   <div class="flex h-full">
     <aside
-      class="relative flex h-full flex-col justify-between gap-y-4 bg-naive-card pb-4"
+      class="flex h-full flex-col justify-between gap-y-4 bg-naive-card pb-4"
       :class="{
         'transition-[background-color,width]': !isSidebarColResizing,
       }"
@@ -63,6 +85,15 @@ watch(x, (newX) => {
     >
       <SidebarMenu />
       <SidebarUserPanel />
+    </aside>
+    <div
+      class="relative flex h-full justify-center border-r border-naive-border transition-[border-color]"
+    >
+      <div
+        ref="sidebarLine"
+        class="absolute z-10 h-full w-[5px] cursor-col-resize"
+        @mousedown="onSidelineMouseDown"
+      />
       <div
         class="absolute top-1/2 right-0 z-50 grid size-6 translate-x-1/2 -translate-y-1/2 cursor-pointer place-items-center rounded-full border border-naive-border bg-white transition-[background-color,border-color] hover:bg-neutral-50 dark:bg-neutral-750 dark:hover:bg-neutral-700"
         @click="handleCollapseClick"
@@ -74,13 +105,6 @@ watch(x, (newX) => {
           }"
         />
       </div>
-    </aside>
-    <div class="relative flex h-full justify-center border-r border-naive-border">
-      <div
-        ref="sidebarLine"
-        class="absolute z-10 h-full w-[5px] cursor-col-resize"
-        @mousedown="onSidelineMouseDown"
-      ></div>
     </div>
   </div>
 </template>

+ 4 - 1
src/layout/header/action/SignOut.vue

@@ -23,7 +23,10 @@ const handleSignOutClick = () => {
 }
 </script>
 <template>
-  <ButtonAnimation @click="handleSignOutClick">
+  <ButtonAnimation
+    @click="handleSignOutClick"
+    title="退出登录"
+  >
     <span class="iconify ph--sign-out" />
   </ButtonAnimation>
 </template>

+ 9 - 6
src/layout/header/logo/index.vue

@@ -12,6 +12,8 @@ defineOptions({
 
 const APP_NAME = import.meta.env.VITE_APP_NAME
 
+const { minWidth: defaultMinWidth, width: defaultWidth } = DEFAULT_PREFERENCES_OPTIONS.sidebarMenu
+
 const { isSidebarColResizing } = useInjection(layoutInjectionKey)
 
 const { navigationMode, sidebarMenu, showLogo } = toRefsPreferencesStore()
@@ -21,21 +23,22 @@ const logoWrapperRef = useTemplateRef<HTMLElement>('logoWrapper')
 const collapseWidth = ref(0)
 
 watch(
-  [() => navigationMode.value, () => sidebarMenu.value.collapsed],
-  ([navigationMode, isCollapsed]) => {
+  [() => navigationMode.value, () => sidebarMenu.value],
+  ([navigationMode, { collapsed, width, minWidth }]) => {
     if (navigationMode === 'horizontal') {
       nextTick(() => {
         collapseWidth.value = logoWrapperRef.value?.clientWidth ?? 0
       })
     } else {
-      const { width, maxWidth } = sidebarMenu.value
-      const { width: defaultWidth, maxWidth: defaultMaxWidth } =
-        DEFAULT_PREFERENCES_OPTIONS.sidebarMenu
-      collapseWidth.value = isCollapsed ? width || defaultWidth : maxWidth || defaultMaxWidth
+      const mergedMinWidth = minWidth || defaultMinWidth
+      const mergedWidth = width || defaultWidth
+
+      collapseWidth.value = collapsed ? mergedMinWidth : mergedWidth
     }
   },
   {
     immediate: true,
+    deep: true,
   },
 )
 </script>

+ 2 - 2
src/layout/header/navigation/Breadcrumb.vue

@@ -95,11 +95,11 @@ const BreadcrumbItem = defineComponent({
         >
           <BreadcrumbItem
             :meta="meta"
-            class="cursor-pointer transition-[background-color,color] not-hover:text-[var(--text-color-3)] hover:bg-[var(--button-color-2-hover)]"
+            class="cursor-pointer transition-[background-color,color] not-hover:text-naive-text3 hover:bg-naive-button2-hover"
           />
         </NDropdown>
         <span
-          class="iconify-[fluent--slash-forward-20-regular] w-3.5 text-[var(--text-color-3)]"
+          class="iconify-[fluent--slash-forward-20-regular] w-3.5 text-naive-text3"
           v-if="!isCurrentRoute(name)"
         />
       </div>

+ 13 - 7
src/layout/index.vue

@@ -6,7 +6,7 @@ import texturePng from '@/assets/texture.png'
 import { CollapseTransition, EmptyPlaceholder } from '@/components'
 import { useInjection } from '@/composables'
 import { mediaQueryInjectionKey, layoutInjectionKey } from '@/injection'
-import { toRefsPreferencesStore, toRefsTabsStore } from '@/stores'
+import { DEFAULT_PREFERENCES_OPTIONS, toRefsPreferencesStore, toRefsTabsStore } from '@/stores'
 
 import FooterLayout from './footer/index.vue'
 import HeaderLayout from './header/index.vue'
@@ -24,14 +24,20 @@ const AsyncMobileLeftAside = defineAsyncComponent(() => import('./mobile/MobileL
 const AsyncMobileRightAside = defineAsyncComponent(() => import('./mobile/MobileRightAside.vue'))
 const AsyncAsideLayout = defineAsyncComponent({
   loader: () => import('./aside/index.vue'),
-  loadingComponent: () =>
-    h('div', {
+  loadingComponent: () => {
+    const { minWidth, width, collapsed } = sidebarMenu.value
+    const { minWidth: defaultMinWidth, width: defaultWidth } =
+      DEFAULT_PREFERENCES_OPTIONS.sidebarMenu
+    const mergedMinWidth = minWidth || defaultMinWidth
+    const mergedWidth = width || defaultWidth
+    const finalWidth = collapsed ? mergedMinWidth : mergedWidth
+
+    return h('div', {
       style: {
-        width: `${
-          sidebarMenu.value.collapsed ? sidebarMenu.value.width : sidebarMenu.value.maxWidth
-        }px`,
+        width: `${finalWidth + 1}px`,
       },
-    }),
+    })
+  },
   delay: 0,
 })
 

+ 1 - 1
src/layout/tabs/index.vue

@@ -377,7 +377,7 @@ const TabList = defineComponent({
                 {!tab.pinned && (
                   <div
                     class={[
-                      'ml-1 flex overflow-hidden rounded-full p-1 transition-[background-color,opacity,scale] hover:bg-naive-button-hover',
+                      'ml-1 flex overflow-hidden rounded-full p-1 transition-[background-color,opacity,scale] hover:bg-naive-button2-hover',
                       {
                         'scale-0 opacity-0': tab.locked || !showTabClose.value,
                         'group-hover:scale-100 group-hover:opacity-100':

+ 6 - 4
src/stores/preferences.ts

@@ -13,6 +13,7 @@ export interface PreferencesOptions {
   navigationMode: NavigationMode
   sidebarMenu: Partial<{
     collapsed: boolean
+    minWidth: number
     width: number
     maxWidth: number
   }>
@@ -35,8 +36,9 @@ export const DEFAULT_PREFERENCES_OPTIONS = {
   navigationMode: 'sidebar',
   sidebarMenu: {
     collapsed: false,
-    width: 64,
-    maxWidth: 256,
+    minWidth: 64,
+    width: 256,
+    maxWidth: 456,
   },
   showFooter: true,
   showTabs: true,
@@ -104,8 +106,8 @@ export const usePreferencesStore = defineStore('preferencesStore', () => {
 
   watch(
     () => preferences.value.enableTextSelect,
-    (newValue) => {
-      document.documentElement.style.userSelect = newValue ? 'auto' : 'none'
+    (enabled) => {
+      document.documentElement.style.userSelect = enabled ? 'auto' : 'none'
     },
     {
       immediate: true,

+ 5 - 11
src/views/dashboard/index.vue

@@ -1,8 +1,9 @@
 <script setup lang="ts">
+import { watchDebounced } from '@vueuse/core'
 import chroma from 'chroma-js'
 import * as echarts from 'echarts'
 import { NNumberAnimation } from 'naive-ui'
-import { onMounted, watch, ref, computed, onUnmounted, nextTick } from 'vue'
+import { onMounted, watch, ref, computed, onUnmounted } from 'vue'
 
 import { ContentWrapper } from '@/components'
 import { toRefsPreferencesStore } from '@/stores'
@@ -1048,16 +1049,9 @@ function resizeAllCharts() {
   if (highestRevenueChartInstance) highestRevenueChartInstance.resize()
 }
 
-watch([() => sidebarMenu.value.collapsed, () => navigationMode.value], () => {
-  if (collapseResizeTimeout !== null) {
-    clearTimeout(collapseResizeTimeout)
-    collapseResizeTimeout = null
-  }
-  nextTick(() => {
-    collapseResizeTimeout = setTimeout(() => {
-      resizeAllCharts()
-    }, 350)
-  })
+watchDebounced([() => sidebarMenu.value, () => navigationMode.value], resizeAllCharts, {
+  debounce: 300,
+  deep: true,
 })
 
 watch([isDark, themeColor], () => {