mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(Tabs): add before-change prop (#6817)
This commit is contained in:
parent
839f65e533
commit
9903230cb6
@ -126,7 +126,7 @@ export default {
|
||||
|
||||
### Sticky
|
||||
|
||||
In sticky mode, the tab will be fixed to top when scroll to top
|
||||
In sticky mode, the tab will be fixed to top when scroll to top.
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active" sticky>
|
||||
@ -138,7 +138,7 @@ In sticky mode, the tab will be fixed to top when scroll to top
|
||||
|
||||
### Custom title
|
||||
|
||||
Use title slot to custom tab title
|
||||
Use title slot to custom tab title.
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active">
|
||||
@ -151,7 +151,7 @@ Use title slot to custom tab title
|
||||
|
||||
### Switch Animation
|
||||
|
||||
Use `animated` props to change tabs with animation
|
||||
Use `animated` props to change tabs with animation.
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active" animated>
|
||||
@ -163,7 +163,7 @@ Use `animated` props to change tabs with animation
|
||||
|
||||
### Swipeable
|
||||
|
||||
In swipeable mode, you can switch tabs with swipe gestrue in the content
|
||||
In swipeable mode, you can switch tabs with swipe gestrue in the content.
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active" swipeable>
|
||||
@ -175,7 +175,7 @@ In swipeable mode, you can switch tabs with swipe gestrue in the content
|
||||
|
||||
### Scrollspy
|
||||
|
||||
In scrollspy mode, the list of content will be tiled
|
||||
In scrollspy mode, the list of content will be tiled.
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active" scrollspy sticky>
|
||||
@ -185,6 +185,34 @@ In scrollspy mode, the list of content will be tiled
|
||||
</van-tabs>
|
||||
```
|
||||
|
||||
### Before Change
|
||||
|
||||
```html
|
||||
<van-tabs :before-change="beforeChange">
|
||||
<van-tab v-for="index in 4" :title="'tab ' + index">
|
||||
content {{ index }}
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
beforeChange(index) {
|
||||
// prevent change
|
||||
if (index === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// async
|
||||
return new Promise((resolve) => {
|
||||
resolve(index !== 3);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Tabs Props
|
||||
@ -209,6 +237,7 @@ In scrollspy mode, the list of content will be tiled
|
||||
| swipe-threshold | Set swipe tabs threshold | _number \| string_ | `4` | - |
|
||||
| title-active-color | Title active color | _string_ | - |
|
||||
| title-inactive-color | Title inactive color | _string_ | - |
|
||||
| before-change `v2.9.3` | Callback function before changing tabs,return `false` to prevent change,support return Promise | _(name) => boolean \| Promise_ | - |
|
||||
|
||||
### Tab Props
|
||||
|
||||
|
@ -14,7 +14,7 @@ Vue.use(Tabs);
|
||||
|
||||
### 基础用法
|
||||
|
||||
通过`v-model`绑定当前激活标签对应的索引值,默认情况下启用第一个标签
|
||||
通过 `v-model` 绑定当前激活标签对应的索引值,默认情况下启用第一个标签。
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active">
|
||||
@ -37,7 +37,7 @@ export default {
|
||||
|
||||
### 通过名称匹配
|
||||
|
||||
在标签指定`name`属性的情况下,`v-model`的值为当前标签的`name`(此时无法通过索引值来匹配标签)
|
||||
在标签指定 `name` 属性的情况下,`v-model` 的值为当前标签的 `name`(此时无法通过索引值来匹配标签)。
|
||||
|
||||
```html
|
||||
<van-tabs v-model="activeName">
|
||||
@ -59,7 +59,7 @@ export default {
|
||||
|
||||
### 标签栏滚动
|
||||
|
||||
标签数量超过 4 个时,标签栏可以在水平方向上滚动,切换时会自动将当前标签居中
|
||||
标签数量超过 4 个时,标签栏可以在水平方向上滚动,切换时会自动将当前标签居中。
|
||||
|
||||
```html
|
||||
<van-tabs>
|
||||
@ -71,7 +71,7 @@ export default {
|
||||
|
||||
### 禁用标签
|
||||
|
||||
设置`disabled`属性即可禁用标签。如果需要监听禁用标签的点击事件,可以在`van-tabs`上监听`disabled`事件
|
||||
设置 `disabled` 属性即可禁用标签,如果需要监听禁用标签的点击事件,可以在 `van-tabs` 上监听`disabled` 事件。
|
||||
|
||||
```html
|
||||
<van-tabs @disabled="onClickDisabled">
|
||||
@ -95,7 +95,7 @@ export default {
|
||||
|
||||
### 样式风格
|
||||
|
||||
`Tab`支持两种样式风格:`line`和`card`,默认为`line`样式,可以通过`type`属性修改样式风格
|
||||
`Tab` 支持两种样式风格:`line` 和`card`,默认为 `line` 样式,可以通过 `type` 属性切换样式风格。
|
||||
|
||||
```html
|
||||
<van-tabs type="card">
|
||||
@ -107,7 +107,7 @@ export default {
|
||||
|
||||
### 点击事件
|
||||
|
||||
可以在`van-tabs`上绑定`click`事件,事件传参为标签对应的索引和标题
|
||||
可以在 `van-tabs` 上绑定 `click` 事件,事件传参为标签对应的标识符和标题。
|
||||
|
||||
```html
|
||||
<van-tabs @click="onClick">
|
||||
@ -130,7 +130,7 @@ export default {
|
||||
|
||||
### 粘性布局
|
||||
|
||||
通过`sticky`属性可以开启粘性布局,粘性布局下,当 Tab 滚动到顶部时会自动吸顶
|
||||
通过 `sticky` 属性可以开启粘性布局,粘性布局下,标签页滚动到顶部时会自动吸顶。
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active" sticky>
|
||||
@ -142,7 +142,7 @@ export default {
|
||||
|
||||
### 自定义标签
|
||||
|
||||
通过 title 插槽可以自定义标签内容
|
||||
通过 `title` 插槽可以自定义标签内容。
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active">
|
||||
@ -155,7 +155,7 @@ export default {
|
||||
|
||||
### 切换动画
|
||||
|
||||
通过`animated`属性可以开启切换标签内容时的转场动画
|
||||
通过 `animated` 属性可以开启切换标签内容时的转场动画。
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active" animated>
|
||||
@ -167,7 +167,7 @@ export default {
|
||||
|
||||
### 滑动切换
|
||||
|
||||
通过`swipeable`属性可以开启滑动切换标签页
|
||||
通过 `swipeable` 属性可以开启滑动切换标签页。
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active" swipeable>
|
||||
@ -179,7 +179,7 @@ export default {
|
||||
|
||||
### 滚动导航
|
||||
|
||||
通过`scrollspy`属性可以开启滚动导航模式,该模式下,内容将会平铺展示
|
||||
通过 `scrollspy` 属性可以开启滚动导航模式,该模式下,内容将会平铺展示。
|
||||
|
||||
```html
|
||||
<van-tabs v-model="active" scrollspy sticky>
|
||||
@ -189,6 +189,37 @@ export default {
|
||||
</van-tabs>
|
||||
```
|
||||
|
||||
### 异步切换
|
||||
|
||||
通过 `before-change` 属性可以在切换标签前执行特定的逻辑。
|
||||
|
||||
```html
|
||||
<van-tabs :before-change="beforeChange">
|
||||
<van-tab v-for="index in 4" :title="'选项 ' + index">
|
||||
内容 {{ index }}
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
beforeChange(index) {
|
||||
// 返回 false 表示阻止此次切换
|
||||
if (index === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 返回 Promise 来执行异步逻辑
|
||||
return new Promise((resolve) => {
|
||||
// 在 resolve 函数中返回 true 或 false
|
||||
resolve(index !== 3);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Tabs Props
|
||||
@ -213,6 +244,7 @@ export default {
|
||||
| swipe-threshold | 滚动阈值,标签数量超过阈值时开始横向滚动 | _number \| string_ | `4` |
|
||||
| title-active-color | 标题选中态颜色 | _string_ | - |
|
||||
| title-inactive-color | 标题默认态颜色 | _string_ | - |
|
||||
| before-change `v2.9.3` | 切换标签前的回调函数,返回 `false` 可阻止切换,支持返回 Promise | _(name) => boolean \| Promise_ | - |
|
||||
|
||||
### Tab Props
|
||||
|
||||
|
@ -93,6 +93,14 @@
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
</demo-block>
|
||||
|
||||
<demo-block v-if="!isWeapp" :title="t('beforeChange')">
|
||||
<van-tabs :before-change="beforeChange">
|
||||
<van-tab :title="t('tab') + index" v-for="index in 4" :key="index">
|
||||
{{ t('content') }} {{ index }}
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
</demo-block>
|
||||
</demo-section>
|
||||
</template>
|
||||
|
||||
@ -112,6 +120,7 @@ export default {
|
||||
title10: '滚动导航',
|
||||
disabled: ' 已被禁用',
|
||||
matchByName: '通过名称匹配',
|
||||
beforeChange: '异步切换',
|
||||
},
|
||||
'en-US': {
|
||||
tab: 'Tab ',
|
||||
@ -127,6 +136,7 @@ export default {
|
||||
title10: 'Scrollspy',
|
||||
disabled: ' is disabled',
|
||||
matchByName: 'Match By Name',
|
||||
beforeChange: 'Before Change',
|
||||
},
|
||||
},
|
||||
|
||||
@ -146,6 +156,16 @@ export default {
|
||||
onClick(index, title) {
|
||||
this.$toast(title);
|
||||
},
|
||||
|
||||
beforeChange(name) {
|
||||
if (name === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
resolve(name !== 3);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -318,5 +318,32 @@ exports[`renders demo correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-tabs van-tabs--line">
|
||||
<div class="van-tabs__wrap van-hairline--top-bottom">
|
||||
<div role="tablist" class="van-tabs__nav van-tabs__nav--line">
|
||||
<div role="tab" aria-selected="true" class="van-tab van-tab--active"><span class="van-tab__text van-tab__text--ellipsis">标签 1</span></div>
|
||||
<div role="tab" class="van-tab"><span class="van-tab__text van-tab__text--ellipsis">标签 2</span></div>
|
||||
<div role="tab" class="van-tab"><span class="van-tab__text van-tab__text--ellipsis">标签 3</span></div>
|
||||
<div role="tab" class="van-tab"><span class="van-tab__text van-tab__text--ellipsis">标签 4</span></div>
|
||||
<div class="van-tabs__line" style="width: 0px; transform: translateX(0px) translateX(-50%);"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-tabs__content">
|
||||
<div role="tabpanel" class="van-tab__pane" style="">
|
||||
内容 1
|
||||
</div>
|
||||
<div role="tabpanel" class="van-tab__pane" style="display: none;">
|
||||
<!---->
|
||||
</div>
|
||||
<div role="tabpanel" class="van-tab__pane" style="display: none;">
|
||||
<!---->
|
||||
</div>
|
||||
<div role="tabpanel" class="van-tab__pane" style="display: none;">
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -362,3 +362,51 @@ test('should not trigger rendered event when disable lazy-render', async () => {
|
||||
await later();
|
||||
expect(onRendered).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('before-change prop', async () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount({
|
||||
template: `
|
||||
<van-tabs @change="onChange" :before-change="beforeChange">
|
||||
<van-tab title="title1">Text</van-tab>
|
||||
<van-tab title="title2">Text</van-tab>
|
||||
<van-tab title="title3">Text</van-tab>
|
||||
<van-tab title="title4">Text</van-tab>
|
||||
<van-tab title="title5">Text</van-tab>
|
||||
</van-tabs>
|
||||
`,
|
||||
methods: {
|
||||
onChange,
|
||||
beforeChange(name) {
|
||||
switch (name) {
|
||||
case 1:
|
||||
return false;
|
||||
case 2:
|
||||
return true;
|
||||
case 3:
|
||||
return Promise.resolve(false);
|
||||
case 4:
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await later();
|
||||
|
||||
const tabs = wrapper.findAll('.van-tab');
|
||||
tabs.at(1).trigger('click');
|
||||
expect(onChange).toHaveBeenCalledTimes(0);
|
||||
|
||||
tabs.at(2).trigger('click');
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
expect(onChange).toHaveBeenLastCalledWith(2, 'title3');
|
||||
|
||||
tabs.at(3).trigger('click');
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
|
||||
tabs.at(4).trigger('click');
|
||||
await later();
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
expect(onChange).toHaveBeenLastCalledWith(4, 'title5');
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Utils
|
||||
import { createNamespace, isDef, addUnit } from '../utils';
|
||||
import { createNamespace, isDef, addUnit, isPromise } from '../utils';
|
||||
import { scrollLeftTo, scrollTopTo } from './utils';
|
||||
import { route } from '../utils/router';
|
||||
import { isHidden } from '../utils/dom/style';
|
||||
@ -53,6 +53,7 @@ export default createComponent({
|
||||
background: String,
|
||||
lineWidth: [Number, String],
|
||||
lineHeight: [Number, String],
|
||||
beforeChange: Function,
|
||||
titleActiveColor: String,
|
||||
titleInactiveColor: String,
|
||||
type: {
|
||||
@ -266,14 +267,34 @@ export default createComponent({
|
||||
}
|
||||
},
|
||||
|
||||
callBeforeChange(name, done) {
|
||||
if (this.beforeChange) {
|
||||
const returnVal = this.beforeChange(name);
|
||||
|
||||
if (isPromise(returnVal)) {
|
||||
returnVal.then((value) => {
|
||||
if (value) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
} else if (returnVal) {
|
||||
done();
|
||||
}
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
},
|
||||
|
||||
// emit event when clicked
|
||||
onClick(item, index) {
|
||||
const { title, disabled, computedName } = this.children[index];
|
||||
if (disabled) {
|
||||
this.$emit('disabled', computedName, title);
|
||||
} else {
|
||||
this.setCurrentIndex(index);
|
||||
this.scrollToCurrentContent();
|
||||
this.callBeforeChange(computedName, () => {
|
||||
this.setCurrentIndex(index);
|
||||
this.scrollToCurrentContent();
|
||||
});
|
||||
this.$emit('click', computedName, title);
|
||||
route(item.$router, item);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user