Skip to content

Commit 657adc6

Browse files
authored
fix:修复tab宽度不实时更新问题 (#3839)
1 parent 08022b1 commit 657adc6

File tree

4 files changed

+180
-39
lines changed

4 files changed

+180
-39
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<template>
2+
<div class="tab-demo-position">
3+
<tiny-radio-group v-model="position" class="mb10">
4+
<tiny-radio-button label="top">top 显示</tiny-radio-button>
5+
<tiny-radio-button label="bottom">bottom 显示</tiny-radio-button>
6+
<tiny-radio-button label="left">left 显示</tiny-radio-button>
7+
<tiny-radio-button label="right">right 显示</tiny-radio-button>
8+
</tiny-radio-group>
9+
<tiny-tabs v-model="activeName4" tab-style="card" :position="position" header-only>
10+
<tiny-tab-item v-for="item in tabs3" :key="item.name" :title="item.title" :name="item.name">
11+
{{ item.content }}
12+
</tiny-tab-item>
13+
</tiny-tabs>
14+
</div>
15+
</template>
16+
17+
<script setup lang="jsx">
18+
import { ref, onMounted, nextTick } from 'vue'
19+
import { TinyTabs, TinyTabItem, TinyRadioGroup, TinyRadioButton } from '@opentiny/vue'
20+
21+
const activeName4 = ref('navigation3')
22+
const position = ref('bottom')
23+
const tabs3 = ref([
24+
{
25+
name: 'navigation1',
26+
title: 'Navigation 1',
27+
content: 'Navigation 1'
28+
},
29+
{
30+
name: 'navigation2',
31+
title: 'Navigation 2',
32+
content: 'Navigation 2'
33+
},
34+
{
35+
name: 'navigation3',
36+
title: 'Navigation 3',
37+
content: 'Navigation 3'
38+
},
39+
{
40+
name: 'navigation4',
41+
title: 'Navigation 4',
42+
content: 'Navigation 4'
43+
},
44+
{
45+
name: 'navigation5',
46+
title: 'Navigation 5',
47+
content: 'Navigation 5'
48+
}
49+
])
50+
</script>
51+
52+
<style scoped>
53+
.tab-demo-position {
54+
min-height: 250px;
55+
}
56+
</style>

examples/sites/demos/pc/app/tabs/header-only.vue

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,50 @@
1414
</div>
1515
</template>
1616

17-
<script setup lang="jsx">
18-
import { ref } from 'vue'
17+
<script>
1918
import { TinyTabs, TinyTabItem, TinyRadioGroup, TinyRadioButton } from '@opentiny/vue'
2019
21-
const activeName4 = ref('navigation1')
22-
const position = ref('left')
23-
const tabs3 = ref([
24-
{
25-
name: 'navigation1',
26-
title: 'Navigation 1',
27-
content: 'Navigation 1'
20+
export default {
21+
components: {
22+
TinyTabs,
23+
TinyTabItem,
24+
TinyRadioGroup,
25+
TinyRadioButton
2826
},
29-
{
30-
name: 'navigation2',
31-
title: 'Navigation 2',
32-
content: 'Navigation 2'
33-
},
34-
{
35-
name: 'navigation3',
36-
title: 'Navigation 3',
37-
content: 'Navigation 3'
38-
},
39-
{
40-
name: 'navigation4',
41-
title: 'Navigation 4',
42-
content: 'Navigation 4'
43-
},
44-
{
45-
name: 'navigation5',
46-
title: 'Navigation 5',
47-
content: 'Navigation 5'
27+
data() {
28+
return {
29+
activeName4: 'navigation1',
30+
position: 'bottom',
31+
tabs3: [
32+
{
33+
name: 'navigation1',
34+
title: 'Navigation 1',
35+
content: 'Navigation 1'
36+
},
37+
{
38+
name: 'navigation2',
39+
title: 'Navigation 2',
40+
content: 'Navigation 2'
41+
},
42+
{
43+
name: 'navigation3',
44+
title: 'Navigation 3',
45+
content: 'Navigation 3'
46+
},
47+
{
48+
name: 'navigation4',
49+
title: 'Navigation 4',
50+
content: 'Navigation 4'
51+
},
52+
{
53+
name: 'navigation5',
54+
title: 'Navigation 5',
55+
content: 'Navigation 5'
56+
}
57+
]
58+
}
4859
}
49-
])
60+
}
5061
</script>
5162

5263
<style scoped>

examples/vue2/vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default defineConfig((config) => {
5454
customName: (name: string) => {
5555
return name === 'default'
5656
? `@opentiny/vue-${lib}$`
57-
: `@opentiny/vue-${lib}/${name.replace(/^icon-/, '')}/index.ts`
57+
: `@opentiny/vue-${lib}/${name.replace(/^icon-/, '')}.ts`
5858
}
5959
}))
6060
],

packages/renderless/src/tabs-mf/vue-nav.ts

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
export const api = ['state']
22

3-
export const renderless = (props, { reactive, inject, computed, onMounted, onBeforeUnmount }, { vm }) => {
3+
export const renderless = (
4+
props,
5+
{ reactive, inject, computed, onMounted, onBeforeUnmount, watch, nextTick },
6+
{ vm }
7+
) => {
48
const tabs = inject('tabs', null)
59

610
const state = reactive({
@@ -13,21 +17,91 @@ export const renderless = (props, { reactive, inject, computed, onMounted, onBef
1317
currentPosition: 0
1418
})
1519

16-
let observer
20+
const updateSliderBar = () => {
21+
const nav = state.currentNav
22+
if (nav && nav.$el) {
23+
state.currentWidth = nav.$el.offsetWidth || 0
24+
state.currentPosition = nav.$el.offsetLeft || 0
25+
}
26+
}
27+
28+
let mutationObserver
29+
let resizeObserver
1730

1831
onMounted(() => {
19-
observer = new MutationObserver((mutationsList) => {
20-
const nav = state.currentNav
21-
state.currentWidth = (nav && nav.$el.offsetWidth) || 0
22-
state.currentPosition = (nav && nav.$el.offsetLeft) || 0
32+
// 使用 MutationObserver 监听 DOM 结构变化
33+
mutationObserver = new MutationObserver(() => {
34+
nextTick(() => {
35+
updateSliderBar()
36+
})
2337
})
2438

25-
observer.observe(vm.$el, { attributes: true, subtree: true })
39+
mutationObserver.observe(vm.$el, { attributes: true, subtree: true, childList: true })
40+
41+
// 使用 ResizeObserver 监听元素尺寸变化
42+
if (typeof ResizeObserver !== 'undefined') {
43+
resizeObserver = new ResizeObserver(() => {
44+
nextTick(() => {
45+
updateSliderBar()
46+
})
47+
})
48+
49+
// 监听所有 nav-item 的尺寸变化
50+
const navItems = vm.$el.querySelectorAll('[data-tag="tiny-tab-nav-item"]')
51+
navItems.forEach((item) => {
52+
resizeObserver.observe(item)
53+
})
54+
}
55+
56+
// 初始更新
57+
nextTick(() => {
58+
updateSliderBar()
59+
})
2660
})
2761

62+
// 监听 currentNav 变化,更新 slider bar
63+
watch(
64+
() => state.currentNav,
65+
() => {
66+
nextTick(() => {
67+
updateSliderBar()
68+
})
69+
},
70+
{ immediate: true }
71+
)
72+
73+
// 监听 navItems 变化,重新设置 ResizeObserver
74+
watch(
75+
() => state.navItems,
76+
() => {
77+
if (resizeObserver) {
78+
resizeObserver.disconnect()
79+
nextTick(() => {
80+
const navItems = vm.$el.querySelectorAll('[data-tag="tiny-tab-nav-item"]')
81+
navItems.forEach((item) => {
82+
resizeObserver.observe(item)
83+
})
84+
updateSliderBar()
85+
})
86+
} else {
87+
// 如果 ResizeObserver 不可用,在 navItems 变化时也更新 slider bar
88+
nextTick(() => {
89+
updateSliderBar()
90+
})
91+
}
92+
},
93+
{ deep: true }
94+
)
95+
2896
onBeforeUnmount(() => {
29-
observer.disconnect()
30-
observer = null
97+
if (mutationObserver) {
98+
mutationObserver.disconnect()
99+
mutationObserver = null
100+
}
101+
if (resizeObserver) {
102+
resizeObserver.disconnect()
103+
resizeObserver = null
104+
}
31105
})
32106

33107
Object.assign(api, {

0 commit comments

Comments
 (0)