fix:修复tab宽度不实时更新问题 (#3839)

This commit is contained in:
liukun 2025-11-24 17:10:02 +08:00 committed by GitHub
parent 08022b1ca7
commit 657adc6a6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 180 additions and 39 deletions

View File

@ -0,0 +1,56 @@
<template>
<div class="tab-demo-position">
<tiny-radio-group v-model="position" class="mb10">
<tiny-radio-button label="top">top 显示</tiny-radio-button>
<tiny-radio-button label="bottom">bottom 显示</tiny-radio-button>
<tiny-radio-button label="left">left 显示</tiny-radio-button>
<tiny-radio-button label="right">right 显示</tiny-radio-button>
</tiny-radio-group>
<tiny-tabs v-model="activeName4" tab-style="card" :position="position" header-only>
<tiny-tab-item v-for="item in tabs3" :key="item.name" :title="item.title" :name="item.name">
{{ item.content }}
</tiny-tab-item>
</tiny-tabs>
</div>
</template>
<script setup lang="jsx">
import { ref, onMounted, nextTick } from 'vue'
import { TinyTabs, TinyTabItem, TinyRadioGroup, TinyRadioButton } from '@opentiny/vue'
const activeName4 = ref('navigation3')
const position = ref('bottom')
const tabs3 = ref([
{
name: 'navigation1',
title: 'Navigation 1',
content: 'Navigation 1'
},
{
name: 'navigation2',
title: 'Navigation 2',
content: 'Navigation 2'
},
{
name: 'navigation3',
title: 'Navigation 3',
content: 'Navigation 3'
},
{
name: 'navigation4',
title: 'Navigation 4',
content: 'Navigation 4'
},
{
name: 'navigation5',
title: 'Navigation 5',
content: 'Navigation 5'
}
])
</script>
<style scoped>
.tab-demo-position {
min-height: 250px;
}
</style>

View File

@ -14,39 +14,50 @@
</div>
</template>
<script setup lang="jsx">
import { ref } from 'vue'
<script>
import { TinyTabs, TinyTabItem, TinyRadioGroup, TinyRadioButton } from '@opentiny/vue'
const activeName4 = ref('navigation1')
const position = ref('left')
const tabs3 = ref([
{
name: 'navigation1',
title: 'Navigation 1',
content: 'Navigation 1'
export default {
components: {
TinyTabs,
TinyTabItem,
TinyRadioGroup,
TinyRadioButton
},
{
name: 'navigation2',
title: 'Navigation 2',
content: 'Navigation 2'
},
{
name: 'navigation3',
title: 'Navigation 3',
content: 'Navigation 3'
},
{
name: 'navigation4',
title: 'Navigation 4',
content: 'Navigation 4'
},
{
name: 'navigation5',
title: 'Navigation 5',
content: 'Navigation 5'
data() {
return {
activeName4: 'navigation1',
position: 'bottom',
tabs3: [
{
name: 'navigation1',
title: 'Navigation 1',
content: 'Navigation 1'
},
{
name: 'navigation2',
title: 'Navigation 2',
content: 'Navigation 2'
},
{
name: 'navigation3',
title: 'Navigation 3',
content: 'Navigation 3'
},
{
name: 'navigation4',
title: 'Navigation 4',
content: 'Navigation 4'
},
{
name: 'navigation5',
title: 'Navigation 5',
content: 'Navigation 5'
}
]
}
}
])
}
</script>
<style scoped>

View File

@ -54,7 +54,7 @@ export default defineConfig((config) => {
customName: (name: string) => {
return name === 'default'
? `@opentiny/vue-${lib}$`
: `@opentiny/vue-${lib}/${name.replace(/^icon-/, '')}/index.ts`
: `@opentiny/vue-${lib}/${name.replace(/^icon-/, '')}.ts`
}
}))
],

View File

@ -1,6 +1,10 @@
export const api = ['state']
export const renderless = (props, { reactive, inject, computed, onMounted, onBeforeUnmount }, { vm }) => {
export const renderless = (
props,
{ reactive, inject, computed, onMounted, onBeforeUnmount, watch, nextTick },
{ vm }
) => {
const tabs = inject('tabs', null)
const state = reactive({
@ -13,21 +17,91 @@ export const renderless = (props, { reactive, inject, computed, onMounted, onBef
currentPosition: 0
})
let observer
const updateSliderBar = () => {
const nav = state.currentNav
if (nav && nav.$el) {
state.currentWidth = nav.$el.offsetWidth || 0
state.currentPosition = nav.$el.offsetLeft || 0
}
}
let mutationObserver
let resizeObserver
onMounted(() => {
observer = new MutationObserver((mutationsList) => {
const nav = state.currentNav
state.currentWidth = (nav && nav.$el.offsetWidth) || 0
state.currentPosition = (nav && nav.$el.offsetLeft) || 0
// 使用 MutationObserver 监听 DOM 结构变化
mutationObserver = new MutationObserver(() => {
nextTick(() => {
updateSliderBar()
})
})
observer.observe(vm.$el, { attributes: true, subtree: true })
mutationObserver.observe(vm.$el, { attributes: true, subtree: true, childList: true })
// 使用 ResizeObserver 监听元素尺寸变化
if (typeof ResizeObserver !== 'undefined') {
resizeObserver = new ResizeObserver(() => {
nextTick(() => {
updateSliderBar()
})
})
// 监听所有 nav-item 的尺寸变化
const navItems = vm.$el.querySelectorAll('[data-tag="tiny-tab-nav-item"]')
navItems.forEach((item) => {
resizeObserver.observe(item)
})
}
// 初始更新
nextTick(() => {
updateSliderBar()
})
})
// 监听 currentNav 变化,更新 slider bar
watch(
() => state.currentNav,
() => {
nextTick(() => {
updateSliderBar()
})
},
{ immediate: true }
)
// 监听 navItems 变化,重新设置 ResizeObserver
watch(
() => state.navItems,
() => {
if (resizeObserver) {
resizeObserver.disconnect()
nextTick(() => {
const navItems = vm.$el.querySelectorAll('[data-tag="tiny-tab-nav-item"]')
navItems.forEach((item) => {
resizeObserver.observe(item)
})
updateSliderBar()
})
} else {
// 如果 ResizeObserver 不可用,在 navItems 变化时也更新 slider bar
nextTick(() => {
updateSliderBar()
})
}
},
{ deep: true }
)
onBeforeUnmount(() => {
observer.disconnect()
observer = null
if (mutationObserver) {
mutationObserver.disconnect()
mutationObserver = null
}
if (resizeObserver) {
resizeObserver.disconnect()
resizeObserver = null
}
})
Object.assign(api, {