mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-07 01:28:30 +08:00
parent
bb5b19325f
commit
cd20581ca6
@ -57,6 +57,47 @@ const tab = ref('例子')
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 使用徽标<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.15</el-tag>
|
||||||
|
|
||||||
|
使用`bage-props`设置徽标属性,可以参考[Badge 组件的 props](/component/badge#attributes)。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<wd-tabs v-model="tabWithBadge" @change="handleChange">
|
||||||
|
<wd-tab v-for="(item, index) in tabsWithBadge" :key="index" :title="`${item.title}`" :badge-props="item.badgeProps">
|
||||||
|
<view class="content">{{ item.title }}徽标</view>
|
||||||
|
</wd-tab>
|
||||||
|
</wd-tabs>
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const tabWithBadge = ref(0)
|
||||||
|
const tabsWithBadge = ref([
|
||||||
|
{
|
||||||
|
title: '普通数值',
|
||||||
|
badgeProps: {
|
||||||
|
modelValue: 10,
|
||||||
|
right: '-8px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '最大值',
|
||||||
|
badgeProps: {
|
||||||
|
modelValue: 100,
|
||||||
|
max: 99,
|
||||||
|
right: '-8px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '点状',
|
||||||
|
badgeProps: {
|
||||||
|
isDot: true,
|
||||||
|
right: '-8px',
|
||||||
|
showZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
## 自动调整底部条宽度
|
## 自动调整底部条宽度
|
||||||
|
|
||||||
设置 `auto-line-width` 属性,自动调整底部条宽度为文本内容宽度。
|
设置 `auto-line-width` 属性,自动调整底部条宽度为文本内容宽度。
|
||||||
@ -160,7 +201,6 @@ const tab = ref('Design')
|
|||||||
</wd-tabs>
|
</wd-tabs>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
标签页在标签数大于等于 6 个时,可以滑动;当标签数大于等于 10 个时,将会显示导航地图,便于快速定位到某个标签。可以通过设置 `slidable-num` 修改可滑动的数量阈值;设置 `map-num` 修改显示导航地图的阈值。`slidable`设置为`always`时,所有的标签会向左侧收缩对齐,超出即可滑动。
|
标签页在标签数大于等于 6 个时,可以滑动;当标签数大于等于 10 个时,将会显示导航地图,便于快速定位到某个标签。可以通过设置 `slidable-num` 修改可滑动的数量阈值;设置 `map-num` 修改显示导航地图的阈值。`slidable`设置为`always`时,所有的标签会向左侧收缩对齐,超出即可滑动。
|
||||||
@ -168,7 +208,7 @@ const tab = ref('Design')
|
|||||||
## Tabs Attributes
|
## Tabs Attributes
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
||||||
| ------------- | -------------------------------- | --------------- | ------ | ------ | -------- |
|
| ------------- | ------------------------------------------------- | --------------- | -------- | ------ | ---------------- |
|
||||||
| v-model | 绑定值 | string / number | - | - | - |
|
| v-model | 绑定值 | string / number | - | - | - |
|
||||||
| slidable-num | 可滑动的标签数阈值,`slidable`设置为`auto`时生效 | number | - | 6 | - |
|
| slidable-num | 可滑动的标签数阈值,`slidable`设置为`auto`时生效 | number | - | 6 | - |
|
||||||
| map-num | 显示导航地图的标签数阈值 | number | - | 10 | - |
|
| map-num | 显示导航地图的标签数阈值 | number | - | 10 | - |
|
||||||
@ -184,11 +224,12 @@ const tab = ref('Design')
|
|||||||
| animated | 是否开启切换标签内容时的转场动画 | boolean | - | false | - |
|
| animated | 是否开启切换标签内容时的转场动画 | boolean | - | false | - |
|
||||||
| duration | 切换动画过渡时间,单位毫秒 | number | - | 300 | - |
|
| duration | 切换动画过渡时间,单位毫秒 | number | - | 300 | - |
|
||||||
| slidable | 是否开启滚动导航 | TabsSlidable | `always` | `auto` | $LOWEST_VERSION$ |
|
| slidable | 是否开启滚动导航 | TabsSlidable | `always` | `auto` | $LOWEST_VERSION$ |
|
||||||
|
| badge-props | 自定义徽标的属性,传入的对象会被透传给 [Badge 组件的 props](/component/badge#attributes) | BadgeProps | - | - | $LOWEST_VERSION$ |
|
||||||
|
|
||||||
## Tab Attributes
|
## Tab Attributes
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
||||||
| -------- | ---------- | ------- | ------ | ------ | -------- |
|
| -------- | ------------------------------------------------------- | ------- | ------ | ------ | ---------------- |
|
||||||
| name | 标签页名称 | string | - | - | - |
|
| name | 标签页名称 | string | - | - | - |
|
||||||
| title | 标题 | string | - | - | - |
|
| title | 标题 | string | - | - | - |
|
||||||
| disabled | 禁用 | boolean | - | false | - |
|
| disabled | 禁用 | boolean | - | false | - |
|
||||||
|
|||||||
@ -23,6 +23,14 @@
|
|||||||
</wd-tabs>
|
</wd-tabs>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="使用徽标" transparent>
|
||||||
|
<wd-tabs v-model="tabWithBadge" @change="handleChange">
|
||||||
|
<wd-tab v-for="(item, index) in tabsWithBadge" :key="index" :title="`${item.title}`" :badge-props="item.badgeProps">
|
||||||
|
<view class="content">{{ item.title }}徽标</view>
|
||||||
|
</wd-tab>
|
||||||
|
</wd-tabs>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
<demo-block title="自动调整底部条宽度" transparent>
|
<demo-block title="自动调整底部条宽度" transparent>
|
||||||
<wd-tabs v-model="autoLineWidthTab" @change="handleChange" auto-line-width>
|
<wd-tabs v-model="autoLineWidthTab" @change="handleChange" auto-line-width>
|
||||||
<block v-for="item in autoLineWidthTabs" :key="item">
|
<block v-for="item in autoLineWidthTabs" :key="item">
|
||||||
@ -120,6 +128,34 @@ import { ref } from 'vue'
|
|||||||
const tabs = ref(['这', '是', '一', '个', '例子'])
|
const tabs = ref(['这', '是', '一', '个', '例子'])
|
||||||
const tab = ref('一')
|
const tab = ref('一')
|
||||||
|
|
||||||
|
const tabWithBadge = ref(0)
|
||||||
|
|
||||||
|
const tabsWithBadge = ref([
|
||||||
|
{
|
||||||
|
title: '普通数值',
|
||||||
|
badgeProps: {
|
||||||
|
modelValue: 10,
|
||||||
|
right: '-8px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '最大值',
|
||||||
|
badgeProps: {
|
||||||
|
modelValue: 100,
|
||||||
|
max: 99,
|
||||||
|
right: '-8px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '点状',
|
||||||
|
badgeProps: {
|
||||||
|
isDot: true,
|
||||||
|
right: '-8px',
|
||||||
|
showZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
const autoLineWidthTabs = ref(['Wot', 'Design', 'Uni'])
|
const autoLineWidthTabs = ref(['Wot', 'Design', 'Uni'])
|
||||||
const autoLineWidthTab = ref('Design')
|
const autoLineWidthTab = ref('Design')
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
@include when(fixed) {
|
@include when(fixed) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
transform: translateY(-50%) translateX(50%);
|
transform: translateY(-50%) translateX(50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: weisheng
|
* @Author: weisheng
|
||||||
* @Date: 2024-03-15 11:36:12
|
* @Date: 2024-03-15 11:36:12
|
||||||
* @LastEditTime: 2024-03-19 16:33:12
|
* @LastEditTime: 2024-11-20 20:29:03
|
||||||
* @LastEditors: weisheng
|
* @LastEditors: weisheng
|
||||||
* @Description:
|
* @Description:
|
||||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-badge\types.ts
|
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-badge/types.ts
|
||||||
* 记得注释
|
* 记得注释
|
||||||
*/
|
*/
|
||||||
import type { ExtractPropTypes, PropType } from 'vue'
|
import type { ExtractPropTypes, PropType } from 'vue'
|
||||||
import { baseProps, makeBooleanProp, makeStringProp } from '../common/props'
|
import { baseProps, makeBooleanProp, makeStringProp, numericProp } from '../common/props'
|
||||||
|
|
||||||
export type BadgeType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
|
export type BadgeType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
|
||||||
|
|
||||||
@ -17,10 +17,7 @@ export const badgeProps = {
|
|||||||
/**
|
/**
|
||||||
* 显示值
|
* 显示值
|
||||||
*/
|
*/
|
||||||
modelValue: {
|
modelValue: numericProp,
|
||||||
type: [Number, String, null] as PropType<number | string | null>,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
/** 当数值为 0 时,是否展示徽标 */
|
/** 当数值为 0 时,是否展示徽标 */
|
||||||
showZero: makeBooleanProp(false),
|
showZero: makeBooleanProp(false),
|
||||||
bgColor: String,
|
bgColor: String,
|
||||||
@ -43,11 +40,11 @@ export const badgeProps = {
|
|||||||
/**
|
/**
|
||||||
* 为正时,角标向下偏移对应的像素
|
* 为正时,角标向下偏移对应的像素
|
||||||
*/
|
*/
|
||||||
top: Number,
|
top: numericProp,
|
||||||
/**
|
/**
|
||||||
* 为正时,角标向左偏移对应的像素
|
* 为正时,角标向左偏移对应的像素
|
||||||
*/
|
*/
|
||||||
right: Number
|
right: numericProp
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BadgeProps = ExtractPropTypes<typeof badgeProps>
|
export type BadgeProps = ExtractPropTypes<typeof badgeProps>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<view :class="['wd-badge', customClass]" :style="customStyle">
|
<view :class="['wd-badge', customClass]" :style="customStyle">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<view
|
<view
|
||||||
v-if="isBadgeShow"
|
v-if="shouldShowBadge"
|
||||||
:class="['wd-badge__content', 'is-fixed', type ? 'wd-badge__content--' + type : '', isDot ? 'is-dot' : '']"
|
:class="['wd-badge__content', 'is-fixed', type ? 'wd-badge__content--' + type : '', isDot ? 'is-dot' : '']"
|
||||||
:style="contentStyle"
|
:style="contentStyle"
|
||||||
>
|
>
|
||||||
@ -21,42 +21,39 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, type CSSProperties } from 'vue'
|
||||||
import { badgeProps } from './types'
|
import { badgeProps } from './types'
|
||||||
|
import { addUnit, isDef, isNumber, objToStyle } from '../common/util'
|
||||||
|
|
||||||
const props = defineProps(badgeProps)
|
const props = defineProps(badgeProps)
|
||||||
const content = ref<number | string | null>(null)
|
const content = computed(() => {
|
||||||
|
const { modelValue, max, isDot } = props
|
||||||
watch(
|
if (isDot) return ''
|
||||||
[() => props.modelValue, () => props.max, () => props.isDot],
|
let value = modelValue
|
||||||
() => {
|
if (value && max && isNumber(value) && !Number.isNaN(value) && !Number.isNaN(max)) {
|
||||||
notice()
|
value = max < value ? `${max}+` : value
|
||||||
},
|
}
|
||||||
{ immediate: true, deep: true }
|
return value
|
||||||
)
|
})
|
||||||
|
|
||||||
const contentStyle = computed(() => {
|
const contentStyle = computed(() => {
|
||||||
return `background-color: ${props.bgColor};top:${props.top || 0}px;right:${props.right || 0}px`
|
const style: CSSProperties = {}
|
||||||
|
if (isDef(props.bgColor)) {
|
||||||
|
style.backgroundColor = props.bgColor
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDef(props.top)) {
|
||||||
|
style.top = addUnit(props.top)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDef(props.right)) {
|
||||||
|
style.right = addUnit(props.right)
|
||||||
|
}
|
||||||
|
return objToStyle(style)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否展示徽标数字
|
// 是否展示徽标数字
|
||||||
const isBadgeShow = computed(() => {
|
const shouldShowBadge = computed(() => !props.hidden && (content.value || (content.value === 0 && props.showZero) || props.isDot))
|
||||||
let isBadgeShow: boolean = false
|
|
||||||
if (!props.hidden && (content.value || (content.value === 0 && props.showZero) || props.isDot)) {
|
|
||||||
isBadgeShow = true
|
|
||||||
}
|
|
||||||
return isBadgeShow
|
|
||||||
})
|
|
||||||
|
|
||||||
function notice() {
|
|
||||||
if (props.isDot) return
|
|
||||||
let value = props.modelValue
|
|
||||||
const max = props.max
|
|
||||||
if (value && max && typeof value === 'number' && !Number.isNaN(value) && !Number.isNaN(max)) {
|
|
||||||
value = max < value ? `${max}+` : value
|
|
||||||
}
|
|
||||||
content.value = value
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { ExtractPropTypes } from 'vue'
|
import type { ExtractPropTypes, PropType } from 'vue'
|
||||||
import { baseProps, makeBooleanProp, numericProp } from '../common/props'
|
import { baseProps, makeBooleanProp, numericProp } from '../common/props'
|
||||||
|
import type { BadgeProps } from '../wd-badge/types'
|
||||||
|
|
||||||
export const tabProps = {
|
export const tabProps = {
|
||||||
...baseProps,
|
...baseProps,
|
||||||
@ -19,7 +20,11 @@ export const tabProps = {
|
|||||||
* 是否懒加载,切换到该tab时才加载内容
|
* 是否懒加载,切换到该tab时才加载内容
|
||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
lazy: makeBooleanProp(true)
|
lazy: makeBooleanProp(true),
|
||||||
|
/**
|
||||||
|
* 徽标属性,透传给 Badge 组件
|
||||||
|
*/
|
||||||
|
badgeProps: Object as PropType<Partial<BadgeProps>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TabProps = ExtractPropTypes<typeof tabProps>
|
export type TabProps = ExtractPropTypes<typeof tabProps>
|
||||||
|
|||||||
@ -92,15 +92,15 @@
|
|||||||
|
|
||||||
@include e(nav-item) {
|
@include e(nav-item) {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
text-align: center;
|
|
||||||
height: $-tabs-nav-height;
|
height: $-tabs-nav-height;
|
||||||
line-height: $-tabs-nav-height;
|
|
||||||
font-size: $-tabs-nav-fs;
|
font-size: $-tabs-nav-fs;
|
||||||
color: $-tabs-nav-color;
|
color: $-tabs-nav-color;
|
||||||
transition: color .3s;
|
transition: color .3s;
|
||||||
@include lineEllipsis();
|
|
||||||
|
|
||||||
@include when(active) {
|
@include when(active) {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -111,6 +111,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(nav-item-text) {
|
||||||
|
@include lineEllipsis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@include edeep(nav-item-badge){
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(line) {
|
@include e(line) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 4px;
|
bottom: 4px;
|
||||||
@ -121,7 +133,7 @@
|
|||||||
background: $-tabs-nav-line-bg-color;
|
background: $-tabs-nav-line-bg-color;
|
||||||
border-radius: calc($-tabs-nav-line-height / 2);
|
border-radius: calc($-tabs-nav-line-height / 2);
|
||||||
|
|
||||||
@include m(inner){
|
@include m(inner) {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%)
|
transform: translateX(-50%)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,33 +2,33 @@
|
|||||||
<template v-if="sticky">
|
<template v-if="sticky">
|
||||||
<wd-sticky-box>
|
<wd-sticky-box>
|
||||||
<view
|
<view
|
||||||
:class="`wd-tabs ${customClass} ${innerSlidable ? 'is-slide' : ''} ${mapNum < items.length && mapNum !== 0 ? 'is-map' : ''}`"
|
:class="`wd-tabs ${customClass} ${innerSlidable ? 'is-slide' : ''} ${mapNum < children.length && mapNum !== 0 ? 'is-map' : ''}`"
|
||||||
:style="customStyle"
|
:style="customStyle"
|
||||||
>
|
>
|
||||||
<wd-sticky :offset-top="offsetTop">
|
<wd-sticky :offset-top="offsetTop">
|
||||||
<!--头部导航容器-->
|
|
||||||
<view class="wd-tabs__nav wd-tabs__nav--sticky">
|
<view class="wd-tabs__nav wd-tabs__nav--sticky">
|
||||||
<view class="wd-tabs__nav--wrap">
|
<view class="wd-tabs__nav--wrap">
|
||||||
<scroll-view :scroll-x="innerSlidable" scroll-with-animation :scroll-left="state.scrollLeft">
|
<scroll-view :scroll-x="innerSlidable" scroll-with-animation :scroll-left="state.scrollLeft">
|
||||||
<view class="wd-tabs__nav-container">
|
<view class="wd-tabs__nav-container">
|
||||||
<!--nav列表-->
|
|
||||||
<view
|
<view
|
||||||
@click="handleSelect(index)"
|
@click="handleSelect(index)"
|
||||||
v-for="(item, index) in items"
|
v-for="(item, index) in children"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="`wd-tabs__nav-item ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`"
|
:class="`wd-tabs__nav-item ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`"
|
||||||
:style="state.activeIndex === index ? (color ? 'color:' + color : '') : inactiveColor ? 'color:' + inactiveColor : ''"
|
:style="state.activeIndex === index ? (color ? 'color:' + color : '') : inactiveColor ? 'color:' + inactiveColor : ''"
|
||||||
>
|
>
|
||||||
|
<wd-badge v-if="item.badgeProps" v-bind="item.badgeProps">
|
||||||
<text class="wd-tabs__nav-item-text">{{ item.title }}</text>
|
<text class="wd-tabs__nav-item-text">{{ item.title }}</text>
|
||||||
|
</wd-badge>
|
||||||
|
<text v-else class="wd-tabs__nav-item-text">{{ item.title }}</text>
|
||||||
|
|
||||||
<view class="wd-tabs__line wd-tabs__line--inner" v-if="state.activeIndex === index && state.useInnerLine"></view>
|
<view class="wd-tabs__line wd-tabs__line--inner" v-if="state.activeIndex === index && state.useInnerLine"></view>
|
||||||
</view>
|
</view>
|
||||||
<!--下划线-->
|
|
||||||
<view class="wd-tabs__line" :style="state.lineStyle"></view>
|
<view class="wd-tabs__line" :style="state.lineStyle"></view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
<!--map表-->
|
<view class="wd-tabs__map" v-if="mapNum < children.length && mapNum !== 0">
|
||||||
<view class="wd-tabs__map" v-if="mapNum < items.length && mapNum !== 0">
|
|
||||||
<view :class="`wd-tabs__map-btn ${state.animating ? 'is-open' : ''}`" @click="toggleMap">
|
<view :class="`wd-tabs__map-btn ${state.animating ? 'is-open' : ''}`" @click="toggleMap">
|
||||||
<view :class="`wd-tabs__map-arrow ${state.animating ? 'is-open' : ''}`">
|
<view :class="`wd-tabs__map-arrow ${state.animating ? 'is-open' : ''}`">
|
||||||
<wd-icon name="arrow-down" />
|
<wd-icon name="arrow-down" />
|
||||||
@ -38,7 +38,7 @@
|
|||||||
{{ mapTitle || translate('all') }}
|
{{ mapTitle || translate('all') }}
|
||||||
</view>
|
</view>
|
||||||
<view :class="`wd-tabs__map-body ${state.animating ? 'is-open' : ''}`" :style="state.mapShow ? '' : 'display:none'">
|
<view :class="`wd-tabs__map-body ${state.animating ? 'is-open' : ''}`" :style="state.mapShow ? '' : 'display:none'">
|
||||||
<view class="wd-tabs__map-nav-item" v-for="(item, index) in items" :key="index" @click="handleSelect(index)">
|
<view class="wd-tabs__map-nav-item" v-for="(item, index) in children" :key="index" @click="handleSelect(index)">
|
||||||
<view
|
<view
|
||||||
:class="`wd-tabs__map-nav-btn ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`"
|
:class="`wd-tabs__map-nav-btn ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`"
|
||||||
:style="
|
:style="
|
||||||
@ -59,14 +59,12 @@
|
|||||||
</view>
|
</view>
|
||||||
</wd-sticky>
|
</wd-sticky>
|
||||||
|
|
||||||
<!--标签页-->
|
|
||||||
<view class="wd-tabs__container" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd" @touchcancel="onTouchEnd">
|
<view class="wd-tabs__container" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd" @touchcancel="onTouchEnd">
|
||||||
<view :class="['wd-tabs__body', animated ? 'is-animated' : '']" :style="bodyStyle">
|
<view :class="['wd-tabs__body', animated ? 'is-animated' : '']" :style="bodyStyle">
|
||||||
<slot />
|
<slot />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!--map表的阴影浮层-->
|
|
||||||
<view
|
<view
|
||||||
class="wd-tabs__mask"
|
class="wd-tabs__mask"
|
||||||
:style="`${state.mapShow ? '' : 'display:none;'} ${state.animating ? 'opacity:1;' : ''}`"
|
:style="`${state.mapShow ? '' : 'display:none;'} ${state.animating ? 'opacity:1;' : ''}`"
|
||||||
@ -77,30 +75,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<view :class="`wd-tabs ${customClass} ${innerSlidable ? 'is-slide' : ''} ${mapNum < items.length && mapNum !== 0 ? 'is-map' : ''}`">
|
<view :class="`wd-tabs ${customClass} ${innerSlidable ? 'is-slide' : ''} ${mapNum < children.length && mapNum !== 0 ? 'is-map' : ''}`">
|
||||||
<!--头部导航容器-->
|
|
||||||
<view class="wd-tabs__nav">
|
<view class="wd-tabs__nav">
|
||||||
<view class="wd-tabs__nav--wrap">
|
<view class="wd-tabs__nav--wrap">
|
||||||
<scroll-view :scroll-x="innerSlidable" scroll-with-animation :scroll-left="state.scrollLeft">
|
<scroll-view :scroll-x="innerSlidable" scroll-with-animation :scroll-left="state.scrollLeft">
|
||||||
<view class="wd-tabs__nav-container">
|
<view class="wd-tabs__nav-container">
|
||||||
<!--nav列表-->
|
|
||||||
<view
|
<view
|
||||||
v-for="(item, index) in items"
|
v-for="(item, index) in children"
|
||||||
@click="handleSelect(index)"
|
@click="handleSelect(index)"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="`wd-tabs__nav-item ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`"
|
:class="`wd-tabs__nav-item ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`"
|
||||||
:style="state.activeIndex === index ? (color ? 'color:' + color : '') : inactiveColor ? 'color:' + inactiveColor : ''"
|
:style="state.activeIndex === index ? (color ? 'color:' + color : '') : inactiveColor ? 'color:' + inactiveColor : ''"
|
||||||
>
|
>
|
||||||
|
<wd-badge custom-class="wd-tabs__nav-item-badge" v-if="item.badgeProps" v-bind="item.badgeProps">
|
||||||
<text class="wd-tabs__nav-item-text">{{ item.title }}</text>
|
<text class="wd-tabs__nav-item-text">{{ item.title }}</text>
|
||||||
|
</wd-badge>
|
||||||
|
<text v-else class="wd-tabs__nav-item-text">{{ item.title }}</text>
|
||||||
<view class="wd-tabs__line wd-tabs__line--inner" v-if="state.activeIndex === index && state.useInnerLine"></view>
|
<view class="wd-tabs__line wd-tabs__line--inner" v-if="state.activeIndex === index && state.useInnerLine"></view>
|
||||||
</view>
|
</view>
|
||||||
<!--下划线-->
|
|
||||||
<view class="wd-tabs__line" :style="state.lineStyle"></view>
|
<view class="wd-tabs__line" :style="state.lineStyle"></view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
<!--map表-->
|
<view class="wd-tabs__map" v-if="mapNum < children.length && mapNum !== 0">
|
||||||
<view class="wd-tabs__map" v-if="mapNum < items.length && mapNum !== 0">
|
|
||||||
<view class="wd-tabs__map-btn" @click="toggleMap">
|
<view class="wd-tabs__map-btn" @click="toggleMap">
|
||||||
<view :class="`wd-tabs__map-arrow ${state.animating ? 'is-open' : ''}`">
|
<view :class="`wd-tabs__map-arrow ${state.animating ? 'is-open' : ''}`">
|
||||||
<wd-icon name="arrow-down" />
|
<wd-icon name="arrow-down" />
|
||||||
@ -110,7 +107,7 @@
|
|||||||
{{ translate('all') }}
|
{{ translate('all') }}
|
||||||
</view>
|
</view>
|
||||||
<view :class="`wd-tabs__map-body ${state.animating ? 'is-open' : ''}`" :style="state.mapShow ? '' : 'display:none'">
|
<view :class="`wd-tabs__map-body ${state.animating ? 'is-open' : ''}`" :style="state.mapShow ? '' : 'display:none'">
|
||||||
<view class="wd-tabs__map-nav-item" v-for="(item, index) in items" :key="index" @click="handleSelect(index)">
|
<view class="wd-tabs__map-nav-item" v-for="(item, index) in children" :key="index" @click="handleSelect(index)">
|
||||||
<view :class="`wd-tabs__map-nav-btn ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`">
|
<view :class="`wd-tabs__map-nav-btn ${state.activeIndex === index ? 'is-active' : ''} ${item.disabled ? 'is-disabled' : ''}`">
|
||||||
{{ item.title }}
|
{{ item.title }}
|
||||||
</view>
|
</view>
|
||||||
@ -119,14 +116,12 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!--标签页-->
|
|
||||||
<view class="wd-tabs__container" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd" @touchcancel="onTouchEnd">
|
<view class="wd-tabs__container" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd" @touchcancel="onTouchEnd">
|
||||||
<view :class="['wd-tabs__body', animated ? 'is-animated' : '']" :style="bodyStyle">
|
<view :class="['wd-tabs__body', animated ? 'is-animated' : '']" :style="bodyStyle">
|
||||||
<slot />
|
<slot />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!--map表的阴影浮层-->
|
|
||||||
<view class="wd-tabs__mask" :style="`${state.mapShow ? '' : 'display:none;'} ${state.animating ? 'opacity:1' : ''}`" @click="toggleMap"></view>
|
<view class="wd-tabs__mask" :style="`${state.mapShow ? '' : 'display:none;'} ${state.animating ? 'opacity:1' : ''}`" @click="toggleMap"></view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -145,7 +140,7 @@ export default {
|
|||||||
import wdIcon from '../wd-icon/wd-icon.vue'
|
import wdIcon from '../wd-icon/wd-icon.vue'
|
||||||
import wdSticky from '../wd-sticky/wd-sticky.vue'
|
import wdSticky from '../wd-sticky/wd-sticky.vue'
|
||||||
import wdStickyBox from '../wd-sticky-box/wd-sticky-box.vue'
|
import wdStickyBox from '../wd-sticky-box/wd-sticky-box.vue'
|
||||||
import { computed, getCurrentInstance, onMounted, watch, nextTick, reactive, type CSSProperties } from 'vue'
|
import { computed, getCurrentInstance, onMounted, watch, nextTick, reactive, type CSSProperties, type ComponentInstance } from 'vue'
|
||||||
import { addUnit, checkNumRange, debounce, getRect, isDef, isNumber, isString, objToStyle } from '../common/util'
|
import { addUnit, checkNumRange, debounce, getRect, isDef, isNumber, isString, objToStyle } from '../common/util'
|
||||||
import { useTouch } from '../composables/useTouch'
|
import { useTouch } from '../composables/useTouch'
|
||||||
import { TABS_KEY, tabsProps } from './types'
|
import { TABS_KEY, tabsProps } from './types'
|
||||||
@ -171,8 +166,6 @@ const state = reactive({
|
|||||||
scrollLeft: 0 // scroll-view偏移量
|
scrollLeft: 0 // scroll-view偏移量
|
||||||
})
|
})
|
||||||
|
|
||||||
// map的开关
|
|
||||||
|
|
||||||
const { children, linkChildren } = useChildren(TABS_KEY)
|
const { children, linkChildren } = useChildren(TABS_KEY)
|
||||||
linkChildren({ state, props })
|
linkChildren({ state, props })
|
||||||
|
|
||||||
@ -181,14 +174,7 @@ const { proxy } = getCurrentInstance() as any
|
|||||||
const touch = useTouch()
|
const touch = useTouch()
|
||||||
|
|
||||||
const innerSlidable = computed(() => {
|
const innerSlidable = computed(() => {
|
||||||
return props.slidable === 'always' || items.value.length > props.slidableNum
|
return props.slidable === 'always' || children.length > props.slidableNum
|
||||||
})
|
|
||||||
|
|
||||||
// tabs数据
|
|
||||||
const items = computed(() => {
|
|
||||||
return children.map((child, index) => {
|
|
||||||
return { disabled: child.disabled, title: child.title, name: isDef(child.name) ? child.name : index }
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const bodyStyle = computed(() => {
|
const bodyStyle = computed(() => {
|
||||||
@ -203,6 +189,10 @@ const bodyStyle = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getTabName = (tab: ComponentInstance<any>, index: number) => {
|
||||||
|
return isDef(tab.name) ? tab.name : index
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新激活项
|
* 更新激活项
|
||||||
* @param value 激活值
|
* @param value 激活值
|
||||||
@ -211,11 +201,11 @@ const bodyStyle = computed(() => {
|
|||||||
*/
|
*/
|
||||||
const updateActive = (value: number | string = 0, init: boolean = false, setScroll: boolean = true) => {
|
const updateActive = (value: number | string = 0, init: boolean = false, setScroll: boolean = true) => {
|
||||||
// 没有tab子元素,不执行任何操作
|
// 没有tab子元素,不执行任何操作
|
||||||
if (items.value.length === 0) return
|
if (children.length === 0) return
|
||||||
|
|
||||||
value = getActiveIndex(value)
|
value = getActiveIndex(value)
|
||||||
// 被禁用,不执行任何操作
|
// 被禁用,不执行任何操作
|
||||||
if (items.value[value].disabled) return
|
if (children[value].disabled) return
|
||||||
state.activeIndex = value
|
state.activeIndex = value
|
||||||
if (setScroll) {
|
if (setScroll) {
|
||||||
updateLineStyle(init === false)
|
updateLineStyle(init === false)
|
||||||
@ -298,11 +288,7 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* @description nav map list 开关
|
|
||||||
*/
|
|
||||||
function toggleMap() {
|
function toggleMap() {
|
||||||
// 必须保证display和transition不在同一个帧
|
|
||||||
if (state.mapShow) {
|
if (state.mapShow) {
|
||||||
state.animating = false
|
state.animating = false
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -353,22 +339,19 @@ async function updateLineStyle(animation: boolean = true) {
|
|||||||
console.error('[wot design] error(wd-tabs): update line style failed', error)
|
console.error('[wot design] error(wd-tabs): update line style failed', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @description 通过控制tab的active来展示选定的tab
|
|
||||||
*/
|
|
||||||
function setActiveTab() {
|
function setActiveTab() {
|
||||||
if (!state.inited) return
|
if (!state.inited) return
|
||||||
if (items.value[state.activeIndex].name !== props.modelValue) {
|
const name = getTabName(children[state.activeIndex], state.activeIndex)
|
||||||
|
if (name !== props.modelValue) {
|
||||||
emit('change', {
|
emit('change', {
|
||||||
index: state.activeIndex,
|
index: state.activeIndex,
|
||||||
name: items.value[state.activeIndex].name
|
name: name
|
||||||
})
|
})
|
||||||
emit('update:modelValue', items.value[state.activeIndex].name)
|
emit('update:modelValue', name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @description scroll-view滑动到active的tab_nav
|
|
||||||
*/
|
|
||||||
function scrollIntoView() {
|
function scrollIntoView() {
|
||||||
if (!state.inited) return
|
if (!state.inited) return
|
||||||
Promise.all([getRect($item, true, proxy), getRect($container, false, proxy)]).then(([navItemsRects, navRect]) => {
|
Promise.all([getRect($item, true, proxy), getRect($container, false, proxy)]).then(([navItemsRects, navRect]) => {
|
||||||
@ -385,13 +368,16 @@ function scrollIntoView() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 单击tab的处理
|
* @description 单击tab的处理
|
||||||
* @param index
|
* @param index
|
||||||
*/
|
*/
|
||||||
function handleSelect(index: number) {
|
function handleSelect(index: number) {
|
||||||
if (index === undefined) return
|
if (index === undefined) return
|
||||||
const { name, disabled } = items.value[index]
|
const { disabled } = children[index]
|
||||||
|
const name = getTabName(children[index], index)
|
||||||
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
emit('disabled', {
|
emit('disabled', {
|
||||||
index,
|
index,
|
||||||
@ -406,10 +392,6 @@ function handleSelect(index: number) {
|
|||||||
name
|
name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @description touch handle
|
|
||||||
* @param event
|
|
||||||
*/
|
|
||||||
function onTouchStart(event: any) {
|
function onTouchStart(event: any) {
|
||||||
if (!props.swipeable) return
|
if (!props.swipeable) return
|
||||||
touch.touchStart(event)
|
touch.touchStart(event)
|
||||||
@ -425,22 +407,21 @@ function onTouchEnd() {
|
|||||||
if (direction.value === 'horizontal' && offsetX.value >= minSwipeDistance) {
|
if (direction.value === 'horizontal' && offsetX.value >= minSwipeDistance) {
|
||||||
if (deltaX.value > 0 && state.activeIndex !== 0) {
|
if (deltaX.value > 0 && state.activeIndex !== 0) {
|
||||||
setActive(state.activeIndex - 1)
|
setActive(state.activeIndex - 1)
|
||||||
} else if (deltaX.value < 0 && state.activeIndex !== items.value.length - 1) {
|
} else if (deltaX.value < 0 && state.activeIndex !== children.length - 1) {
|
||||||
setActive(state.activeIndex + 1)
|
|
||||||
setActive(state.activeIndex + 1)
|
setActive(state.activeIndex + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getActiveIndex(value: number | string) {
|
function getActiveIndex(value: number | string) {
|
||||||
// name代表的索引超过了items的边界,自动用0兜底
|
// name代表的索引超过了children长度的边界,自动用0兜底
|
||||||
if (isNumber(value) && value >= items.value.length) {
|
if (isNumber(value) && value >= children.length) {
|
||||||
// eslint-disable-next-line prettier/prettier
|
// eslint-disable-next-line prettier/prettier
|
||||||
console.error('[wot design] warning(wd-tabs): the type of tabs\' value is Number shouldn\'t be less than its children')
|
console.error('[wot design] warning(wd-tabs): the type of tabs\' value is Number shouldn\'t be less than its children')
|
||||||
value = 0
|
value = 0
|
||||||
}
|
}
|
||||||
// 如果是字符串直接匹配,匹配不到用0兜底
|
// 如果是字符串直接匹配,匹配不到用0兜底
|
||||||
if (isString(value)) {
|
if (isString(value)) {
|
||||||
const index = items.value.findIndex((item) => item.name === value)
|
const index = children.findIndex((item) => item.name === value)
|
||||||
value = index === -1 ? 0 : index
|
value = index === -1 ? 0 : index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user