|
|
@@ -1,65 +1,66 @@
|
|
|
<template>
|
|
|
- <!-- NOTE:min-h-1在使用useElementSize时非常重要 -->
|
|
|
- <view ref="container" class="h-full fx-col flex-1 min-h-1">
|
|
|
- <uv-tabs :current="current" :list="tabs" :key-name="keyName" v-bind="tabBindings"
|
|
|
- class="bg-white bd-b-1" @change="handleTabChange">
|
|
|
- <template v-if="$slots.tab" #default="scope">
|
|
|
- <slot name="tab" v-bind="scope"/>
|
|
|
- </template>
|
|
|
- </uv-tabs>
|
|
|
- <uv-line v-if="border"/>
|
|
|
- <view>
|
|
|
- <slot name="header"></slot>
|
|
|
- </view>
|
|
|
- <view class="flex-1 min-h-1">
|
|
|
- <!-- :style="{height: swiperHeight+'px'}" -->
|
|
|
- <swiper :current="current" class="h-full" v-bind="swiperBindings"
|
|
|
- @change="handleSwiperChange">
|
|
|
- <swiper-item v-for="t in tabs" :key="t.name">
|
|
|
- <!-- 延迟渲染 -->
|
|
|
- <!-- 如果配置相同的template可共享卡槽 -->
|
|
|
- <!-- 内容如果要支持滚动,请使用css class - .tabs-swiper-content -->
|
|
|
- <keep-alive v-if="lazy">
|
|
|
- <slot v-if="t.show" :name="t.template||template||t.name" v-bind="t"/>
|
|
|
- </keep-alive>
|
|
|
- <slot v-else :name="t.template||template||t.name" v-bind="t"/>
|
|
|
- </swiper-item>
|
|
|
+ <!-- NOTE:min-h-1在使用useElementSize时非常重要 -->
|
|
|
+ <view ref="container" class="h-full fx-col flex-1 min-h-1">
|
|
|
+ <uv-tabs :current="current" :list="tabs" :key-name="keyName" v-bind="tabBindings" class="bg-white bd-b-1"
|
|
|
+ @change="handleTabChange">
|
|
|
+ <template v-if="$slots.tab" #default="scope">
|
|
|
+ <slot name="tab" v-bind="scope" />
|
|
|
+ </template>
|
|
|
+ </uv-tabs>
|
|
|
+ <uv-line v-if="border" />
|
|
|
+ <view>
|
|
|
+ <slot name="header"></slot>
|
|
|
+ </view>
|
|
|
+ <view class="flex-1 min-h-1 relative">
|
|
|
+ <!-- :style="{height: swiperHeight+'px'}" -->
|
|
|
+ <view class="absolute inset-0">
|
|
|
+ <swiper :current="current" class="h-full" v-bind="swiperBindings" @change="handleSwiperChange">
|
|
|
+ <swiper-item v-for="t in tabs" :key="t.name">
|
|
|
+ <!-- 延迟渲染 -->
|
|
|
+ <!-- 如果配置相同的template可共享卡槽 -->
|
|
|
+ <!-- 内容如果要支持滚动,请使用css class - .tabs-swiper-content -->
|
|
|
+ <keep-alive v-if="lazy">
|
|
|
+ <slot v-if="t.show" :name="t.template || template || t.name" v-bind="t" />
|
|
|
+ </keep-alive>
|
|
|
+ <slot v-else :name="t.template || template || t.name" v-bind="t" />
|
|
|
+ </swiper-item>
|
|
|
</swiper>
|
|
|
- </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
+ </view>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import {ref, computed, watch} from 'vue'
|
|
|
+import { ref, computed, watch } from 'vue'
|
|
|
import _ from "lodash";
|
|
|
-import {createPropDefine} from "@/utils";
|
|
|
-import {useElementSize} from "@vueuse/core";
|
|
|
+import { createPropDefine } from "@/utils";
|
|
|
+import { useElementSize } from "@vueuse/core";
|
|
|
|
|
|
const props = defineProps({
|
|
|
- // support synchronization
|
|
|
- modelValue: createPropDefine(0, Number),
|
|
|
- tabs: createPropDefine([], Array),
|
|
|
- keyName: createPropDefine('name'),
|
|
|
- // other options of uv-tabs
|
|
|
- tabOptions: createPropDefine(null, Object),
|
|
|
- // other uni-app swiper options
|
|
|
- swiperOptions: createPropDefine(null, Object),
|
|
|
- // support lazy rendering, default is true
|
|
|
- lazy: createPropDefine(true, Boolean),
|
|
|
- // whether cache all tabs? if not use cacheSize, higher priority than cacheSize
|
|
|
- cacheAll: createPropDefine(false, Boolean),
|
|
|
- // cached accessed tab. <=0 means cache all.
|
|
|
- cacheSize: createPropDefine(5, Number),
|
|
|
- // tab content will render after delay time, recommended for complex dynamic content.
|
|
|
- // related to lazy, default is true
|
|
|
- delay: createPropDefine(true, Boolean),
|
|
|
- preload: createPropDefine(true, Boolean), // 一般情况下preload会非常平滑,但有大请求时可以禁用
|
|
|
- // bigger than the animation time。 default 400ms.
|
|
|
- delayTime: createPropDefine(400, Number),
|
|
|
- border: createPropDefine(false, Boolean),
|
|
|
- tabsHeight: createPropDefine(44, Number),
|
|
|
- // 统一指定模板,优先级低于tab.template,因为有时候让tab指定template会破坏原数据结构
|
|
|
- template: createPropDefine('')
|
|
|
+ // support synchronization
|
|
|
+ modelValue: createPropDefine(0, Number),
|
|
|
+ tabs: createPropDefine([], Array),
|
|
|
+ keyName: createPropDefine('name'),
|
|
|
+ // other options of uv-tabs
|
|
|
+ tabOptions: createPropDefine(null, Object),
|
|
|
+ // other uni-app swiper options
|
|
|
+ swiperOptions: createPropDefine(null, Object),
|
|
|
+ // support lazy rendering, default is true
|
|
|
+ lazy: createPropDefine(true, Boolean),
|
|
|
+ // whether cache all tabs? if not use cacheSize, higher priority than cacheSize
|
|
|
+ cacheAll: createPropDefine(false, Boolean),
|
|
|
+ // cached accessed tab. <=0 means cache all.
|
|
|
+ cacheSize: createPropDefine(5, Number),
|
|
|
+ // tab content will render after delay time, recommended for complex dynamic content.
|
|
|
+ // related to lazy, default is true
|
|
|
+ delay: createPropDefine(true, Boolean),
|
|
|
+ preload: createPropDefine(true, Boolean), // 一般情况下preload会非常平滑,但有大请求时可以禁用
|
|
|
+ // bigger than the animation time。 default 400ms.
|
|
|
+ delayTime: createPropDefine(400, Number),
|
|
|
+ border: createPropDefine(false, Boolean),
|
|
|
+ tabsHeight: createPropDefine(44, Number),
|
|
|
+ // 统一指定模板,优先级低于tab.template,因为有时候让tab指定template会破坏原数据结构
|
|
|
+ template: createPropDefine('')
|
|
|
})
|
|
|
const emits = defineEmits(['update:modelValue', 'change'])
|
|
|
|
|
|
@@ -67,69 +68,68 @@ const emits = defineEmits(['update:modelValue', 'change'])
|
|
|
const current = ref(0)
|
|
|
const cachedTabs = ref([])
|
|
|
const container = ref(null)
|
|
|
-const {height} = useElementSize(container)
|
|
|
+const { height } = useElementSize(container)
|
|
|
const swiperHeight = computed(() => height.value - props.tabsHeight - (props.border ? 1 : 0))
|
|
|
|
|
|
const tabBindings = computed(() => {
|
|
|
- // make some default options for u-tabs here
|
|
|
- return {
|
|
|
- scrollable: props.tabs.length > 4,
|
|
|
- itemStyle: {height: props.tabsHeight + 'px'},
|
|
|
- ...props.tabOptions
|
|
|
- }
|
|
|
+ // make some default options for u-tabs here
|
|
|
+ return {
|
|
|
+ scrollable: props.tabs.length > 4,
|
|
|
+ itemStyle: { height: props.tabsHeight + 'px' },
|
|
|
+ ...props.tabOptions
|
|
|
+ }
|
|
|
})
|
|
|
const swiperBindings = computed(() => {
|
|
|
- // make some default options for uni-app swiper here
|
|
|
- return {
|
|
|
- ...props.swiperOptions
|
|
|
- }
|
|
|
+ // make some default options for uni-app swiper here
|
|
|
+ return {
|
|
|
+ ...props.swiperOptions
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
// 保持localCurrent与props.current同步,即props优先级更高
|
|
|
-watch(() => props.modelValue, (val) => current.value = val, {immediate: true})
|
|
|
+watch(() => props.modelValue, (val) => current.value = val, { immediate: true })
|
|
|
|
|
|
watch([current, () => props.tabs], ([current]) => {
|
|
|
- const {cacheAll, cacheSize, tabs, delay, preload, delayTime} = props
|
|
|
- if (!tabs.length) return // no data, wait for tabs ready.
|
|
|
+ const { cacheAll, cacheSize, tabs, delay, preload, delayTime } = props
|
|
|
+ if (!tabs.length) return // no data, wait for tabs ready.
|
|
|
|
|
|
- // delay control, first tab must render immediately.
|
|
|
- const effectDelay = cachedTabs.value.length && delay ? delayTime : 0
|
|
|
+ // delay control, first tab must render immediately.
|
|
|
+ const effectDelay = cachedTabs.value.length && delay ? delayTime : 0
|
|
|
|
|
|
- // cache tabs, keep accessed sequence.
|
|
|
- const next = [current]
|
|
|
- if (preload) {
|
|
|
- // keep current -1 +1 in cache list while preload=true.
|
|
|
- // this will make swiper action much-much smooth.
|
|
|
- if (current + 1 < tabs.length) next.unshift(current + 1)
|
|
|
- if (current - 1 >= 0) next.unshift(current - 1)
|
|
|
- }
|
|
|
- _.pull(cachedTabs.value, ...next)
|
|
|
- cachedTabs.value.push(...next)
|
|
|
- while (!cacheAll && cacheSize && cachedTabs.value.length > cacheSize) {
|
|
|
- cachedTabs.value.shift()
|
|
|
- }
|
|
|
+ // cache tabs, keep accessed sequence.
|
|
|
+ const next = [current]
|
|
|
+ if (preload) {
|
|
|
+ // keep current -1 +1 in cache list while preload=true.
|
|
|
+ // this will make swiper action much-much smooth.
|
|
|
+ if (current + 1 < tabs.length) next.unshift(current + 1)
|
|
|
+ if (current - 1 >= 0) next.unshift(current - 1)
|
|
|
+ }
|
|
|
+ _.pull(cachedTabs.value, ...next)
|
|
|
+ cachedTabs.value.push(...next)
|
|
|
+ while (!cacheAll && cacheSize && cachedTabs.value.length > cacheSize) {
|
|
|
+ cachedTabs.value.shift()
|
|
|
+ }
|
|
|
|
|
|
- // reset show property of all tabs
|
|
|
- if (effectDelay) {
|
|
|
- setTimeout(() => {
|
|
|
- tabs.forEach((t, i) => t.show = cachedTabs.value.includes(i))
|
|
|
- }, effectDelay)
|
|
|
- } else {
|
|
|
- tabs.forEach((t, i) => t.show = cachedTabs.value.includes(i))
|
|
|
- }
|
|
|
-}, {immediate: true})
|
|
|
+ // reset show property of all tabs
|
|
|
+ if (effectDelay) {
|
|
|
+ setTimeout(() => {
|
|
|
+ tabs.forEach((t, i) => t.show = cachedTabs.value.includes(i))
|
|
|
+ }, effectDelay)
|
|
|
+ } else {
|
|
|
+ tabs.forEach((t, i) => t.show = cachedTabs.value.includes(i))
|
|
|
+ }
|
|
|
+}, { immediate: true })
|
|
|
|
|
|
-const handleTabChange = function ({index}) {
|
|
|
- current.value = index
|
|
|
- emits('update:modelValue', current.value)
|
|
|
- emits('change', current.value)
|
|
|
+const handleTabChange = function ({ index }) {
|
|
|
+ current.value = index
|
|
|
+ emits('update:modelValue', current.value)
|
|
|
+ emits('change', current.value)
|
|
|
}
|
|
|
const handleSwiperChange = function (e) {
|
|
|
- current.value = e.detail.current
|
|
|
- emits('update:modelValue', current.value)
|
|
|
- emits('change', current.value)
|
|
|
+ current.value = e.detail.current
|
|
|
+ emits('update:modelValue', current.value)
|
|
|
+ emits('change', current.value)
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
-<style>
|
|
|
-</style>
|
|
|
+<style></style>
|