vue1.x所用时间组件封装

275 阅读5分钟

页面中引用举例

<templete>
    <div>
        <time-range @ddd="ddd">

        </time-range>
    </div>
</templete>

<script type="text/javascript">
    import Vue from "vue"
    
    import timeRange from "components/timeRange"
    
    export default {
        data() {
            return {
                
            }
        },
        components: {
            timeRange
        },
        methods: {
            ddd(start, end) {
                if (start && start.length === 19) {
                    this.start = start
                } else {
                    this.start = ''
                }

                if (end && end.length === 19) {
                    this.end = end
                } else {
                    this.end = ''
                }
            },
        }
    }
</script>

包含一个开始时间一个结束时间的组件(timeRange.vue)

<template>
    <div class="inlineClass">
        <input class="input" size="50" type="text" @click.stop="open($event,'picker1')"
               @input="inputChangeFn"
               v-model="calendar.items.picker1.value" placeholder="开始时间" id="start" name="start">
        -
        <input class="input" size="50" type="text" @click.stop="open($event,'picker2')"
               @input="inputChangeFn2"
               v-model="calendar.items.picker2.value" placeholder="结束时间" id="end" name="end">

        <calendar
                :show.sync="calendar.show"
                :type="calendar.type"
                :value.sync="calendar.value"
                :x="calendar.x"
                :y="calendar.y"
                :begin.sync="calendar.begin"
                :end.sync="calendar.end"
                :range.sync="calendar.range"
                :weeks="calendar.weeks"
                :months="calendar.months"
                :sep.sync="calendar.sep">
        </calendar>
    </div>
</template>
<script>
    import Vue from "vue"
    import calendar from "../calendar.vue"
    import moment from "moment"

    export default {
        // 	# Options
        // * :show    是否显示
        // * :type    date|datetime
        // * :value	默认值
        // * :begin  可选开始时间
        // * :end    可选结束时间
        // * :x      显示x坐标
        // * :y      显示y坐标
        // * :range  是否多选
        // * :weeks	星期标题
        // * :months	月份标题
        // * :sep		分隔符

        props: ['start', 'end'],
        components: {
            calendar
        },
        data() {
            return {
                // 数据绑定
                calendar: {
                    show: false,
                    x: 0,
                    y: 0,
                    picker: "",
                    type: "date",
                    value: "",
                    begin: "",
                    end: "",
                    value: "",
                    weeks: [],
                    months: [],
                    sep: "-",
                    range: false,
                    items: {
                        // 单选模式
                        picker1: {
                            type: "date",
                            end: moment().format().split('T')[0],
                            value: "",
                            sep: "-",
                            weeks: ['日', '一', '二', '三', '四', '五', '六'],
                            months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
                        },
                        picker2: {
                            value: this.start,
                            sep: "-",
                            begin: this.start
                        }
                    }
                },


            }
        },
        methods: {
            // 打开显示选择器
            open(e, type) {
                // 设置类型
                this.calendar.picker = type
                this.calendar.type = this.calendar.items[type].type
                this.calendar.range = this.calendar.items[type].range
                this.calendar.begin = this.calendar.items[type].begin
                this.calendar.end = this.calendar.items[type].end
                this.calendar.value = this.calendar.items[type].value
                // 可不用写
                this.calendar.sep = this.calendar.items[type].sep
                this.calendar.weeks = this.calendar.items[type].weeks
                this.calendar.months = this.calendar.items[type].months

                this.calendar.show = true
                this.calendar.x = e.target.offsetLeft
                this.calendar.y = e.target.offsetTop + e.target.offsetHeight + 8
            },

            inputChangeFn(e) {
                const target = e.target
                const value = target.value

                console.log(value)

                if (!value) {
                    this.start = ""

                    this.$emit('ddd', this.start, this.end)
                }
            },


            inputChangeFn2(e) {
                const target = e.target
                const value = target.value

                console.log(value)

                if (!value) {
                    this.end = ""

                    this.$emit('ddd', this.start, this.end)

                }
            },


            // 在对应页面书写此方法,searchStartTime和searchEndTime分别对应相应页面所需要的开始时间和结束时间
            // ddd(start, end) {
            //     if (start && start.length === 19) {
            //         this.searchStartTime = start
            //     } else {
            //         this.searchStartTime = ''
            //     }
            //
            //     if (end && end.length === 19) {
            //         this.searchEndTime = end
            //     } else {
            //         this.searchEndTime = ''
            //     }
            // },

        },

        // 处理值的传递
        watch: {
            'calendar.value': function (value) {
                this.calendar.items[this.calendar.picker].value = value
                this.calendar.picker == 'picker1' ? this.start = value + ' 00:00:00' : this.end = value + ' 23:59:59'

                this.$emit('ddd', this.start, this.end)
            },

        },


    }
