Ver Fonte

feat: public ContentWrapper component

nian há 1 mês atrás
pai
commit
f06433612d

+ 62 - 0
src/components/ContentWrapper.vue

@@ -0,0 +1,62 @@
+<script setup lang="ts">
+import { NScrollbar } from 'naive-ui'
+import { getCurrentInstance } from 'vue'
+
+import { useComponentThemeOverrides } from '@/composables/useComponentThemeOverrides'
+
+import type { ScrollbarProps } from 'naive-ui'
+import type { CSSProperties, ComponentPublicInstance } from 'vue'
+
+export interface ContentWrapperProps extends /* @vue-ignore */ ScrollbarProps {
+  scrollable?: boolean
+  contentClass?: string
+  contentStyle?: CSSProperties
+}
+
+defineOptions({
+  inheritAttrs: false,
+})
+
+const {
+  scrollable = true,
+  contentClass,
+  contentStyle,
+} = defineProps<ContentWrapperProps>()
+
+const { scrollbarInMainLayout } = useComponentThemeOverrides()
+
+const instance = getCurrentInstance()
+
+function forwardRef(ref: Element | ComponentPublicInstance | null) {
+  if (instance) {
+    instance.exposed = ref || {}
+    instance.exposeProxy = ref || {}
+  }
+}
+</script>
+<template>
+  <div class="h-full">
+    <NScrollbar
+      v-if="scrollable"
+      :ref="forwardRef"
+      :theme-overrides="scrollbarInMainLayout"
+      v-bind="$attrs"
+    >
+      <div
+        class="p-4 max-sm:p-2"
+        :class="contentClass"
+        :style="contentStyle"
+      >
+        <slot />
+      </div>
+    </NScrollbar>
+    <div
+      v-else
+      class="h-full p-4 max-sm:p-2"
+      :class="contentClass"
+      :style="contentStyle"
+    >
+      <slot />
+    </div>
+  </div>
+</template>

+ 3 - 0
src/components/index.ts

@@ -2,6 +2,9 @@ export * from './button-animation'
 export { default as CollapseTransition } from './CollapseTransition.vue'
 
 export * from './CollapseTransition.vue'
+export { default as ContentWrapper } from './ContentWrapper.vue'
+
+export * from './ContentWrapper.vue'
 export { default as EmptyPlaceholder } from './EmptyPlaceholder.vue'
 
 export * from './EmptyPlaceholder.vue'

+ 0 - 1
src/composables/index.ts

@@ -1,6 +1,5 @@
 export * from './useComponentModifier'
 export * from './useComponentThemeOverrides'
-export * from './useDataTable'
 export * from './useDiscreteApi'
 export * from './useInjection'
 export * from './usePersonalization'

+ 4 - 13
src/layout/index.vue

@@ -1,11 +1,10 @@
 <script setup lang="ts">
 import { isEmpty } from 'lodash-es'
-import { NScrollbar } from 'naive-ui'
 import { computed, defineAsyncComponent, watch } from 'vue'
 
 import texturePng from '@/assets/texture.png'
 import { CollapseTransition, EmptyPlaceholder } from '@/components'
-import { useComponentThemeOverrides, useInjection } from '@/composables'
+import { useInjection } from '@/composables'
 import { mediaQueryInjectionKey, layoutInjectionKey } from '@/injection'
 import { usePreferencesStore, useTabsStore } from '@/stores'
 
