vue实现时间选择器,精确到秒

时间组件代码:

/**时间选择器,精确到秒
 * <iic-datetime v-model="time"></iic-datetime>
 * time: new Date()
 */
Vue.component("iic-datetime", {
    props: {
        value: {
            type: [Date, String],
            default: ""
        },
        dateFormat: {//时间到天的格式
            type: String,
            default: "yyyy-MM-dd"
        },
        timeFormat: {//时间从小时到秒的格式
            type: String,
            default: "hh:mm:ss"
        }
    },
    mounted: function() {
        var dateNode = this.$refs.dateTimeRef;
        var timeNode = this.$refs.timeRef;
        var inputTimeNode = this.$refs.inputTimeRef;
        var that = this;
        window.addEventListener("click", function(e){
            if (dateNode.contains(e.target)) {
                if (!timeNode.contains(e.target) && !inputTimeNode.contains(e.target)) {
                    that.timePanelStatus = false;
                }
            } else {
                that.panelState = false;
            }
        });
        if (this.value) {
            this.initDate( new Date(this.value) );
        }
    },
    destroyed: function() {
        window.removeEventListener("click", this.eventListener);
    },
    data: function() {
        return {
            fullTimeValue: "",//显示日期加精确到秒的时间
            dateValue: "", // 输入框显示日期
            timeValue: "",//时间选项
            timePanelStatus: true,//时间选择面板是否展示
            date: new Date().getDate(), // 当前日期
            panelState: false, // 初始值,默认panel关闭
            tmpMonth: new Date().getMonth(), // 临时月份,可修改
            month: new Date().getMonth(),
            tmpYear: new Date().getFullYear(), // 临时年份,可修改
            weekList: [
            { label: "周一", value: 0 },
            { label: "周二", value: 1 },
            { label: "周三", value: 2 },
            { label: "周四", value: 3 },
            { label: "周五", value: 4 },
            { label: "周六", value: 5 },
            { label: "周天", value: 6 }
            ], // 周
            monthList: [
            { label: "一月", value: 0 },
            { label: "二月", value: 1 },
            { label: "三月", value: 2 },
            { label: "四月", value: 3 },
            { label: "五月", value: 4 },
            { label: "六月", value: 5 },
            { label: "七月", value: 6 },
            { label: "八月", value: 7 },
            { label: "九月", value: 8 },
            { label: "十月", value: 9 },
            { label: "十一月", value: 10 },
            { label: "十二月", value: 11 }
            ], // 月
            hourList: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],//小时
            nowValue: 0, // 当前选中日期值
            choseHour: 0,//选中的小时
            choseMine: 1,
            choseSec: 1,
            panelType: "date" // 面板状态
        }
    },
    watch: {
        choseHour: function(){
            this.freshDate();
        },
        choseMine: function(){
            this.freshDate();
        },
        choseSec: function(){
            this.freshDate();
        }
    },
    computed: {
        minuList: function(){
            var minuList = [];
            for (var i = 1; i <= 59; i++){
                minuList.push(i);
            }
            return minuList;
        },
        secList: function(){
            var secList = [];
            for (var i = 1; i <= 59; i++){
                secList.push(i);
            }
            return secList;
        },
        dateList: function(){
            //获取当月的天数
            var currentMonthLength = new Date(this.tmpYear, this.tmpMonth + 1, 0).getDate();
            //先将当月的日期塞入dateList
            var dateList = Array.from(
            { length: currentMonthLength },
            function (val, index) {
                    return {
                        currentMonth: true,
                        value: index + 1
                    };
                }
            );
            // 获取当月1号的星期是为了确定在1号前需要插多少天
            var startDay = new Date(this.tmpYear, this.tmpMonth, 1).getDay();
            // 确认上个月一共多少天
            var previousMongthLength = new Date(this.tmpYear, this.tmpMonth, 0).getDate();
            // 在1号前插入上个月日期
            for (var i = 0, len = startDay; i < len; i++) {
                dateList = [
                    { previousMonth: true, value: previousMongthLength - i }
                ].concat(dateList);
            }
            // 补全剩余位置,至少14天,则 i < 15
            for (var j = 1, item = 1; j < 15; j++, item++) {
                dateList[dateList.length] = { nextMonth: true, value: j };
            }
            return dateList;
        },
        changeTmpMonth: function() {
            return this.monthList[this.tmpMonth].label;
        },
        // 通过改变this.tmpYear则可以改变年份数组
        yearList: function() {
            return Array.from({ length: 12 }, function(value, index){
                return this.tmpYear + index;
            });
        }
    },
    methods: {
        initDate: function(initDate){
            this.choseHour = initDate.getHours();
            this.choseMine = initDate.getMinutes();
            this.choseSec = initDate.getSeconds();
            this.fullTimeValue = this.formatDate(initDate.getTime());
            var dateTimeArr = this.fullTimeValue.split(" ");
            this.dateValue = dateTimeArr[0];
            this.timeValue = dateTimeArr[1];
        
        },
        setDateWithNow: function(){
            this.initDate( new Date() );
        },
        togglePanel: function(){
            this.panelState = !this.panelState;
        },
        hourScroll: function(evt){
            var scrollTop = evt.target.scrollTop;
            this.choseHour = window.parseInt(scrollTop / 20);
        },
        minuScroll: function(evt){
            var scrollTop = evt.target.scrollTop;
            this.choseMine = window.parseInt(scrollTop / 20) + 1;
        },
        secScroll: function(evt){
            var scrollTop = evt.target.scrollTop;
            this.choseSec = window.parseInt(scrollTop / 20) + 1;
        },
        openPanel: function(){
            this.panelState = !this.panelState;
            this.panelType = "date";
        },
        openTime: function(){
            this.timePanelStatus = true;
        },
        selectYear: function(item){
            this.tmpYear = item;
            this.panelType = "month";
        },
        selectMonth: function(item){
            this.tmpMonth = item.value;
            this.panelType = "date";
        },
        freshDate: function(item){
            if (item) {
                // 赋值 当前 nowValue,用于控制样式突出显示当前月份日期
                this.nowValue = item.value;
                // 选择了上个月
                if (item.previousMonth) this.tmpMonth--;
                // 选择了下个月
                if (item.nextMonth) this.tmpMonth++;
            }
             //计算出选中时间对象
            var selectDay = new Date(this.tmpYear, this.tmpMonth, this.nowValue, this.choseHour, this.choseMine, this.choseSec);
            // 格式日期为字符串后,赋值给 input
            this.fullTimeValue = this.formatDate(selectDay.getTime());
            var dateTimeArr = this.fullTimeValue.split(" ");
            this.dateValue = dateTimeArr[0];
            this.timeValue = dateTimeArr[1];
            this.$emit("input", selectDay);
        },
        // 日期格式方法
        formatDate: function(date) {
            fmt = this.dateFormat + " " + this.timeFormat;
            if (date === null || date === "null") {
                return "--";
            }
            date = new Date(Number(date));
            var o = {
                "M+": date.getMonth() + 1, // 月份
                "d+": date.getDate(), // 日
                "h+": date.getHours(), // 小时
                "m+": date.getMinutes(), // 分
                "s+": date.getSeconds(), // 秒
                "q+": Math.floor((date.getMonth() + 3) / 3), // 季度
                S: date.getMilliseconds() // 毫秒
            };
            if (/(y+)/.test(fmt))
                fmt = fmt.replace(
                RegExp.$1,
                (date.getFullYear() + "").substr(4 - RegExp.$1.length)
                );
            for (var k in o) {
                if (new RegExp("(" + k + ")").test(fmt))
                fmt = fmt.replace(
                    RegExp.$1,
                    RegExp.$1.length === 1
                    ? o[k]
                    : ("00" + o[k]).substr(("" + o[k]).length)
                );
            }
            return fmt;
        },
        validateDate: function(item) {
            if (this.nowValue === item.value && item.currentMonth) {
                return true;
            }
        },
        left: function() {
            if (this.panelType === "year") this.tmpYear--;
            else {
                if (this.tmpMonth === 0) {
                this.tmpYear--;
                this.tmpMonth = 11;
                } else this.tmpMonth--;
            }
            },
            leftBig: function() {
                if (this.panelType === "year") {
                    this.tmpYear -= 12;
                } else {
                    this.tmpYear--;
                }
            },
            right: function() {
                if (this.panelType === "year") {
                    this.tmpYear++;
                } else {
                    if (this.tmpMonth === 11) {
                    this.tmpYear++;
                    this.tmpMonth = 0;
                    } else this.tmpMonth++;
                }
            },
            rightBig: function() {
                if (this.panelType === "year") {
                    this.tmpYear += 12;
                } else {
                    this.tmpYear++;
                }
            },
    },
    template:
    `<div ref="dateTimeRef" class="iic-input-date">
        <input class="input date-value" v-model="fullTimeValue" @click="openPanel"/>
        <div class="date-panel" v-show="panelState">
            <div>
                <input class="input input-date" v-model="dateValue"/>
                <input class="input input-time" v-model="timeValue" ref="inputTimeRef" @click="openTime"/>
            </div>
            <div class="topbar">
                <span @click="leftBig">&lt;&lt;</span>
                <span @click="left">&lt;</span>
                <span class="year" @click="panelType = \'year\'">{{tmpYear}}</span>
                <span class="month" @click="panelType = \'month\'">{{changeTmpMonth}}</span>
                <span @click="right">&gt;</span>
                <span @click="rightBig">&gt;&gt;</span>
            </div>
            <div v-show="timePanelStatus" ref="timeRef" class="time-zone">
                <div class="scroll-content">
                    <div @scroll="hourScroll" class="scroll-wrapper">
                        <ul ref="hourScollRef" class="time-scroll">
                            <li v-for="hour in hourList" :class="{'active': choseHour === hour}">{{hour}}</li>
                        </ul>
                    </div>
                </div>
                <div class="scroll-content">
                    <div @scroll="minuScroll" class="scroll-wrapper">
                        <ul ref="minuScrollRef" class="time-scroll">
                            <li v-for="minu in minuList" :class="{'active': choseMine === minu}">{{minu}}</li>
                        </ul>
                    </div>
                </div>
                <div class="scroll-content">
                    <div @scroll="secScroll" class="scroll-wrapper">
                        <ul ref="secScrollRef" class="time-scroll">
                            <li v-for="sec in secList" :class="{'active': choseSec === sec}">{{sec}}</li>
                        </ul>
                    </div>
                </div>
                <div class="bottom">
                    <button class="btn-ok" @click="timePanelStatus = false;">确认</button>
                </div>
            </div>
            <div class="type-year" v-show="panelType === \'year\'">
                <ul class="year-list">
                <li v-for="(item, index) in yearList"
                    :key="index"
                    @click="selectYear(item)"
                >
                    <span :class="{selected: item === tmpYear}" >{{item}}</span>
                </li>
                </ul>
            </div>
            <div class="type-year" v-show="panelType === \'month\'">
                <ul class="year-list">
                <li v-for="(item, index) in monthList"
                    :key="index"
                    @click="selectMonth(item)"
                >
                    <span :class="{selected: item.value === tmpMonth}" >{{item.label}}</span>
                </li>
                </ul>
            </div>
            <div class="date-group" v-show="panelType === \'date\'">
                <span v-for="(item, index) in weekList" :key="index" class="weekday">{{item.label}}</span>
                <ul class="date-list">
                <li v-for="(item, index) in dateList"
                    v-text="item.value"
                    :class="{preMonth: item.previousMonth, nextMonth: item.nextMonth,
                    selected: date === item.value && month === tmpMonth && item.currentMonth, invalid: validateDate(item)}"
                    :key="index"
                    @click="freshDate(item)">
                </li>
                </ul>
            </div>
            <div class="bottom">
                <button @click="setDateWithNow">当前时间</button>
                <button @click="togglePanel">确认</button>
            </div>
        </div>
    </div>`
});

