Skip to content

Commit cd20581

Browse files
feat: ✨ Tabs 支持设置徽标 (#724)
Closes: #689,#672
1 parent bb5b193 commit cd20581

File tree

8 files changed

+198
-127
lines changed

8 files changed

+198
-127
lines changed

docs/component/tabs.md

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,47 @@ const tab = ref('例子')
5757
}
5858
```
5959

60+
## 使用徽标<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.15</el-tag>
61+
62+
使用`bage-props`设置徽标属性,可以参考[Badge 组件的 props](/component/badge#attributes)
63+
64+
```html
65+
<wd-tabs v-model="tabWithBadge" @change="handleChange">
66+
<wd-tab v-for="(item, index) in tabsWithBadge" :key="index" :title="`${item.title}`" :badge-props="item.badgeProps">
67+
<view class="content">{{ item.title }}徽标</view>
68+
</wd-tab>
69+
</wd-tabs>
70+
```
71+
72+
```typescript
73+
const tabWithBadge = ref(0)
74+
const tabsWithBadge = ref([
75+
{
76+
title: '普通数值',
77+
badgeProps: {
78+
modelValue: 10,
79+
right: '-8px'
80+
}
81+
},
82+
{
83+
title: '最大值',
84+
badgeProps: {
85+
modelValue: 100,
86+
max: 99,
87+
right: '-8px'
88+
}
89+
},
90+
{
91+
title: '点状',
92+
badgeProps: {
93+
isDot: true,
94+
right: '-8px',
95+
showZero: true
96+
}
97+
}
98+
])
99+
```
100+
60101
## 自动调整底部条宽度
61102

62103
设置 `auto-line-width` 属性,自动调整底部条宽度为文本内容宽度。
@@ -160,39 +201,39 @@ const tab = ref('Design')
160201
</wd-tabs>
161202
```
162203

163-
164204
---
165205

166206
标签页在标签数大于等于 6 个时,可以滑动;当标签数大于等于 10 个时,将会显示导航地图,便于快速定位到某个标签。可以通过设置 `slidable-num` 修改可滑动的数量阈值;设置 `map-num` 修改显示导航地图的阈值。`slidable`设置为`always`时,所有的标签会向左侧收缩对齐,超出即可滑动。
167207

168208
## Tabs Attributes
169209

