实现如下可以自定义时间选项的时间组件。
<CustomTimePicker
v-model="movie.playTime"
:defaultTime="defaultTime"
:prevTime="prevTime"
:duration="duration"
@cacul="getTime(s_index, m_index, movie)"
></CustomTimePicker>
<template>
<!-- 这是个很不成熟的组件,一次性组件,用完即抛 -->
<!-- 这是个很不成熟的组件,一次性组件,用完即抛 -->
<!-- 这是个很不成熟的组件,一次性组件,用完即抛 -->
<div class="custom-timer-picker-container">
<div @click.stop="showTimeFun()" style="position: relative;">
<el-input
placeholder="请选择时间"
:value="value"
@mouseenter.native="handleMouseEnter()"
@mouseleave.native="handleMouseLeave()"
>
<i slot="prefix" class="el-input__icon el-icon-time"></i>
<i slot="suffix"
class="el-input__icon"
@click.stop="handleClickIcon()"
:class="[showClose ? '' + 'el-icon-circle-close' : '']">
</i>
</el-input>
</div>
<div class="custom-timer-picker" v-clickoutside="handleClose" :style="{ visibility: pickerVisible ? 'visible' : 'hidden' }">
<transition name="el-zoom-in-top" @after-leave="$emit('dodestroy')">
<div class="el-time-panel">
<div class="el-time-panel__content">
<div class="time-box">
<!-- 时 -->
<el-scrollbar
@mousemove.native="adjustCurrentSpinner('hours')"
class="el-time-spinner__wrapper"
wrap-style="max-height: inherit;"
view-class="el-time-spinner__list"
noresize
tag="ul"
ref="hours">
<li
@click.stop="handleClick('hours', { value: item.hour, index: index, disabled: item.disabled })"
v-for="(item, index) in hourList"
:key="item.hour"
class="el-time-spinner__item"
:class="{ 'active': item.hour === hours, 'disabled': item.disabled }"
>
{{ item.hour }}
</li>
</el-scrollbar>
<!-- 分 -->
<el-scrollbar
@mousemove.native="adjustCurrentSpinner('minutes')"
class="el-time-spinner__wrapper"
wrap-style="max-height: inherit;"
view-class="el-time-spinner__list"
noresize
tag="ul"
ref="minutes">
<li
@click.stop="handleClick('minutes', { value: item.minute, index: index, disabled: item.disabled })"
v-for="(item, index) in minuteList"
:key="item.minute"
class="el-time-spinner__item"
:class="{ 'active': item.minute === minutes, 'disabled': item.disabled }"
>
{{ item.minute }}
</li>
</el-scrollbar>
<!-- 秒 -->
<el-scrollbar
@mousemove.native="adjustCurrentSpinner('seconds')"
class="el-time-spinner__wrapper"
wrap-style="max-height: inherit;"
view-class="el-time-spinner__list"
noresize
tag="ul"
ref="seconds">
<li
@click.stop="handleClick('seconds', { value: item.second, index: index, disabled: item.disabled })"
v-for="(item, index) in secondList"
:key="item.second"
class="el-time-spinner__item"
:class="{ 'active': item.second === seconds, 'disabled': item.disabled }"
>
{{ item.second }}
</li>
</el-scrollbar>
</div>
</div>
<div class="el-time-panel__footer">
<button
type="button"
class="el-time-panel__btn cancel"
@click.stop="handleCancel">取消</button>
<button
type="button"
class="el-time-panel__btn confirm"
@click.stop="handleConfirm()">确定</button>
</div>
</div>
</transition>
</div>
</div>
</template>
<script>
import Clickoutside from 'element-ui/src/utils/clickoutside';
export default {
props: {
// defaultTime用于设置当前时间控件的默认时间,点击取消按钮或者选择了 disabled 的时间的场景使用
defaultTime: {
type: String,
default () {
return ''
}
},
// prevTime表示前一个设置的最大播放时间,配合 duration 用于计算当前时间控件可选的最早时间
prevTime: {
type: String,
default () {
return ''
}
},
// duration表示前一个设置最大播放时间的影片的播放时长,配合 prevTime 用于计算当前时间控件可选的最早时间
duration: {
type: Number,
default () {
return 0
}
},
// 当前时间控件的时间
value: null
},
directives: {
Clickoutside
},
data() {
return {
pickerVisible: false,
showClose: false,
hours: '',
minutes: '',
seconds: '',
limitTime: '06:00:00',
hourList: [
{
hour: '06',
disabled: false
},
{
hour: '07',
disabled: false
},
{
hour: '08',
disabled: false
},
{
hour: '09',
disabled: false
},
{
hour: '10',
disabled: false
},
{
hour: '11',
disabled: false
},
{
hour: '12',
disabled: false
},
{
hour: '13',
disabled: false
},
{
hour: '14',
disabled: false
},
{
hour: '15',
disabled: false
},
{
hour: '16',
disabled: false
},
{
hour: '17',
disabled: false
},
{
hour: '18',
disabled: false
},
{
hour: '19',
disabled: false
},
{
hour: '20',
disabled: false
},
{
hour: '21',
disabled: false
},
{
hour: '22',
disabled: false
},
{
hour: '23',
disabled: false
}
],
minuteList: [
{
minute: '00',
disabled: false
},
{
minute: '15',
disabled: false
},
{
minute: '30',
disabled: false
},
{
minute: '45',
disabled: false
},
{
minute: '59',
disabled: false
}
],
secondList: [
{
second: '00',
disabled: false
},
{
second: '59',
disabled: false
}
]
}
},
watch: {
'pickerVisible'(val) {
if(val) {
this.init_hours_minutes_seconds()
this.$nextTick(()=>{ //这是必要的,不然秒级的59选项选不上
this.adjustSpinners()
this.initHourDisableItem()
this.initMinuteDisableItem()
})
}
},
'hours'(newVal) {
if(newVal == '23') {
if(!this.minuteList.some(item => item.minute == '59')) {
this.minuteList.push({minute: '59', disabled: false})
}
} else {
if(this.minuteList.some(item => item.minute == '59')) {
this.minuteList.pop()
if(this.minutes == '59') {
this.minutes = this.minuteList[this.minuteList.length - 1].minute
this.adjustSpinners()
}
}
}
this.initMinuteDisableItem()
// 在可选范围区域,则emit
if(this.checkStatus()) {
let time = `${this.hours}:${this.minutes}:${this.seconds}`
this.pickVal(time, true)
}
},
'minutes'(newVal) {
if(newVal == '59') {
if(!this.secondList.some(item => item.second == '59')) {
this.secondList.push({second: '59', disabled: false})
}
} else {
if(this.secondList.some(item => item.second == '59')) {
this.secondList.pop()
if(this.seconds == '59') {
this.seconds = this.secondList[this.secondList.length - 1].second
this.adjustSpinners()
}
}
}
this.initHourDisableItem()
// 在可选范围区域,则emit
if(this.checkStatus()) {
let time = `${this.hours}:${this.minutes}:${this.seconds}`
this.pickVal(time, true)
}
},
'seconds'() {
// 在可选范围区域,则emit
if(this.checkStatus()) {
let time = `${this.hours}:${this.minutes}:${this.seconds}`
this.pickVal(time, true)
}
}
},
mounted() {
this.$nextTick(() => {
this.bindScrollEvent();
});
},
methods: {
showTimeFun() {
// 调用 cacul 方法先获取 defaultTime、prevTime、duration
this.$emit('cacul')
setTimeout(() => {
this.pickerVisible = true
})
},
handleClose () {
this.pickerVisible = false
},
handleMouseEnter() {
if(this.value) {
this.showClose = true
}
},
handleMouseLeave() {
this.showClose = false
},
handleClickIcon() {
this.pickVal('', true)
},
generateTimeList(delta) {
let f = n => `${n}`.padStart(2, '0')
let result = [], date = new Date()
date.setHours(6)
date.setMinutes(delta)
let day = date.getDate()
while (date.getDate() === day) {
let h = date.getHours()
let m = date.getMinutes()
result.push(`${f(h)}:${f(m)}:00`)
date.setMinutes(m + delta)
}
result = ['06:00:00', ...result, '23:59:59']
return result
},
getLimitTimeToSeconds() {
let sum = 0
sum += this.$moment.duration(this.prevTime).as('seconds'); // 上一部电影播放时间转化成秒
sum += this.duration // 加上上一部电影的播放时长
if(this.prevTime || this.duration) {
sum += 8 * 60 // 先加上8分钟(这是最早能选择的时间)
}
if(this.prevTime) {
sum -= 6 * 60 * 60 // 因为从6点开始选择时间,所以减去6个小时的秒数
}
return sum
},
getLimitTime() {
let timeToseconds = this.getLimitTimeToSeconds()
let time = ''
let timeArr = this.generateTimeList(15)
for(let i = 0; i < timeArr.length; i++) {
let totalTime = this.$moment.duration(timeArr[i]).as('seconds') - 6 * 60 * 60 // 需要减去6个小时
if(totalTime > timeToseconds) { // 找到第一个能选择的时间
time = timeArr[i]
break
}
}
return time
},
init_hours_minutes_seconds() {
// 如果当前时间空间选择了时间的话,并且为可选时间,那么初始化为已选择的时间
// 如果没有选择时间,那么使用上一个时间控件选择的时间加上时长转化过来的时分秒加上8分钟
// 如果上个控件没有时间,也没有时长,那么使用 06:00:00
let isBeyond = false
if(this.prevTime) {
isBeyond = this.$moment.duration(this.prevTime).as('seconds') + this.duration + 8*60 > this.$moment.duration(this.defaultTime).as('seconds')
} else if(this.duration) {
isBeyond = this.duration + 8*60 + 6*60*60 > this.$moment.duration(this.defaultTime).as('seconds')
}
if (isBeyond) {
let time = this.getLimitTime()
if(time) {
this.limitTime = time // 这是最早能选择的可选项时间
let arr = time.split(':')
this.hours = arr[0]
this.minutes = arr[1]
this.seconds = arr[2]
} else {
// 找不到时间了 证明已经排满时间了 则全部置灰
this.hours = '23'
this.minutes = '59'
this.seconds = '59'
}
return false
}
if (this.defaultTime) {
let arr = this.defaultTime.split(':')
this.hours = arr[0]
this.minutes = arr[1]
this.seconds = arr[2]
return false
}
if (this.value) {
let arr = this.value.split(':')
this.hours = arr[0]
this.minutes = arr[1]
this.seconds = arr[2]
return false
}
this.hours = '06'
this.minutes = '00'
this.seconds = '00'
},
bindScrollEvent() {
const bindFuntion = (type) => {
this.$refs[type].wrap.onscroll = (e) => {
this.handleScroll(type, e);
};
};
bindFuntion('hours');
bindFuntion('minutes');
bindFuntion('seconds');
},
handleScroll(type) {
let a = this.$refs[type].wrap.scrollTop
let b = this.scrollBarHeight(type) * 0.5 - 10
let c = this.typeItemHeight(type)
let d = type === 'hours' ? this.hourList.length - 1 : type === 'minutes' ? this.minuteList.length - 1 : this.secondList.length - 1
const index = Math.min(Math.round((a - b / c + 3) / c), d);
this.modifyDateField(type, index);
},
modifyDateField(type, index, value) {
switch (type) {
case 'hours':
this.hours = this.hourList[index].hour;
break
case 'minutes':
this.minutes = this.minuteList[index].minute;
break
case 'seconds':
this.seconds = this.secondList[index].second;
break
}
},
checkStatus() {
let hourArr = this.hourList.filter(item => item.hour == this.hours)
let minuteArr = this.minuteList.filter(item => item.minute == this.minutes)
let secondArr = this.secondList.filter(item => item.second == this.seconds)
return !hourArr[0].disabled && !minuteArr[0].disabled && !secondArr[0].disabled
},
scrollBarHeight(type) {
return this.$refs[type].$el.offsetHeight;
},
typeItemHeight(type) {
return this.$refs[type].$el.querySelector('li').offsetHeight;
},
getLiIndex(type, value) {
let index = 0
if(type == 'hours') index = this.hourList.findIndex(item => item.hour == value)
if(type == 'minutes') index = this.minuteList.findIndex(item => item.minute == value)
if(type == 'seconds') index = this.secondList.findIndex(item => item.second == value)
return index
},
handleClick(type, {value, index, disabled}) {
if (!disabled) {
this.modifyDateField(type, index, value);
this.adjustSpinner(type, value);
}
},
adjustCurrentSpinner(type) {
this.adjustSpinner(type, this[type]);
},
adjustSpinners() {
this.adjustSpinner('hours', this.hours);
this.adjustSpinner('minutes', this.minutes);
this.adjustSpinner('seconds', this.seconds);
},
adjustSpinner(type, value) {
const el = this.$refs[type].wrap;
if (el) {
let index = this.getLiIndex(type, value)
el.scrollTop = Math.max(0, index * this.typeItemHeight(type));
}
},
handleConfirm() {
if(this.checkStatus()) {
let time = `${this.hours}:${this.minutes}:${this.seconds}`
this.pickVal(time, false)
} else {
this.pickVal(this.defaultTime, false)
}
},
handleCancel() {
this.pickVal(this.defaultTime, false)
},
pickVal(time, visible) {
if(!visible) {
this.pickerVisible = false
}
this.$emit('input', time)
},
initHourDisableItem() {
let currentTime = ''
for(let i = 0; i < this.hourList.length; i++) {
for(let j = 0; j < this.minuteList.length; j++) {
if(this.minuteList[j].minute == this.minutes) {
for(let k = 0; k < this.secondList.length; k++) {
currentTime = `${this.hourList[i].hour}:${this.minuteList[j].minute}:${this.secondList[k].second}`
let the_currentTime_seconds = this.$moment.duration(currentTime).as('seconds') - 6 * 60 * 60 // 需要减去6个小时;
let the_earliest_limitTime_secondes = this.getLimitTimeToSeconds()
if(the_currentTime_seconds < the_earliest_limitTime_secondes) {
this.hourList[i].disabled = true
this.secondList[k].disabled = true
} else {
this.hourList[i].disabled = false
this.secondList[k].disabled = false
}
if(the_currentTime_seconds >= 24 * 60 * 60) {
this.hourList[i].disabled = true
this.secondList[k].disabled = true
}
}
break
}
}
}
},
initMinuteDisableItem() {
let currentTime = ''
for(let i = 0; i < this.hourList.length; i++) {
if(this.hourList[i].hour == this.hours) {
for(let j = 0; j < this.minuteList.length; j++) {
for(let k = 0; k < this.secondList.length; k++) {
currentTime = `${this.hourList[i].hour}:${this.minuteList[j].minute}:${this.secondList[k].second}`
let the_currentTime_seconds = this.$moment.duration(currentTime).as('seconds') - 6 * 60 * 60 // 需要减去6个小时;
let the_earliest_limitTime_secondes = this.getLimitTimeToSeconds()
if(the_currentTime_seconds < the_earliest_limitTime_secondes) {
this.minuteList[j].disabled = true
this.secondList[k].disabled = true
} else {
this.minuteList[j].disabled = false
this.secondList[k].disabled = false
}
if(the_currentTime_seconds >= 24 * 60 * 60) {
this.minuteList[j].disabled = true
this.secondList[k].disabled = true
}
}
}
break
}
}
}
}
}
</script>
<style lang="stylus" scoped>
.el-time-spinner__wrapper {
width: 33.3%;
}
</style>