From d3cd31da800a8f3c1f59f67d6f61443c51f509c4 Mon Sep 17 00:00:00 2001 From: chenxi <2465950588@qq.com> Date: Mon, 15 Sep 2025 02:14:55 -0700 Subject: [PATCH] =?UTF-8?q?fix(tabs):=20=E4=BC=98=E5=8C=96=E5=A4=9A?= =?UTF-8?q?=E7=AB=AF=E9=A1=B5=E7=AD=BE=E6=B8=B2=E6=9F=93=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/renderless/src/tabs-mf/vue-nav.ts | 31 +++++---------- .../renderless/src/tabs-mf/vue-slider-bar.ts | 25 ++++++++++++ packages/renderless/src/tabs-mf/vue.ts | 4 +- packages/vue/src/tabs/src/mobile-first.vue | 2 +- .../vue/src/tabs/src/mobile-first/tab-bar.vue | 6 ++- .../vue/src/tabs/src/mobile-first/tab-nav.vue | 15 ++++---- .../tabs/src/mobile-first/tab-slider-bar.vue | 38 +++++++++++++++++++ 7 files changed, 88 insertions(+), 33 deletions(-) create mode 100644 packages/renderless/src/tabs-mf/vue-slider-bar.ts create mode 100644 packages/vue/src/tabs/src/mobile-first/tab-slider-bar.vue diff --git a/packages/renderless/src/tabs-mf/vue-nav.ts b/packages/renderless/src/tabs-mf/vue-nav.ts index 4d3529bd16..f794d78dbd 100644 --- a/packages/renderless/src/tabs-mf/vue-nav.ts +++ b/packages/renderless/src/tabs-mf/vue-nav.ts @@ -7,43 +7,32 @@ export const renderless = (props, { reactive, inject, computed, onMounted, onBef navItems: computed(() => tabs.state.items), currentNav: computed(() => tabs.state.navs[state.currentIndex]), currentIndex: computed(() => - tabs.state.navs.findIndex((item) => tabs.state.currentItem && tabs.state.currentItem.name === item.name) + tabs.state.navs.findIndex((item) => tabs.state.cacheCurrentItem && tabs.state.cacheCurrentItem.name === item.name) ), currentWidth: 0, currentPosition: 0 }) - let rafId, observer + let observer onMounted(() => { observer = new MutationObserver((mutationsList) => { - for (let mutation of mutationsList) { - if (mutation.type === 'attributes') { - if (rafId) { - cancelAnimationFrame(rafId) - } - - rafId = requestAnimationFrame(() => { - const nav = state.currentNav - - state.currentWidth = (nav && nav.$el.offsetWidth) || 0 - state.currentPosition = (nav && nav.$el.offsetLeft) || 0 - }) - } - } + const nav = state.currentNav + state.currentWidth = (nav && nav.$el.offsetWidth) || 0 + state.currentPosition = (nav && nav.$el.offsetLeft) || 0 }) observer.observe(vm.$el, { attributes: true, subtree: true }) }) onBeforeUnmount(() => { - if (rafId) { - cancelAnimationFrame(rafId) - } - observer.disconnect() observer = null }) - return { state } + Object.assign(api, { + state + }) + + return api } diff --git a/packages/renderless/src/tabs-mf/vue-slider-bar.ts b/packages/renderless/src/tabs-mf/vue-slider-bar.ts new file mode 100644 index 0000000000..ec634a6ea1 --- /dev/null +++ b/packages/renderless/src/tabs-mf/vue-slider-bar.ts @@ -0,0 +1,25 @@ +export const api = ['state'] + +export const renderless = (props, { reactive, inject, computed, onMounted, onUnmounted, onUpdated }, { vm, emit }) => { + const tabs = inject('tabs', null) + + const state = reactive({ + currentNav: computed(() => props.currentNav || null), + currentWidth: computed(() => props.currentWidth + 'px'), + currentPosition: computed(() => props.currentPosition + 'px') + }) + + const handleTransitionEnd = () => { + tabs.state.currentItem = tabs.state.cacheCurrentItem + } + + onMounted(() => { + vm.$el.addEventListener('transitionend', handleTransitionEnd) + }) + + onUnmounted(() => { + vm.$el.removeEventListener('transitionend', handleTransitionEnd) + }) + + return { state } +} diff --git a/packages/renderless/src/tabs-mf/vue.ts b/packages/renderless/src/tabs-mf/vue.ts index e1f399c64e..331daaf693 100644 --- a/packages/renderless/src/tabs-mf/vue.ts +++ b/packages/renderless/src/tabs-mf/vue.ts @@ -39,7 +39,8 @@ export const renderless = (props, hooks, { vm, emit, nextTick }) => { const state = reactive({ items: [], navs: [], - currentItem: computed(() => state.items.find((item) => item.selected)), + currentItem: null, + cacheCurrentItem: computed(() => state.items.find((item) => item.selected)), key: computed(() => (state.currentItem ? state.currentItem.name : random())), separator: props.separator, swipeable: computed(() => api.computedSwipeable()), @@ -93,6 +94,7 @@ export const renderless = (props, hooks, { vm, emit, nextTick }) => { onMounted(() => { // 在 vue2 类似 tabSwipe0 这些动态的 ref 只能在 nextTick 中拿到 nextTick(() => api.observeTabSwipeSize()) + state.currentItem = state.cacheCurrentItem props.activeName && api.scrollTo(props.activeName) props.modelValue && api.scrollTo(props.modelValue) }) diff --git a/packages/vue/src/tabs/src/mobile-first.vue b/packages/vue/src/tabs/src/mobile-first.vue index 4739d86867..f61b0d56f2 100644 --- a/packages/vue/src/tabs/src/mobile-first.vue +++ b/packages/vue/src/tabs/src/mobile-first.vue @@ -49,7 +49,7 @@ export default defineComponent({ vnodes = [] state.items.forEach((item) => { - if (item !== state.currentItem) { + if (!state.currentItem || item.name !== state.currentItem.name) { return } diff --git a/packages/vue/src/tabs/src/mobile-first/tab-bar.vue b/packages/vue/src/tabs/src/mobile-first/tab-bar.vue index 891545ff9a..9b206e7125 100644 --- a/packages/vue/src/tabs/src/mobile-first/tab-bar.vue +++ b/packages/vue/src/tabs/src/mobile-first/tab-bar.vue @@ -1,6 +1,6 @@