170-
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
171-
| ------------- | -------------------------------- | --------------- | ------ | ------ | -------- |
172-
| v-model | 绑定值 | string / number | - | - | - |
173-
| slidable-num | 可滑动的标签数阈值,`slidable`设置为`auto`时生效 | number | - | 6 | - |
174-
| map-num | 显示导航地图的标签数阈值 | number | - | 10 | - |
175-
| map-title | 导航地图标题 | string | - | - | $LOWEST_VERSION$ |
176-
| sticky | 粘性布局 | boolean | - | false | - |
177-
| offset-top | 粘性布局时距离窗口顶部距离 | number | - | 0 | - |
178-
| swipeable | 开启手势滑动 | boolean | - | false | - |
179-
| autoLineWidth | 底部条宽度跟随文字,指定`lineWidth`时此选项不生效 | boolean | - | false | $LOWEST_VERSION$ |
180-
| lineWidth | 底部条宽度,单位像素 | number | - | 19 | - |
181-
| lineHeight | 底部条高度,单位像素 | number | - | 3 | - |
182-
| color | 文字颜色 | string | - | - | - |
183-
| inactiveColor | 非活动标签文字颜色 | string | - | - | - |
184-
| animated | 是否开启切换标签内容时的转场动画 | boolean | - | false | - |
185-
| duration | 切换动画过渡时间,单位毫秒 | number | - | 300 | - |
186-
| slidable | 是否开启滚动导航 | TabsSlidable | `always` | `auto` | $LOWEST_VERSION$ |
210+
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
211+
| ------------- | ------------------------------------------------- | --------------- | -------- | ------ | ---------------- |
212+
| v-model | 绑定值 | string / number | - | - | - |
213+
| slidable-num | 可滑动的标签数阈值,`slidable`设置为`auto`时生效 | number | - | 6 | - |
214+
| map-num | 显示导航地图的标签数阈值 | number | - | 10 | - |
215+
| map-title | 导航地图标题 | string | - | - | $LOWEST_VERSION$ |
216+
| sticky | 粘性布局 | boolean | - | false | - |
217+
| offset-top | 粘性布局时距离窗口顶部距离 | number | - | 0 | - |
218+
| swipeable | 开启手势滑动 | boolean | - | false | - |
219+
| autoLineWidth | 底部条宽度跟随文字,指定`lineWidth`时此选项不生效 | boolean | - | false | $LOWEST_VERSION$ |
220+
| lineWidth | 底部条宽度,单位像素 | number | - | 19 | - |
221+
| lineHeight | 底部条高度,单位像素 | number | - | 3 | - |
222+
| color | 文字颜色 | string | - | - | - |
223+
| inactiveColor | 非活动标签文字颜色 | string | - | - | - |
224+
| animated | 是否开启切换标签内容时的转场动画 | boolean | - | false | - |
225+
| duration | 切换动画过渡时间,单位毫秒 | number | - | 300 | - |
226+
| slidable | 是否开启滚动导航 | TabsSlidable | `always` | `auto` | $LOWEST_VERSION$ |
227+
| badge-props | 自定义徽标的属性,传入的对象会被透传给 [Badge 组件的 props](/component/badge#attributes) | BadgeProps | - | - | $LOWEST_VERSION$ |
187228

188229
## Tab Attributes
189230

190-
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
191-
| -------- | ---------- | ------- | ------ | ------ | -------- |
192-
| name | 标签页名称 | string | - | - | - |
193-
| title | 标题 | string | - | - | - |
194-
| disabled | 禁用 | boolean | - | false | - |
195-
| lazy | 延迟渲染,默认开启,开启`animated`后此选项始终为`false` | boolean | - | true | $LOWEST_VERSION$ |
231+
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
232+
| -------- | ------------------------------------------------------- | ------- | ------ | ------ | ---------------- |
233+
| name | 标签页名称 | string | - | - | - |
234+
| title | 标题 | string | - | - | - |
235+
| disabled | 禁用 | boolean | - | false | - |
236+
| lazy | 延迟渲染,默认开启,开启`animated`后此选项始终为`false` | boolean | - | true | $LOWEST_VERSION$ |
196237

197238
## Tabs Events
198239

src/pages/tabs/Index.vue

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
</wd-tabs>
2424
</demo-block>
2525

26+
<demo-block title="使用徽标" transparent>
27+
<wd-tabs v-model="tabWithBadge" @change="handleChange">
28+
<wd-tab v-for="(item, index) in tabsWithBadge" :key="index" :title="`${item.title}`" :badge-props="item.badgeProps">
29+
<view class="content">{{ item.title }}徽标</view>
30+
</wd-tab>
31+
</wd-tabs>
32+
</demo-block>
33+
2634
<demo-block title="自动调整底部条宽度" transparent>
2735
<wd-tabs v-model="autoLineWidthTab" @change="handleChange" auto-line-width>
2836
<block v-for="item in autoLineWidthTabs" :key="item">
@@ -120,6 +128,34 @@ import { ref } from 'vue'
120128
const tabs = ref(['', '', '', '', '例子'])
121129
const tab = ref('')
122130
131+
const tabWithBadge = ref(0)
132+
133+
const tabsWithBadge = ref([
134+
{
135+
title: '普通数值',
136+
badgeProps: {
137+
modelValue: 10,
138+
right: '-8px'
139+
}
140+
},
141+
{
142+
title: '最大值',
143+
badgeProps: {
144+
modelValue: 100,
145+
max: 99,
146+
right: '-8px'
147+
}
148+
},
149+
{
150+
title: '点状',
151+
badgeProps: {
152+
isDot: true,
153+
right: '-8px',
154+
showZero: true
155+
}
156+
}
157+
])
158+
123159
const autoLineWidthTabs = ref(['Wot', 'Design', 'Uni'])
124160
const autoLineWidthTab = ref('Design')
125161

src/uni_modules/wot-design-uni/components/wd-badge/index.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
@include when(fixed) {
3333
position: absolute;
34+
top: 0px;
35+
right: 0px;
3436
transform: translateY(-50%) translateX(50%);
3537
}
3638

src/uni_modules/wot-design-uni/components/wd-badge/types.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/*
22
* @Author: weisheng
33
* @Date: 2024-03-15 11:36:12
4-
* @LastEditTime: 2024-03-19 16:33:12
4+
* @LastEditTime: 2024-11-20 20:29:03
55
* @LastEditors: weisheng
66
* @Description:
7-
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-badge\types.ts
7+
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-badge/types.ts
88
* 记得注释
99
*/
1010
import type { ExtractPropTypes, PropType } from 'vue'
11-
import { baseProps, makeBooleanProp, makeStringProp } from '../common/props'
11+
import { baseProps, makeBooleanProp, makeStringProp, numericProp } from '../common/props'
1212

1313
export type BadgeType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
1414

@@ -17,10 +17,7 @@ export const badgeProps = {
1717
/**
1818
* 显示值
1919
*/
20-
modelValue: {
21-
type: [Number, String, null] as PropType<number | string | null>,
22-
default: null
23-
},
20+
modelValue: numericProp,
2421
/** 当数值为 0 时,是否展示徽标 */
2522
showZero: makeBooleanProp(false),
2623
bgColor: String,
@@ -43,11 +40,11 @@ export const badgeProps = {
4340
/**
4441
* 为正时,角标向下偏移对应的像素
4542
*/
46-
top: Number,
43+
top: numericProp,
4744
/**
4845
* 为正时,角标向左偏移对应的像素
4946
*/
50-
right: Number
47+
right: numericProp
5148
}
5249

5350
export type BadgeProps = ExtractPropTypes<typeof badgeProps>

src/uni_modules/wot-design-uni/components/wd-badge/wd-badge.vue

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<view :class="['wd-badge', customClass]" :style="customStyle">
33
<slot></slot>
44
<view
5-
v-if="isBadgeShow"
5+
v-if="shouldShowBadge"
66
:class="['wd-badge__content', 'is-fixed', type ? 'wd-badge__content--' + type : '', isDot ? 'is-dot' : '']"
77
:style="contentStyle"
88
>
@@ -21,42 +21,39 @@ export default {
2121
}
2222
</script>
2323
<script lang="ts" setup>
24-
import { computed, ref, watch } from 'vue'
24+
import { computed, type CSSProperties } from 'vue'
2525
import { badgeProps } from './types'
26+
import { addUnit, isDef, isNumber, objToStyle } from '../common/util'
2627
2728
const props = defineProps(badgeProps)
28-
const content = ref<number | string | null>(null)
29-
30-
watch(
31-
[() => props.modelValue, () => props.max, () => props.isDot],
32-
() => {
33-
notice()
34-
},
35-
{ immediate: true, deep: true }
36-
)
29+
const content = computed(() => {
30+
const { modelValue, max, isDot } = props
31+
if (isDot) return ''
32+
let value = modelValue
33+
if (value && max && isNumber(value) && !Number.isNaN(value) && !Number.isNaN(max)) {
34+
value = max < value ? `${max}+` : value
35+
}
36+
return value
37+
})
3738
3839
const contentStyle = computed(() => {
39-
return `background-color: ${props.bgColor};top:${props.top || 0}px;right:${props.right || 0}px`
40-
})
40+
const style: CSSProperties = {}
41+
if (isDef(props.bgColor)) {
42+
style.backgroundColor = props.bgColor
43+
}
4144
42-
// 是否展示徽标数字
43-
const isBadgeShow = computed(() => {
44-
let isBadgeShow: boolean = false
45-
if (!props.hidden && (content.value || (content.value === 0 && props.showZero) || props.isDot)) {
46-
isBadgeShow = true
45+
if (isDef(props.top)) {
46+
style.top = addUnit(props.top)
4747
}
48-
return isBadgeShow
49-
})
5048
51-
function notice() {
52-
if (props.isDot) return
53-
let value = props.modelValue
54-
const max = props.max
55-
if (value && max && typeof value === 'number' && !Number.isNaN(value) && !Number.isNaN(max)) {
56-
value = max < value ? `${max}+` : value
49+
if (isDef(props.right)) {
50+
style.right = addUnit(props.right)
5751
}
58-
content.value = value
59-
}
52+
return objToStyle(style)
53+
})
54+
55+
// 是否展示徽标数字
56+
const shouldShowBadge = computed(() => !props.hidden && (content.value || (content.value === 0 && props.showZero) || props.isDot))
6057
</script>
6158

6259
<style lang="scss" scoped>

src/uni_modules/wot-design-uni/components/wd-tab/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { ExtractPropTypes } from 'vue'
1+
import type { ExtractPropTypes, PropType } from 'vue'
22
import { baseProps, makeBooleanProp, numericProp } from '../common/props'
3+
import type { BadgeProps } from '../wd-badge/types'
34

45
export const tabProps = {
56
...baseProps,
@@ -19,7 +20,11 @@ export const tabProps = {
1920
* 是否懒加载,切换到该tab时才加载内容
2021
* @default true
2122
*/
22-
lazy: makeBooleanProp(true)
23+
lazy: makeBooleanProp(true),
24+
/**
25+
* 徽标属性,透传给 Badge 组件
26+
*/
27+
badgeProps: Object as PropType<Partial<BadgeProps>>
2328
}
2429

2530
export type TabProps = ExtractPropTypes<typeof tabProps>

src/uni_modules/wot-design-uni/components/wd-tabs/index.scss

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
border: 1px solid $-tabs-nav-active-color;
3434
background-color: $-dark-background;
3535
}
36-
36+
3737
@include when(disabled) {
3838
color: $-dark-color-gray;
3939
border-color: #f4f4f4;
@@ -92,15 +92,15 @@
9292

9393
@include e(nav-item) {
9494
position: relative;
95+
display: flex;
96+
align-items: center;
97+
justify-content: center;
9598
flex: 1;
9699
min-width: 0;
97-
text-align: center;
98100
height: $-tabs-nav-height;
99-
line-height: $-tabs-nav-height;
100101
font-size: $-tabs-nav-fs;
101102
color: $-tabs-nav-color;
102103
transition: color .3s;
103-
@include lineEllipsis();
104104

105105
@include when(active) {
106106
font-weight: 600;
@@ -111,6 +111,18 @@
111111
}
112112
}
113113

114+
@include e(nav-item-text) {
115+
@include lineEllipsis();
116+
}
117+
118+
@include edeep(nav-item-badge){
119+
display: flex;
120+
align-items: center;
121+
justify-content: center;
122+
max-width: 100%;
123+
min-width: 0;
124+
}
125+
114126
@include e(line) {
115127
position: absolute;
116128
bottom: 4px;
@@ -121,8 +133,8 @@
121133
background: $-tabs-nav-line-bg-color;
122134
border-radius: calc($-tabs-nav-line-height / 2);
123135

124-
@include m(inner){
125-
left: 50%;
136+
@include m(inner) {
137+
left: 50%;
126138
transform: translateX(-50%)
127139
}
128140
}

0 commit comments

Comments
 (0)