mirror of
https://gitee.com/vant-contrib/vant-weapp.git
synced 2025-04-05 19:41:45 +08:00
feat(Calendar): add new component calendar (#2894)
This commit is contained in:
parent
d073ea2767
commit
163ac52542
@ -18,6 +18,8 @@ const libDir = path.resolve(__dirname, '../lib');
|
|||||||
const esDir = path.resolve(__dirname, '../dist');
|
const esDir = path.resolve(__dirname, '../dist');
|
||||||
const exampleDir = path.resolve(__dirname, '../example/dist');
|
const exampleDir = path.resolve(__dirname, '../example/dist');
|
||||||
|
|
||||||
|
const baseCssPath = path.resolve(__dirname, '../packages/common/index.wxss');
|
||||||
|
|
||||||
const lessCompiler = dist =>
|
const lessCompiler = dist =>
|
||||||
function compileLess() {
|
function compileLess() {
|
||||||
return gulp
|
return gulp
|
||||||
@ -27,7 +29,11 @@ const lessCompiler = dist =>
|
|||||||
.pipe(
|
.pipe(
|
||||||
insert.transform((contents, file) => {
|
insert.transform((contents, file) => {
|
||||||
if (!file.path.includes('packages' + path.sep + 'common')) {
|
if (!file.path.includes('packages' + path.sep + 'common')) {
|
||||||
contents = `@import '../common/index.wxss';${contents}`;
|
const relativePath = path.relative(
|
||||||
|
path.normalize(`${file.path}${path.sep}..`),
|
||||||
|
baseCssPath
|
||||||
|
);
|
||||||
|
contents = `@import '${relativePath}';${contents}`;
|
||||||
}
|
}
|
||||||
return contents;
|
return contents;
|
||||||
})
|
})
|
||||||
|
@ -48,7 +48,8 @@
|
|||||||
"pages/dropdown-menu/index",
|
"pages/dropdown-menu/index",
|
||||||
"pages/index-bar/index",
|
"pages/index-bar/index",
|
||||||
"pages/skeleton/index",
|
"pages/skeleton/index",
|
||||||
"pages/divider/index"
|
"pages/divider/index",
|
||||||
|
"pages/calendar/index"
|
||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"navigationBarBackgroundColor": "#f8f8f8",
|
"navigationBarBackgroundColor": "#f8f8f8",
|
||||||
@ -119,7 +120,8 @@
|
|||||||
"van-grid-item": "./dist/grid-item/index",
|
"van-grid-item": "./dist/grid-item/index",
|
||||||
"van-dropdown-menu": "./dist/dropdown-menu/index",
|
"van-dropdown-menu": "./dist/dropdown-menu/index",
|
||||||
"van-dropdown-item": "./dist/dropdown-item/index",
|
"van-dropdown-item": "./dist/dropdown-item/index",
|
||||||
"van-skeleton": "./dist/skeleton/index"
|
"van-skeleton": "./dist/skeleton/index",
|
||||||
|
"van-calendar": "./dist/calendar/index"
|
||||||
},
|
},
|
||||||
"sitemapLocation": "sitemap.json"
|
"sitemapLocation": "sitemap.json"
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,10 @@ export default [
|
|||||||
groupName: '表单组件',
|
groupName: '表单组件',
|
||||||
icon: 'https://img.yzcdn.cn/vant/form-0401.svg',
|
icon: 'https://img.yzcdn.cn/vant/form-0401.svg',
|
||||||
list: [
|
list: [
|
||||||
|
{
|
||||||
|
path: '/calendar',
|
||||||
|
title: 'Calendar 日历',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/checkbox',
|
path: '/checkbox',
|
||||||
title: 'Checkbox 复选框'
|
title: 'Checkbox 复选框'
|
||||||
|
150
example/pages/calendar/index.js
Normal file
150
example/pages/calendar/index.js
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import Page from '../../common/page';
|
||||||
|
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
date: {
|
||||||
|
maxRange: [],
|
||||||
|
selectSingle: null,
|
||||||
|
selectRange: [],
|
||||||
|
selectMultiple: [],
|
||||||
|
quickSelect1: null,
|
||||||
|
quickSelect2: [],
|
||||||
|
customColor: [],
|
||||||
|
customConfirm: [],
|
||||||
|
customRange: null,
|
||||||
|
customDayText: [],
|
||||||
|
customPosition: null
|
||||||
|
},
|
||||||
|
type: 'single',
|
||||||
|
round: true,
|
||||||
|
color: undefined,
|
||||||
|
minDate: Date.now(),
|
||||||
|
maxDate: new Date(
|
||||||
|
new Date().getFullYear(),
|
||||||
|
new Date().getMonth() + 6,
|
||||||
|
new Date().getDate()
|
||||||
|
).getTime(),
|
||||||
|
maxRange: undefined,
|
||||||
|
position: undefined,
|
||||||
|
formatter: undefined,
|
||||||
|
showConfirm: false,
|
||||||
|
showCalendar: false,
|
||||||
|
tiledMinDate: new Date(2012, 0, 10).getTime(),
|
||||||
|
tiledMaxDate: new Date(2012, 2, 20).getTime(),
|
||||||
|
confirmText: undefined,
|
||||||
|
confirmDisabledText: undefined
|
||||||
|
},
|
||||||
|
|
||||||
|
onConfirm(event) {
|
||||||
|
console.log(event);
|
||||||
|
this.setData({ showCalendar: false });
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
[`date.${this.data.id}`]: event.detail
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelect(event) {
|
||||||
|
console.log(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.setData({ showCalendar: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
console.log('open');
|
||||||
|
},
|
||||||
|
|
||||||
|
onOpened() {
|
||||||
|
console.log('opened');
|
||||||
|
},
|
||||||
|
|
||||||
|
onClosed() {
|
||||||
|
console.log('closed');
|
||||||
|
},
|
||||||
|
|
||||||
|
resetSettings() {
|
||||||
|
this.setData({
|
||||||
|
round: true,
|
||||||
|
color: null,
|
||||||
|
minDate: Date.now(),
|
||||||
|
maxDate: new Date(
|
||||||
|
new Date().getFullYear(),
|
||||||
|
new Date().getMonth() + 6,
|
||||||
|
new Date().getDate()
|
||||||
|
).getTime(),
|
||||||
|
maxRange: null,
|
||||||
|
position: 'bottom',
|
||||||
|
formatter: null,
|
||||||
|
showConfirm: true,
|
||||||
|
confirmText: '确定',
|
||||||
|
confirmDisabledText: null
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
show(event) {
|
||||||
|
this.resetSettings();
|
||||||
|
const { type, id } = event.currentTarget.dataset;
|
||||||
|
const data = {
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
showCalendar: true
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case 'quickSelect1':
|
||||||
|
case 'quickSelect2':
|
||||||
|
data.showConfirm = false;
|
||||||
|
break;
|
||||||
|
case 'customColor':
|
||||||
|
data.color = '#07c160';
|
||||||
|
break;
|
||||||
|
case 'customConfirm':
|
||||||
|
data.confirmText = '完成';
|
||||||
|
data.confirmDisabledText = '请选择结束时间';
|
||||||
|
break;
|
||||||
|
case 'customRange':
|
||||||
|
data.minDate = new Date(2010, 0, 1).getTime();
|
||||||
|
data.maxDate = new Date(2010, 0, 31).getTime();
|
||||||
|
break;
|
||||||
|
case 'customDayText':
|
||||||
|
data.minDate = new Date(2010, 4, 1).getTime();
|
||||||
|
data.maxDate = new Date(2010, 4, 31).getTime();
|
||||||
|
data.formatter = this.dayFormatter;
|
||||||
|
break;
|
||||||
|
case 'customPosition':
|
||||||
|
data.round = false;
|
||||||
|
data.position = 'right';
|
||||||
|
break;
|
||||||
|
case 'maxRange':
|
||||||
|
data.maxRange = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setData(data);
|
||||||
|
},
|
||||||
|
|
||||||
|
dayFormatter(day) {
|
||||||
|
const month = day.date.getMonth() + 1;
|
||||||
|
const date = day.date.getDate();
|
||||||
|
|
||||||
|
if (month === 5) {
|
||||||
|
if (date === 1) {
|
||||||
|
day.topInfo = '劳动节';
|
||||||
|
} else if (date === 4) {
|
||||||
|
day.topInfo = '五四青年节';
|
||||||
|
} else if (date === 11) {
|
||||||
|
day.text = '今天';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day.type === 'start') {
|
||||||
|
day.bottomInfo = '入店';
|
||||||
|
} else if (day.type === 'end') {
|
||||||
|
day.bottomInfo = '离店';
|
||||||
|
}
|
||||||
|
|
||||||
|
return day;
|
||||||
|
}
|
||||||
|
});
|
3
example/pages/calendar/index.json
Normal file
3
example/pages/calendar/index.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "Calendar 日历"
|
||||||
|
}
|
139
example/pages/calendar/index.wxml
Normal file
139
example/pages/calendar/index.wxml
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<wxs src="./index.wxs" module="computed"></wxs>
|
||||||
|
|
||||||
|
<demo-block title="基础用法">
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="选择单个日期"
|
||||||
|
data-type="single"
|
||||||
|
data-id="selectSingle"
|
||||||
|
value="{{ computed.formatFullDate(date.selectSingle) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="选择多个日期"
|
||||||
|
data-type="multiple"
|
||||||
|
data-id="selectMultiple"
|
||||||
|
value="{{ computed.formatMultiple(date.selectMultiple) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="选择日期区间"
|
||||||
|
data-type="range"
|
||||||
|
data-id="selectRange"
|
||||||
|
value="{{ computed.formatRange(date.selectRange) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="快捷选择">
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="选择单个日期"
|
||||||
|
data-type="single"
|
||||||
|
data-id="quickSelect1"
|
||||||
|
value="{{ computed.formatFullDate(date.quickSelect1) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="选择日期区间"
|
||||||
|
data-type="range"
|
||||||
|
data-id="quickSelect2"
|
||||||
|
value="{{ computed.formatRange(date.quickSelect2) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="自定义日历">
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="自定义颜色"
|
||||||
|
data-type="range"
|
||||||
|
data-id="customColor"
|
||||||
|
value="{{ computed.formatRange(date.customColor) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="自定义日期范围"
|
||||||
|
data-type="single"
|
||||||
|
data-id="customRange"
|
||||||
|
value="{{ computed.formatFullDate(date.customRange) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="自定义按钮文字"
|
||||||
|
data-type="range"
|
||||||
|
data-id="customConfirm"
|
||||||
|
value="{{ computed.formatRange(date.customConfirm) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="自定义日期文案"
|
||||||
|
data-type="range"
|
||||||
|
data-id="customDayText"
|
||||||
|
value="{{ computed.formatRange(date.customDayText) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="自定义弹出位置"
|
||||||
|
data-type="single"
|
||||||
|
data-id="customPosition"
|
||||||
|
value="{{ computed.formatFullDate(date.customPosition) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-cell
|
||||||
|
is-link
|
||||||
|
title="日期区间最大范围"
|
||||||
|
data-type="range"
|
||||||
|
data-id="maxRange"
|
||||||
|
value="{{ computed.formatRange(date.maxRange) }}"
|
||||||
|
bind:click="show"
|
||||||
|
/>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="平铺展示">
|
||||||
|
<van-calendar
|
||||||
|
title="日历"
|
||||||
|
poppable="{{ false }}"
|
||||||
|
show-confirm="{{ false }}"
|
||||||
|
min-date="{{ tiledMinDate }}"
|
||||||
|
max-date="{{ tiledMaxDate }}"
|
||||||
|
class="tiled-calendar"
|
||||||
|
/>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<van-calendar
|
||||||
|
show="{{ showCalendar }}"
|
||||||
|
type="{{ type }}"
|
||||||
|
color="{{ color }}"
|
||||||
|
round="{{ round }}"
|
||||||
|
position="{{ position }}"
|
||||||
|
min-date="{{ minDate }}"
|
||||||
|
max-date="{{ maxDate }}"
|
||||||
|
max-range="{{ maxRange }}"
|
||||||
|
formatter="{{ formatter }}"
|
||||||
|
show-confirm="{{ showConfirm }}"
|
||||||
|
confirm-text="{{ confirmText }}"
|
||||||
|
confirm-disabled-text="{{ confirmDisabledText }}"
|
||||||
|
bind:confirm="onConfirm"
|
||||||
|
bind:select="onSelect"
|
||||||
|
bind:open="onOpen"
|
||||||
|
bind:opened="onOpened"
|
||||||
|
bind:close="onClose"
|
||||||
|
bind:closed="onClosed"
|
||||||
|
>
|
||||||
|
</van-calendar>
|
33
example/pages/calendar/index.wxs
Normal file
33
example/pages/calendar/index.wxs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
function formatDate(date) {
|
||||||
|
if (date) {
|
||||||
|
date = getDate(date);
|
||||||
|
return date.getMonth() + 1 + '/' + date.getDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFullDate(date) {
|
||||||
|
if (date) {
|
||||||
|
date = getDate(date);
|
||||||
|
return date.getFullYear() + '/' + formatDate(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMultiple(dates) {
|
||||||
|
if (dates.length) {
|
||||||
|
return '选择了 ' + dates.length + '个日期';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRange(dateRange) {
|
||||||
|
if (dateRange.length) {
|
||||||
|
return formatDate(dateRange[0]) + ' - ' + formatDate(dateRange[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
formatDate: formatDate,
|
||||||
|
formatFullDate: formatFullDate,
|
||||||
|
formatMultiple: formatMultiple,
|
||||||
|
formatRange: formatRange
|
||||||
|
};
|
3
example/pages/calendar/index.wxss
Normal file
3
example/pages/calendar/index.wxss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.tiled-calendar {
|
||||||
|
--calendar-height: 500px;
|
||||||
|
}
|
@ -343,6 +343,12 @@
|
|||||||
"pathName": "pages/dropdown-menu/index",
|
"pathName": "pages/dropdown-menu/index",
|
||||||
"query": "",
|
"query": "",
|
||||||
"scene": null
|
"scene": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": -1,
|
||||||
|
"name": "calendar",
|
||||||
|
"pathName": "pages/calendar/index",
|
||||||
|
"scene": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
330
packages/calendar/README.md
Normal file
330
packages/calendar/README.md
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
# Calendar 日历
|
||||||
|
|
||||||
|
### 引入
|
||||||
|
|
||||||
|
在`app.json`或`index.json`中引入组件,详细介绍见[快速上手](#/quickstart#yin-ru-zu-jian)
|
||||||
|
|
||||||
|
```json
|
||||||
|
"usingComponents": {
|
||||||
|
"van-calendar": "path/to/@vant/weapp/dist/calendar/index"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 代码演示
|
||||||
|
|
||||||
|
### 选择单个日期
|
||||||
|
|
||||||
|
下面演示了结合单元格来使用日历组件的用法,日期选择完成后会触发`confirm`事件
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-cell title="选择单个日期" value="{{ date }}" bind:click="onShow" />
|
||||||
|
<van-calendar show="{{ show }}" bind:close="onClose" bind:confirm="onConfirm" />
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
date: '',
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
this.setData({ show: true });
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
this.setData({ show: false });
|
||||||
|
},
|
||||||
|
formatDate(date) {
|
||||||
|
date = new Date(date);
|
||||||
|
return `${date.getMonth() + 1}/${date.getDate()}`;
|
||||||
|
},
|
||||||
|
onConfirm(event) {
|
||||||
|
this.setData({
|
||||||
|
show: false,
|
||||||
|
date: this.formatDate(event.detail)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 选择多个日期
|
||||||
|
|
||||||
|
设置`type`为`multiple`后可以选择多个日期,此时`confirm`事件返回的 date 为数组结构,数组包含若干个选中的日期。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-cell title="选择多个日期" value="{{ text }}" bind:click="onShow" />
|
||||||
|
<van-calendar
|
||||||
|
show="{{ show }}"
|
||||||
|
type="multiple"
|
||||||
|
bind:close="onClose"
|
||||||
|
bind:confirm="onConfirm"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
text: '',
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
this.setData({ show: true });
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
this.setData({ show: false });
|
||||||
|
},
|
||||||
|
onConfirm(event) {
|
||||||
|
this.setData({
|
||||||
|
show: false,
|
||||||
|
date: `选择了 ${event.detail.length} 个日期`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 选择日期区间
|
||||||
|
|
||||||
|
设置`type`为`range`后可以选择日期区间,此时`confirm`事件返回的 date 为数组结构,数组第一项为开始时间,第二项为结束时间。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-cell title="选择日期区间" value="{{ date }}" bind:click="onShow" />
|
||||||
|
<van-calendar
|
||||||
|
show="{{ show }}"
|
||||||
|
type="range"
|
||||||
|
bind:close="onClose"
|
||||||
|
bind:confirm="onConfirm"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
date: '',
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
this.setData({ show: true });
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
this.setData({ show: false });
|
||||||
|
},
|
||||||
|
formatDate(date) {
|
||||||
|
date = new Date(date);
|
||||||
|
return `${date.getMonth() + 1}/${date.getDate()}`;
|
||||||
|
},
|
||||||
|
onConfirm(date) {
|
||||||
|
const [start, end] = date;
|
||||||
|
this.setData({
|
||||||
|
show: false,
|
||||||
|
date: `${this.formatDate(start)} - ${this.formatDate(end)}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 快捷选择
|
||||||
|
|
||||||
|
将`show-confirm`设置为`false`可以隐藏确认按钮,这种情况下选择完成后会立即触发`confirm`事件
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-calendar show="{{ show }}" show-confirm="{{ false }}" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义颜色
|
||||||
|
|
||||||
|
通过`color`属性可以自定义日历的颜色,对选中日期和底部按钮生效
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-calendar show="{{ show }}" color="#07c160" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义日期范围
|
||||||
|
|
||||||
|
通过`min-date`和`max-date`定义日历的范围
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-calendar
|
||||||
|
show="{{ show }}"
|
||||||
|
min-date="{{ minDate }}"
|
||||||
|
max-date="{{ maxDate }}"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
show: false,
|
||||||
|
minDate: new Date(2010, 0, 1).getTime(),
|
||||||
|
maxDate: new Date(2010, 0, 31).getTime()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义按钮文字
|
||||||
|
|
||||||
|
通过`confirm-text`设置按钮文字,通过`confirm-disabled-text`设置按钮禁用时的文字
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-calendar
|
||||||
|
show="{{ show }}"
|
||||||
|
type="range"
|
||||||
|
confirm-text="完成"
|
||||||
|
confirm-disabled-text="请选择结束时间"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义日期文案
|
||||||
|
|
||||||
|
通过传入`formatter`函数来对日历上每一格的内容进行格式化
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-calendar show="{{ show }}" type="range" formatter="{{ formatter }}" />
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
formatter(day) {
|
||||||
|
const month = day.date.getMonth() + 1;
|
||||||
|
const date = day.date.getDate();
|
||||||
|
|
||||||
|
if (month === 5) {
|
||||||
|
if (date === 1) {
|
||||||
|
day.topInfo = '劳动节';
|
||||||
|
} else if (date === 4) {
|
||||||
|
day.topInfo = '五四青年节';
|
||||||
|
} else if (date === 11) {
|
||||||
|
day.text = '今天';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day.type === 'start') {
|
||||||
|
day.bottomInfo = '入住';
|
||||||
|
} else if (day.type === 'end') {
|
||||||
|
day.bottomInfo = '离店';
|
||||||
|
}
|
||||||
|
|
||||||
|
return day;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义弹出位置
|
||||||
|
|
||||||
|
通过`position`属性自定义弹出层的弹出位置,可选值为`top`、`left`、`right`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-calendar show="{{ show }}" round="false" position="right" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 日期区间最大范围
|
||||||
|
|
||||||
|
选择日期区间时,可以通过`max-range`属性来指定最多可选天数,选择的范围超过最多可选天数时,会弹出相应的提示文案
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-calendar type="range" max-range="{{ 3 }}" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 平铺展示
|
||||||
|
|
||||||
|
将`poppable`设置为`false`,日历会直接展示在页面内,而不是以弹层的形式出现
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-calendar
|
||||||
|
title="日历"
|
||||||
|
poppable="{{ false }}"
|
||||||
|
show-confirm="{{ false }}"
|
||||||
|
class="calendar"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
.calendar {
|
||||||
|
--calendar-height: 500px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| --------------------- | -------------------------------------------------------------------------------------------------- | -------------------- | ------------------ |
|
||||||
|
| type | 选择类型:<br>`single`表示选择单个日期,<br>`multiple`表示选择多个日期,<br>`range`表示选择日期区间 | _string_ | `single` |
|
||||||
|
| title | 日历标题 | _string_ | `日期选择` |
|
||||||
|
| color | 主题色,对底部按钮和选中日期生效 | _string_ | `#ee0a24` |
|
||||||
|
| min-date | 可选择的最小日期 | _number_ | 当前日期 |
|
||||||
|
| max-date | 可选择的最大日期 | _number_ | 当前日期的六个月后 |
|
||||||
|
| default-date | 默认选中的日期,`type`为`multiple`或`range`时为数组 | _number \| number[]_ | 今天 |
|
||||||
|
| row-height | 日期行高 | _number \| string_ | `64` |
|
||||||
|
| formatter | 日期格式化函数 | _(day: Day) => Day_ | - |
|
||||||
|
| poppable | 是否以弹层的形式展示日历 | _boolean_ | `true` |
|
||||||
|
| show-mark | 是否显示月份背景水印 | _boolean_ | `true` |
|
||||||
|
| show-title | 是否展示日历标题 | _boolean_ | `true` |
|
||||||
|
| show-subtitle | 是否展示日历副标题(年月) | _boolean_ | `true` |
|
||||||
|
| show-confirm | 是否展示确认按钮 | _boolean_ | `true` |
|
||||||
|
| confirm-text | 确认按钮的文字 | _string_ | `确定` |
|
||||||
|
| confirm-disabled-text | 确认按钮处于禁用状态时的文字 | _string_ | `确定` |
|
||||||
|
|
||||||
|
### Poppable Props
|
||||||
|
|
||||||
|
当 Canlendar 的 `poppable` 为 `true` 时,支持以下 props:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| ---------------------- | --------------------------------------- | --------- | -------- |
|
||||||
|
| show | 是否显示日历弹窗 | _boolean_ | `false` |
|
||||||
|
| position | 弹出位置,可选值为 `top` `right` `left` | _string_ | `bottom` |
|
||||||
|
| round | 是否显示圆角弹窗 | _boolean_ | `true` |
|
||||||
|
| close-on-click-overlay | 是否在点击遮罩层后关闭 | _boolean_ | `true` |
|
||||||
|
| safe-area-inset-bottom | 是否开启底部安全区适配 | _boolean_ | `true` |
|
||||||
|
|
||||||
|
### Range Props
|
||||||
|
|
||||||
|
当 Canlendar 的 `type` 为 `range` 时,支持以下 props:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| -------------- | ------------------------------------ | ------------------ | ------------------------ |
|
||||||
|
| max-range | 日期区间最多可选天数,默认无限制 | _number \| string_ | - |
|
||||||
|
| range-prompt | 范围选择超过最多可选天数时的提示文案 | _string_ | `选择天数不能超过 xx 天` |
|
||||||
|
| allow-same-day | 是否允许日期范围的起止时间为同一天 | _boolean_ | `fasle` |
|
||||||
|
|
||||||
|
### Day 数据结构
|
||||||
|
|
||||||
|
日历中的每个日期都对应一个 Day 对象,通过`formatter`属性可以自定义 Day 对象的内容
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| ---------- | ------------------------------------------------------------------ | -------- |
|
||||||
|
| date | 日期对应的 Date 对象 | _Date_ |
|
||||||
|
| type | 日期类型,可选值为`selected`、`start`、`middle`、`end`、`disabled` | _string_ |
|
||||||
|
| text | 中间显示的文字 | _string_ |
|
||||||
|
| topInfo | 上方的提示信息 | _string_ |
|
||||||
|
| bottomInfo | 下方的提示信息 | _string_ |
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
| 事件名 | 说明 | 回调参数 |
|
||||||
|
| ------- | ------------------------------------------------------------------ | ----------------------- |
|
||||||
|
| select | 点击任意日期时触发 | _value: Date \| Date[]_ |
|
||||||
|
| confirm | 日期选择完成后触发,若`show-confirm`为`true`,则点击确认按钮后触发 | _value: Date \| Date[]_ |
|
||||||
|
| open | 打开弹出层时触发 | - |
|
||||||
|
| close | 关闭弹出层时触发 | - |
|
||||||
|
| opened | 打开弹出层且动画结束后触发 | - |
|
||||||
|
| closed | 关闭弹出层且动画结束后触发 | - |
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
| ------ | ------------------ |
|
||||||
|
| title | 自定义标题 |
|
||||||
|
| footer | 自定义底部区域内容 |
|
||||||
|
|
||||||
|
### 方法
|
||||||
|
|
||||||
|
通过 selectComponent 可以获取到 Calendar 实例并调用实例方法
|
||||||
|
|
||||||
|
| 方法名 | 说明 | 参数 | 返回值 |
|
||||||
|
| ------ | ---------------------- | ---- | ------ |
|
||||||
|
| reset | 重置选中的日期到默认值 | - | - |
|
57
packages/calendar/calendar.wxml
Normal file
57
packages/calendar/calendar.wxml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<wxs src="./index.wxs" module="computed"></wxs>
|
||||||
|
|
||||||
|
<template name="calendar">
|
||||||
|
<view class="van-calendar">
|
||||||
|
<header
|
||||||
|
title="{{ title }}"
|
||||||
|
showTitle="{{ showTitle }}"
|
||||||
|
subtitle="{{ subtitle }}"
|
||||||
|
showSubtitle="{{ showSubtitle }}"
|
||||||
|
>
|
||||||
|
<slot name="title" slot="title"></slot>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<scroll-view class="van-calendar__body" scroll-y scroll-into-view="{{ scrollIntoView }}">
|
||||||
|
<month
|
||||||
|
wx:for="{{ computed.getMonths(minDate, maxDate) }}"
|
||||||
|
wx:key="index"
|
||||||
|
id="month{{ index }}"
|
||||||
|
class="month"
|
||||||
|
data-date="{{ item }}"
|
||||||
|
date="{{ item }}"
|
||||||
|
type="{{ type }}"
|
||||||
|
color="{{ color }}"
|
||||||
|
minDate="{{ minDate }}"
|
||||||
|
maxDate="{{ maxDate }}"
|
||||||
|
showMark="{{ showMark }}"
|
||||||
|
formatter="{{ formatter }}"
|
||||||
|
rowHeight="{{ rowHeight }}"
|
||||||
|
currentDate="{{ currentDate }}"
|
||||||
|
showSubtitle="{{ showSubtitle }}"
|
||||||
|
allowSameDay="{{ allowSameDay }}"
|
||||||
|
showMonthTitle="{{ index !== 0 || !showSubtitle }}"
|
||||||
|
bind:click="onClickDay"
|
||||||
|
/>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<view class="van-calendar__footer {{ safeAreaInsetBottom ? 'van-calendar__footer--safe-area-inset-bottom' : '' }}">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="van-calendar__footer {{ safeAreaInsetBottom ? 'van-calendar__footer--safe-area-inset-bottom' : '' }}">
|
||||||
|
<van-button
|
||||||
|
wx:if="{{ showConfirm }}"
|
||||||
|
round
|
||||||
|
block
|
||||||
|
type="danger"
|
||||||
|
color="{{ color }}"
|
||||||
|
custom-class="van-calendar__confirm"
|
||||||
|
disabled="{{ computed.getButtonDisabled(type, currentDate) }}"
|
||||||
|
nativeType="text"
|
||||||
|
bind:click="onConfirm"
|
||||||
|
>
|
||||||
|
{{ computed.getButtonDisabled(type, currentDate) ? confirmDisabledText : confirmText }}
|
||||||
|
</van-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
3
packages/calendar/components/header/index.json
Normal file
3
packages/calendar/components/header/index.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"component": true
|
||||||
|
}
|
39
packages/calendar/components/header/index.less
Normal file
39
packages/calendar/components/header/index.less
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
@import '../../../common/style/var.less';
|
||||||
|
@import '../../../common/style/theme.less';
|
||||||
|
|
||||||
|
.van-calendar {
|
||||||
|
&__header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
.theme(box-shadow, '@calendar-header-box-shadow');
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header-title,
|
||||||
|
&__header-subtitle {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.theme(height, '@calendar-header-title-height');
|
||||||
|
.theme(font-weight, '@font-weight-bold');
|
||||||
|
.theme(line-height, '@calendar-header-title-height');
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header-title:empty,
|
||||||
|
&__header-title + &__header-title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header-title:empty + &__header-title {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__weekdays {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__weekday {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.theme(font-size, '@calendar-weekdays-font-size');
|
||||||
|
.theme(line-height, '@calendar-weekdays-height');
|
||||||
|
}
|
||||||
|
}
|
19
packages/calendar/components/header/index.ts
Normal file
19
packages/calendar/components/header/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { VantComponent } from '../../../common/component';
|
||||||
|
|
||||||
|
VantComponent({
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
value: '日期选择'
|
||||||
|
},
|
||||||
|
subtitle: String,
|
||||||
|
showTitle: Boolean,
|
||||||
|
showSubtitle: Boolean
|
||||||
|
},
|
||||||
|
|
||||||
|
data: {
|
||||||
|
weekdays: ['日', '一', '二', '三', '四', '五', '六']
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {}
|
||||||
|
});
|
16
packages/calendar/components/header/index.wxml
Normal file
16
packages/calendar/components/header/index.wxml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<view class="van-calendar__header">
|
||||||
|
<block wx:if="{{ showTitle }}">
|
||||||
|
<view class="van-calendar__header-title"><slot name="title"></slot></view>
|
||||||
|
<view class="van-calendar__header-title">{{ title }}</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
|
<view wx:if="{{ showSubtitle }}" class="van-calendar__header-subtitle">
|
||||||
|
{{ subtitle }}
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="van-calendar__weekdays">
|
||||||
|
<view wx:for="{{ weekdays }}" wx:key="index" class="van-calendar__weekday">
|
||||||
|
{{ item }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
3
packages/calendar/components/month/index.json
Normal file
3
packages/calendar/components/month/index.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"component": true
|
||||||
|
}
|
125
packages/calendar/components/month/index.less
Normal file
125
packages/calendar/components/month/index.less
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
@import '../../../common/style/var';
|
||||||
|
@import '../../../common/style/theme.less';
|
||||||
|
|
||||||
|
.van-calendar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
.theme(background-color, '@calendar-background-color');
|
||||||
|
|
||||||
|
&__month-title {
|
||||||
|
text-align: center;
|
||||||
|
.theme(height, '@calendar-header-title-height');
|
||||||
|
.theme(font-weight, '@font-weight-bold');
|
||||||
|
.theme(font-size, '@calendar-month-title-font-size');
|
||||||
|
.theme(line-height, '@calendar-header-title-height');
|
||||||
|
}
|
||||||
|
|
||||||
|
&__days {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__month-mark {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 0;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
.theme(color, '@calendar-month-mark-color');
|
||||||
|
.theme(font-size, '@calendar-month-mark-font-size');
|
||||||
|
}
|
||||||
|
|
||||||
|
&__day,
|
||||||
|
&__selected-day {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__day {
|
||||||
|
position: relative;
|
||||||
|
width: 14.285%;
|
||||||
|
|
||||||
|
.theme(height, '@calendar-day-height');
|
||||||
|
.theme(font-size, '@calendar-day-font-size');
|
||||||
|
|
||||||
|
&--end,
|
||||||
|
&--start,
|
||||||
|
&--start-end,
|
||||||
|
&--multiple-middle,
|
||||||
|
&--multiple-selected {
|
||||||
|
.theme(color, '@calendar-range-edge-color');
|
||||||
|
.theme(background-color, '@calendar-range-edge-background-color');
|
||||||
|
}
|
||||||
|
|
||||||
|
&--start {
|
||||||
|
.theme(border-radius, '@border-radius-md 0 0 @border-radius-md');
|
||||||
|
}
|
||||||
|
|
||||||
|
&--end {
|
||||||
|
.theme(border-radius, '0 @border-radius-md @border-radius-md 0');
|
||||||
|
}
|
||||||
|
|
||||||
|
&--start-end,
|
||||||
|
&--multiple-selected {
|
||||||
|
.theme(border-radius, '@border-radius-md');
|
||||||
|
}
|
||||||
|
|
||||||
|
&--middle {
|
||||||
|
.theme(color, '@calendar-range-middle-color');
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: currentColor;
|
||||||
|
content: '';
|
||||||
|
|
||||||
|
.theme(opacity, '@calendar-range-middle-background-opacity');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
.theme(color, '@calendar-day-disabled-color');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__top-info,
|
||||||
|
&__bottom-info {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
.theme(font-size, '@calendar-info-font-size');
|
||||||
|
.theme(line-height, '@calendar-info-line-height');
|
||||||
|
|
||||||
|
@media (max-width: 350px) {
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__top-info {
|
||||||
|
top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bottom-info {
|
||||||
|
bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__selected-day {
|
||||||
|
.theme(width, '@calendar-selected-day-size');
|
||||||
|
.theme(height, '@calendar-selected-day-size');
|
||||||
|
.theme(color, '@calendar-selected-day-color');
|
||||||
|
.theme(background-color, '@calendar-selected-day-background-color');
|
||||||
|
.theme(border-radius, '@border-radius-md');
|
||||||
|
}
|
||||||
|
}
|
190
packages/calendar/components/month/index.ts
Normal file
190
packages/calendar/components/month/index.ts
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import { VantComponent } from '../../../common/component';
|
||||||
|
import {
|
||||||
|
getMonthEndDay,
|
||||||
|
compareDay,
|
||||||
|
getPrevDay,
|
||||||
|
getNextDay
|
||||||
|
} from '../../utils';
|
||||||
|
|
||||||
|
VantComponent({
|
||||||
|
props: {
|
||||||
|
date: {
|
||||||
|
type: null,
|
||||||
|
observer: 'setDays'
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
observer: 'setDays'
|
||||||
|
},
|
||||||
|
color: String,
|
||||||
|
minDate: {
|
||||||
|
type: null,
|
||||||
|
observer: 'setDays'
|
||||||
|
},
|
||||||
|
maxDate: {
|
||||||
|
type: null,
|
||||||
|
observer: 'setDays'
|
||||||
|
},
|
||||||
|
showMark: Boolean,
|
||||||
|
rowHeight: [Number, String],
|
||||||
|
formatter: {
|
||||||
|
type: null,
|
||||||
|
observer: 'setDays'
|
||||||
|
},
|
||||||
|
currentDate: {
|
||||||
|
type: [null, Array],
|
||||||
|
observer: 'setDays'
|
||||||
|
},
|
||||||
|
allowSameDay: Boolean,
|
||||||
|
showSubtitle: Boolean,
|
||||||
|
showMonthTitle: Boolean
|
||||||
|
},
|
||||||
|
|
||||||
|
data: {
|
||||||
|
visible: true,
|
||||||
|
days: []
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onClick(event) {
|
||||||
|
const { index } = event.currentTarget.dataset;
|
||||||
|
const item = this.data.days[index];
|
||||||
|
if (item.type !== 'disabled') {
|
||||||
|
this.$emit('click', item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setDays() {
|
||||||
|
const days = [];
|
||||||
|
const startDate = new Date(this.data.date);
|
||||||
|
const year = startDate.getFullYear();
|
||||||
|
const month = startDate.getMonth();
|
||||||
|
|
||||||
|
const totalDay = getMonthEndDay(
|
||||||
|
startDate.getFullYear(),
|
||||||
|
startDate.getMonth() + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let day = 1; day <= totalDay; day++) {
|
||||||
|
const date = new Date(year, month, day);
|
||||||
|
const type = this.getDayType(date);
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
date,
|
||||||
|
type,
|
||||||
|
text: day,
|
||||||
|
bottomInfo: this.getBottomInfo(type)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.data.formatter) {
|
||||||
|
config = this.data.formatter(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
days.push(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setData({ days });
|
||||||
|
},
|
||||||
|
|
||||||
|
getMultipleDayType(day) {
|
||||||
|
const { currentDate } = this.data;
|
||||||
|
if (!Array.isArray(currentDate)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSelected = date =>
|
||||||
|
currentDate.some(item => compareDay(item, date) === 0);
|
||||||
|
|
||||||
|
if (isSelected(day)) {
|
||||||
|
const prevDay = getPrevDay(day);
|
||||||
|
const nextDay = getNextDay(day);
|
||||||
|
const prevSelected = isSelected(prevDay);
|
||||||
|
const nextSelected = isSelected(nextDay);
|
||||||
|
|
||||||
|
if (prevSelected && nextSelected) {
|
||||||
|
return 'multiple-middle';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevSelected) {
|
||||||
|
return 'end';
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextSelected ? 'start' : 'multiple-selected';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
|
||||||
|
getRangeDayType(day) {
|
||||||
|
const { currentDate, allowSameDay } = this.data;
|
||||||
|
if (!Array.isArray(currentDate)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [startDay, endDay] = currentDate;
|
||||||
|
|
||||||
|
if (!startDay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const compareToStart = compareDay(day, startDay);
|
||||||
|
|
||||||
|
if (!endDay) {
|
||||||
|
return compareToStart === 0 ? 'start' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const compareToEnd = compareDay(day, endDay);
|
||||||
|
|
||||||
|
if (compareToStart === 0 && compareToEnd === 0 && allowSameDay) {
|
||||||
|
return 'start-end';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareToStart === 0) {
|
||||||
|
return 'start';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareToEnd === 0) {
|
||||||
|
return 'end';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareToStart > 0 && compareToEnd < 0) {
|
||||||
|
return 'middle';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getDayType(day) {
|
||||||
|
const { type, minDate, maxDate, currentDate } = this.data;
|
||||||
|
|
||||||
|
if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) {
|
||||||
|
return 'disabled';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'single') {
|
||||||
|
return compareDay(day, currentDate) === 0 ? 'selected' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'multiple') {
|
||||||
|
return this.getMultipleDayType(day);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (type === 'range') {
|
||||||
|
return this.getRangeDayType(day);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getBottomInfo(type) {
|
||||||
|
if (this.data.type === 'range') {
|
||||||
|
if (type === 'start') {
|
||||||
|
return '开始';
|
||||||
|
}
|
||||||
|
if (type === 'end') {
|
||||||
|
return '结束';
|
||||||
|
}
|
||||||
|
if (type === 'start-end') {
|
||||||
|
return '开始/结束';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
39
packages/calendar/components/month/index.wxml
Normal file
39
packages/calendar/components/month/index.wxml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<wxs src="./index.wxs" module="computed"></wxs>
|
||||||
|
<wxs src="../../../wxs/utils.wxs" module="utils" />
|
||||||
|
|
||||||
|
<view class="van-calendar__month" style="{{ computed.getMonthStyle(visible, date, rowHeight) }}">
|
||||||
|
<view wx:if="{{ showMonthTitle }}" class="van-calendar__month-title">
|
||||||
|
{{ computed.formatMonthTitle(date) }}
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view wx:if="{{ visible }}" class="van-calendar__days">
|
||||||
|
<view wx:if="{{ showMark }}" class="van-calendar__month-mark">
|
||||||
|
{{ computed.getMark(date) }}
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
wx:for="{{ days }}"
|
||||||
|
wx:key="index"
|
||||||
|
style="{{ computed.getDayStyle(item.type, index, date, rowHeight, color) }}"
|
||||||
|
class="{{ utils.bem('calendar__day', [item.type]) }} {{ item.className }}"
|
||||||
|
data-index="{{ index }}"
|
||||||
|
bindtap="onClick"
|
||||||
|
>
|
||||||
|
<view wx:if="{{ item.type === 'selected' }}" class="van-calendar__selected-day" style="background: {{ color }}">
|
||||||
|
<view wx:if="{{ item.topInfo }}" class="van-calendar__top-info">{{ item.topInfo }}</view>
|
||||||
|
{{ item.text }}
|
||||||
|
<view wx:if="{{ item.bottomInfo }}" class="van-calendar__bottom-info">
|
||||||
|
{{ item.bottomInfo }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view wx:else>
|
||||||
|
<view wx:if="{{ item.topInfo }}" class="van-calendar__top-info">{{ item.topInfo }}</view>
|
||||||
|
{{ item.text }}
|
||||||
|
<view wx:if="{{ item.bottomInfo }}" class="van-calendar__bottom-info">
|
||||||
|
{{ item.bottomInfo }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
67
packages/calendar/components/month/index.wxs
Normal file
67
packages/calendar/components/month/index.wxs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
var utils = require('../../utils.wxs');
|
||||||
|
|
||||||
|
function getMark(date) {
|
||||||
|
return getDate(date).getMonth() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ROW_HEIGHT = 64;
|
||||||
|
|
||||||
|
function getDayStyle(type, index, date, rowHeight, color) {
|
||||||
|
var style = [];
|
||||||
|
var offset = getDate(date).getDay();
|
||||||
|
|
||||||
|
if (index === 0) {
|
||||||
|
style.push(['margin-left', (100 * offset) / 7 + '%']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowHeight !== ROW_HEIGHT) {
|
||||||
|
style.push(['height', rowHeight + 'px']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color) {
|
||||||
|
if (
|
||||||
|
type === 'start' ||
|
||||||
|
type === 'end' ||
|
||||||
|
type === 'multiple-selected' ||
|
||||||
|
type === 'multiple-middle'
|
||||||
|
) {
|
||||||
|
style.push(['background', color]);
|
||||||
|
} else if (type === 'middle') {
|
||||||
|
style.push(['color', color]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return style
|
||||||
|
.map(function(item) {
|
||||||
|
return item.join(':');
|
||||||
|
})
|
||||||
|
.join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMonthTitle(date) {
|
||||||
|
date = getDate(date);
|
||||||
|
return date.getFullYear() + '年' + (date.getMonth() + 1) + '月';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMonthStyle(visible, date, rowHeight) {
|
||||||
|
if (!visible) {
|
||||||
|
date = getDate(date);
|
||||||
|
|
||||||
|
var totalDay = utils.getMonthEndDay(
|
||||||
|
date.getFullYear(),
|
||||||
|
date.getMonth() + 1
|
||||||
|
);
|
||||||
|
var offset = getDate(date).getDay();
|
||||||
|
var padding = Math.ceil((totalDay + offset) / 7) * rowHeight;
|
||||||
|
|
||||||
|
return 'padding-bottom:' + padding + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getMark: getMark,
|
||||||
|
getDayStyle: getDayStyle,
|
||||||
|
formatMonthTitle: formatMonthTitle,
|
||||||
|
getMonthStyle: getMonthStyle
|
||||||
|
};
|
9
packages/calendar/index.json
Normal file
9
packages/calendar/index.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"component": true,
|
||||||
|
"usingComponents": {
|
||||||
|
"header": "./components/header/index",
|
||||||
|
"month": "./components/month/index",
|
||||||
|
"van-button": "../button/index",
|
||||||
|
"van-popup": "../popup/index"
|
||||||
|
}
|
||||||
|
}
|
54
packages/calendar/index.less
Normal file
54
packages/calendar/index.less
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
@import '../common/style/var';
|
||||||
|
@import '../common/style/theme.less';
|
||||||
|
|
||||||
|
.van-calendar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.theme(height, '@calendar-height');
|
||||||
|
.theme(background-color, '@calendar-background-color');
|
||||||
|
|
||||||
|
&__close-icon {
|
||||||
|
top: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__popup--top,
|
||||||
|
&__popup--bottom {
|
||||||
|
.theme(height, '@calendar-popup-height');
|
||||||
|
}
|
||||||
|
|
||||||
|
&__popup--left,
|
||||||
|
&__popup--right {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
.theme(padding, '0 @padding-md');
|
||||||
|
|
||||||
|
&--safe-area-inset-bottom {
|
||||||
|
padding-bottom: constant(safe-area-inset-bottom);
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer:empty,
|
||||||
|
&__footer + &__footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer:empty + &__footer {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__confirm {
|
||||||
|
.theme(height, '@calendar-confirm-button-height') !important;
|
||||||
|
.theme(margin, '@calendar-confirm-button-margin') !important;
|
||||||
|
.theme(line-height, '@calendar-confirm-button-line-height') !important;
|
||||||
|
}
|
||||||
|
}
|
302
packages/calendar/index.ts
Normal file
302
packages/calendar/index.ts
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
import { VantComponent } from '../common/component';
|
||||||
|
import {
|
||||||
|
ROW_HEIGHT,
|
||||||
|
getNextDay,
|
||||||
|
compareDay,
|
||||||
|
copyDates,
|
||||||
|
calcDateNum,
|
||||||
|
formatMonthTitle,
|
||||||
|
compareMonth,
|
||||||
|
getMonths
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
import Toast from '../toast/toast';
|
||||||
|
|
||||||
|
VantComponent({
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
value: '日期选择'
|
||||||
|
},
|
||||||
|
color: String,
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
observer(val) {
|
||||||
|
if (val) {
|
||||||
|
this.initRect();
|
||||||
|
this.scrollIntoView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatter: null,
|
||||||
|
confirmText: {
|
||||||
|
type: String,
|
||||||
|
value: '确定'
|
||||||
|
},
|
||||||
|
rangePrompt: String,
|
||||||
|
defaultDate: {
|
||||||
|
type: [Number, Array],
|
||||||
|
observer(val) {
|
||||||
|
this.setData({ currentDate: val });
|
||||||
|
this.scrollIntoView();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allowSameDay: Boolean,
|
||||||
|
confirmDisabledText: String,
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
value: 'single',
|
||||||
|
observer: 'reset'
|
||||||
|
},
|
||||||
|
minDate: {
|
||||||
|
type: null,
|
||||||
|
value: Date.now()
|
||||||
|
},
|
||||||
|
maxDate: {
|
||||||
|
type: null,
|
||||||
|
value: new Date(
|
||||||
|
new Date().getFullYear(),
|
||||||
|
new Date().getMonth() + 6,
|
||||||
|
new Date().getDate()
|
||||||
|
).getTime()
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
type: String,
|
||||||
|
value: 'bottom'
|
||||||
|
},
|
||||||
|
rowHeight: {
|
||||||
|
type: [Number, String],
|
||||||
|
value: ROW_HEIGHT
|
||||||
|
},
|
||||||
|
round: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
poppable: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
showMark: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
showTitle: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
showConfirm: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
showSubtitle: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
safeAreaInsetBottom: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
closeOnClickOverlay: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
maxRange: {
|
||||||
|
type: [Number, String],
|
||||||
|
value: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: {
|
||||||
|
subtitle: '',
|
||||||
|
currentDate: null,
|
||||||
|
scrollIntoView: ''
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.setData({
|
||||||
|
currentDate: this.getInitialDate()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.data.show || !this.data.poppable) {
|
||||||
|
this.initRect();
|
||||||
|
this.scrollIntoView();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
reset() {
|
||||||
|
this.setData({ currentDate: this.getInitialDate() });
|
||||||
|
this.scrollIntoView();
|
||||||
|
},
|
||||||
|
|
||||||
|
initRect() {
|
||||||
|
if (this.contentObserver != null) {
|
||||||
|
this.contentObserver.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentObserver = this.createIntersectionObserver({
|
||||||
|
thresholds: [0, 0.1, 0.9, 1],
|
||||||
|
observeAll: true
|
||||||
|
});
|
||||||
|
|
||||||
|
this.contentObserver = contentObserver;
|
||||||
|
|
||||||
|
contentObserver.relativeTo('.van-calendar__body');
|
||||||
|
contentObserver.observe('.month', res => {
|
||||||
|
if (res.boundingClientRect.top <= res.relativeRect.top) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.setData({ subtitle: formatMonthTitle(res.dataset.date) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialDate() {
|
||||||
|
const { type, defaultDate, minDate } = this.data;
|
||||||
|
|
||||||
|
if (type === 'range') {
|
||||||
|
const [startDay, endDay] = defaultDate || [];
|
||||||
|
return [
|
||||||
|
startDay || minDate,
|
||||||
|
endDay || getNextDay(new Date(minDate)).getTime()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'multiple') {
|
||||||
|
return [defaultDate || minDate];
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultDate || minDate;
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollIntoView() {
|
||||||
|
setTimeout(() => {
|
||||||
|
const {
|
||||||
|
currentDate,
|
||||||
|
type,
|
||||||
|
show,
|
||||||
|
poppable,
|
||||||
|
minDate,
|
||||||
|
maxDate
|
||||||
|
} = this.data;
|
||||||
|
const targetDate = type === 'single' ? currentDate : currentDate[0];
|
||||||
|
const displayed = show || !poppable;
|
||||||
|
if (!targetDate || !displayed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const months = getMonths(minDate, maxDate);
|
||||||
|
|
||||||
|
months.some((month, index) => {
|
||||||
|
if (compareMonth(month, targetDate) === 0) {
|
||||||
|
this.setData({ scrollIntoView: `month${index}` });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
},
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
this.$emit('open');
|
||||||
|
},
|
||||||
|
|
||||||
|
onOpened() {
|
||||||
|
this.$emit('opened');
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
|
||||||
|
onClosed() {
|
||||||
|
this.$emit('closed');
|
||||||
|
},
|
||||||
|
|
||||||
|
onClickDay(event) {
|
||||||
|
const { date } = event.detail;
|
||||||
|
const { type, currentDate, allowSameDay } = this.data;
|
||||||
|
|
||||||
|
if (type === 'range') {
|
||||||
|
const [startDay, endDay] = currentDate;
|
||||||
|
|
||||||
|
if (startDay && !endDay) {
|
||||||
|
const compareToStart = compareDay(date, startDay);
|
||||||
|
|
||||||
|
if (compareToStart === 1) {
|
||||||
|
this.select([startDay, date], true);
|
||||||
|
} else if (compareToStart === -1) {
|
||||||
|
this.select([date, null]);
|
||||||
|
} else if (allowSameDay) {
|
||||||
|
this.select([date, date]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.select([date, null]);
|
||||||
|
}
|
||||||
|
} else if (type === 'multiple') {
|
||||||
|
let selectedIndex: number;
|
||||||
|
|
||||||
|
const selected = currentDate.some((dateItem: number, index: number) => {
|
||||||
|
const equal = compareDay(dateItem, date) === 0;
|
||||||
|
if (equal) {
|
||||||
|
selectedIndex = index;
|
||||||
|
}
|
||||||
|
return equal;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
currentDate.splice(selectedIndex, 1);
|
||||||
|
this.setData({ currentDate });
|
||||||
|
} else {
|
||||||
|
this.select([...currentDate, date]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.select(date, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
select(date, complete) {
|
||||||
|
const getTime = (date: Date | number) =>
|
||||||
|
(date instanceof Date ? date.getTime() : date);
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
currentDate: Array.isArray(date) ? date.map(getTime) : getTime(date)
|
||||||
|
});
|
||||||
|
this.$emit('select', copyDates(date));
|
||||||
|
|
||||||
|
if (complete && this.data.type === 'range') {
|
||||||
|
const valid = this.checkRange();
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (complete && !this.data.showConfirm) {
|
||||||
|
this.onConfirm();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
checkRange() {
|
||||||
|
const { maxRange, currentDate, rangePrompt } = this.data;
|
||||||
|
|
||||||
|
if (maxRange && calcDateNum(currentDate) > maxRange) {
|
||||||
|
Toast(rangePrompt || `选择天数不能超过 ${maxRange} 天`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
onConfirm() {
|
||||||
|
if (this.data.type === 'range' && !this.checkRange()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wx.nextTick(() => {
|
||||||
|
this.$emit('confirm', copyDates(this.data.currentDate));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
29
packages/calendar/index.wxml
Normal file
29
packages/calendar/index.wxml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<wxs src="./index.wxs" module="computed" />
|
||||||
|
|
||||||
|
<import src="./calendar.wxml" />
|
||||||
|
|
||||||
|
<van-popup
|
||||||
|
wx:if="{{ poppable }}"
|
||||||
|
custom-class="van-calendar__popup--{{ position }}"
|
||||||
|
close-icon-class="van-calendar__close-icon"
|
||||||
|
show="{{ show }}"
|
||||||
|
round="{{ round }}"
|
||||||
|
position="{{ position }}"
|
||||||
|
closeable="{{ showTitle || showSubtitle }}"
|
||||||
|
close-on-click-overlay="{{ closeOnClickOverlay }}"
|
||||||
|
bind:enter="onOpen"
|
||||||
|
bind:close="onClose"
|
||||||
|
bind:after-enter="onOpened"
|
||||||
|
bind:after-leave="onClosed"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
is="calendar"
|
||||||
|
data="{{ title, subtitle, showTitle, showSubtitle, minDate, maxDate, type, color, showMark, formatter, rowHeight, currentDate, safeAreaInsetBottom, showConfirm, confirmDisabledText, confirmText, scrollIntoView, allowSameDay }}"
|
||||||
|
/>
|
||||||
|
</van-popup>
|
||||||
|
|
||||||
|
<template
|
||||||
|
wx:else
|
||||||
|
is="calendar"
|
||||||
|
data="{{ title, subtitle, showTitle, showSubtitle, minDate, maxDate, type, color, showMark, formatter, rowHeight, currentDate, safeAreaInsetBottom, showConfirm, confirmDisabledText, confirmText, scrollIntoView, allowSameDay }}"
|
||||||
|
/>
|
33
packages/calendar/index.wxs
Normal file
33
packages/calendar/index.wxs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
var utils = require('./utils.wxs');
|
||||||
|
|
||||||
|
function getMonths(minDate, maxDate) {
|
||||||
|
var months = [];
|
||||||
|
var cursor = getDate(minDate);
|
||||||
|
|
||||||
|
cursor.setDate(1);
|
||||||
|
|
||||||
|
do {
|
||||||
|
months.push(cursor.getTime());
|
||||||
|
cursor.setMonth(cursor.getMonth() + 1);
|
||||||
|
} while (utils.compareMonth(cursor, getDate(maxDate)) !== 1);
|
||||||
|
|
||||||
|
return months;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getButtonDisabled(type, currentDate) {
|
||||||
|
if (type === 'range') {
|
||||||
|
return !currentDate[0] || !currentDate[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'multiple') {
|
||||||
|
return !currentDate.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !currentDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getMonths: getMonths,
|
||||||
|
getButtonDisabled: getButtonDisabled
|
||||||
|
};
|
103
packages/calendar/utils.ts
Normal file
103
packages/calendar/utils.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
export const ROW_HEIGHT = 64;
|
||||||
|
|
||||||
|
export function formatMonthTitle(date: Date) {
|
||||||
|
if (!(date instanceof Date)) {
|
||||||
|
date = new Date(date);
|
||||||
|
}
|
||||||
|
return `${date.getFullYear()}年${date.getMonth() + 1}月`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compareMonth(date1: Date | number, date2: Date | number) {
|
||||||
|
if (!(date1 instanceof Date)) {
|
||||||
|
date1 = new Date(date1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(date2 instanceof Date)) {
|
||||||
|
date2 = new Date(date2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const year1 = date1.getFullYear();
|
||||||
|
const year2 = date2.getFullYear();
|
||||||
|
const month1 = date1.getMonth();
|
||||||
|
const month2 = date2.getMonth();
|
||||||
|
|
||||||
|
if (year1 === year2) {
|
||||||
|
return month1 === month2 ? 0 : month1 > month2 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return year1 > year2 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compareDay(day1: Date | number, day2: Date | number) {
|
||||||
|
if (!(day1 instanceof Date)) {
|
||||||
|
day1 = new Date(day1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(day2 instanceof Date)) {
|
||||||
|
day2 = new Date(day2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const compareMonthResult = compareMonth(day1, day2);
|
||||||
|
|
||||||
|
if (compareMonthResult === 0) {
|
||||||
|
const date1 = day1.getDate();
|
||||||
|
const date2 = day2.getDate();
|
||||||
|
|
||||||
|
return date1 === date2 ? 0 : date1 > date2 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return compareMonthResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDayByOffset(date: Date, offset: number) {
|
||||||
|
date = new Date(date);
|
||||||
|
date.setDate(date.getDate() + offset);
|
||||||
|
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPrevDay(date: Date) {
|
||||||
|
return getDayByOffset(date, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNextDay(date: Date) {
|
||||||
|
return getDayByOffset(date, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calcDateNum(date: [Date, Date]) {
|
||||||
|
const day1 = new Date(date[0]).getTime();
|
||||||
|
const day2 = new Date(date[1]).getTime();
|
||||||
|
return (day2 - day1) / (1000 * 60 * 60 * 24) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function copyDates(dates: Date | Date[]) {
|
||||||
|
if (Array.isArray(dates)) {
|
||||||
|
return dates.map(date => {
|
||||||
|
if (date === null) {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(date);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(dates);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMonthEndDay(year: number, month: number): number {
|
||||||
|
return 32 - new Date(year, month - 1, 32).getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMonths(minDate: number, maxDate: number) {
|
||||||
|
const months = [];
|
||||||
|
const cursor = new Date(minDate);
|
||||||
|
|
||||||
|
cursor.setDate(1);
|
||||||
|
|
||||||
|
do {
|
||||||
|
months.push(cursor.getTime());
|
||||||
|
cursor.setMonth(cursor.getMonth() + 1);
|
||||||
|
} while (compareMonth(cursor, maxDate) !== 1);
|
||||||
|
|
||||||
|
return months;
|
||||||
|
}
|
25
packages/calendar/utils.wxs
Normal file
25
packages/calendar/utils.wxs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
function getMonthEndDay(year, month) {
|
||||||
|
return 32 - getDate(year, month - 1, 32).getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareMonth(date1, date2) {
|
||||||
|
date1 = getDate(date1);
|
||||||
|
date2 = getDate(date2);
|
||||||
|
|
||||||
|
var year1 = date1.getFullYear();
|
||||||
|
var year2 = date2.getFullYear();
|
||||||
|
var month1 = date1.getMonth();
|
||||||
|
var month2 = date2.getMonth();
|
||||||
|
|
||||||
|
if (year1 === year2) {
|
||||||
|
return month1 === month2 ? 0 : month1 > month2 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return year1 > year2 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getMonthEndDay: getMonthEndDay,
|
||||||
|
compareMonth: compareMonth
|
||||||
|
};
|
@ -109,6 +109,35 @@
|
|||||||
@button-plain-background-color: @white;
|
@button-plain-background-color: @white;
|
||||||
@button-disabled-opacity: @disabled-opacity;
|
@button-disabled-opacity: @disabled-opacity;
|
||||||
|
|
||||||
|
// Calendar
|
||||||
|
@calendar-height: 100%;
|
||||||
|
@calendar-background-color: @white;
|
||||||
|
@calendar-popup-height: 80%;
|
||||||
|
@calendar-header-box-shadow: 0 2px 10px rgba(125, 126, 128, 0.16);
|
||||||
|
@calendar-header-title-height: 44px;
|
||||||
|
@calendar-header-title-font-size: @font-size-lg;
|
||||||
|
@calendar-header-subtitle-font-size: @font-size-md;
|
||||||
|
@calendar-weekdays-height: 30px;
|
||||||
|
@calendar-weekdays-font-size: @font-size-sm;
|
||||||
|
@calendar-month-title-font-size: @font-size-md;
|
||||||
|
@calendar-month-mark-color: fade(@gray-2, 80%);
|
||||||
|
@calendar-month-mark-font-size: 160px;
|
||||||
|
@calendar-day-height: 64px;
|
||||||
|
@calendar-day-font-size: @font-size-lg;
|
||||||
|
@calendar-range-edge-color: @white;
|
||||||
|
@calendar-range-edge-background-color: @red;
|
||||||
|
@calendar-range-middle-color: @red;
|
||||||
|
@calendar-range-middle-background-opacity: 0.1;
|
||||||
|
@calendar-selected-day-size: 54px;
|
||||||
|
@calendar-selected-day-color: @white;
|
||||||
|
@calendar-info-font-size: @font-size-xs;
|
||||||
|
@calendar-info-line-height: 14px;
|
||||||
|
@calendar-selected-day-background-color: @red;
|
||||||
|
@calendar-day-disabled-color: @gray-5;
|
||||||
|
@calendar-confirm-button-height: 36px;
|
||||||
|
@calendar-confirm-button-margin: 7px 0;
|
||||||
|
@calendar-confirm-button-line-height: 34px;
|
||||||
|
|
||||||
// Card
|
// Card
|
||||||
@card-padding: @padding-xs @padding-md;
|
@card-padding: @padding-xs @padding-md;
|
||||||
@card-font-size: @font-size-sm;
|
@card-font-size: @font-size-sm;
|
||||||
|
@ -13,16 +13,16 @@ export const transition = function(showDefaultValue: boolean) {
|
|||||||
return Behavior({
|
return Behavior({
|
||||||
properties: {
|
properties: {
|
||||||
customStyle: String,
|
customStyle: String,
|
||||||
// @ts-ignore
|
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: showDefaultValue,
|
value: showDefaultValue,
|
||||||
|
// @ts-ignore
|
||||||
observer: 'observeShow'
|
observer: 'observeShow'
|
||||||
},
|
},
|
||||||
// @ts-ignore
|
|
||||||
duration: {
|
duration: {
|
||||||
type: null,
|
type: null,
|
||||||
value: 300,
|
value: 300,
|
||||||
|
// @ts-ignore
|
||||||
observer: 'observeDuration'
|
observer: 'observeDuration'
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
|
@ -8,7 +8,8 @@ VantComponent({
|
|||||||
'enter-to-class',
|
'enter-to-class',
|
||||||
'leave-class',
|
'leave-class',
|
||||||
'leave-active-class',
|
'leave-active-class',
|
||||||
'leave-to-class'
|
'leave-to-class',
|
||||||
|
'close-icon-class'
|
||||||
],
|
],
|
||||||
|
|
||||||
mixins: [transition(false)],
|
mixins: [transition(false)],
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<van-icon
|
<van-icon
|
||||||
wx:if="{{ closeable }}"
|
wx:if="{{ closeable }}"
|
||||||
name="{{ closeIcon }}"
|
name="{{ closeIcon }}"
|
||||||
class="van-popup__close-icon van-popup__close-icon--{{ closeIconPosition }}"
|
class="close-icon-class van-popup__close-icon van-popup__close-icon--{{ closeIconPosition }}"
|
||||||
bind:tap="onClickCloseIcon"
|
bind:tap="onClickCloseIcon"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user