mx-tabs-swiper.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. <template>
  2. <!-- NOTE:min-h-1在使用useElementSize时非常重要 -->
  3. <view ref="container" class="fx-col flex-1 min-h-1">
  4. <uv-tabs :current="current" :list="tabs" :key-name="keyName" v-bind="tabBindings"
  5. class="bg-white bd-b-1" @change="handleTabChange">
  6. <template v-if="$slots.tab" #default="scope">
  7. <slot name="tab" v-bind="scope"/>
  8. </template>
  9. </uv-tabs>
  10. <uv-line v-if="border"/>
  11. <swiper :current="current" :style="{height: swiperHeight+'px'}" v-bind="swiperBindings"
  12. @change="handleSwiperChange">
  13. <swiper-item v-for="t in tabs" :key="t.name">
  14. <!-- 延迟渲染 -->
  15. <!-- 如果配置相同的template可共享卡槽 -->
  16. <!-- 内容如果要支持滚动,请使用css class - .tabs-swiper-content -->
  17. <keep-alive v-if="lazy">
  18. <slot v-if="t.show" :name="t.template||template||t.name" v-bind="t"/>
  19. </keep-alive>
  20. <slot v-else :name="t.template||template||t.name" v-bind="t"/>
  21. </swiper-item>
  22. </swiper>
  23. </view>
  24. </template>
  25. <script setup>
  26. import {ref, computed, watch} from 'vue'
  27. import _ from "lodash";
  28. import {createPropDefine} from "@/utils";
  29. import {useElementSize} from "@vueuse/core";
  30. const props = defineProps({
  31. // support synchronization
  32. modelValue: createPropDefine(0, Number),
  33. tabs: createPropDefine([], Array),
  34. keyName: createPropDefine('name'),
  35. // other options of uv-tabs
  36. tabOptions: createPropDefine(null, Object),
  37. // other uni-app swiper options
  38. swiperOptions: createPropDefine(null, Object),
  39. // support lazy rendering, default is true
  40. lazy: createPropDefine(true, Boolean),
  41. // whether cache all tabs? if not use cacheSize, higher priority than cacheSize
  42. cacheAll: createPropDefine(false, Boolean),
  43. // cached accessed tab. <=0 means cache all.
  44. cacheSize: createPropDefine(5, Number),
  45. // tab content will render after delay time, recommended for complex dynamic content.
  46. // related to lazy, default is true
  47. delay: createPropDefine(true, Boolean),
  48. preload: createPropDefine(true, Boolean), // 一般情况下preload会非常平滑,但有大请求时可以禁用
  49. // bigger than the animation time。 default 400ms.
  50. delayTime: createPropDefine(400, Number),
  51. border: createPropDefine(false, Boolean),
  52. tabsHeight: createPropDefine(44, Number),
  53. // 统一指定模板,优先级低于tab.template,因为有时候让tab指定template会破坏原数据结构
  54. template: createPropDefine('')
  55. })
  56. const emits = defineEmits(['update:modelValue', 'change'])
  57. // 因为外部可能不会使用v-model:current接收回传,所以这里本地保持住该值
  58. const current = ref(0)
  59. const cachedTabs = ref([])
  60. const container = ref(null)
  61. const {height} = useElementSize(container)
  62. const swiperHeight = computed(() => height.value - props.tabsHeight - (props.border ? 1 : 0))
  63. const tabBindings = computed(() => {
  64. // make some default options for u-tabs here
  65. return {
  66. scrollable: props.tabs.length > 4,
  67. itemStyle: {height: props.tabsHeight + 'px'},
  68. ...props.tabOptions
  69. }
  70. })
  71. const swiperBindings = computed(() => {
  72. // make some default options for uni-app swiper here
  73. return {
  74. ...props.swiperOptions
  75. }
  76. })
  77. // 保持localCurrent与props.current同步,即props优先级更高
  78. watch(() => props.modelValue, (val) => current.value = val, {immediate: true})
  79. watch([current, () => props.tabs], ([current]) => {
  80. const {cacheAll, cacheSize, tabs, delay, preload, delayTime} = props
  81. if (!tabs.length) return // no data, wait for tabs ready.
  82. // delay control, first tab must render immediately.
  83. const effectDelay = cachedTabs.value.length && delay ? delayTime : 0
  84. // cache tabs, keep accessed sequence.
  85. const next = [current]
  86. if (preload) {
  87. // keep current -1 +1 in cache list while preload=true.
  88. // this will make swiper action much-much smooth.
  89. if (current + 1 < tabs.length) next.unshift(current + 1)
  90. if (current - 1 >= 0) next.unshift(current - 1)
  91. }
  92. _.pull(cachedTabs.value, ...next)
  93. cachedTabs.value.push(...next)
  94. while (!cacheAll && cacheSize && cachedTabs.value.length > cacheSize) {
  95. cachedTabs.value.shift()
  96. }
  97. // reset show property of all tabs
  98. if (effectDelay) {
  99. setTimeout(() => {
  100. tabs.forEach((t, i) => t.show = cachedTabs.value.includes(i))
  101. }, effectDelay)
  102. } else {
  103. tabs.forEach((t, i) => t.show = cachedTabs.value.includes(i))
  104. }
  105. }, {immediate: true})
  106. const handleTabChange = function ({index}) {
  107. current.value = index
  108. emits('update:modelValue', current.value)
  109. emits('change', current.value)
  110. }
  111. const handleSwiperChange = function (e) {
  112. current.value = e.detail.current
  113. emits('update:modelValue', current.value)
  114. emits('change', current.value)
  115. }
  116. </script>
  117. <style>
  118. </style>