feat(tab): add before-change event support (#5139)

* feat(tab): add before-change event support

* docs(tab): delete unnecessary content
This commit is contained in:
landluck 2022-12-14 10:24:32 +08:00 committed by GitHub
parent e7426dba36
commit 76522a173f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 281 additions and 16 deletions

View File

@ -185,6 +185,49 @@ Page({
</van-popup> </van-popup>
``` ```
### 异步切换
通过 `before-change` 事件可以在切换标签前执行特定的逻辑,实现切换前校验、异步切换的目的
```html
<van-tabs active="{{ active }}" use-before-change="{{ true }}" bind:change="onChange" bind:before-change="onBeforeChange" >
<van-tab title="标签 1">内容 1</van-tab>
<van-tab title="标签 2">内容 2</van-tab>
<van-tab title="标签 3">内容 3</van-tab>
<van-tab title="标签 4">内容 4</van-tab>
</van-tabs>
```
```js
Page({
data: {
active: 1,
},
onChange(event) {
wx.showToast({
title: `切换到标签 ${event.detail.name}`,
icon: 'none',
});
},
onBeforeChange(event) {
const { callback, title } = event.detail;
wx.showModal({
title: '异步切换',
content: `确定要切换至 ${title} tab吗`,
success: (res) => {
if (res.confirm) {
callback(true)
} else if (res.cancel) {
callback(false)
}
},
})
}
});
```
## API ## API
### Tabs Props ### Tabs Props
@ -208,6 +251,7 @@ Page({
| title-active-color | 标题选中态颜色 | _string_ | - | | title-active-color | 标题选中态颜色 | _string_ | - |
| title-inactive-color | 标题默认态颜色 | _string_ | - | | title-inactive-color | 标题默认态颜色 | _string_ | - |
| z-index | z-index 层级 | _number_ | `1` | | z-index | z-index 层级 | _number_ | `1` |
| use-before-change `v1.10.10` | 是否开启切换前校验 | _boolean_ | `false` |
### Tab Props ### Tab Props
@ -238,18 +282,19 @@ Page({
| 事件名 | 说明 | 参数 | | 事件名 | 说明 | 参数 |
| --- | --- | --- | | --- | --- | --- |
| bind:click | 点击标签时触发 | name标签标识符title标题 | | bind:click | 点击标签时触发 | name标签标识符title标题 |
| bind:before-change `v1.10.10` | tab 切换前会触发,在回调函数中返回 `false` 可终止 tab 切换,绑定事件的同时需要将`use-before-change`属性设置为`true` | `event.detail.name`: 当前切换的 tab 标识符, `event.detail.title`: 当前切换的 tab 标题, `event.detail.index`: 当前切换的 tab 下标,`event.detail.callback`: 回调函数,调用`callback(false)`终止 tab 切换 |
| bind:change | 当前激活的标签改变时触发 | name标签标识符title标题 | | bind:change | 当前激活的标签改变时触发 | name标签标识符title标题 |
| bind:disabled | 点击被禁用的标签时触发 | name标签标识符title标题 | | bind:disabled | 点击被禁用的标签时触发 | name标签标识符title标题 |
| bind:scroll | 滚动时触发 | { scrollTop: 距离顶部位置, isFixed: 是否吸顶 } | | bind:scroll | 滚动时触发 | { scrollTop: 距离顶部位置, isFixed: 是否吸顶 } |
### 外部样式类 ### 外部样式类
| 类名 | 说明 | | 类名 | 说明 |
| ---------------- | ---------------- | | ---------------- | ------------------ |
| custom-class | 根节点样式类 | | custom-class | 根节点样式类 |
| nav-class | 标签栏样式类 | | nav-class | 标签栏样式类 |
| tab-class | 标签样式类 | | tab-class | 标签样式类 |
| tab-active-class | 标签激活态样式类 | | tab-active-class | 标签激活态样式类 |
| wrap-class | 标签栏根节点样式类 | | wrap-class | 标签栏根节点样式类 |
### 方法 ### 方法

View File

@ -41,5 +41,20 @@ VantComponent({
icon: 'none', icon: 'none',
}); });
}, },
onBeforeChange(event) {
const { callback, title } = event.detail;
wx.showModal({
title: '异步切换',
content: `确定要切换至 ${title} tab吗`,
success: (res) => {
if (res.confirm) {
callback(true);
} else if (res.cancel) {
callback(false);
}
},
});
},
}, },
}); });

View File

@ -147,3 +147,18 @@
</van-tab> </van-tab>
</van-tabs> </van-tabs>
</demo-block> </demo-block>
<demo-block title="异步切换">
<van-tabs active="{{ 1 }}" swipeable use-before-change bind:change="onChange" bind:before-change="onBeforeChange" >
<van-tab
wx:for="{{ tabs4 }}"
wx:key="index"
title="{{ '标签 ' + item }}"
>
<view class="content">
{{ '内容' + item }}
</view>
</van-tab>
</van-tabs>
</demo-block>

View File