@@ -21,7 +20,6 @@ defineOptions({
 
 const tabsStore = useTabsStore()
 const preferencesStore = usePreferencesStore()
-const { scrollbarInMainLayout } = useComponentThemeOverrides()
 const { isSmallScreen } = useInjection(mediaQueryInjectionKey)
 const { layoutSlideDirection, setLayoutSlideDirection } = useInjection(layoutInjectionKey)
 
@@ -74,7 +72,7 @@ watch(
       <MobileHeader v-else />
       <div class="flex flex-1 overflow-hidden">
         <AsideLayout v-if="!isSmallScreen" />
-        <div class="relative flex flex-1 flex-col overflow-x-hidden">
+        <div class="flex flex-1 flex-col overflow-hidden">
           <CollapseTransition
             :display="
               !isSmallScreen && !isEmpty(tabsStore.tabs) && preferencesStore.preferences.showTabs
@@ -85,16 +83,9 @@ watch(
           >
             <Tabs />
           </CollapseTransition>
-          <NScrollbar
-            class="transition-[padding]"
-            :class="{
-              'pb-4': isSmallScreen && layoutSlideDirection,
-            }"
-            container-class="main-container"
-            :theme-overrides="scrollbarInMainLayout"
-          >
+          <div class="relative flex-1 overflow-hidden">
             <MainLayout />
-          </NScrollbar>
+          </div>
           <EmptyPlaceholder
             :show="isEmpty(tabsStore.tabs)"
             description="空标签页"

+ 3 - 2
src/views/about/index.vue

@@ -3,6 +3,7 @@ import { NCard, NSplit, NButton, NScrollbar, NTag } from 'naive-ui'
 import { onMounted, ref } from 'vue'
 
 import packageJson from '@/../package.json'
+import { ContentWrapper } from '@/components'
 import { useInjection } from '@/composables'
 import { mediaQueryInjectionKey } from '@/injection'
 
@@ -186,7 +187,7 @@ onMounted(async () => {
 })
 </script>
 <template>
-  <div class="flex flex-col gap-y-4 p-4">
+  <ContentWrapper content-class="flex flex-col gap-y-4">
     <NCard
       :title="`关于 ${APP_NAME}`"
       :size="isMediumScreen ? 'small' : undefined"
@@ -311,5 +312,5 @@ onMounted(async () => {
         </NSplit>
       </NCard>
     </div>
-  </div>
+  </ContentWrapper>
 </template>

+ 3 - 2
src/views/dashboard/index.vue

@@ -4,6 +4,7 @@ import * as echarts from 'echarts'
 import { NNumberAnimation } from 'naive-ui'
 import { onMounted, watch, ref, computed, onUnmounted, nextTick } from 'vue'
 
+import { ContentWrapper } from '@/components'
 import { usePersonalization } from '@/composables'
 import { usePreferencesStore } from '@/stores'
 import twc from '@/utils/tailwindColor'
@@ -1118,7 +1119,7 @@ watch([isDark, color], () => {
 })
 </script>
 <template>
-  <div class="space-y-4 p-4">
+  <ContentWrapper content-class="flex flex-col gap-y-4">
     <div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
       <div
         v-for="item in cardList"
@@ -1217,5 +1218,5 @@ watch([isDark, color], () => {
         </div>
       </div>
     </div>
-  </div>
+  </ContentWrapper>
 </template>

+ 3 - 2
src/views/data-show/data-form/index.vue

@@ -22,6 +22,7 @@ import {
 } from 'naive-ui'
 import { computed, ref, useTemplateRef, watch } from 'vue'
 
+import { ContentWrapper } from '@/components'
 import { useInjection, useResettableReactive } from '@/composables'
 import { mediaQueryInjectionKey } from '@/injection'
 
@@ -222,7 +223,7 @@ watch(
 )
 </script>
 <template>
-  <div class="flex h-full flex-col gap-y-2 overflow-hidden p-4">
+  <ContentWrapper content-class="flex flex-col gap-y-2">
     <NAlert
       type="info"
       closable
@@ -544,5 +545,5 @@ watch(
         </template>
       </NSplit>
     </NCard>
-  </div>
+  </ContentWrapper>
 </template>

+ 16 - 16
src/views/data-show/data-table/index.vue

@@ -19,12 +19,8 @@ import {
 } from 'naive-ui'
 import { defineComponent, reactive, ref, useTemplateRef, nextTick } from 'vue'
 
-import {
-  useInjection,
-  useComponentModifier,
-  useDataTable,
-  useResettableReactive,
-} from '@/composables'
+import { ContentWrapper } from '@/components'
+import { useInjection, useComponentModifier, useResettableReactive } from '@/composables'
 import { mediaQueryInjectionKey } from '@/injection'
 
 import ModalData from './ModalData.vue'
@@ -58,8 +54,6 @@ const formRef = useTemplateRef<InstanceType<typeof NForm>>('formRef')
 
 const dataTableRef = useTemplateRef<InstanceType<typeof NDataTable>>('dataTableRef')
 
-const { maxHeight, updateMaxHeight } = useDataTable(dataTableRef)
-
 const message = useMessage()
 
 const modal = useModal()
@@ -445,15 +439,21 @@ async function getDataList() {
 getDataList()
 </script>
 <template>
-  <div class="main-wrap flex flex-col gap-y-2 p-4">
+  <ContentWrapper
+    content-class="flex flex-col gap-y-2"
+    :scrollable="false"
+  >
     <NAlert
       type="info"
       closable
-      @after-leave="updateMaxHeight"
     >
-      一个数据表格的例子,不算复杂,有一个高度的计算,也许对你有帮助
+      一个数据表格的例子,不算复杂,也许对你有帮助
     </NAlert>
-    <NCard :size="isMediumScreen ? 'small' : undefined">
+    <NCard
+      :size="isMediumScreen ? 'small' : undefined"
+      class="flex-1"
+      content-class="flex flex-col"
+    >
       <div class="mb-2 flex justify-end gap-x-4 max-xl:mb-4 max-xl:flex-wrap">
         <NForm
           ref="formRef"
@@ -536,14 +536,14 @@ getDataList()
           </NButton>
         </div>
       </div>
-      <div>
+      <div class="flex flex-1 flex-col">
         <NDataTable
+          class="flex-1"
           ref="dataTableRef"
           v-model:checked-row-keys="checkedRowKeys"
           :remote="true"
-          :max-height="isMediumScreen ? undefined : maxHeight"
+          flex-height
           :scroll-x="enableScrollX ? 1800 : 0"
-          :min-height="166.6"
           :columns="columns"
           :data="dataList"
           :row-key="(row) => row.id"
@@ -616,5 +616,5 @@ getDataList()
       v-bind="dropdownOptions"
       :show="enableContextmenu && showDropdown"
     />
-  </div>
+  </ContentWrapper>
 </template>

+ 3 - 3
src/views/drag-drop/index.vue

@@ -3,7 +3,7 @@ import { NAlert, NCard, NSplit, NScrollbar, NButton } from 'naive-ui'
 import { ref, watch } from 'vue'
 import { VueDraggable } from 'vue-draggable-plus'
 
-import { EmptyPlaceholder } from '@/components'
+import { ContentWrapper, EmptyPlaceholder } from '@/components'
 import { useInjection } from '@/composables'
 import { mediaQueryInjectionKey } from '@/injection'
 
@@ -115,7 +115,7 @@ watch(
 )
 </script>
 <template>
-  <div class="flex flex-col gap-y-2 p-4">
+  <ContentWrapper content-class="flex flex-col gap-y-2">
     <NAlert
       type="info"
       closable
@@ -301,5 +301,5 @@ watch(
         </template>
       </NSplit>
     </NCard>
-  </div>
+  </ContentWrapper>
 </template>

+ 3 - 2
src/views/dynamic-route/index.vue

@@ -2,6 +2,7 @@
 import { NCard, NAlert, NButton } from 'naive-ui'
 import { RouterLink, useRouter } from 'vue-router'
 
+import { ContentWrapper } from '@/components'
 import { useInjection } from '@/composables'
 import { mediaQueryInjectionKey } from '@/injection'
 
@@ -14,7 +15,7 @@ defineOptions({
 const router = useRouter()
 </script>
 <template>
-  <div class="flex flex-col gap-y-2 p-4">
+  <ContentWrapper content-class="flex flex-col gap-y-2">
     <NAlert
       type="info"
       closable
@@ -46,5 +47,5 @@ const router = useRouter()
         </RouterLink>
       </div>
     </NCard>
-  </div>
+  </ContentWrapper>
 </template>

+ 3 - 2
src/views/error-page/404.vue

@@ -2,6 +2,7 @@
 import { NButton, NAlert } from 'naive-ui'
 import { reactive } from 'vue'
 
+import { ContentWrapper } from '@/components'
 import router from '@/router'
 
 import ErrorPage from './index.vue'
@@ -22,7 +23,7 @@ const changeStateCode200 = () => {
 }
 </script>
 <template>
-  <div class="h-full p-4">
+  <ContentWrapper>
     <NAlert
       type="info"
       closable
@@ -63,5 +64,5 @@ const changeStateCode200 = () => {
         >
       </ErrorPage>
     </div>
-  </div>
+  </ContentWrapper>
 </template>

+ 4 - 2
src/views/feedback/index.vue

@@ -2,6 +2,8 @@
 import { NAlert, NCard, useMessage, NButton, useModal, NModal, useNotification } from 'naive-ui'
 import { reactive } from 'vue'
 
+import { ContentWrapper } from '@/components'
+
 import type { ModalProps } from 'naive-ui'
 
 defineOptions({
@@ -75,7 +77,7 @@ const createDialogApi = (type: ModalProps['type'] = 'success') => {
 }
 </script>
 <template>
-  <div class="flex flex-col gap-y-2 p-4">
+  <ContentWrapper content-class="flex flex-col gap-y-2">
     <NAlert
       type="info"
       closable
@@ -458,5 +460,5 @@ const createDialogApi = (type: ModalProps['type'] = 'success') => {
       negative-text="算了"
     >
     </NModal>
-  </div>
+  </ContentWrapper>
 </template>

+ 4 - 2
src/views/multi-level-menu/index.vue

@@ -1,12 +1,14 @@
 <script setup lang="ts">
 import { useRoute } from 'vue-router'
 
+import { ContentWrapper } from '@/components'
+
 const route = useRoute()
 </script>
 <template>
-  <div class="p-4">
+  <ContentWrapper>
     <div class="flex h-96 items-center justify-center text-3xl">
       {{ route.meta.title }}
     </div>
-  </div>
+  </ContentWrapper>
 </template>