</script>
<style scoped>
    .inlineClass {
        display: inline-block;
        margin-right: 10px;
    }
</style>

基础日历组件(calendar.vue)

<style scoped>
.calendar {
    width: 300px;
    padding: 10px;
    background: #fff;
    position: absolute;
    border: 1px solid #DEDEDE;
    border-radius: 2px;
    opacity:.95;
    transition: all .5s ease;
}
.calendar tr:hover{
    background:none;
}
.calendar-enter, .calendar-leave {
    opacity: 0;
    transform: translate3d(0,-10px, 0);
}
.calendar:before {
    position: absolute;
    left:30px;
    top: -10px;
    content: "";
    border:5px solid rgba(0, 0, 0, 0);
    border-bottom-color: #DEDEDE;
}
.calendar:after {
    position: absolute;
    left:30px;
    top: -9px;
    content: "";
    border:5px solid rgba(0, 0, 0, 0);
    border-bottom-color: #fff;
}

.calendar-tools{
    height:32px;
    font-size: 20px;
    line-height: 32px;
    color:#5e7a88;
}
.calendar-tools .float.left{
    float:left;
}
.calendar-tools .float.right{
    float:right;
}
.calendar-tools input{
    font-size: 20px;
    line-height: 32px;
    color: #5e7a88;
    width: 130px !important;
    text-align: center;
    border:none;
    background-color: transparent;
}
.calendar-tools span{
    cursor: pointer;
}
.calendar-prev{
    float:left;
}
.calendar-next{
    float:right;
}
 

.calendar table {
    clear: both;
    width: 100%;
    margin-bottom:10px;
    border-collapse: collapse;
    color: #444444;
}
.calendar td {
    margin:2px !important;
    padding:0px 0;
    width: 14.28571429%;
    height:34px;
    text-align: center;
    vertical-align: middle;
    font-size:14px;
    line-height: 125%;
    cursor: pointer;
}

.calendar td.week{
    pointer-events:none !important;
    cursor: default !important;    
}
.calendar td.disabled {
    color: #c0c0c0;
    pointer-events:none !important;
    cursor: default !important;
}
.calendar td span{
    display:block;
    height:30px;
    line-height:30px;
    margin:2px;
    border-radius:2px;
}
.calendar td span:hover{
    background:#f3f8fa;
}
.calendar td.selected span{
    background-color: #5e7a88;
    color: #fff;
}
.calendar td.selected span:hover{
    background-color: #5e7a88;
    color: #fff;
}


.calendar thead td {
  text-transform: uppercase;
}
.calendar .timer{
    margin:10px 0;
    text-align: center;
}
.calendar .timer input{
    border-radius: 2px;
    padding:5px;
    font-size: 14px;
    line-height: 18px;
    color: #5e7a88;
    width: 50px;
    text-align: center;
    border:1px solid #efefef;
}
.calendar .timer input:focus{
    border:1px solid #5e7a88;
}
.calendar-button{
    text-align: center;
}

.calendar-button span{
    cursor: pointer;
    display: inline-block;
    min-height: 1em;
    min-width: 5em;
    vertical-align: baseline;
    background:#5e7a88;
    color:#fff;
    margin: 0 .25em 0 0;
    padding: .6em 2em;
    font-size: 1em;
    line-height: 1em;
    text-align: center;
    border-radius: .3em;
}
.calendar-button span.cancel{
    background:#efefef;
    color:#666;
}

.calendar .lunar{
     font-size:11px;
     line-height: 150%;
     color:#aaa;   
}
.calendar td.selected .lunar{
     color:#fff;   
}
</style>

