252962bb
刘淇
uni_modu
|
1
|
<template>
|
9441e28e
刘淇
考勤打卡
|
2
3
4
5
6
7
8
9
10
11
12
13
|
<u-popup
:show="show"
mode="bottom"
:overlay="overlay"
:duration="duration"
:overlayStyle="overlayStyle"
:overlayOpacity="overlayOpacity"
:zIndex="zIndex"
:safeAreaInsetBottom="safeAreaInsetBottom"
:safeAreaInsetTop="safeAreaInsetTop"
:bgColor="bgColor"
:closeable="!pageInline"
|
ecbfbe06
刘淇
树巡检记录
|
14
|
@close="close"
|
9441e28e
刘淇
考勤打卡
|
15
16
17
18
|
@closed="$emit('closed')"
:round="round"
:pageInline="pageInline"
:closeOnClickOverlay="closeOnClickOverlay"
|
ecbfbe06
刘淇
树巡检记录
|
19
|
>
|
9441e28e
刘淇
考勤打卡
|
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
|
<view class="u-calendar">
<uHeader
:title="title"
:subtitle="subtitle"
:showSubtitle="showSubtitle"
:showTitle="showTitle"
:weekText="weekText"
:showSwitch="monthSwitch"
:showToday="showToday"
:todayText="todayText"
:todayDisabled="todayDisabled"
:prevDisabled="switchPrevDisabled"
:nextDisabled="switchNextDisabled"
:prevYearDisabled="switchPrevYearDisabled"
:nextYearDisabled="switchNextYearDisabled"
@prev="prevMonth"
@next="nextMonth"
@prevYear="prevYear"
@nextYear="nextYear"
@today="jumpToToday"
></uHeader>
<view v-if="showTimePanel" class="u-calendar__time-panel">
<view v-if="mode === 'single'" class="u-calendar__time-row">
<text class="u-calendar__time-date">{{ singleDateLabel }}</text>
<view class="u-calendar__time-trigger" @click="openTimePicker('single')">
<text class="u-calendar__time-text">{{ singleTime }}</text>
</view>
</view>
<view v-else-if="mode === 'range' && rangeResultMode === 'boundary'">
<view class="u-calendar__time-row">
<text class="u-calendar__time-date">{{ rangeStartDateLabel }}</text>
<view class="u-calendar__time-trigger" @click="openTimePicker('start')">
<text class="u-calendar__time-text">{{ rangeStartTime }}</text>
</view>
</view>
<view class="u-calendar__time-row">
<text class="u-calendar__time-date">{{ rangeEndDateLabel }}</text>
<view class="u-calendar__time-trigger" @click="openTimePicker('end')">
<text class="u-calendar__time-text">{{ rangeEndTime }}</text>
|
252962bb
刘淇
uni_modu
|
59
60
|
</view>
</view>
|
9441e28e
刘淇
考勤打卡
|
61
|
</view>
|
252962bb
刘淇
uni_modu
|
62
|
</view>
|
9441e28e
刘淇
考勤打卡
|
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
|
<scroll-view
v-if="!monthSwitch"
:style="{
height: addUnit(listHeight, 'px')
}"
scroll-y
@scroll="onScroll"
:scroll-top="scrollTop"
:scrollIntoView="scrollIntoView"
>
<uMonth
:color="color"
:rowHeight="rowHeight"
:showMark="showMark"
:months="months"
:mode="mode"
:maxCount="maxCount"
:startText="startText"
:endText="endText"
:defaultDate="defaultDate"
:minDate="innerMinDate"
:maxDate="innerMaxDate"
:maxMonth="monthNum"
:readonly="readonly"
:maxRange="maxRange"
:rangePrompt="rangePrompt"
:showRangePrompt="showRangePrompt"
:allowSameDay="allowSameDay"
:forbidDays="forbidDays"
:forbidDaysToast="forbidDaysToast"
:monthFormat="monthFormat"
:todayDate="todayDate"
:todayColor="todayColor"
ref="month"
@monthSelected="monthSelected"
@updateMonthTop="onUpdateMonthTop"
></uMonth>
</scroll-view>
<view
v-else
:style="{
height: addUnit(listHeight, 'px')
}"
>
<uMonth
:color="color"
:rowHeight="rowHeight"
:showMark="showMark"
:months="currentMonths"
:mode="mode"
:maxCount="maxCount"
:startText="startText"
:endText="endText"
:defaultDate="defaultDate"
:minDate="innerMinDate"
:maxDate="innerMaxDate"
:maxMonth="monthNum"
:readonly="readonly"
:maxRange="maxRange"
:rangePrompt="rangePrompt"
:showRangePrompt="showRangePrompt"
:allowSameDay="allowSameDay"
:forbidDays="forbidDays"
:forbidDaysToast="forbidDaysToast"
:monthFormat="monthFormat"
:todayDate="todayDate"
:todayColor="todayColor"
ref="month"
@monthSelected="monthSelected"
@updateMonthTop="onUpdateMonthTop"
></uMonth>
|
252962bb
刘淇
uni_modu
|
134
|
</view>
|
9441e28e
刘淇
考勤打卡
|
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
|
<slot name="footer" v-if="showConfirm">
<view class="u-calendar__confirm">
<u-button
shape="circle"
:text="
buttonDisabled ? confirmDisabledText : confirmText
"
:color="color"
@click="confirm"
:disabled="buttonDisabled"
></u-button>
</view>
</slot>
</view>
</u-popup>
<u-popup
:show="timePickerShow"
mode="center"
:round="8"
:closeOnClickOverlay="true"
@close="closeTimePicker"
>
<view class="u-calendar__time-picker">
<view class="u-calendar__time-picker__header">
<text class="u-calendar__time-picker__cancel" @click="closeTimePicker">取消</text>
<text class="u-calendar__time-picker__title">选择时间</text>
<text class="u-calendar__time-picker__confirm" @click="confirmTimePicker">确定</text>
|
252962bb
刘淇
uni_modu
|
162
|
</view>
|
9441e28e
刘淇
考勤打卡
|
163
164
165
166
167
168
169
170
171
172
173
|
<picker-view class="u-calendar__time-picker__body" :value="timePickerValue" @change="onTimePickerChange">
<picker-view-column>
<view v-for="(item, index) in hourOptions" :key="`h-${index}`" class="u-calendar__time-picker__item">{{ item }}</view>
</picker-view-column>
<picker-view-column v-if="timePrecision !== 'hour'">
<view v-for="(item, index) in minuteOptions" :key="`m-${index}`" class="u-calendar__time-picker__item">{{ item }}</view>
</picker-view-column>
<picker-view-column v-if="timePrecision === 'second'">
<view v-for="(item, index) in secondOptions" :key="`s-${index}`" class="u-calendar__time-picker__item">{{ item }}</view>
</picker-view-column>
</picker-view>
|
252962bb
刘淇
uni_modu
|
174
|
</view>
|
9441e28e
刘淇
考勤打卡
|
175
|
</u-popup>
|
252962bb
刘淇
uni_modu
|
176
177
178
|
</template>
<script>
|
9441e28e
刘淇
考勤打卡
|
179
180
181
182
183
184
185
186
187
188
|
import uHeader from './header.vue'
import uMonth from './month.vue'
import { props } from './props.js'
import util from './util.js'
import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
import Calendar from '../../libs/util/calendar.js'
import { mpMixin } from '../../libs/mixin/mpMixin.js'
import { mixin } from '../../libs/mixin/mixin.js'
import { addUnit, getPx, range, error, padZero } from '../../libs/function/index';
import test from '../../libs/function/test';
|
ecbfbe06
刘淇
树巡检记录
|
189
|
import { t } from '../../libs/i18n'
|
9441e28e
刘淇
考勤打卡
|
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
|
/**
* Calendar 日历
* @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中.
* @tutorial https://uview-plus.jiangruyi.com/components/calendar.html
*
* @property {String} title 标题内容 (默认 日期选择 )
* @property {Boolean} showTitle 是否显示标题 (默认 true )
* @property {Boolean} showSubtitle 是否显示副标题 (默认 true )
* @property {String} mode 日期类型选择 single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 ( 默认 'single' )
* @property {String} startText mode=range时,第一个日期底部的提示文字 (默认 '开始' )
* @property {String} endText mode=range时,最后一个日期底部的提示文字 (默认 '结束' )
* @property {Array} customList 自定义列表
* @property {String} color 主题色,对底部按钮和选中日期有效 (默认 ‘#3c9cff' )
* @property {String | Number} minDate 最小的可选日期 (默认 0 )
* @property {String | Number} maxDate 最大可选日期 (默认 0 )
* @property {Array | String| Date} defaultDate 默认选中的日期,mode为multiple或range是必须为数组格式
* @property {String | Number} maxCount mode=multiple时,最多可选多少个日期 (默认 Number.MAX_SAFE_INTEGER )
* @property {String | Number} rowHeight 日期行高 (默认 56 )
* @property {Function} formatter 日期格式化函数
* @property {Boolean} showLunar 是否显示农历 (默认 false )
* @property {Boolean} showMark 是否显示月份背景色 (默认 true )
* @property {String} confirmText 确定按钮的文字 (默认 '确定' )
* @property {String} confirmDisabledText 确认按钮处于禁用状态时的文字 (默认 '确定' )
* @property {Boolean} show 是否显示日历弹窗 (默认 false )
* @property {Boolean} overlay 是否显示遮罩 (默认 true )
* @property {String | Number} duration 动画时长,单位ms (默认 300 )
* @property {Object | String} overlayStyle 自定义遮罩的样式
* @property {Number | String} overlayOpacity 遮罩透明度,0-1之间 (默认 0.5 )
* @property {String | Number} zIndex 弹出层的z-index值 (默认 10075 )
* @property {Boolean} safeAreaInsetBottom 是否留出底部安全距离 (默认 true )
* @property {Boolean} safeAreaInsetTop 是否留出顶部安全距离(状态栏高度) (默认 false )
* @property {String} bgColor 弹窗背景色,设置为transparent可去除白色背景
* @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭日历 (默认 false )
* @property {Boolean} readonly 是否为只读状态,只读状态下禁止选择日期 (默认 false )
* @property {String | Number} maxRange 日期区间最多可选天数,默认无限制,mode = range时有效
* @property {String} rangePrompt 范围选择超过最多可选天数时的提示文案,mode = range时有效
* @property {Boolean} showRangePrompt 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 (默认 true )
* @property {Boolean} allowSameDay 是否允许日期范围的起止时间为同一天,mode = range时有效 (默认 false )
* @property {String} rangeResultMode 区间模式下确认返回值格式,all-返回区间内所有日期,boundary-仅返回起止日期 (默认 'all' )
* @property {Boolean} enableTime 是否开启时分秒选择 (默认 false )
* @property {String} timePrecision 时间精度,hour-仅时,minute-时分,second-时分秒 (默认 'minute' )
* @property {String} defaultTime 默认时间,支持HH/HH:mm/HH:mm:ss,不传则按00:00补齐
* @property {Number|String} round 圆角值,默认无圆角 (默认 0 )
* @property {Number|String} monthNum 最多展示的月份数量 (默认 3 )
* @property {Boolean} monthSwitch 是否启用非滚动的单月切换模式 (默认 false )
* @property {String} todayColor 今天日期的独立高亮颜色,默认跟随主题色
* @property {Array} weekText 星期文案 (默认 ['一', '二', '三', '四', '五', '六', '日'] )
*
* @event {Function()} confirm 点击确定按钮时触发 选择日期相关的返回参数
* @event {Function()} close 日历关闭时触发 可定义页面关闭时的回调事件
* @event {Function()} closed 日历已关闭(离场动画结束、弹窗真正消失后)触发
* @example <u-calendar :defaultDate="defaultDateMultiple" :show="show" mode="multiple" @confirm="confirm">
</u-calendar>
* */
|
ecbfbe06
刘淇
树巡检记录
|
244
|
export default {
|
9441e28e
刘淇
考勤打卡
|
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
|
name: 'u-calendar',
mixins: [mpMixin, mixin, props],
components: {
uHeader,
uMonth
},
data() {
return {
// 需要显示的月份的数组
months: [],
// 在月份滚动区域中,当前视图中月份的index索引
monthIndex: 0,
// 月份滚动区域的高度
listHeight: 0,
// month组件中选择的日期数组
selected: [],
scrollIntoView: '',
scrollIntoViewScroll: '',
scrollTop:0,
timePickerShow: false,
timePickerTarget: 'single',
timePickerValue: [0, 0, 0],
hourOptions: [],
minuteOptions: [],
secondOptions: [],
singleTime: '00:00',
rangeStartTime: '00:00',
rangeEndTime: '00:00',
// 过滤处理方法
innerFormatter: (value) => value
}
},
watch: {
scrollIntoView: {
immediate: true,
handler(n) {
// console.log('scrollIntoView', n)
}
},
selectedChange: {
immediate: true,
handler(n) {
this.setMonth()
}
},
timePrecision() {
this.initTimeOptions()
this.initTimeValues()
},
defaultTime() {
this.initTimeValues()
},
// 打开弹窗时,设置月份数据
|
ecbfbe06
刘淇
树巡检记录
|
298
|
show: {
|
9441e28e
刘淇
考勤打卡
|
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
immediate: true,
handler(n) {
if (n) {
this.setMonth()
// 弹层每次打开都会重新挂载并滚动回默认月份,副标题指向的月份索引要一起复位,
// 否则会沿用上一次关闭前滚动到的月份,导致副标题与日历内容对应不上。
// 页面内模式的内容一直挂载着,滚动位置不会重置,所以不做处理
if (!this.pageInline) {
this.monthIndex = this.getDefaultMonthIndex()
}
} else {
// 关闭时重置scrollIntoView,否则会出现二次打开日历,当前月份数据显示不正确。
// scrollIntoView需要有一个值变动过程,才会产生作用。
this.scrollIntoView = ''
}
}
}
},
computed: {
// 由于maxDate和minDate可以为字符串(2021-10-10),或者数值(时间戳),但是dayjs如果接受字符串形式的时间戳会有问题,这里进行处理
innerMaxDate() {
return test.number(this.maxDate)
? Number(this.maxDate)
: this.maxDate
|
ecbfbe06
刘淇
树巡检记录
|
323
|
},
|
9441e28e
刘淇
考勤打卡
|
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
|
innerMinDate() {
return test.number(this.minDate)
? Number(this.minDate)
: this.minDate
},
todayDate() {
return dayjs().format('YYYY-MM-DD')
},
todayText() {
return t('up.calendar.today')
},
todayDisabled() {
const today = dayjs(this.todayDate)
const minDate = this.innerMinDate
? dayjs(this.innerMinDate)
: null
const maxDate = this.innerMaxDate
? dayjs(this.innerMaxDate)
: null
if (minDate && today.isBefore(minDate, 'day')) {
return true
}
if (maxDate && today.isAfter(maxDate, 'day')) {
return true
}
return false
},
// 多个条件的变化,会引起选中日期的变化,这里统一管理监听
selectedChange() {
return [this.innerMinDate, this.innerMaxDate, this.defaultDate]
},
subtitle() {
// 初始化时,this.months为空数组,所以需要特别判断处理
if (this.months.length) {
if (uni.getLocale() == 'zh-Hans' || uni.getLocale() == 'zh-Hant') {
return this.months[this.monthIndex].year + '年' + (this.months[this.monthIndex].month < 10 ? '0' + this.months[this.monthIndex].month : this.months[this.monthIndex].month) + '月'
} else {
return (this.months[this.monthIndex].month < 10 ? '0' + this.months[this.monthIndex].month : this.months[this.monthIndex].month) + '/' + this.months[this.monthIndex].year
}
} else {
return ''
|
252962bb
刘淇
uni_modu
|
365
366
|
}
},
|
9441e28e
刘淇
考勤打卡
|
367
368
369
|
currentMonths() {
if (this.monthSwitch && this.months.length) {
return [this.months[this.monthIndex]]
|
252962bb
刘淇
uni_modu
|
370
|
}
|
9441e28e
刘淇
考勤打卡
|
371
|
return this.months
|
252962bb
刘淇
uni_modu
|
372
|
},
|
9441e28e
刘淇
考勤打卡
|
373
374
|
switchPrevDisabled() {
return this.monthIndex <= 0
|
ecbfbe06
刘淇
树巡检记录
|
375
|
},
|
9441e28e
刘淇
考勤打卡
|
376
377
|
switchNextDisabled() {
return this.monthIndex >= this.months.length - 1
|
ecbfbe06
刘淇
树巡检记录
|
378
|
},
|
9441e28e
刘淇
考勤打卡
|
379
380
|
switchPrevYearDisabled() {
return this.monthIndex - 12 < 0
|
ecbfbe06
刘淇
树巡检记录
|
381
|
},
|
9441e28e
刘淇
考勤打卡
|
382
383
|
switchNextYearDisabled() {
return this.monthIndex + 12 > this.months.length - 1
|
ecbfbe06
刘淇
树巡检记录
|
384
|
},
|
9441e28e
刘淇
考勤打卡
|
385
386
387
388
389
390
391
|
showTimePanel() {
if (!this.enableTime) return false
if (this.mode === 'single') return true
if (this.mode === 'range' && this.rangeResultMode === 'boundary') {
return true
}
return false
|
ecbfbe06
刘淇
树巡检记录
|
392
|
},
|
9441e28e
刘淇
考勤打卡
|
393
394
|
singleDateLabel() {
return this.selected[0] || '--'
|
ecbfbe06
刘淇
树巡检记录
|
395
|
},
|
9441e28e
刘淇
考勤打卡
|
396
397
|
rangeStartDateLabel() {
return this.selected[0] || '--'
|
ecbfbe06
刘淇
树巡检记录
|
398
|
},
|
9441e28e
刘淇
考勤打卡
|
399
400
401
402
403
|
rangeEndDateLabel() {
if (this.selected.length >= 2) {
return this.selected[this.selected.length - 1]
}
return '--'
|
ecbfbe06
刘淇
树巡检记录
|
404
|
},
|
9441e28e
刘淇
考勤打卡
|
405
406
407
408
409
410
411
412
413
414
415
|
buttonDisabled() {
// 如果为range类型,且选择的日期个数不足1个时,让底部的按钮出于disabled状态
if (this.mode === 'range') {
if (this.selected.length <= 1) {
return true
} else {
return false
}
} else {
return false
}
|
ecbfbe06
刘淇
树巡检记录
|
416
417
|
}
},
|
9441e28e
刘淇
考勤打卡
|
418
419
420
|
mounted() {
this.start = Date.now()
this.init()
|
ecbfbe06
刘淇
树巡检记录
|
421
|
},
|
9441e28e
刘淇
考勤打卡
|
422
423
424
425
426
|
emits: ["confirm", "close", "closed"],
methods: {
addUnit,
padTime(num) {
return String(num).padStart(2, '0')
|
ecbfbe06
刘淇
树巡检记录
|
427
|
},
|
9441e28e
刘淇
考勤打卡
|
428
429
430
431
432
433
434
435
436
437
438
439
|
initTimeOptions() {
this.hourOptions = Array.from({ length: 24 }, (_, i) => this.padTime(i))
this.minuteOptions = Array.from({ length: 60 }, (_, i) => this.padTime(i))
this.secondOptions = Array.from({ length: 60 }, (_, i) => this.padTime(i))
},
parseTimeValue(value = '') {
const raw = String(value || '').trim()
const parts = raw.length ? raw.split(':') : []
const getNumber = (index, max) => {
const current = Number(parts[index] || 0)
if (Number.isNaN(current)) return 0
return Math.max(0, Math.min(max, current))
|
252962bb
刘淇
uni_modu
|
440
|
}
|
9441e28e
刘淇
考勤打卡
|
441
442
443
444
|
const hour = getNumber(0, 23)
const minute = getNumber(1, 59)
const second = getNumber(2, 59)
return [hour, minute, second]
|
252962bb
刘淇
uni_modu
|
445
|
},
|
9441e28e
刘淇
考勤打卡
|
446
447
448
449
450
451
452
453
454
|
getDefaultTimeValue() {
const [hour, minute, second] = this.parseTimeValue(this.defaultTime)
if (this.timePrecision === 'hour') {
return this.padTime(hour)
}
if (this.timePrecision === 'second') {
return `${this.padTime(hour)}:${this.padTime(minute)}:${this.padTime(second)}`
}
return `${this.padTime(hour)}:${this.padTime(minute)}`
|
ecbfbe06
刘淇
树巡检记录
|
455
|
},
|
9441e28e
刘淇
考勤打卡
|
456
457
458
459
460
|
initTimeValues() {
const value = this.getDefaultTimeValue()
this.singleTime = value
this.rangeStartTime = value
this.rangeEndTime = value
|
ecbfbe06
刘淇
树巡检记录
|
461
|
},
|
9441e28e
刘淇
考勤打卡
|
462
463
464
465
466
467
468
469
470
471
|
timeToPickerValue(value = '') {
const [hour, minute, second] = this.parseTimeValue(value)
return [hour, minute, second]
},
pickerValueToTime(value = []) {
const hour = Number(value[0] || 0)
const minute = Number(value[1] || 0)
const second = Number(value[2] || 0)
if (this.timePrecision === 'hour') {
return this.padTime(hour)
|
ecbfbe06
刘淇
树巡检记录
|
472
|
}
|
9441e28e
刘淇
考勤打卡
|
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
|
if (this.timePrecision === 'second') {
return `${this.padTime(hour)}:${this.padTime(minute)}:${this.padTime(second)}`
}
return `${this.padTime(hour)}:${this.padTime(minute)}`
},
openTimePicker(target) {
this.timePickerTarget = target
let currentValue = this.singleTime
if (target === 'start') currentValue = this.rangeStartTime
if (target === 'end') currentValue = this.rangeEndTime
this.timePickerValue = this.timeToPickerValue(currentValue)
this.timePickerShow = true
},
onTimePickerChange(e) {
this.timePickerValue = e.detail.value
},
closeTimePicker() {
this.timePickerShow = false
},
confirmTimePicker() {
const value = this.pickerValueToTime(this.timePickerValue)
if (this.timePickerTarget === 'single') this.singleTime = value
if (this.timePickerTarget === 'start') this.rangeStartTime = value
if (this.timePickerTarget === 'end') this.rangeEndTime = value
this.timePickerShow = false
|
ecbfbe06
刘淇
树巡检记录
|
498
|
},
|
9441e28e
刘淇
考勤打卡
|
499
500
501
502
503
504
505
|
timeToSecond(timeText = '') {
const [hour, minute, second] = this.parseTimeValue(timeText)
return hour * 3600 + minute * 60 + second
},
validateSameDayRangeTime() {
if (!this.enableTime || this.mode !== 'range' || this.rangeResultMode !== 'boundary') {
return true
|
ecbfbe06
刘淇
树巡检记录
|
506
|
}
|
9441e28e
刘淇
考勤打卡
|
507
508
509
510
511
512
513
514
515
516
517
518
|
if (this.selected.length < 2) return true
const startDate = this.selected[0]
const endDate = this.selected[this.selected.length - 1]
if (startDate !== endDate) return true
const startSeconds = this.timeToSecond(this.rangeStartTime)
const endSeconds = this.timeToSecond(this.rangeEndTime)
if (endSeconds < startSeconds) {
uni.showToast({
title: '结束时间不能早于开始时间',
icon: 'none'
})
return false
|
ecbfbe06
刘淇
树巡检记录
|
519
|
}
|
9441e28e
刘淇
考勤打卡
|
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
|
return true
},
appendTime(dateText, timeText) {
return `${dateText} ${timeText}`
},
getConfirmValue(selected = this.selected) {
let result = selected
if (
this.mode === 'range' &&
this.rangeResultMode === 'boundary' &&
selected.length >= 2
) {
const len = selected.length - 1
result = [selected[0], selected[len]]
}
if (!this.showTimePanel || !this.enableTime) {
return result
}
if (this.mode === 'single' && result.length >= 1) {
return [this.appendTime(result[0], this.singleTime)]
}
if (this.mode === 'range' && this.rangeResultMode === 'boundary' && result.length >= 2) {
return [
this.appendTime(result[0], this.rangeStartTime),
this.appendTime(result[1], this.rangeEndTime)
]
}
return result
},
// 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
setFormatter(e) {
this.innerFormatter = e
},
// month组件内部选择日期后,通过事件通知给父组件
monthSelected(e,scene ='init') {
this.selected = e
if (!this.showConfirm) {
// 在不需要确认按钮的情况下,如果为单选,或者范围多选且已选长度大于2,则直接进行返还
if (
this.mode === 'multiple' ||
this.mode === 'single' ||
(this.mode === 'range' && this.selected.length >= 2)
) {
if( scene === 'init'){
return
}
if( scene === 'tap') {
if (!this.validateSameDayRangeTime()) return
this.$emit('confirm', this.getConfirmValue())
}
|
252962bb
刘淇
uni_modu
|
570
|
}
|
ecbfbe06
刘淇
树巡检记录
|
571
|
}
|
ecbfbe06
刘淇
树巡检记录
|
572
|
},
|
9441e28e
刘淇
考勤打卡
|
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
|
init() {
// 校验maxDate,不能小于minDate。
if (
this.innerMaxDate &&
this.innerMinDate &&
new Date(this.innerMaxDate).getTime() < new Date(this.innerMinDate).getTime()
) {
return error('maxDate不能小于minDate时间')
}
// 滚动区域的高度
let bottomPadding = 0;
if (this.pageInline) {
bottomPadding = 0
} else {
bottomPadding = 30
}
this.listHeight = this.rowHeight * (this.monthSwitch ? 6 : 5) + bottomPadding
this.initTimeOptions()
this.initTimeValues()
this.setMonth()
},
|
ecbfbe06
刘淇
树巡检记录
|
594
|
close() {
|
9441e28e
刘淇
考勤打卡
|
595
|
this.$emit('close')
|
ecbfbe06
刘淇
树巡检记录
|
596
|
},
|
9441e28e
刘淇
考勤打卡
|
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
|
// 点击确定按钮
confirm() {
if (!this.buttonDisabled) {
if (!this.validateSameDayRangeTime()) return
this.$emit('confirm', this.getConfirmValue())
}
},
// 获得两个日期之间的月份数
getMonths(minDate, maxDate) {
const minYear = dayjs(minDate).year()
const minMonth = dayjs(minDate).month() + 1
const maxYear = dayjs(maxDate).year()
const maxMonth = dayjs(maxDate).month() + 1
return (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1
},
// 设置月份数据
setMonth() {
// 最小日期的毫秒数
const minDate = this.innerMinDate || dayjs().valueOf()
// 如果没有指定最大日期,则往后推3个月
const maxDate =
this.innerMaxDate ||
dayjs(minDate)
.add(this.monthNum - 1, 'month')
.valueOf()
// 最大最小月份之间的共有多少个月份,
const months = range(
1,
this.monthNum,
this.getMonths(minDate, maxDate)
)
// 记录上一次测量到的各月份top值,月份未发生变化时高度也不会变,
// 复用后可避免重新生成数据后onScroll没有判断依据
const topCache = {}
this.months.forEach(item => {
if (typeof item.top === 'number') {
topCache[`${item.year}-${item.month}`] = item.top
|
252962bb
刘淇
uni_modu
|
634
|
}
|
9441e28e
刘淇
考勤打卡
|
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
|
})
// 先清空数组
this.months = []
// 最大最小日期只需格式化一次,YYYY-MM-DD的字典序即时间序,可直接比较字符串
const minDateStr = dayjs(minDate).format('YYYY-MM-DD')
const maxDateStr = dayjs(maxDate).format('YYYY-MM-DD')
const formatter = this.formatter || this.innerFormatter
for (let i = 0; i < months; i++) {
// 每个月只构造一个dayjs对象,当月的日期数据都由它派生,
// 避免按天重复解析(monthNum较大时这里是主要开销)
const monthStart = dayjs(minDate).add(i, 'month')
const monthValue = monthStart.month() + 1
this.months.push({
date: new Array(monthStart.daysInMonth())
.fill(1)
.map((item, index) => {
// 日期,取值1-31
let day = index + 1
const current = monthStart.date(day)
// 星期,0-6,0为周日
const week = current.day()
const date = current.format('YYYY-MM-DD')
let bottomInfo = ''
if (this.showLunar) {
// 将日期转为农历格式
const lunar = Calendar.solar2lunar(
current.year(),
monthValue,
day
)
bottomInfo = lunar.IDayCn
}
let config = {
day,
week,
// 小于最小允许的日期,或者大于最大的日期,则设置为disabled状态
disabled:
date < minDateStr || date > maxDateStr,
// 返回一个日期对象,供外部的formatter获取当前日期的年月日等信息,进行加工处理
date: new Date(date),
// 已格式化好的日期字符串,供month组件比较使用,
// 省掉渲染期对每个日期格子的重复解析
dateStr: date,
bottomInfo,
dot: false,
month: monthValue
}
return formatter(config)
}),
// 当前所属的月份
month: monthValue,
// 当前年份
year: monthStart.year()
})
}
this.months.forEach(item => {
const cachedTop = topCache[`${item.year}-${item.month}`]
if (typeof cachedTop === 'number') {
item.top = cachedTop
}
})
if (this.monthSwitch) {
this.monthIndex = this.getDefaultMonthIndex()
}
},
getDefaultMonthIndex() {
let selected = dayjs().format('YYYY-MM')
if (this.defaultDate) {
if (!test.array(this.defaultDate)) {
selected = dayjs(this.defaultDate).format('YYYY-MM')
} else if (this.defaultDate.length) {
selected = dayjs(this.defaultDate[0]).format('YYYY-MM')
}
}
const index = this.months.findIndex(({ year, month }) => {
return `${year}-${padZero(month)}` === selected
})
return index === -1 ? 0 : index
},
prevMonth() {
if (!this.switchPrevDisabled) {
this.monthIndex -= 1
}
},
nextMonth() {
if (!this.switchNextDisabled) {
this.monthIndex += 1
}
},
prevYear() {
if (!this.switchPrevYearDisabled) {
this.monthIndex -= 12
}
},
nextYear() {
if (!this.switchNextYearDisabled) {
this.monthIndex += 12
}
},
jumpToToday() {
if (this.todayDisabled) {
return
}
const targetMonth = dayjs(this.todayDate).format('YYYY-MM')
const selectToday = () => {
if (this.mode === 'range') {
return
}
this.$refs.month && this.$refs.month.selectDate(this.todayDate)
}
if (this.monthSwitch) {
const todayMonthIndex = this.months.findIndex(({ year, month }) => {
return `${year}-${padZero(month)}` === targetMonth
})
if (todayMonthIndex !== -1) {
this.monthIndex = todayMonthIndex
this.$nextTick(selectToday)
|
252962bb
刘淇
uni_modu
|
752
|
}
|
9441e28e
刘淇
考勤打卡
|
753
|
return
|
ecbfbe06
刘淇
树巡检记录
|
754
|
}
|
9441e28e
刘淇
考勤打卡
|
755
756
|
this.scrollIntoDefaultMonth(targetMonth)
this.$nextTick(selectToday)
|
ecbfbe06
刘淇
树巡检记录
|
757
|
},
|
9441e28e
刘淇
考勤打卡
|
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
|
// 滚动到默认设置的月份
scrollIntoDefaultMonth(selected) {
// 查询默认日期在可选列表的下标
const _index = this.months.findIndex(({
year,
month
}) => {
month = padZero(month)
return `${year}-${month}` === selected
})
if (_index !== -1) {
// 滚动位置是程序主动设置的,同步更新副标题指向的月份索引。
// 只依赖scroll事件的话,滚动位置没有真正变化时收不到事件,副标题就会停留在上一次的月份
this.monthIndex = _index
// #ifndef MP-WEIXIN
this.$nextTick(() => {
this.scrollIntoView = ''
this.scrollIntoView = `month-${_index}`
this.scrollIntoViewScroll = this.scrollIntoView
})
// #endif
// #ifdef MP-WEIXIN
// scrollTop与scrollIntoView同理,需要有一个值变动过程才会生效,
// 否则二次打开日历时沿用上次的数值,scroll-view不会滚动到默认月份
const _top = this.months[_index].top || 0;
this.scrollTop = 0;
if (_top !== 0) {
this.$nextTick(() => {
this.scrollTop = _top;
})
}
// #endif
|
252962bb
刘淇
uni_modu
|
790
|
}
|
ecbfbe06
刘淇
树巡检记录
|
791
|
},
|
9441e28e
刘淇
考勤打卡
|
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
|
// scroll-view滚动监听
onScroll(event) {
// 不允许小于0的滚动值,如果scroll-view到顶了,继续下拉,会出现负数值
const scrollTop = Math.max(0, event.detail.scrollTop)
// 月份的top值尚未测量出来时不做判断,否则所有月份的判断阈值相同,
// 会把副标题固定到最后一个月份上
if (!this.months.some(item => typeof item.top === 'number')) return
// 将当前滚动条数值,与各月份的top值比较,可以得出当前滚动到了哪一个月份的索引
let monthIndex = 0
for (let i = 0; i < this.months.length; i++) {
const top = this.months[i].top
if (typeof top !== 'number') continue
if (scrollTop >= top) {
monthIndex = i
} else {
break
}
}
this.monthIndex = monthIndex
this.scrollIntoViewScroll = `month-${monthIndex}`
},
// 更新月份的top值
onUpdateMonthTop(topArr = []) {
if (this.monthSwitch) {
return
}
this.updateMonthTop(topArr)
|
ecbfbe06
刘淇
树巡检记录
|
819
|
},
|
9441e28e
刘淇
考勤打卡
|
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
|
updateMonthTop(topArr = []) {
// 设置对应月份的top值,用于onScroll方法更新月份
topArr.map((item, index) => {
this.months[index].top = item
})
// 获取默认日期的下标
if (!this.defaultDate) {
// 如果没有设置默认日期,则将当天日期设置为默认选中的日期
const selected = dayjs().format("YYYY-MM")
this.scrollIntoDefaultMonth(selected)
return
}
let selected = dayjs().format("YYYY-MM");
// 单选模式,可以是字符串或数组,Date对象等
if (!test.array(this.defaultDate)) {
selected = dayjs(this.defaultDate).format("YYYY-MM")
} else {
selected = dayjs(this.defaultDate[0]).format("YYYY-MM");
|
ecbfbe06
刘淇
树巡检记录
|
839
|
}
|
9441e28e
刘淇
考勤打卡
|
840
|
this.scrollIntoDefaultMonth(selected)
|
252962bb
刘淇
uni_modu
|
841
842
|
}
}
|
ecbfbe06
刘淇
树巡检记录
|
843
|
}
|
252962bb
刘淇
uni_modu
|
844
|
</script>
|
ecbfbe06
刘淇
树巡检记录
|
845
|
|
9441e28e
刘淇
考勤打卡
|
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
|
<style lang="scss" scoped>
.u-calendar {
color: var(--up-main-color, $u-main-color);
background-color: var(--up-card-bg-color, #ffffff);
&__time-panel {
padding: 8px 16px 0;
}
&__time-row {
height: 34px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
&__time-date {
font-size: 13px;
color: #303133;
}
&__time-trigger {
min-width: 92px;
height: 30px;
padding: 0 10px;
border-radius: 15px;
border: 1px solid #dcdfe6;
display: flex;
align-items: center;
justify-content: center;
}
&__time-text {
font-size: 14px;
color: #303133;
}
&__time-picker {
width: 320px;
background-color: #ffffff;
border-radius: 8px;
|
252962bb
刘淇
uni_modu
|
888
|
overflow: hidden;
|
9441e28e
刘淇
考勤打卡
|
889
|
}
|
252962bb
刘淇
uni_modu
|
890
|
|
9441e28e
刘淇
考勤打卡
|
891
892
893
894
895
896
897
898
|
&__time-picker__header {
height: 44px;
padding: 0 14px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #f0f0f0;
}
|
252962bb
刘淇
uni_modu
|
899
|
|
9441e28e
刘淇
考勤打卡
|
900
901
902
903
|
&__time-picker__cancel {
font-size: 14px;
color: #909399;
}
|
252962bb
刘淇
uni_modu
|
904
|
|
9441e28e
刘淇
考勤打卡
|
905
906
907
908
|
&__time-picker__title {
font-size: 15px;
color: #303133;
font-weight: 600;
|
252962bb
刘淇
uni_modu
|
909
|
}
|
ecbfbe06
刘淇
树巡检记录
|
910
|
|
9441e28e
刘淇
考勤打卡
|
911
912
913
|
&__time-picker__confirm {
font-size: 14px;
color: #3c9cff;
|
252962bb
刘淇
uni_modu
|
914
|
}
|
9441e28e
刘淇
考勤打卡
|
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
|
&__time-picker__body {
width: 320px;
height: 200px;
}
&__time-picker__item {
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
color: #303133;
}
&__confirm {
padding: 7px 18px;
}
}
</style>
|