|
| 1 | +<template lang="pug"> |
| 2 | +.c-timepicker__timerange(@click="openPanel") |
| 3 | + .c-timerange__wrapper |
| 4 | + c-input( |
| 5 | + v-model="startTime" |
| 6 | + @change="startInputChange" |
| 7 | + ) |
| 8 | + span 至 |
| 9 | + c-input( |
| 10 | + v-model="endTime" |
| 11 | + @change="endInputChange" |
| 12 | + ) |
| 13 | + .c-timerange__panel(:class="{show: isOpen}") |
| 14 | + .c-timerange__container |
| 15 | + .c-timepicker__wrap |
| 16 | + p 开始时间 |
| 17 | + c-timepanel( |
| 18 | + :isShown="isOpen" |
| 19 | + :hour="startHour" |
| 20 | + :minute="startMinute" |
| 21 | + :second="startSecond" |
| 22 | + :format="format" |
| 23 | + :secondStep="secondStep" |
| 24 | + :minuteStep="minuteStep" |
| 25 | + :hourStep="hourStep" |
| 26 | + :maxTime="endTime" |
| 27 | + @change="startTimeChange" |
| 28 | + ) |
| 29 | + .c-timepicker__wrap |
| 30 | + p 结束时间 |
| 31 | + c-timepanel( |
| 32 | + :isShown="isOpen" |
| 33 | + :hour="endHour" |
| 34 | + :minute="endMinute" |
| 35 | + :second="endSecond" |
| 36 | + :format="format" |
| 37 | + :minTime="startTime" |
| 38 | + :secondStep="secondStep" |
| 39 | + :minuteStep="minuteStep" |
| 40 | + :hourStep="hourStep" |
| 41 | + @change="endTimeChange" |
| 42 | + ) |
| 43 | + .c-timerange__btns |
| 44 | + c-button( |
| 45 | + @click="confirmRange" |
| 46 | + size="sm" |
| 47 | + outline |
| 48 | + primary |
| 49 | + ) 确定 |
| 50 | + c-button( |
| 51 | + size="sm" |
| 52 | + @click="cancel" |
| 53 | + outline |
| 54 | + ) 取消 |
| 55 | +</template> |
| 56 | +<script> |
| 57 | +import './index.css' |
| 58 | +import { getScrollBarSize } from '@util' |
| 59 | +import ZIndexManager from '../../scripts/utils/zIndexManager.js' |
| 60 | +export default { |
| 61 | + name: 'c-timerange', |
| 62 | + props: { |
| 63 | + value: Array, |
| 64 | + format: String, |
| 65 | + hourStep: { |
| 66 | + type: Number, |
| 67 | + default: 1 |
| 68 | + }, |
| 69 | + minuteStep: { |
| 70 | + type: Number, |
| 71 | + default: 1 |
| 72 | + }, |
| 73 | + secondStep: { |
| 74 | + type: Number, |
| 75 | + default: 1 |
| 76 | + } |
| 77 | + }, |
| 78 | + data () { |
| 79 | + return { |
| 80 | + isOpen: false, |
| 81 | + startHour: '', |
| 82 | + startMinute: '', |
| 83 | + startSecond: '', |
| 84 | + endHour: '', |
| 85 | + endMinute: '', |
| 86 | + endSecond: '', |
| 87 | + timerange: '', |
| 88 | + startTime: '', |
| 89 | + endTime: '' |
| 90 | + } |
| 91 | + }, |
| 92 | + mounted () { |
| 93 | + if (typeof document === 'object') { |
| 94 | + this.timerange = this.$el.querySelector('.c-timerange__panel') |
| 95 | + document.body.appendChild(this.timerange) |
| 96 | + this.resize() |
| 97 | + window.addEventListener('resize', this.resize, false) |
| 98 | + } |
| 99 | + this.initTime() |
| 100 | + }, |
| 101 | + watch: { |
| 102 | + value (newVal) { |
| 103 | + this.initTime() |
| 104 | + }, |
| 105 | + isOpen () { |
| 106 | + if (this.isOpen) { |
| 107 | + this.resize() |
| 108 | + window.addEventListener('mouseup', this.onBodyClick, true) |
| 109 | + } else { |
| 110 | + window.removeEventListener('mouseup', this.onBodyClick, true) |
| 111 | + } |
| 112 | + } |
| 113 | + }, |
| 114 | + methods: { |
| 115 | + initTime () { |
| 116 | + if (this.value.length === 2) { |
| 117 | + this.startTime = this.value[0] |
| 118 | + this.endTime = this.value[1] |
| 119 | + } |
| 120 | + if (this.startTime) { |
| 121 | + [this.startHour, this.startMinute, this.startSecond] = this.startTime.split(':') |
| 122 | + } |
| 123 | + if (this.endTime) { |
| 124 | + [this.endHour, this.endMinute, this.endSecond] = this.endTime.split(':') |
| 125 | + } |
| 126 | + }, |
| 127 | + confirmRange () { |
| 128 | + console.log('confirm') |
| 129 | + // 验证数据合法性 |
| 130 | + if (this.checkValue(this.startTime) && this.checkValue(this.endTime)) { |
| 131 | + this.emitEvent() |
| 132 | + this.close() |
| 133 | + } else { |
| 134 | + this.cancel() |
| 135 | + } |
| 136 | + }, |
| 137 | + checkValue (value) { |
| 138 | + if (!value) return false |
| 139 | + const [hour, minute, second] = value |
| 140 | + return !(second > 59 || minute > 59 || hour > 23) |
| 141 | + }, |
| 142 | + cancel () { |
| 143 | + this.initTime() |
| 144 | + this.emitEvent() |
| 145 | + this.close() |
| 146 | + }, |
| 147 | + openPanel () { |
| 148 | + this.isOpen = true |
| 149 | + }, |
| 150 | + close () { |
| 151 | + this.isOpen = false |
| 152 | + }, |
| 153 | + onBodyClick (e) { |
| 154 | + const isInPicker = this.$el.contains(e.target) |
| 155 | + const isInPanel = this.timerange.contains(e.target) |
| 156 | + if (!isInPicker && !isInPanel) { |
| 157 | + this.confirmRange() |
| 158 | + this.$el.focus() |
| 159 | + } |
| 160 | + }, |
| 161 | + getStyle () { |
| 162 | + const clientRect = this.$parent.$refs.timepicker.getBoundingClientRect() |
| 163 | + const windowH = window.innerHeight |
| 164 | + const windowW = window.innerWidth |
| 165 | + const marginTop = 2 |
| 166 | + const scrollHeight = document.body.scrollWidth > window.innerWidth ? 20 : 0 |
| 167 | + const droplistHeight = this.timerange.clientHeight |
| 168 | + const droplistWidth = this.timerange.clientWidth |
| 169 | + const defaultTop = clientRect.top + clientRect.height + marginTop + window.pageYOffset |
| 170 | + const clientHeight = clientRect.height + marginTop |
| 171 | +
|
| 172 | + const clientY = clientRect.y |
| 173 | + const compTop = windowH - droplistHeight - scrollHeight |
| 174 | + const marginRight = getScrollBarSize() + 5 // scrollbar width |
| 175 | + const left = droplistWidth + clientRect.left + marginRight + window.pageXOffset > windowW ? windowW - droplistWidth - marginRight : clientRect.left + window.pageXOffset |
| 176 | + const top = droplistHeight + clientHeight + clientY + scrollHeight > windowH ? compTop : defaultTop |
| 177 | + const zIndex = ZIndexManager.next() |
| 178 | + return ` |
| 179 | + position: absolute; |
| 180 | + top: ${top}px; |
| 181 | + left: ${left}px; |
| 182 | + z-index: ${zIndex}; |
| 183 | + ` |
| 184 | + }, |
| 185 | + resize () { |
| 186 | + this.$nextTick(() => { |
| 187 | + this.timerange.style.cssText = this.getStyle() |
| 188 | + }) |
| 189 | + }, |
| 190 | + generateValue (hour, minute, second) { |
| 191 | + const result = [] |
| 192 | + hour && result.push(hour) |
| 193 | + minute && result.push(minute) |
| 194 | + second && result.push(second) |
| 195 | + return result.join(':') |
| 196 | + }, |
| 197 | + startInputChange (value) { |
| 198 | + const regValid = /^\d{2}:\d{2}:\d{2}$/.test(value) |
| 199 | + if (regValid && this.checkValue(value)) { |
| 200 | + [this.startHour, this.startMinute, this.startSecond] = value.split(':') |
| 201 | + } |
| 202 | + }, |
| 203 | + endInputChange (value) { |
| 204 | + const regValid = /^\d{2}:\d{2}:\d{2}$/.test(value) |
| 205 | + if (regValid && this.checkValue(value)) { |
| 206 | + [this.endHour, this.endMinute, this.endSecond] = value.split(':') |
| 207 | + } |
| 208 | + }, |
| 209 | + startTimeChange ({ hour, minute, second }) { |
| 210 | + this.startHour = hour |
| 211 | + this.startMinute = minute |
| 212 | + this.startSecond = second |
| 213 | + this.startTime = this.generateValue(hour, minute, second) |
| 214 | + }, |
| 215 | + endTimeChange ({ hour, minute, second }) { |
| 216 | + this.endHour = hour |
| 217 | + this.endMinute = minute |
| 218 | + this.endSecond = second |
| 219 | + this.endTime = this.generateValue(hour, minute, second) |
| 220 | + }, |
| 221 | + emitEvent () { |
| 222 | + // this.$emit('input', [this.startTime, this.endTime]) |
| 223 | + this.$emit('change', [this.startTime, this.endTime]) |
| 224 | + } |
| 225 | + } |
| 226 | +} |
| 227 | +</script> |
0 commit comments