@ -1548,5 +1548,165 @@ exports[`should render demo and match snapshot 1`] = `
</van-tabs> </van-tabs>
</wx-view> </wx-view>
</demo-block> </demo-block>
<demo-block>
<wx-view
class="custom-class demo-block van-clearfix "
>
<wx-view
class="demo-block__title"
>
异步切换
</wx-view>
<van-tabs
bind:before-change="onBeforeChange"
bind:change="onChange"
>
<wx-view
class="custom-class van-tabs van-tabs--line"
>
<van-sticky
bind:scroll="onTouchScroll"
>
<wx-view
class="custom-class van-sticky"
style="z-index:1"
>
<wx-view
class="van-sticky-wrap"
style="z-index:1"
>
<wx-view
class="van-tabs__wrap wrap-class"
>
<wx-scroll-view
class="van-tabs__scroll van-tabs__scroll--line"
scrollLeft="{{0}}"
scrollWithAnimation="{{false}}"
scrollX="{{false}}"
style=""
>
<wx-view
class="van-tabs__nav van-tabs__nav--line nav-class"
style=""
>
<wx-view
class="van-tabs__line"
style="width:40px;opacity:0;transform:translateX(0px);-webkit-transform:translateX(0px)"
/>
<wx-view
class="tab-class van-ellipsis van-tab"
data-index="{{0}}"
style=""
bind:tap="onTap"
>
<wx-view
class="van-ellipsis"
style=""
>
标签 1
</wx-view>
</wx-view>
<wx-view
class="tab-class tab-active-class van-ellipsis van-tab van-tab--active"
data-index="{{1}}"
style=""
bind:tap="onTap"
>
<wx-view
class="van-ellipsis"
style=""
>
标签 2
</wx-view>
</wx-view>
<wx-view
class="tab-class van-ellipsis van-tab"
data-index="{{2}}"
style=""
bind:tap="onTap"
>
<wx-view
class="van-ellipsis"
style=""
>
标签 3
</wx-view>
</wx-view>
<wx-view
class="tab-class van-ellipsis van-tab"
data-index="{{3}}"
style=""
bind:tap="onTap"
>
<wx-view
class="van-ellipsis"
style=""
>
标签 4
</wx-view>
</wx-view>
</wx-view>
</wx-scroll-view>
</wx-view>
</wx-view>
</wx-view>
</van-sticky>
<wx-view
class="van-tabs__content"
bind:touchcancel="onTouchEnd"
bind:touchend="onTouchEnd"
bind:touchmove="onTouchMove"
bind:touchstart="onTouchStart"
>
<wx-view
class="van-tabs__track van-tabs__track"
style=""
>
<van-tab>
<wx-view
class="custom-class van-tab__pane van-tab__pane--inactive"
style="display: none;"
/>
</van-tab>
<van-tab>
<wx-view
class="custom-class van-tab__pane van-tab__pane--active"
style=""
>
<wx-view
class="content"
>
内容2
</wx-view>
</wx-view>
</van-tab>
<van-tab>
<wx-view
class="custom-class van-tab__pane van-tab__pane--inactive"
style="display: none;"
/>
</van-tab>
<van-tab>
<wx-view
class="custom-class van-tab__pane van-tab__pane--inactive"
style="display: none;"
/>
</van-tab>
</wx-view>
</wx-view>
</wx-view>
</van-tabs>
</wx-view>
</demo-block>
</main> </main>
`; `;

View File

@ -93,6 +93,10 @@ VantComponent({
type: Boolean, type: Boolean,
value: true, value: true,
}, },
useBeforeChange: {
type: Boolean,
value: false,
},
}, },
data: { data: {
@ -134,17 +138,13 @@ VantComponent({
trigger(eventName: string, child?: TrivialInstance) { trigger(eventName: string, child?: TrivialInstance) {
const { currentIndex } = this.data; const { currentIndex } = this.data;
const currentChild = child || this.children[currentIndex]; const data = this.getChildData(currentIndex, child);
if (!isDef(currentChild)) { if (!isDef(data)) {
return; return;
} }
this.$emit(eventName, { this.$emit(eventName, data);
index: currentChild.index,
name: currentChild.getComputedName(),
title: currentChild.data.title,
});
}, },
onTap(event: WechatMiniprogram.TouchEvent) { onTap(event: WechatMiniprogram.TouchEvent) {
@ -153,12 +153,15 @@ VantComponent({
if (child.data.disabled) { if (child.data.disabled) {
this.trigger('disabled', child); this.trigger('disabled', child);
} else { return;
}
this.onBeforeChange(index).then(() => {
this.setCurrentIndex(index); this.setCurrentIndex(index);
nextTick(() => { nextTick(() => {
this.trigger('click'); this.trigger('click');
}); });
} });
}, },
// correct the index of active tab // correct the index of active tab
@ -313,7 +316,7 @@ VantComponent({
if (direction === 'horizontal' && offsetX >= minSwipeDistance) { if (direction === 'horizontal' && offsetX >= minSwipeDistance) {
const index = this.getAvaiableTab(deltaX); const index = this.getAvaiableTab(deltaX);
if (index !== -1) { if (index !== -1) {
this.setCurrentIndex(index); this.onBeforeChange(index).then(() => this.setCurrentIndex(index));
} }
} }
@ -343,5 +346,32 @@ VantComponent({
return -1; return -1;
}, },
onBeforeChange(index: number): Promise<void> {
const { useBeforeChange } = this.data;
if (!useBeforeChange) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
this.$emit('before-change', {
...this.getChildData(index),
callback: (status) => (status ? resolve() : reject()),
});
});
},
getChildData(index: number, child?: TrivialInstance) {
const currentChild = child || this.children[index];
if (!isDef(currentChild)) {
return;
}
return {
index: currentChild.index,
name: currentChild.getComputedName(),
title: currentChild.data.title,
};
},
}, },
}); });