小程序时间选择控件封装
# 小程序时间选择控件封装
在工作中,有一个需求是产品想按照企业微信审批的时间选择来做我们的需求,故此简单封装了一个小程序的时间选择控件
也是使用了微信原生的组件,保证了 安卓和iOS交互的统一性,具体如下
# JS
首先对公共的方法提取了 class
/**
*
* @export
* @class DateControl
* @param options: { rangeYear : Number, startYear : Number}
*/
export default class DateControl {
constructor (now, options) {
// 获取年份跨度以及起始年
const {rangeYear, startYear} = options
this.now = now
this.years = Array.from(Array(rangeYear).keys(), x => x + startYear)
this.months = Array.from(Array(12).keys(), x => ++x)
this.days = Array.from(Array(31).keys(), x => ++x)
}
// 判断是否闰年
static isLeap(year) {
if((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0){
return true;
}
return false;
}
// 计算日期差值
static differenceDays(start, end) {
return (end - start) / (1000 * 60 * 60 * 24)
}
// 日期减法
static subtractDate (date, n) {
return new Date(date.setDate(date.getDate() - n))
}
// 月份补零 ios 不补零 new Date 会有问题
static addZero(n) {
return n.toString().padStart(2, '0')
}
// 返回和当前相比较小的日期
static minDate (date, compare = new Date) {
return new Date(Math.min(date, compare))
}
// 获取年
static getYear (date) {
return date.getFullYear()
}
static getMonth (date, type) {
return type ? DateControl.addZero(date.getMonth() + 1) : date.getMonth() + 1
}
static getDate (date, type) {
return type ? DateControl.addZero(date.getDate()) : date.getDate()
}
// 处理为合适的日期 不超过 now 对应的 月份 及 天数
optionalDateControl (year = DateControl.getYear(this.now), month = DateControl.getMonth(this.now), day = DateControl.getDate(this.now)) {
month = month >>> 0 // 保证为数字
day = day >>> 0
// 取出 30天的
const _30 = [4,6,9,11].includes(month)
const currentYear = DateControl.getYear(this.now) // 获取初始化时间的年份
const currentMonth = DateControl.getMonth(this.now) // 月份
const currentDay = DateControl.getDate(this.now) // 天
// 判断是否闰年
const isLeap = DateControl.isLeap(year)
let days = _30 ? this.days.filter(d => d < 31) : this.days; // 处理 30天和31天的
// 单独对2月进行判断
if(month === 2) {
days = isLeap ? this.days.filter(d => d < 30) : this.days.filter(d => d < 29)
}
// 取出当前和最大中的较小值 对已经选择的天做修正
day = Math.min(day, days[days.length - 1]);
// 处理月份
// 当前年
const isCurrentYear = year === currentYear; // 判断是否是当前年
let months = this.months
if (isCurrentYear) {
months = this.months.filter(item => item <= currentMonth); // 如果是当前年,日期可选最大为当月,不可超过当前时间
month = Math.min(month, months[months.length - 1]) // 对已经选择的月做修正
if (month === currentMonth) { // 如果是当前月 还要对天数也做一个修正
day = Math.min(day, currentDay);
days = this.days.filter(d => d <= currentDay)
}
}
return [months, days, year, month, day]
}
// type 为 0 表示需要增加 currentDate 应小于 comparedDate 为 1 表示减少 currentDate 应 大于 comparedDate
// currentDate 为当前选中的日期 comparedDate 为需要对比的日期
// rangeDays 表示二者相差的最大天数
dateToRepair (currentDate, comparedDate, rangeDays, type = 0 ) {
// 获取二者相差天数
const differenceDays = DateControl.differenceDays(comparedDate, currentDate);
// 根据 currentDate 获取到 当前日
const currentDay = DateControl.getDate(currentDate)
// 定义一个结果,但并不赋值
let resultDate
// 如果二者相差天数超过了最大限制
if (Math.abs(differenceDays) > rangeDays) {
// 拿到需要修正的天数
const fixDays = type ? 0 - rangeDays : 0 + rangeDays
// 根据 fixDays 对日期做加法,这里用减法,所以取负
resultDate = DateControl.minDate(DateControl.subtractDate(currentDate, -fixDays), this.now)
} else if (!type && differenceDays > 0) {
// 当前应该为 currentDate 小于 comparedDate ,结果大于了,这里对进行加1天 作为修复数据
resultDate = DateControl.minDate(DateControl.subtractDate(currentDate, -1), this.now)
} else if(type && differenceDays < 0) {
// 当前应为 currentDate 大于 comparedDate , 结果小于了,对其进行减一天作为修复数据
resultDate = DateControl.minDate(DateControl.subtractDate(currentDate, 1), this.now)
}
// 最终,如果有值,则返回修复时间对应的 年月日,并且补零,否则返回 undefined
return resultDate ? {
repairedYear: DateControl.getYear(resultDate),
repairedMonth: DateControl.getMonth(resultDate, true),
repairedDays: DateControl.getDate(resultDate, true)
} : undefined
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
组件的 js
/*
* @Date: 2021-04-25 10:44:10
* @LastEditTime: 2021-04-29 22:38:53
* @Description: 时间选择控件,支持 开始时间和 结束时间选择
* 1. 支持传入 range 指最大可选时间跨度,单位 年
* 2. 支持传入 startTime 和 endTime 用作数据回显以及默认选中,如 结束时间超过了当前时间或者
* 结束时间 和 开始时间差值超过了最大可选时间跨度,会默认使用结束时间减去 最大可选时间跨度得到开始时间
* 3. 如选择开始时间和已经存在的结束时间相差超过时间跨度,会同步修改结束时间
* 4. 开始时间大于结束时间,会修改结束时间为 开始时间 + 1天
* 5. 结束时间小于开始时间,会同步修改开始时间为 结束时间 - 1天
* 6. 在所有时间超过当前时间时,会默认和当前时间比较,取最小
* 7. 最终返回的日期为 年月日 组成的时间戳, 以数组形式返回, [start, end]
* 8. 最大可选日期为当日,超过后会修正
*
* 不支持
* 1. 不支持传入默认时间配置,如有需要可自己复制代码实现业务需求
* 2. 默认添加了一层保险,在最终返回时间时。会做一个判断,如果结束时间小于开始时间,会吐司提示,吐司提示内容不支持传入
* 3. 时间控件还存在一些问题,目前为想好怎么处理
* a. 在闰年的 2.29 号,切换年时 结束时间如果同步修改的话,会到 3.2 号,这是一个 bug
* 4. 目前未支持 时间的筛选,只有日期的
* 5. 目前未支持自定义颜色等
* 6. 返回的日期格式 为 时间戳,默认不会添加时分秒的时间,为当日的 00:00:00
*
* @FilePath: /digital-applet/components/datePicker/datePicker.js
*/
import DateControl from './handler'
import MessageBox from '../../utils/ui/messageBox'
const self_control = new DateControl(new Date, {
rangeYear: new Date().getFullYear() - 1989,
startYear: 1990
})
const end_date = self_control.now
const start_date = new Date(self_control.now.getTime() - 86400000)
Component({
properties: {
range: {
type: Number,
value: 1
},
startTime: {
type: Number,
value: start_date.getTime()
},
endTime: {
type: Number,
value: end_date.getTime()
},
dataType: {
type: String,
value: 0
}
},
data: {
years: self_control.years,
start_year: '',
end_year: '',
months: self_control.months,
start_month: '',
end_month: '',
days: self_control.days,
start_day: '',
end_day: '',
value: [],
type: 0,
_range_days: 0
},
ready: function () {
this._init()
},
methods: {
// 初始化各种数据
_init() {
// 判断传入的值,是否超过时间跨度
const _range_days = this.data.range * 365
const _isError = DateControl.differenceDays(new Date(this.data.startTime), new Date(this.data.endTime)) > _range_days
// 定义初始化开始时间对象
const startTimeDate = _isError ? DateControl.subtractDate(new Date(this.data.endTime), _range_days) : new Date(this.data.startTime)
const endTimeDate = new Date(this.data.endTime)
// 获取开始时间年月日
const start_year = DateControl.getYear(startTimeDate)
const start_month = DateControl.getMonth(startTimeDate, true)
const start_day = DateControl.getDate(startTimeDate, true)
// 初始化数据 日期最大可选范围
const [months, days] = self_control.optionalDateControl(start_year, start_month, start_day)
this.setData({
start_year,
end_year: DateControl.getYear(endTimeDate),
start_month,
end_month: DateControl.getMonth(endTimeDate, true),
start_day,
end_day: DateControl.getDate(endTimeDate, true),
value: [start_year - 1990, Number(start_month) - 1, Number(start_day) - 1],
days,
months,
_range_days
})
},
// 确认时间
_confirm() {
const _startText = `${this.data.start_year}/${this.data.start_month}/${this.data.start_day}`
const _endText = `${this.data.end_year}/${this.data.end_month}/${this.data.end_day}`
const startTime = new Date(`${_startText} 00:00:00`).getTime()
const endTime = new Date(`${_endText} 00:00:00`).getTime()
console.log(new Date(`${_startText} 00:00:00`))
if (endTime - startTime < 0) {
MessageBox.toast('开始时间不能大于结束时间')
return
}
this.triggerEvent('confirm', {time: [startTime, endTime]}, {})
},
// 切换开始和结束
_tabChange(event) {
const type = +event.currentTarget.dataset.type;
const prefix = type ? 'end_' : 'start_';
this.setData({
type,
value: [this.data[`${prefix}year`] - 1990, this.data[`${prefix}month`] - 1, this.data[`${prefix}day`] - 1]
})
},
_bindChange: function (e) {
const val = e.detail.value
let year = this.data.years[val[0]];
let month = this.data.months[val[1]];
let day = this.data.days[val[2]];
const [months, days, _year, _month, _day] = self_control.optionalDateControl(year, month, day)
let fix_prefix;
const prefix = this.data.type ? (fix_prefix = 'start_', 'end_') : ( fix_prefix = 'end_', 'start_');
// 判断是否闰年
const _isLeap = DateControl.isLeap(_year)
const _temp_day = DateControl.addZero((!_isLeap && _month === 2 && _day > 28) ? 28 : _day)
const _temp = new Date(`${_year}-${DateControl.addZero(_month)}-${_temp_day}`); // 本次选中的时间的日期对象
const fix_date = new Date(`${this.data[`${fix_prefix}year`]}-${this.data[`${fix_prefix}month`]}-${this.data[`${fix_prefix}day`]}`); // 需要对比的时间日期对象
const result = self_control.dateToRepair(_temp, fix_date, this.data._range_days, this.data.type)
if (result) {
this.setData({
[`${fix_prefix}year`]: result.repairedYear,
[`${fix_prefix}month`]: result.repairedMonth,
[`${fix_prefix}day`]: result.repairedDays
})
}
// 更新数据
this.setData({
[`${prefix}year`]: _year,
[`${prefix}month`]: DateControl.addZero(_month),
[`${prefix}day`]: DateControl.addZero(_day),
days,
months
})
}
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# HTML
<view>
<view class="header">
<view class="btns">
<view class="btn {{!type ? 'active' : ''}}" data-type="0" catchtap="_tabChange">
<view>开始</view>
<text>{{start_year}}年{{start_month}}月{{start_day}}日</text>
</view>
<view class="btn {{type ? 'active' : ''}}" data-type="1" catchtap="_tabChange">
<view>结束</view>
<text>{{end_year}}年{{end_month}}月{{end_day}}日</text>
</view>
</view>
<view data-type="{{dataType}}" catchtap="_confirm" class="text">确定</view>
</view>
<picker-view indicator-class="active-date" style="width: 100%; height: 300px;background:#fff;" value="{{value}}" bindchange="_bindChange">
<picker-view-column>
<view wx:for="{{years}}" wx:key="*this" class="date-item">{{item}}年</view>
</picker-view-column>
<picker-view-column>
<view wx:for="{{months}}" wx:key="*this" class="date-item">{{item}}月</view>
</picker-view-column>
<picker-view-column>
<view wx:for="{{days}}" wx:key="*this" class="date-item">{{item}}日</view>
</picker-view-column>
</picker-view>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# css
.intro {
margin: 30px;
text-align: center;
}
.header{
font-size: 24rpx;
background:#fff;
display: flex;
height: 100rpx;
align-items: center;
border-radius: 8rpx 8rpx 0 0;
border-bottom: 1px solid #eee;
box-sizing: border-box;
padding: 12rpx 24rpx;
}
.header .btns{
flex: 1;
display: flex;
margin-right: 80rpx;
align-items: center;
box-sizing: border-box;
overflow: hidden;
border-radius:8rpx;
}
.header .btns .btn{
width: 220rpx;
box-sizing: border-box;
padding-left:24rpx;
color:#219AFF;
border:1px solid #219AFF;
}
.header .btns .btn.active{
background:#219AFF;
color:#fff;
}
.header .text{
flex:0 0 166rpx;
height:100%;
text-align: center;
font-size: 28rpx;
line-height:76rpx;
color:#219AFF;
}
.active-date{
height: 110rpx;
}
.date-item{
line-height: 88rpx;
text-align: center;
font-size:28rpx;
}
.active-date .date-item{
font-size: 32rpx;
line-height: 110rpx;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
编辑 (opens new window)
上次更新: 2021/05/26 14:18:07