mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
Merge branch 'dev' of https://github.com/youzan/vant into dev
This commit is contained in:
commit
2399dc8460
5
SECURITY.md
Normal file
5
SECURITY.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
chenjiahan@buaa.edu.cn
|
@ -117,6 +117,7 @@ Use `content` slot to custom :content of badge.
|
||||
| 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 | _number \| string_ | - |
|
||||
| offset `v3.0.5` | Offset of badge dot | _[number, number]_ | - |
|
||||
|
||||
### Slots
|
||||
|
||||
|
@ -129,6 +129,7 @@ app.use(Badge);
|
||||
| color | 徽标背景颜色 | _string_ | `#ee0a24` |
|
||||
| dot | 是否展示为小红点 | _boolean_ | `false` |
|
||||
| max | 最大值,超过最大值会显示 `{max}+`,仅当 content 为数字时有效 | _number \| string_ | - |
|
||||
| offset `v3.0.5` | 设置徽标的偏移量,数组的两项分别对应水平和垂直方向的偏移量 | _[number, number]_ | - |
|
||||
|
||||
### Slots
|
||||
|
||||
|
@ -3,6 +3,12 @@
|
||||
<van-badge content="5">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
<van-badge content="10">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
<van-badge content="Hot">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
<van-badge dot>
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
@ -12,6 +18,9 @@
|
||||
<van-badge content="20" max="9">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
<van-badge content="50" max="20">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
<van-badge content="200" max="99">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
@ -21,12 +30,37 @@
|
||||
<van-badge content="5" color="#1989fa">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
<van-badge content="10" color="#1989fa">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
<van-badge dot color="#1989fa">
|
||||
<div class="child" />
|
||||
</van-badge>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="t('customContent')">
|
||||
<van-badge>
|
||||
<div class="child" />
|
||||
<template #content>
|
||||
<van-icon name="success" class="badge-icon" />
|
||||
</template>
|
||||
</van-badge>
|
||||
<van-badge>
|
||||
<div class="child" />
|
||||
<template #content>
|
||||
<van-icon name="cross" class="badge-icon" />
|
||||
</template>
|
||||
</van-badge>
|
||||
<van-badge>
|
||||
<div class="child" />
|
||||
<template #content>
|
||||
<van-icon name="down" class="badge-icon" />
|
||||
</template>
|
||||
</van-badge>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="t('standalone')">
|
||||
<van-badge content="20" style="margin-left: 16px" />
|
||||
<van-badge content="200" max="99" style="margin-left: 16px" />
|
||||
</demo-block>
|
||||
</template>
|
||||
@ -76,6 +110,7 @@ export default {
|
||||
|
||||
.badge-icon {
|
||||
display: block;
|
||||
margin-left: 0;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { PropType } from 'vue';
|
||||
import type { PropType, CSSProperties } from 'vue';
|
||||
import { isDef, createNamespace } from '../utils';
|
||||
import { isNumeric } from '../utils/validate/number';
|
||||
|
||||
@ -9,6 +9,7 @@ export default createComponent({
|
||||
dot: Boolean,
|
||||
max: [Number, String],
|
||||
color: String,
|
||||
offset: (Array as unknown) as PropType<[number, number]>,
|
||||
content: [Number, String],
|
||||
tag: {
|
||||
type: String as PropType<keyof HTMLElementTagNameMap>,
|
||||
@ -38,10 +39,25 @@ export default createComponent({
|
||||
|
||||
const renderBadge = () => {
|
||||
if (hasContent() || props.dot) {
|
||||
const style: CSSProperties = {
|
||||
background: props.color,
|
||||
};
|
||||
|
||||
if (props.offset) {
|
||||
const [x, y] = props.offset;
|
||||
if (slots.default) {
|
||||
style.top = `${y}px`;
|
||||
style.right = `${-x}px`;
|
||||
} else {
|
||||
style.marginTop = `${y}px`;
|
||||
style.marginLeft = `${x}px`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class={bem({ dot: props.dot, fixed: !!slots.default })}
|
||||
style={{ background: props.color }}
|
||||
style={style}
|
||||
>
|
||||
{renderContent()}
|
||||
</div>
|
||||
|
@ -9,6 +9,20 @@ exports[`should render demo and match snapshot 1`] = `
|
||||
5
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
<div class="van-badge van-badge--fixed">
|
||||
10
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
<div class="van-badge van-badge--fixed">
|
||||
Hot
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
@ -24,6 +38,13 @@ exports[`should render demo and match snapshot 1`] = `
|
||||
9+
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
<div class="van-badge van-badge--fixed">
|
||||
20+
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
@ -42,6 +63,15 @@ exports[`should render demo and match snapshot 1`] = `
|
||||
5
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
<div class="van-badge van-badge--fixed"
|
||||
style="background: rgb(25, 137, 250);"
|
||||
>
|
||||
10
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
@ -52,6 +82,37 @@ exports[`should render demo and match snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
<div class="van-badge van-badge--fixed">
|
||||
<i class="van-badge__wrapper van-icon van-icon-success badge-icon">
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
<div class="van-badge van-badge--fixed">
|
||||
<i class="van-badge__wrapper van-icon van-icon-cross badge-icon">
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-badge__wrapper">
|
||||
<div class="child">
|
||||
</div>
|
||||
<div class="van-badge van-badge--fixed">
|
||||
<i class="van-badge__wrapper van-icon van-icon-down badge-icon">
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-badge"
|
||||
style="margin-left: 16px;"
|
||||
>
|
||||
20
|
||||
</div>
|
||||
<div class="van-badge"
|
||||
style="margin-left: 16px;"
|
||||
>
|
||||
|
@ -40,3 +40,32 @@ test('should render content slot correctly', () => {
|
||||
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should change dot position when using offset prop', () => {
|
||||
const wrapper = mount(Badge, {
|
||||
props: {
|
||||
dot: true,
|
||||
offset: [2, 4],
|
||||
},
|
||||
slots: {
|
||||
default: () => 'Child',
|
||||
},
|
||||
});
|
||||
|
||||
const badge = wrapper.find('.van-badge').element;
|
||||
expect(badge.style.top).toEqual('4px');
|
||||
expect(badge.style.right).toEqual('-2px');
|
||||
});
|
||||
|
||||
test('should change dot position when using offset prop without children', () => {
|
||||
const wrapper = mount(Badge, {
|
||||
props: {
|
||||
dot: true,
|
||||
offset: [2, 4],
|
||||
},
|
||||
});
|
||||
|
||||
const badge = wrapper.find('.van-badge').element;
|
||||
expect(badge.style.marginTop).toEqual('4px');
|
||||
expect(badge.style.marginLeft).toEqual('2px');
|
||||
});
|
||||
|
@ -336,7 +336,7 @@ Use [ref](https://v3.vuejs.org/guide/component-template-refs.html) to get Calend
|
||||
|
||||
| Name | Description | Attribute | Return value |
|
||||
| --- | --- | --- | --- |
|
||||
| reset | Reset selected date to default date | - | - |
|
||||
| reset | Reset selected date, will reset to default date when no params passed | _date?: Date \| Date[]_ | - |
|
||||
| scrollToDate | Scroll to date | _date: Date_ | - |
|
||||
|
||||
### Less Variables
|
||||
|
@ -340,10 +340,10 @@ export default {
|
||||
|
||||
通过 ref 可以获取到 Calendar 实例并调用实例方法,详见[组件实例方法](#/zh-CN/advanced-usage#zu-jian-shi-li-fang-fa)。
|
||||
|
||||
| 方法名 | 说明 | 参数 | 返回值 |
|
||||
| ------------ | ---------------------- | ------------ | ------ |
|
||||
| reset | 重置选中的日期到默认值 | - | - |
|
||||
| scrollToDate | 滚动到某个日期 | _date: Date_ | - |
|
||||
| 方法名 | 说明 | 参数 | 返回值 |
|
||||
| --- | --- | --- | --- |
|
||||
| reset | 将选中的日期重置到指定日期,未传参时会重置到默认日期 | _date?: Date \| Date[]_ | - |
|
||||
| scrollToDate | 滚动到某个日期 | _date: Date_ | - |
|
||||
|
||||
### 样式变量
|
||||
|
||||
|
@ -311,8 +311,8 @@ export default createComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
state.currentDate = getInitialDate(state.currentDate);
|
||||
const reset = (date = getInitialDate()) => {
|
||||
state.currentDate = date;
|
||||
scrollIntoView();
|
||||
};
|
||||
|
||||
@ -500,7 +500,9 @@ export default createComponent({
|
||||
);
|
||||
|
||||
watch(() => props.show, init);
|
||||
watch([() => props.type, () => props.minDate, () => props.maxDate], reset);
|
||||
watch([() => props.type, () => props.minDate, () => props.maxDate], () => {
|
||||
reset(getInitialDate(state.currentDate));
|
||||
});
|
||||
watch(
|
||||
() => props.defaultDate,
|
||||
(value) => {
|
||||
|
@ -196,31 +196,6 @@ test('default range date', async () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('reset method', async () => {
|
||||
const wrapper = mount(Calendar, {
|
||||
props: {
|
||||
minDate,
|
||||
maxDate,
|
||||
type: 'range',
|
||||
poppable: false,
|
||||
defaultDate: [minDate, getNextDay(minDate)],
|
||||
},
|
||||
});
|
||||
|
||||
await later();
|
||||
|
||||
const days = wrapper.findAll('.van-calendar__day');
|
||||
days.at(15).trigger('click');
|
||||
days.at(18).trigger('click');
|
||||
|
||||
wrapper.vm.reset();
|
||||
|
||||
wrapper.find('.van-calendar__confirm').trigger('click');
|
||||
expect(formatRange(wrapper.emitted('confirm')[0][0])).toEqual(
|
||||
'2010/1/10-2010/1/11'
|
||||
);
|
||||
});
|
||||
|
||||
test('set show-confirm to false', async () => {
|
||||
const wrapper = mount(Calendar, {
|
||||
props: {
|
||||
|
50
src/calendar/test/index.spec.js
Normal file
50
src/calendar/test/index.spec.js
Normal file
@ -0,0 +1,50 @@
|
||||
import Calendar from '..';
|
||||
import { mount } from '../../../test';
|
||||
import { getNextDay, getPrevDay } from '../utils';
|
||||
import { minDate, maxDate } from './utils';
|
||||
|
||||
test('should reset to default date when calling reset method', async () => {
|
||||
const defaultDate = [minDate, getNextDay(minDate)];
|
||||
const wrapper = mount(Calendar, {
|
||||
props: {
|
||||
minDate,
|
||||
maxDate,
|
||||
type: 'range',
|
||||
poppable: false,
|
||||
lazyRender: false,
|
||||
defaultDate,
|
||||
},
|
||||
});
|
||||
|
||||
const days = wrapper.findAll('.van-calendar__day');
|
||||
await days[15].trigger('click');
|
||||
await days[18].trigger('click');
|
||||
|
||||
wrapper.vm.reset();
|
||||
|
||||
wrapper.find('.van-calendar__confirm').trigger('click');
|
||||
expect(wrapper.emitted('confirm')[0][0]).toEqual(defaultDate);
|
||||
});
|
||||
|
||||
test('should reset to specific date when calling reset method with date', async () => {
|
||||
const wrapper = mount(Calendar, {
|
||||
props: {
|
||||
minDate,
|
||||
maxDate,
|
||||
type: 'range',
|
||||
poppable: false,
|
||||
lazyRender: false,
|
||||
defaultDate: [minDate, getNextDay(minDate)],
|
||||
},
|
||||
});
|
||||
|
||||
const days = wrapper.findAll('.van-calendar__day');
|
||||
await days[15].trigger('click');
|
||||
await days[18].trigger('click');
|
||||
|
||||
const newDate = [getPrevDay(maxDate), maxDate];
|
||||
wrapper.vm.reset(newDate);
|
||||
|
||||
wrapper.find('.van-calendar__confirm').trigger('click');
|
||||
expect(wrapper.emitted('confirm')[0][0]).toEqual(newDate);
|
||||
});
|
@ -120,7 +120,12 @@ export default createComponent({
|
||||
if (message) {
|
||||
const hasTitle = title || slots.title;
|
||||
return (
|
||||
<div class={bem('content', { isolated: !hasTitle })}>
|
||||
<div
|
||||
// add key to force re-render
|
||||
// see: https://github.com/youzan/vant/issues/7963
|
||||
key={allowHtml ? 1 : 0}
|
||||
class={bem('content', { isolated: !hasTitle })}
|
||||
>
|
||||
<div
|
||||
class={bem('message', {
|
||||
'has-title': hasTitle,
|
||||
|
@ -47,7 +47,7 @@ export default createComponent({
|
||||
},
|
||||
swipeDuration: {
|
||||
type: [Number, String],
|
||||
default: 500,
|
||||
default: 300,
|
||||
},
|
||||
startPosition: {
|
||||
type: [Number, String],
|
||||
|
@ -94,11 +94,15 @@ export default {
|
||||
});
|
||||
|
||||
const setScale = (scale) => {
|
||||
state.scale = range(scale, +props.minZoom, +props.maxZoom);
|
||||
emit('scale', {
|
||||
scale: state.scale,
|
||||
index: state.active,
|
||||
});
|
||||
scale = range(scale, +props.minZoom, +props.maxZoom);
|
||||
|
||||
if (scale !== state.scale) {
|
||||
state.scale = scale;
|
||||
emit('scale', {
|
||||
scale,
|
||||
index: props.active,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const resetScale = () => {
|
||||
@ -235,6 +239,7 @@ export default {
|
||||
state.imageRatio = naturalHeight / naturalWidth;
|
||||
};
|
||||
|
||||
watch(() => props.active, resetScale);
|
||||
watch(
|
||||
() => props.show,
|
||||
(value) => {
|
||||
|
@ -127,7 +127,7 @@ export default {
|
||||
| showIndex | Whether to show index | _boolean_ | `true` |
|
||||
| showIndicators | Whether to show indicators | _boolean_ | `false` |
|
||||
| loop | Whether to enable loop | _boolean_ | `true` |
|
||||
| swipeDuration | Animation duration (ms) | _number \| string_ | `500` |
|
||||
| swipeDuration | Animation duration (ms) | _number \| string_ | `300` |
|
||||
| onClose | Emitted when ImagePreview is closed | _Function_ | - |
|
||||
| onChange | Emitted when current image changed | _Function_ | - |
|
||||
| onScale | Emitted when scaling current image | _Function_ | - |
|
||||
@ -147,7 +147,7 @@ export default {
|
||||
| --- | --- | --- | --- |
|
||||
| images | Images URL list | _string[]_ | `[]` |
|
||||
| start-position | Start position | _number \| string_ | `0` |
|
||||
| swipe-duration | Animation duration (ms) | _number \| string_ | `500` |
|
||||
| swipe-duration | Animation duration (ms) | _number \| string_ | `300` |
|
||||
| show-index | Whether to show index | _boolean_ | `true` |
|
||||
| show-indicators | Whether to show indicators | _boolean_ | `false` |
|
||||
| loop | Whether to enable loop | _boolean_ | `true` |
|
||||
|
@ -161,7 +161,7 @@ export default {
|
||||
| --- | --- | --- | --- |
|
||||
| images | 需要预览的图片 URL 数组 | _string[]_ | `[]` |
|
||||
| startPosition | 图片预览起始位置索引 | _number \| string_ | `0` |
|
||||
| swipeDuration | 动画时长,单位为`ms` | _number \| string_ | `500` |
|
||||
| swipeDuration | 动画时长,单位为 `ms` | _number \| string_ | `300` |
|
||||
| showIndex | 是否显示页码 | _boolean_ | `true` |
|
||||
| showIndicators | 是否显示轮播指示器 | _boolean_ | `false` |
|
||||
| loop | 是否开启循环播放 | _boolean_ | `true` |
|
||||
@ -175,7 +175,7 @@ export default {
|
||||
| minZoom | 手势缩放时,最小缩放比例 | _number \| string_ | `1/3` |
|
||||
| closeable | 是否显示关闭图标 | _boolean_ | `false` |
|
||||
| closeIcon | 关闭图标名称或图片链接 | _string_ | `clear` |
|
||||
| closeIconPosition | 关闭图标位置,可选值为`top-left`<br>`bottom-left` `bottom-right` | _string_ | `top-right` |
|
||||
| closeIconPosition | 关闭图标位置,可选值为 `top-left`<br>`bottom-left` `bottom-right` | _string_ | `top-right` |
|
||||
| teleport | 指定挂载的节点,[用法示例](#/zh-CN/popup#zhi-ding-gua-zai-wei-zhi) | _string \| Element_ | - |
|
||||
|
||||
### Props
|
||||
@ -186,7 +186,7 @@ export default {
|
||||
| --- | --- | --- | --- |
|
||||
| images | 需要预览的图片 URL 数组 | _string[]_ | `[]` |
|
||||
| start-position | 图片预览起始位置索引 | _number \| string_ | `0` |
|
||||
| swipe-duration | 动画时长,单位为 ms | _number \| string_ | `500` |
|
||||
| swipe-duration | 动画时长,单位为 ms | _number \| string_ | `300` |
|
||||
| show-index | 是否显示页码 | _boolean_ | `true` |
|
||||
| show-indicators | 是否显示轮播指示器 | _boolean_ | `false` |
|
||||
| loop | 是否开启循环播放 | _boolean_ | `true` |
|
||||
|
@ -19,7 +19,7 @@ const defaultConfig = {
|
||||
closeIcon: 'clear',
|
||||
beforeClose: null,
|
||||
startPosition: 0,
|
||||
swipeDuration: 500,
|
||||
swipeDuration: 300,
|
||||
showIndicators: false,
|
||||
closeOnPopstate: true,
|
||||
closeIconPosition: 'top-right',
|
||||
|
@ -22,7 +22,7 @@ exports[`should render cover slot correctly 1`] = `
|
||||
<transition-stub>
|
||||
<div class="van-popup van-popup--center van-image-preview">
|
||||
<div class="van-swipe van-image-preview__swipe">
|
||||
<div style="transition-duration: 500ms; transform: translateX(0px);"
|
||||
<div style="transition-duration: 300ms; transform: translateX(0px);"
|
||||
class="van-swipe__track"
|
||||
>
|
||||
</div>
|
||||
|
@ -100,7 +100,7 @@ export default {
|
||||
|
||||
### 级联选择
|
||||
|
||||
使用 `columns` 的 `children` 字段可以实现选项级联的效果。
|
||||
使用 `columns` 的 `children` 字段可以实现选项级联的效果。如果级联层级较多,推荐使用 [Cascader 级联选项组件](#/zh-CN/cascader)。
|
||||
|
||||
```html
|
||||
<van-picker title="标题" :columns="columns" />
|
||||
@ -143,7 +143,7 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
> 级联选择的数据嵌套深度需要保持一致,如果部分选项没有子选项,可以使用空字符串进行占位
|
||||
> 级联选择的数据嵌套深度需要保持一致,如果部分选项没有子选项,可以使用空字符串进行占位。
|
||||
|
||||
### 禁用选项
|
||||
|
||||
|
2
types/calendar.d.ts
vendored
2
types/calendar.d.ts
vendored
@ -1,7 +1,7 @@
|
||||
import { VanComponent } from './component';
|
||||
|
||||
export class Calendar extends VanComponent {
|
||||
reset(): void;
|
||||
reset(date?: Date | Date[]): void;
|
||||
|
||||
scrollToDate(date: Date): void;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user