<template>
    <div @click.stop=""  class="calendar" v-show="show" :style="{'left':x+'px','top':y+'px'}" transition="calendar" transition-mode="out-in">
        <div  v-if="type!='time'">
            <div class="calendar-tools">
                <span class="calendar-prev" @click="prev">
                    <svg width="16" height="16" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g class="transform-group"><g transform="scale(0.015625, 0.015625)"><path d="M671.968 912c-12.288 0-24.576-4.672-33.952-14.048L286.048 545.984c-18.752-18.72-18.752-49.12 0-67.872l351.968-352c18.752-18.752 49.12-18.752 67.872 0 18.752 18.72 18.752 49.12 0 67.872l-318.016 318.048 318.016 318.016c18.752 18.752 18.752 49.12 0 67.872C696.544 907.328 684.256 912 671.968 912z" fill="#5e7a88"></path></g></g></svg>
                </span>
                <span class="calendar-next"  @click="next">
                    <svg width="16" height="16" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g class="transform-group"><g transform="scale(0.015625, 0.015625)"><path d="M761.056 532.128c0.512-0.992 1.344-1.824 1.792-2.848 8.8-18.304 5.92-40.704-9.664-55.424L399.936 139.744c-19.264-18.208-49.632-17.344-67.872 1.888-18.208 19.264-17.376 49.632 1.888 67.872l316.96 299.84-315.712 304.288c-19.072 18.4-19.648 48.768-1.248 67.872 9.408 9.792 21.984 14.688 34.56 14.688 12 0 24-4.48 33.312-13.44l350.048-337.376c0.672-0.672 0.928-1.6 1.6-2.304 0.512-0.48 1.056-0.832 1.568-1.344C757.76 538.88 759.2 535.392 761.056 532.128z" fill="#5e7a88"></path></g></g></svg>
                </span>
                <div class="text center">
                    <input type="text" v-model="year" value="{{year}}" @change="render(year,month)" min="1970" max="2100" maxlength="4">
                     / 
                    {{monthString}}
                </div>
            </div>
            <table cellpadding="5">
            <thead>
                <tr>
                    <td v-for="week in weeks" class="week">{{week}}</td>
                </tr>
             </thead>
            <tr v-for="(k1,day) in days">
                <td 
                v-for="(k2,child) in day" 
                :class="{'selected':child.selected,'disabled':child.disabled}"
                @click="select(k1,k2,$event)" @touchstart="select(k1,k2,$event)">
                <span>{{child.day}}</span>
                <div class="lunar" v-if="showLunar">{{child.lunar}}</div>
                </td>
            </tr>
            </table>
        </div>
        <div class="calendar-time" v-show="type=='datetime'||type=='time'">
 
            <div class="timer">
                <input type="text" v-model="hour" value="{{hour}}" min="0" max="23" maxlength="2">
                时
                <input type="text" v-model="minute" value="{{minute}}" min="0" max="59" maxlength="2">
                分
                <input type="text" v-model="second" value="{{second}}" min="0" max="59" maxlength="2">
                秒
            </div>
        </div>
        <div class="calendar-button" v-show="type=='datetime'||type=='time'||range">
            <span @click="ok">确定</span>
            <span @click="cancel" class="cancel">取消</span>
        </div>
    </div>
</template>

