<template> <div :class="classes"> <div :class="[prefixCls+ '-list']" ref="hours"> <ul :class="[prefixCls + '-ul']"> <li :class="getCellCls(item)" v-for="item in hoursList" v-show="!item.hide" @click="handleClick('hours', item)">{{ formatTime(item.text) }}</li> </ul> </div> <div :class="[prefixCls+ '-list']" ref="minutes"> <ul :class="[prefixCls + '-ul']"> <li :class="getCellCls(item)" v-for="item in minutesList" v-show="!item.hide" @click="handleClick('minutes', item)">{{ formatTime(item.text) }}</li> </ul> </div> <div :class="[prefixCls+ '-list']" v-show="showSeconds" ref="seconds"> <ul :class="[prefixCls + '-ul']"> <li :class="getCellCls(item)" v-for="item in secondsList" v-show="!item.hide" @click="handleClick('seconds', item)">{{ formatTime(item.text) }}</li> </ul> </div> </div> </template> <script> import Options from '../time-mixins'; import { deepCopy, scrollTop, firstUpperCase } from '../../../utils/assist'; const prefixCls = 'ivu-time-picker-cells'; const timeParts = ['hours', 'minutes', 'seconds']; export default { name: 'TimeSpinner', mixins: [Options], props: { hours: { type: [Number, String], default: NaN }, minutes: { type: [Number, String], default: NaN }, seconds: { type: [Number, String], default: NaN }, showSeconds: { type: Boolean, default: true }, steps: { type: Array, default: () => [] } }, data () { return { spinerSteps: [1, 1, 1].map((one, i) => Math.abs(this.steps[i]) || one), prefixCls: prefixCls, compiled: false, focusedColumn: -1, // which column inside the picker focusedTime: [0, 0, 0] // the values array into [hh, mm, ss] }; }, computed: { classes () { return [ `${prefixCls}`, { [`${prefixCls}-with-seconds`]: this.showSeconds } ]; }, hoursList () { let hours = []; const step = this.spinerSteps[0]; const focusedHour = this.focusedColumn === 0 && this.focusedTime[0]; const hour_tmpl = { text: 0, selected: false, disabled: false, hide: false }; for (let i = 0; i < 24; i += step) { const hour = deepCopy(hour_tmpl); hour.text = i; hour.focused = i === focusedHour; if (this.disabledHours.length && this.disabledHours.indexOf(i) > -1) { hour.disabled = true; if (this.hideDisabledOptions) hour.hide = true; } if (this.hours === i) hour.selected = true; hours.push(hour); } return hours; }, minutesList () { let minutes = []; const step = this.spinerSteps[1]; const focusedMinute = this.focusedColumn === 1 && this.focusedTime[1]; const minute_tmpl = { text: 0, selected: false, disabled: false, hide: false }; for (let i = 0; i < 60; i += step) { const minute = deepCopy(minute_tmpl); minute.text = i; minute.focused = i === focusedMinute; if (this.disabledMinutes.length && this.disabledMinutes.indexOf(i) > -1) { minute.disabled = true; if (this.hideDisabledOptions) minute.hide = true; } if (this.minutes === i) minute.selected = true; minutes.push(minute); } return minutes; }, secondsList () { let seconds = []; const step = this.spinerSteps[2]; const focusedMinute = this.focusedColumn === 2 && this.focusedTime[2]; const second_tmpl = { text: 0, selected: false, disabled: false, hide: false }; for (let i = 0; i < 60; i += step) { const second = deepCopy(second_tmpl); second.text = i; second.focused = i === focusedMinute; if (this.disabledSeconds.length && this.disabledSeconds.indexOf(i) > -1) { second.disabled = true; if (this.hideDisabledOptions) second.hide = true; } if (this.seconds === i) second.selected = true; seconds.push(second); } return seconds; } }, methods: { getCellCls (cell) { return [ `${prefixCls}-cell`, { [`${prefixCls}-cell-selected`]: cell.selected, [`${prefixCls}-cell-focused`]: cell.focused, [`${prefixCls}-cell-disabled`]: cell.disabled } ]; }, chooseValue(values){ const changes = timeParts.reduce((obj, part, i) => { const value = values[i]; if (this[part] === value) return obj; return { ...obj, [part]: value }; }, {}); if (Object.keys(changes).length > 0) { this.emitChange(changes); } }, handleClick (type, cell) { if (cell.disabled) return; const data = {[type]: cell.text}; this.emitChange(data); }, emitChange(changes){ this.$emit('on-change', changes); this.$emit('on-pick-click'); }, scroll (type, index) { const from = this.$refs[type].scrollTop; const to = 24 * this.getScrollIndex(type, index); scrollTop(this.$refs[type], from, to, 500); }, getScrollIndex (type, index) { const Type = firstUpperCase(type); const disabled = this[`disabled${Type}`]; if (disabled.length && this.hideDisabledOptions) { let _count = 0; disabled.forEach(item => item <= index ? _count++ : ''); index -= _count; } return index; }, updateScroll () { this.$nextTick(() => { timeParts.forEach(type => { this.$refs[type].scrollTop = 24 * this[`${type}List`].findIndex(obj => obj.text == this[type]); }); }); }, formatTime (text) { return text < 10 ? '0' + text : text; }, updateFocusedTime(col, time) { this.focusedColumn = col; this.focusedTime = time.slice(); } }, watch: { hours (val) { if (!this.compiled) return; this.scroll('hours', this.hoursList.findIndex(obj => obj.text == val)); }, minutes (val) { if (!this.compiled) return; this.scroll('minutes', this.minutesList.findIndex(obj => obj.text == val)); }, seconds (val) { if (!this.compiled) return; this.scroll('seconds', this.secondsList.findIndex(obj => obj.text == val)); }, focusedTime(updated, old){ timeParts.forEach((part, i) => { if (updated[i] === old[i] || typeof updated[i] === 'undefined') return; const valueIndex = this[`${part}List`].findIndex(obj => obj.text === updated[i]); this.scroll(part, valueIndex); }); } }, mounted () { this.$nextTick(() => this.compiled = true); } }; </script>