123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- <template>
- <!-- NOTE:min-h-1在使用useElementSize时非常重要 -->
- <view ref="container" class="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"/>
- <swiper :current="current" :style="{height: swiperHeight+'px'}" 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>
- </template>
- <script setup>
- import {ref, computed, watch} from 'vue'
- import _ from "lodash";
- 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('')
- })
- const emits = defineEmits(['update:modelValue', 'change'])
- // 因为外部可能不会使用v-model:current接收回传,所以这里本地保持住该值
- const current = ref(0)
- const cachedTabs = ref([])
- const container = ref(null)
- 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
- }
- })
- const swiperBindings = computed(() => {
- // 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([current, () => props.tabs], ([current]) => {
- 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
- // 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})
- 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)
- }
- </script>
- <style>
- </style>
|