mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat: add Badge component #6573
This commit is contained in:
parent
f75c8231c6
commit
475e60b282
@ -18,7 +18,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
position: relative;
|
|
||||||
width: 1em;
|
width: 1em;
|
||||||
margin: 0 auto 5px;
|
margin: 0 auto 5px;
|
||||||
color: @action-bar-icon-color;
|
color: @action-bar-icon-color;
|
||||||
|
@ -32,10 +32,9 @@ export default createComponent({
|
|||||||
|
|
||||||
if (slots.icon) {
|
if (slots.icon) {
|
||||||
return (
|
return (
|
||||||
<div class={bem('icon')}>
|
<Badge dot={dot} content={badge} class={bem('icon')}>
|
||||||
{slots.icon()}
|
{slots.icon()}
|
||||||
<Badge dot={dot} badge={badge} />
|
</Badge>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
78
src/badge/README.md
Normal file
78
src/badge/README.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Badge
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import { Badge } from 'vant';
|
||||||
|
|
||||||
|
const app = createApp();
|
||||||
|
app.use(Badge);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-badge :content="5">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge dot>
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.child {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: #f2f3f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Max
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-badge :content="20" :max="9">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge :content="200" :max="99">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Color
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-badge :content="5" color="#1989fa">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge dot color="#1989fa">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Standaline
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-badge :content="200" :max="99" />
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
| Attribute | Description | Type | Default |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| content | Badge content | _string \| number_ | - |
|
||||||
|
| color | Background color | _string_ | `#ee0a24` |
|
||||||
|
| dot | Whether to show dot | _boolean_ | `false` |
|
||||||
|
| max | Max value,show `{max}+` when exceed,only works when content is number type | _number_ | - |
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ------- | ------------ |
|
||||||
|
| default | Default slot |
|
91
src/badge/README.zh-CN.md
Normal file
91
src/badge/README.zh-CN.md
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# Badge 徽标
|
||||||
|
|
||||||
|
### 介绍
|
||||||
|
|
||||||
|
在右上角展示徽标数字或小红点。
|
||||||
|
|
||||||
|
### 引入
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import { Badge } from 'vant';
|
||||||
|
|
||||||
|
const app = createApp();
|
||||||
|
app.use(Badge);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 代码演示
|
||||||
|
|
||||||
|
### 基础用法
|
||||||
|
|
||||||
|
设置 `content` 属性后,Badge 会在子元素的右上角显示对应的徽标,也可以通过 `dot` 来显示小红点。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-badge :content="5">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge dot>
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.child {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: #f2f3f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 最大值
|
||||||
|
|
||||||
|
设置 `max` 属性后,当 `content` 的数值超过最大值时,会自动显示为 `{max}+`。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-badge :content="20" :max="9">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge :content="200" :max="99">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义颜色
|
||||||
|
|
||||||
|
通过 `color` 属性来设置徽标的颜色。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-badge :content="5" color="#1989fa">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge dot color="#1989fa">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 独立展示
|
||||||
|
|
||||||
|
当 Badge 没有子元素时,会作为一个独立的元素进行展示。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-badge :content="200" :max="99" />
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| content | 徽标内容 | _string \| number_ | - |
|
||||||
|
| color | 徽标背景颜色 | _string_ | `#ee0a24` |
|
||||||
|
| dot | 是否展示为小红点 | _boolean_ | `false` |
|
||||||
|
| max | 最大值,超过最大值会显示 `{max}+`,仅当 content 为 number 类型时有效 | _number_ | - |
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
| ------- | ---------------- |
|
||||||
|
| default | 徽标包裹的子元素 |
|
||||||
|
| content | 自定义徽标内容 |
|
78
src/badge/demo/index.vue
Normal file
78
src/badge/demo/index.vue
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<demo-section>
|
||||||
|
<demo-block :title="t('basicUsage')">
|
||||||
|
<van-badge :content="5">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge dot>
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block :title="t('max')">
|
||||||
|
<van-badge :content="20" :max="9">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge :content="200" :max="99">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block :title="t('customColor')">
|
||||||
|
<van-badge :content="5" :color="BLUE">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
<van-badge dot :color="BLUE">
|
||||||
|
<div class="child" />
|
||||||
|
</van-badge>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block :title="t('standalone')">
|
||||||
|
<van-badge :content="200" :max="99" />
|
||||||
|
</demo-block>
|
||||||
|
</demo-section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { BLUE } from '../../utils/constant';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
i18n: {
|
||||||
|
'zh-CN': {
|
||||||
|
max: '最大值',
|
||||||
|
standalone: '独立展示',
|
||||||
|
customColor: '自定义颜色',
|
||||||
|
},
|
||||||
|
'en-US': {
|
||||||
|
max: 'Max',
|
||||||
|
standalone: 'Standalone',
|
||||||
|
customColor: 'Custom Color',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
BLUE,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@import '../../style/var';
|
||||||
|
|
||||||
|
.demo-badge {
|
||||||
|
background-color: @white;
|
||||||
|
|
||||||
|
.van-badge__wrapper {
|
||||||
|
margin-left: @padding-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.child {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: @gray-2;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,9 +1,7 @@
|
|||||||
@import '../style/var';
|
@import '../style/var';
|
||||||
|
|
||||||
.van-badge {
|
.van-badge {
|
||||||
position: absolute;
|
display: inline-block;
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: @badge-size;
|
min-width: @badge-size;
|
||||||
padding: @badge-padding;
|
padding: @badge-padding;
|
||||||
@ -15,9 +13,15 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: @badge-background-color;
|
background-color: @badge-background-color;
|
||||||
border: @badge-border-width solid @white;
|
border: @badge-border-width solid @white;
|
||||||
border-radius: @badge-size;
|
border-radius: @border-radius-max;
|
||||||
transform: translate(50%, -50%);
|
|
||||||
transform-origin: 100%;
|
&--fixed {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
transform: translate(50%, -50%);
|
||||||
|
transform-origin: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&--dot {
|
&--dot {
|
||||||
width: @badge-dot-size;
|
width: @badge-dot-size;
|
||||||
@ -26,4 +30,9 @@
|
|||||||
background-color: @badge-dot-color;
|
background-color: @badge-dot-color;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,65 @@
|
|||||||
|
import type { PropType } from 'vue';
|
||||||
import { isDef, createNamespace } from '../utils';
|
import { isDef, createNamespace } from '../utils';
|
||||||
|
|
||||||
const [createComponent, bem] = createNamespace('badge');
|
const [createComponent, bem] = createNamespace('badge');
|
||||||
|
|
||||||
export default createComponent({
|
export default createComponent({
|
||||||
props: {
|
props: {
|
||||||
|
max: Number,
|
||||||
dot: Boolean,
|
dot: Boolean,
|
||||||
badge: [Number, String],
|
color: String,
|
||||||
|
content: [Number, String],
|
||||||
|
tag: {
|
||||||
|
type: String as PropType<keyof HTMLElementTagNameMap>,
|
||||||
|
default: 'div',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props) {
|
setup(props, { slots }) {
|
||||||
return () => {
|
const hasContent = () =>
|
||||||
const { dot, badge } = props;
|
!!(slots.default || (isDef(props.content) && props.content !== ''));
|
||||||
const hasBadge = isDef(badge) && badge !== '';
|
|
||||||
|
|
||||||
if (dot || hasBadge) {
|
const renderContent = () => {
|
||||||
return <div class={bem({ dot })}>{dot ? '' : badge}</div>;
|
const { dot, max, content } = props;
|
||||||
|
|
||||||
|
if (!dot && hasContent()) {
|
||||||
|
if (slots.content) {
|
||||||
|
return slots.content();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDef(max) && typeof content === 'number' && content > max) {
|
||||||
|
return `${max}+`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderBadge = () => {
|
||||||
|
if (hasContent() || props.dot) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={bem({ dot: props.dot, fixed: !!slots.default })}
|
||||||
|
style={{ background: props.color }}
|
||||||
|
>
|
||||||
|
{renderContent()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (slots.default) {
|
||||||
|
const { tag } = props;
|
||||||
|
return (
|
||||||
|
<tag class={bem('wrapper')}>
|
||||||
|
{slots.default()}
|
||||||
|
{renderBadge()}
|
||||||
|
</tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderBadge();
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -66,10 +66,9 @@ export default createComponent({
|
|||||||
const renderIcon = () => {
|
const renderIcon = () => {
|
||||||
if (slots.icon) {
|
if (slots.icon) {
|
||||||
return (
|
return (
|
||||||
<div class={bem('icon-wrapper')}>
|
<Badge dot={props.dot} content={props.badge}>
|
||||||
{slots.icon()}
|
{slots.icon()}
|
||||||
<Badge dot={props.dot} badge={props.badge} />
|
</Badge>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,10 +12,6 @@
|
|||||||
font-size: @grid-item-icon-size;
|
font-size: @grid-item-icon-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
color: @grid-item-text-color;
|
color: @grid-item-text-color;
|
||||||
font-size: @grid-item-text-font-size;
|
font-size: @grid-item-text-font-size;
|
||||||
|
@ -31,7 +31,10 @@ export default createComponent({
|
|||||||
const isImageIcon = isImage(name);
|
const isImageIcon = isImage(name);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tag
|
<Badge
|
||||||
|
dot={dot}
|
||||||
|
tag={tag}
|
||||||
|
content={badge}
|
||||||
class={[classPrefix, isImageIcon ? '' : `${classPrefix}-${name}`]}
|
class={[classPrefix, isImageIcon ? '' : `${classPrefix}-${name}`]}
|
||||||
style={{
|
style={{
|
||||||
color,
|
color,
|
||||||
@ -40,8 +43,7 @@ export default createComponent({
|
|||||||
>
|
>
|
||||||
{slots.default?.()}
|
{slots.default?.()}
|
||||||
{isImageIcon && <img class={bem('image')} src={name} />}
|
{isImageIcon && <img class={bem('image')} src={name} />}
|
||||||
<Badge dot={dot} badge={badge} />
|
</Badge>
|
||||||
</tag>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -38,10 +38,9 @@ export default createComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<a class={bem({ select: selected, disabled })} onClick={onClick}>
|
<a class={bem({ select: selected, disabled })} onClick={onClick}>
|
||||||
<div class={bem('text')}>
|
<Badge dot={dot} content={badge} class={bem('text')}>
|
||||||
{slots.title ? slots.title() : title}
|
{slots.title ? slots.title() : title}
|
||||||
<Badge dot={dot} badge={badge} class={bem('badge')} />
|
</Badge>
|
||||||
</div>
|
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -18,11 +18,6 @@
|
|||||||
background-color: @sidebar-active-color;
|
background-color: @sidebar-active-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:last-child)::after {
|
&:not(:last-child)::after {
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
}
|
}
|
||||||
|
@ -73,10 +73,9 @@ export default createComponent({
|
|||||||
style={{ color }}
|
style={{ color }}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<div class={bem('icon')}>
|
<Badge dot={dot} content={badge} class={bem('icon')}>
|
||||||
{renderIcon()}
|
{renderIcon()}
|
||||||
<Badge dot={dot} badge={badge} />
|
</Badge>
|
||||||
</div>
|
|
||||||
<div class={bem('text')}>
|
<div class={bem('text')}>
|
||||||
{slots.default?.({ active: active.value })}
|
{slots.default?.({ active: active.value })}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
position: relative;
|
|
||||||
margin-bottom: @tabbar-item-margin-bottom;
|
margin-bottom: @tabbar-item-margin-bottom;
|
||||||
font-size: @tabbar-item-icon-size;
|
font-size: @tabbar-item-icon-size;
|
||||||
|
|
||||||
|
@ -63,10 +63,9 @@ export default createComponent({
|
|||||||
|
|
||||||
if (props.dot || (isDef(props.badge) && props.badge !== '')) {
|
if (props.dot || (isDef(props.badge) && props.badge !== '')) {
|
||||||
return (
|
return (
|
||||||
<span class={bem('text-wrapper')}>
|
<Badge dot={props.dot} content={props.badge}>
|
||||||
{Text}
|
{Text}
|
||||||
{<Badge dot={props.dot} badge={props.badge} />}
|
</Badge>
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,10 +31,6 @@
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.van-tabs {
|
.van-tabs {
|
||||||
|
@ -240,6 +240,10 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
title: '展示组件',
|
title: '展示组件',
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
path: 'badge',
|
||||||
|
title: 'Badge 徽标',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'circle',
|
path: 'circle',
|
||||||
title: 'Circle 环形进度条',
|
title: 'Circle 环形进度条',
|
||||||
@ -570,6 +574,10 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
title: 'Display Components',
|
title: 'Display Components',
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
path: 'badge',
|
||||||
|
title: 'Badge',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'circle',
|
path: 'circle',
|
||||||
title: 'Circle',
|
title: 'Circle',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user