2:在vue实例中使用:

 <div id="date">
            <iic-datetime v-model="time"></iic-datetime>
        </div>
        <script>
            new Vue({
                el: "#date",
                data: {
                    time: new Date()
                },
                watch: {
                    time: function(oldVal, newVal){
                        console.info(oldVal);
                        console.info(newVal);
                    }
                },
            });
        </script>

样式:

<style>
            /* datetime */
.iic-input-date .topbar {padding-top: 8px;}
.iic-input-date .topbar span {display: inline-block;width: 20px;height: 30px;line-height: 30px; color: #515a6e;cursor: pointer;}
.iic-input-date .topbar span:hover {color: #2d8cf0;}
.iic-input-date .topbar .year,
.topbar .month {width: 60px;}
.iic-input-date .year-list {height: 200px;width: 210px;}
.iic-input-date .year-list .selected {background: #2d8cf0;border-radius: 4px;color: #fff;}
.iic-input-date .year-list li {display: inline-block;width: 70px;height: 50px;line-height: 50px;border-radius: 10px;cursor: pointer;}
.iic-input-date .year-list span {display: inline-block;line-height: 16px;padding: 8px;}
.iic-input-date .year-list span:hover { background: #e1f0fe;}
.iic-input-date .weekday {display: inline-block; font-size: 13px;width: 30px; color: #c5c8ce;text-align: center;}
.iic-input-date {width: 260px;text-align: center;font-family: "Avenir", Helvetica, Arial, sans-serif;position: relative;}
.iic-input-date .date-panel {box-shadow: 0 0 8px #ccc;background: #fff;}
.iic-input-date ul {list-style: none;  padding: 0;margin: 0;}
.iic-input-date .date-list { width: 210px;text-align: left;height: 180px; overflow: hidden;margin-top: 4px;}
.iic-input-date .date-list li {  display: inline-block;width: 28px;height: 28px; line-height: 30px; text-align: center;cursor: pointer; color: #000;border: 1px solid #fff; border-radius: 4px;}
.iic-input-date .date-list .selected {border: 1px solid #2d8cf0;}
.iic-input-date .date-list .invalid { background: #2d8cf0; color: #fff;}
.iic-input-date .date-list .preMonth, .iic-input-date .date-list .nextMonth { color: #c5c8ce;}
.iic-input-date .date-list li:hover {background: #e1f0fe;}
.iic-input-date .date-panel .date-group{margin-left: 5px;}
.iic-input-date .input { display: inline-block; box-sizing: border-box; width: 45%; height: 32px;line-height: 1.5;padding: 4px 7px;font-size: 12px;border: 1px solid #dcdee2;border-radius: 4px;color: #515a6e;background-color: #fff;background-image: none;cursor: text;transition: border 0.2s ease-in-out, background 0.2s ease-in-out,box-shadow 0.2s ease-in-out;margin-bottom: 6px;}
.iic-input-date .date-value{width: 100%;}
.iic-input-date .input-date{left: 0;position: absolute; top: 36px;}
.iic-input-date .input-time{right: 0;position: absolute; top: 36px;}
.iic-input-date .time-zone{    top: 73px;
    position: absolute;
    width: 115px;
    height: 130px;
    background-color: white;
    white-space: nowrap;
    border: 1px solid #e4e7ed;
    right: 0;}
.iic-input-date .time-zone::before{content: "";
    top: 50%;
    position: absolute;
    margin-top: -15px;
    height: 32px;
    z-index: -1;
    left: 0;
    right: 0;
    box-sizing: border-box;
    padding-top: 6px;
    text-align: left;
    border-top: 1px solid #e4e7ed;
    border-bottom: 1px solid #e4e7ed;}
.iic-input-date .scroll-content{overflow: hidden;height: 100px;width: 35px;display: inline-block;}
.iic-input-date .scroll-wrapper{overflow-y: scroll;display: inline-block;height: 100%;}
.iic-input-date .time-scroll::before, .iic-input-date .time-scroll::after{content: "";
    display: block;
    width: 100%;
    height: 40px;}
.iic-input-date .bottom{height: 30px;
    border-top: 1px solid #e4e4e4;
    padding: 4px;
    text-align: right;
    box-sizing: border-box;
    position: relative;}
.iic-input-date .bottom > button{border: 1px solid #dcdfe6;
    cursor: pointer;
    background-color: #fff;
    border-radius: 3px;
    font-size: 12px;}
.iic-input-date .time-zone .time-scroll{width: 35px;display: inline-block;overflow: hidden;}
.iic-input-date .time-scroll > li{height: 20px;cursor: pointer;color: #606266;font-size: 12px; line-height: 20px;}
.iic-input-date .time-scroll .active{color: #409eff;}
.iic-input-date .time-scroll >li:hover{background-color: #f5f7fa;}
.fadeDownBig-enter-active,
.fadeDownBig-leave-active,
.fadeInDownBig { -webkit-animation-duration: 0.5s; animation-duration: 0.5s; -webkit-animation-fill-mode: both; animation-fill-mode: both;}
.fadeDownBig-enter-active {-webkit-animation-name: fadeInDownBig;  animation-name: fadeInDownBig;}
.fadeDownBig-leave-active {-webkit-animation-name: fadeOutDownBig; animation-name: fadeOutDownBig;}
@-webkit-keyframes fadeInDownBig {
  from {
    opacity: 0.8;
    -webkit-transform: translate3d(0, -4px, 0);
    transform: translate3d(0, -4px, 0);
  }
  to {
    opacity: 1;
    -webkit-transform: none;
    transform: none;
  }
}
@keyframes fadeInDownBig {
  from {
    opacity: 0.8;
    -webkit-transform: translate3d(0, -4px, 0);
    transform: translate3d(0, -4px, 0);
  }
  to {
    opacity: 1;
    -webkit-transform: none;
    transform: none;
  }
}
@-webkit-keyframes fadeOutDownBig {
  from {
    opacity: 1;
  }
  to {
    opacity: 0.8;
    -webkit-transform: translate3d(0, -4px, 0);
    transform: translate3d(0, -4px, 0);
  }
}
@keyframes fadeOutDownBig {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
</style>

效果图:

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值