<script>
export default {
    props: {
        show: {
            type: Boolean,
            twoWay: true,
            default: false
        },
        type: {
            type: String,
            default: "date"
        },
        value: {
            type: String,
            twoWay: true,
            default: ""
        },
        x: {
            type: Number,
            default: 0
        },
        y: {
            type: Number,
            default: 0
        },
        begin: {
            type: String,
            twoWay: true,
            default: ""
        },
        end: {
            type: String,
            default: ""
        },
        range: {
            type: Boolean,
            default: false
        },
        rangeBegin: {
            type: Array,
            default: Array
        },
        rangeEnd: {
            type: Array,
            default: Array
        },
        sep:{
            type: String,
            twoWay: true,
            default: ""
        },
        weeks: {
            type: Array,
            default:function(){
                return ['日', '一', '二', '三', '四', '五', '六']
            }
        },
        months:{
            type: Array,
            default:function(){
                return ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
            }
        }
    },
    data() {
        return {
            year: 0,
            month: 0,
            day: 0,
            hour: 0,
            minute: 0,
            second: 0,
            days: [],
            today: [],
            currentMonth: Number,
            monthString:"",
        }
    },
    created() {
        this.init()
        // 延迟绑定事件,防止关闭
        window.setTimeout(() => {
            document.addEventListener('click', (e) => {
                e.stopPropagation()
                this.cancel()
            }, false)
        }, 500)
    },
    // 测试用
    watch: {
        // year(val, old) {
        //     console.log("new %s old %s time:%s", val, old, +new Date)
        // },
        show(){
            this.init()
        },
        value(){
            this.init()
        }
    },
    methods: {
        // 日期补零
        zero(n) {
            return n < 10 ? '0' + n : n
        },
        // 初始化一些东西
        init(){
            var now = new Date();
            if (this.value != "") {
                if (this.value.indexOf("-") != -1) this.sep = "-"
                if (this.value.indexOf(".") != -1) this.sep = "."
                if (this.value.indexOf("/") != -1) this.sep = "/"
                if (this.type == "date") {
                    var split = this.value.split(this.sep)
                    this.year = parseInt(split[0])
                    this.month = parseInt(split[1]) - 1
                    this.day = parseInt(split[2])
                } else if (this.type == "datetime") {
                    var split = this.value.split(" ")
                    var splitDate = split[0].split(this.sep)
                    this.year = parseInt(splitDate[0])
                    this.month = parseInt(splitDate[1]) - 1
                    this.day = parseInt(splitDate[2])
                    if (split.length > 1) {
                        var splitTime = split[1].split(":")
                        this.hour = splitTime[0]
                        this.minute = splitTime[1]
                        this.second = splitTime[2]
                    }
                }
                if (this.range) {
                    var split = this.value.split(" ~ ")
                    if (split.length > 1) {
                        var beginSplit = split[0].split(this.sep)
                        var endSplit = split[1].split(this.sep)
                        this.rangeBegin = [parseInt(beginSplit[0]), parseInt(beginSplit[1] - 1), parseInt(beginSplit[2])]
                        this.rangeEnd = [parseInt(endSplit[0]), parseInt(endSplit[1] - 1), parseInt(endSplit[2])]
                    }
                }
            } else {
                if(this.sep=="")this.sep = "/"
                this.year = now.getFullYear()
                this.month = now.getMonth()
                this.day = now.getDate()
                this.hour = this.zero(now.getHours())
                this.minute = this.zero(now.getMinutes())
                this.second = this.zero(now.getSeconds())
                if (this.range) {
                    this.rangeBegin = Array
                    this.rangeEnd = Array
                }
            }
            this.monthString=this.months[this.month]
            this.render(this.year, this.month)
        },
        // 渲染日期
        render(y, m) {
            if (!this.range) {
                this.rangeBegin = []
                this.rangeEnd = []
            }
            var firstDayOfMonth = new Date(y, m, 1).getDay()         //当月第一天
            var lastDateOfMonth = new Date(y, m + 1, 0).getDate()    //当月最后一天
            var lastDayOfLastMonth = new Date(y, m, 0).getDate()     //最后一月的最后一天
            this.year = y
            this.currentMonth = this.months[m]
            var seletSplit = this.value.split(" ")[0].split(this.sep)

            var i, line = 0,temp = []
            for (i = 1; i <= lastDateOfMonth; i++) {
                var dow = new Date(y, m, i).getDay()
                // 第一行
                if (dow == 0) {
                    temp[line] = []
                } else if (i == 1) {
                    temp[line] = []
                    var k = lastDayOfLastMonth - firstDayOfMonth + 1
                    for (var j = 0; j < firstDayOfMonth; j++) {
                        temp[line].push({
                            day: k,
                            disabled: true
                        })
                        k++;
                    }
                }
       
                // 如果是日期范围
                if (this.range) {
                    var options = {
                        day: i
                    }
                    if (this.rangeBegin.length > 0) {
                        var beginTime = Number(new Date(this.rangeBegin[0], this.rangeBegin[1], this.rangeBegin[2]))
                        var endTime = Number(new Date(this.rangeEnd[0], this.rangeEnd[1], this.rangeEnd[2]))
                        var thisTime = Number(new Date(this.year, this.month, i))
                        if (beginTime <= thisTime && endTime >= thisTime) {
                            options.selected = true
                        }
                    }
                    temp[line].push(options)
                } else {
                    // 单选模式
                    var chk = new Date()
                    var chkY = chk.getFullYear()
                    var chkM = chk.getMonth()
                    // 匹配上次选中的日期
                    if (
                        parseInt(seletSplit[0]) == this.year &&
                        parseInt(seletSplit[1]) - 1 == this.month &&
                        parseInt(seletSplit[2]) == i) {
                        temp[line].push({
                            day: i,
                            selected: true
                        })
                        this.today = [line, temp[line].length - 1]
                    }

                     // 没有默认值的时候显示选中今天日期
                    else if (chkY == this.year && chkM == this.month && i == this.day && this.value == "") {
                        temp[line].push({
                            day: i,
                            selected: true
                        })
                        this.today = [line, temp[line].length - 1]
                    }else{
                        // 设置可选范围
                        // console.log(this.begin,this.end);
                         var options = {
                            day: i,
                            selected: false,
                        }
                        if (this.begin != "") {

                            var beginSplit = this.begin.split(this.sep)
                            var beginTime = Number(new Date(
                                parseInt(beginSplit[0]),
                                parseInt(beginSplit[1]) - 1,
                                parseInt(beginSplit[2])
                            ))
                            if (beginTime > Number(new Date(this.year, this.month, i))) options.disabled = true
                        }
                        if (this.end != ""){
                            var endSplit = this.end.split(this.sep)
                            var endTime = Number(new Date(
                                parseInt(endSplit[0]),
                                parseInt(endSplit[1]) - 1,
                                parseInt(endSplit[2])
                            ))
                            if (endTime <  Number(new Date(this.year, this.month, i))) options.disabled = true
                        }
                        temp[line].push(options)
                    }
                }

                // 最后一行
                if (dow == 6) {
                    line++
                } else if (i == lastDateOfMonth) {
                    var k = 1
                    for (dow; dow < 6; dow++) {
                        temp[line].push({
                            day: k,
                            disabled: true
                        })
                        k++
                    }
                }
            } //end for

            this.days = temp
        },
        // 上月
        prev(e) {
            e.stopPropagation()
            if (this.month == 0) {
                this.month = 11
                this.year = parseInt(this.year) - 1
            } else {
                this.month = parseInt(this.month) - 1
            }
            this.monthString=this.months[this.month]
            this.render(this.year, this.month)
        },
        //  下月
        next(e) {
            e.stopPropagation()
            if (this.month == 11) {
                this.month = 0
                this.year = parseInt(this.year) + 1
            } else {
                this.month = parseInt(this.month) + 1
            }
            this.monthString=this.months[this.month]
            this.render(this.year, this.month)
        },
        // 选中日期
        select(k1, k2, e) {
            if (e != undefined) e.stopPropagation()
                // 日期范围
            if (this.range) {
                if (this.rangeBegin.length == 0 || this.rangeEndTemp != 0) {
                    this.rangeBegin = [this.year, this.month, this.days[k1][k2].day, this.hour, this.minute, this.second]
                    this.rangeBeginTemp = this.rangeBegin
                    this.rangeEnd = [this.year, this.month, this.days[k1][k2].day, this.hour, this.minute, this.second]
                    this.rangeEndTemp = 0
                } else {
                    this.rangeEnd = [this.year, this.month, this.days[k1][k2].day, this.hour, this.minute, this.second]
                    this.rangeEndTemp = 1
                        // 判断结束日期小于开始日期则自动颠倒过来
                    if (+new Date(this.rangeEnd[0], this.rangeEnd[1], this.rangeEnd[2]) < +new Date(this.rangeBegin[0], this.rangeBegin[1], this.rangeBegin[2])) {
                        this.rangeBegin = this.rangeEnd
                        this.rangeEnd = this.rangeBeginTemp
                    }
                }
                this.render(this.year, this.month)
            } else {
                // 取消上次选中
                if (this.today.length > 0) {
                    if (this.today[0] !== 5) {
                        this.days[this.today[0]][this.today[1]].selected = false
                    }

                }
                // 设置当前选中天
                this.days[k1][k2].selected = true
                this.day = this.days[k1][k2].day
                this.today = [k1, k2]
                if (this.type == 'date') {
                    this.value = this.year + this.sep + this.zero(this.month + 1) + this.sep + this.zero(this.days[k1][k2].day)
                    this.show = false
                }
            }

        },
        // 多选的时候提交
        ok() {
            // 只有有日期的时候才执行
            if(this.type!="time"){
                let isSelected=false
                this.days.forEach(v=>{
                    v.forEach(vv=>{
                        if(vv.selected){
                            isSelected=true
                        }
                    })
                })
                if(!isSelected)return false
            }
           
            if (this.range) {
                this.value = this.output(this.rangeBegin) + " ~ " + this.output(this.rangeEnd)
            } else {
                this.value = this.output([
                    this.year,
                    this.month, 
                    this.day,
                    parseInt(this.hour),
                    parseInt(this.minute),
                    parseInt(this.second)
                ])
            }
            this.show = false
        },
        // 隐藏控件
        cancel() {
            this.show = false
        },
        // 格式化输出
        output(args) {
            if (this.type == 'time') {
                return this.zero(args[3]) + ":" + this.zero(args[4]) + ":" + this.zero(args[5])
            }
            if (this.type == 'datetime') {
                return args[0] + this.sep + this.zero(args[1] + 1) + this.sep + this.zero(args[2]) + " " + this.zero(args[3]) + ":" + this.zero(args[4]) + ":" + this.zero(args[5])
            }
            if (this.type == 'date') {
                return args[0] + this.sep + this.zero(args[1] + 1) + this.sep + this.zero(args[2])
            }
        }
    }
}
</script>