基于vue的时段选择器


基于vue的时段选择器

最近在项目中遇到一个需求,需要在每周的七天中用拖动选择多个时间段用来投放广告,网上没有找到类似的,于是手写了一个vue的时段选择器,见下:

效果

时段选择器

代码:

<template>
<div class="byted-weektime">
    <div class="calendar">
    <!-- <div class="schedule"></div> -->
    <!-- <div class="schedule" style="opacity: 0; display: none; left: 680px; top: 294px; width: 11px; height: 30px;"></div> -->
    <table class="calendar-table" style="width:610px">
        <thead class="calendar-head">
        <tr>
            <th rowspan="6" class="week-td">星期/时间</th>
            <th colspan="24">00:00 - 12:00</th>
            <th colspan="24">12:00 - 24:00</th>
        </tr>
        <tr>
            <td colspan="2" v-for="index in tableHeader" :key="index">{{index}}</td>
        </tr>
        </thead>
        <!-- <tbody @mousemove.prevent.stop="kuangMove" @mouseleave.prevent.stop="kuangLeave" @mousedown.prevent.stop="kuangDown" @mouseup.prevent.stop="kuangUp"> -->
        <!-- 不画框,没bug -->
        <tbody id="tableBody">
        <div
            id="kuang"
            :style="{width:kuangObj.width+'px',height:kuangObj.height+'px',top:kuangObj.top+'px',left:kuangObj.left+'px',bottom:kuangObj.bottom+'px',right:kuangObj.right+'px'}"
        ></div>
        <tr>
            <td>星期一</td>
            <td
            @mousedown.prevent="handleMouseDown(i,0)"
            @mouseup.prevent="handleMouseUp(i,0)"
            class="calendar-atom-time"
            :key="i"
            :class="item.class"
            v-for="(item,i) in rowUnit[0]"
            ></td>
        </tr>
        <tr>
            <td>星期二</td>
            <td
            @mousedown.prevent="handleMouseDown(i,1)"
            @mouseup.prevent="handleMouseUp(i,1)"
            class="calendar-atom-time"
            :key="i"
            :class="item.class"
            v-for="(item,i) in rowUnit[1]"
            ></td>
        </tr>
        <tr>
            <td>星期三</td>
            <td
            @mousedown.prevent="handleMouseDown(i,2)"
            @mouseup.prevent="handleMouseUp(i,2)"
            class="calendar-atom-time"
            :key="i"
            :class="item.class"
            v-for="(item,i) in rowUnit[2]"
            ></td>
        </tr>
        <tr>
            <td>星期四</td>
            <td
            @mousedown.prevent="handleMouseDown(i,3)"
            @mouseup.prevent="handleMouseUp(i,3)"
            class="calendar-atom-time"
            :key="i"
            :class="item.class"
            v-for="(item,i) in rowUnit[3]"
            ></td>
        </tr>
        <tr>
            <td>星期五</td>
            <td
            @mousedown.prevent="handleMouseDown(i,4)"
            @mouseup.prevent="handleMouseUp(i,4)"
            class="calendar-atom-time"
            :key="i"
            :class="item.class"
            v-for="(item,i) in rowUnit[4]"
            ></td>
        </tr>
        <tr>
            <td>星期六</td>
            <td
            @mousedown.prevent="handleMouseDown(i,5)"
            @mouseup.prevent="handleMouseUp(i,5)"
            class="calendar-atom-time"
            :key="i"
            :class="item.class"
            v-for="(item,i) in rowUnit[5]"
            ></td>
        </tr>
        <tr>
            <td>星期日</td>
            <td
            @mousedown.prevent="handleMouseDown(i,6)"
            @mouseup.prevent="handleMouseUp(i,6)"
            class="calendar-atom-time"
            :key="i"
            :class="item.class"
            v-for="(item,i) in rowUnit[6]"
            ></td>
        </tr>

        <tr>
            <td colspan="49" class="td-table-tip">
            <div class="clearfix">
                <span class="pull-left tip-text">请用鼠标点选时间段</span>
                <a @click="clear" class="pull-right">清空</a>
            </div>
            </td>
        </tr>
        <tr>
            <td colspan="49" class="timeContent">
            <div :key="index" v-for="(item,index) in timeStr" v-show="item.length">
                <span>{{weekDate[index+1]}}:</span>
                <strong>
                <span>{{item}}</span>
                </strong>
            </div>
            </td>
        </tr>
        </tbody>
    </table>
    </div>
</div>
</template>

<script>
export default {
name: 'timeSelect',
props: ['value'],
data() {
    return {
    tableHeader: [
        '00',
        '01',
        '02',
        '03',
        '04',
        '05',
        '06',
        '07',
        '08',
        '09',
        '10',
        '11',
        '12',
        '13',
        '14',
        '15',
        '16',
        '17',
        '18',
        '19',
        '20',
        '21',
        '22',
        '23'
    ],
    weekDate: {
        '1': '星期一',
        '2': '星期二',
        '3': '星期三',
        '4': '星期四',
        '5': '星期五',
        '6': '星期六',
        '7': '星期日'
    },
    rowUnit: [
        //每一个单元格
        //[{class:null,timeData:0},{class:null,timeData:1}...]  星期一
    ],
    timeContent: [
        //选中的时间段原始数据
        //{arr:[]},{arr:[]}...
    ],
    timeSection: [
        //时间段,可以返回给后端的数据
        // [
        //     [0,1,2,3],[7,8]    => [0,2],[3.5,4.5]
        // ],
        // [
        //     [4,5,6],[10]       => [2,3.5],[5,5.5]
        // ]
    ],
    timeStr: [
        //时间段,前端显示的数据
        // '00:00~02:00 | 03:30~04:30',
        // '02:00~03:30 | 05:00~05:30',
        // '',
        // '',
        // '',
        // '',
        // ''
    ],
    beginDay: 0,
    beginTime: 0,
    downEvent: false,
    kuangObj: {
        width: 0,
        height: 0,
        top: 0,
        left: 0,
        bottom: 0,
        right: 0,
        oldLeft: 0,
        oldTop: 0,
        flag: false
    }
    }
},
created() {
    this.init()
},
watch: {
    timeContent: {
    handler(cur) {
        let data = cur.map(item => {
        let arr = []
        for (let i = 0; i < 48; i++) {
            arr[i] = {
            class: item.includes(i) ? 'ui-selected' : null,
            timeData: i
            }
        }
        return arr
        })
        let inputData = cur.map(i => {
        return i.join(',')
        })
        this.$emit('input', inputData)
        this.rowUnit = data
    },
    deep: true
    }
},
mounted() {
    //画框程序
    // let oBox = document.getElementById("tableBody")
    // let oDiv = document.getElementById("kuang")
    //  //鼠标按下,获取初始点
    // oBox.onmousedown = function (ev) {
    //       var x1 = ev.clientX - oBox.offsetLeft;
    //       var y1 = ev.clientY - oBox.offsetTop;
    //       oBox.onmousemove = function (ev) {
    //      var x2 = ev.clientX - oBox.offsetLeft;
    //      var y2 = ev.clientY - oBox.offsetTop;
    //      //3.设置div的样式
    //      oDiv.style.left = (x2 > x1 ? x1 : x2) +"px";
    //      oDiv.style.top = (y2 > y1 ? y1 : y2) +"px";
    //      oDiv.style.width = Math.abs(x2-x1)+"px";
    //      oDiv.style.height =Math.abs(y2-y1)+"px";
    //      }
    //   return false;  //解除在划动过程中鼠标样式改变的BUG
    // }
    // //在鼠标抬起后终止onmousemove事件
    //     document.onmouseup = function () {
    //    oBox.onmousemove = null;
    //    oDiv.style.left = 0 +"px";
    //    oDiv.style.top = 0 +"px";
    //    oDiv.style.width = 0+"px";
    //    oDiv.style.height =0+"px";
    //     }
},
methods: {
    getTime() {
    return this.timeContent.map(i => {
        return i.join(',')
    })
    },
    init() {
    if (this.value.length < 1) {
        for (let i = 0; i < 7; i++) {
        this.timeContent.push([])
        }
    } else {
        this.timeContent = this.value.map(i => (i == '' ? [] : i.split(',').map(item => Number(item))))
    }
    for (let i = 0; i < 7; i++) {
        this.timeStr.push('')
    }
    // for (let i = 0; i < 7; i++) {
    //   let arr = []
    //   for (let j = 0; j < 48; j++) {
    //     arr.push({ class: null, timeData: j })
    //   }
    //   // this.rowUnit.push(arr)
    //   this.timeContent=[]
    //   this.timeSection.push([])
    //   this.timeStr.push('')
    // }
    },

    handleMouseDown(i, day) {
    this.downEvent = true //按下时鼠标不在范围内则不算
    this.beginDay = day
    this.beginTime = i
    },
    handleMouseUp(i, day) {
    //当点击事件是在table内才触发选取数据操作
    Array.prototype.remove = function(val) {
        var index = this.indexOf(val)
        if (index > -1) {
        this.splice(index, 1)
        }
    }
    if (this.downEvent) {
        //选时间段
        let _this = this
        let begin = this.beginTime
        let start = begin <= i ? begin : i //x轴 起点
        let length = Math.abs(begin - i)
        let end = start + length //x轴 终点

        let dayStart = this.beginDay <= day ? this.beginDay : day //y轴 起点
        let dayLength = Math.abs(this.beginDay - day)
        let dayEnd = dayStart + dayLength //y轴 终点

        //当框选范围内所有块都是选中状态时,执行反选
        function isAdd() {
        for (let x = dayStart; x < dayEnd + 1; x++) {
            for (let y = start; y < end + 1; y++) {
            if (_this.rowUnit[x][y].class == null) return true
            }
        }
        return false
        }

        if (isAdd()) {
        //没选中的全都选上
        for (let x = dayStart; x < dayEnd + 1; x++) {
            for (let y = start; y < end + 1; y++) {
            if (this.rowUnit[x][y].class == null) {
                // this.rowUnit[x][y].class = 'ui-selected'
                this.timeContent[x].push(this.rowUnit[x][y].timeData)
            }
            }
        }
        } else {
        //反选
        for (let x = dayStart; x < dayEnd + 1; x++) {
            for (let y = start; y < end + 1; y++) {
            // this.rowUnit[x][y].class = null
            this.timeContent[x].remove(this.rowUnit[x][y].timeData)
            }
        }
        }
        //过滤时间段,将临近的时间段合并
        this.filterTime(dayStart, dayEnd)
    }
    this.downEvent = false
    // console.log(JSON.stringify(this.timeContent))
    },
    filterTime(start, end) {
    //选中的x,y坐标信息 x:0-47  y:0-6
    function sortCut(arr) {
        //提取连续的数字
        var result = []
        arr.forEach(function(v, i) {
        var temp = result[result.length - 1]
        if (!i) {
            result.push([v])
        } else if (v % 1 === 0 && v - temp[temp.length - 1] == 1) {
            temp.push(v)
        } else {
            result.push([v])
        }
        })
        return result
    }
    function toStr(num) {
        if (Number.isInteger(num)) {
        let str = num < 10 ? '0' + num : num.toString()
        return str + ':00'
        } else {
        let str = Math.floor(num) < 10 ? '0' + Math.floor(num) : Math.floor(num).toString()
        return str + ':30'
        }
    }
    function timeToStr(arr) {
        //把数组转成方便人看到字符串
        let str = ''
        arr.forEach((arr, index) => {
        let str1 = ''
        if (index == 0) {
            str1 = toStr(arr[0]) + '~' + toStr(arr[1])
        } else {
            str1 = ' , ' + toStr(arr[0]) + '~' + toStr(arr[1])
        }
        str += str1
        })
        return str
    }
    //排序,分割成
    for (let i = start; i < end + 1; i++) {
        let arr1 = sortCut(this.timeContent[i].sort((a, b) => a - b))
        let arr2 = []
        arr1.forEach(arr => {
        //转成带小数点的时间段,以及供前端显示的字符串
        let arr3 = []
        arr3.push(arr[0] / 2)
        arr3.push(arr[arr.length - 1] / 2 + 0.5)
        arr2.push(arr3)
        })
        //console.log(arr2)
        this.timeStr[i] = timeToStr(arr2)
        // this.timeSection[i] = arr2
    }
    // console.log(this.timeSection)
    },
    clear() {
    this.rowUnit.forEach(item => {
        item.forEach(item1 => {
        item1.class = null
        })
    })
    this.timeContent.forEach(item => {
        item.length = 0
    })
    // this.timeSection.forEach(item => {
    //   //赋值成空数组[]出问题
    //   item.length = 0
    // })
    //遍历赋值成'',不管用
    this.timeStr.length = 0
    for (let i = 0; i < 7; i++) {
        this.timeStr.push('')
    }
    //this.initState = true
    },
    //画框操作
    kuangMove() {
    if (!this.kuangObj.flag) return
    if (this.downEvent) {
        let x1 = this.kuangObj.oldLeft
        let y1 = this.kuangObj.oldTop
        let x2 = event.layerX
        let y2 = event.layerY
        this.kuangObj.left = x2 > x1 ? x1 : x2
        this.kuangObj.top = y2 > y1 ? y1 : y2
        this.kuangObj.width = Math.abs(x2 - x1)
        this.kuangObj.height = Math.abs(y2 - y1)
    }
    },
    kuangDown() {
    this.kuangObj.flag = true
    this.kuangObj.oldLeft = event.layerX
    this.kuangObj.oldTop = event.layerY
    },
    kuangUp() {
    this.kuangObj.flag = false
    this.clearDragData()
    },
    kuangLeave() {
    this.kuangObj.flag = false
    this.clearDragData()
    },
    clearDragData() {
    for (let prop in this.kuangObj) {
        this.kuangObj[prop] = 0
    }
    }
}
}
</script>

<style scoped>
.byted-weektime .calendar {
-webkit-user-select: none;
position: relative;
display: inline-block;
}
/*.byted-weektime .calendar .schedule{background:#2F88FF;width:0;height:0;position:fixed;display:none;top:0;left:0;pointer-events:none;-webkit-transition:all 400ms ease;-moz-transition:all 400ms ease;-ms-transition:all 400ms ease;transition:all 400ms ease}*/
.byted-weektime .calendar .calendar-table {
border-collapse: collapse;
border-radius: 4px;
}
.byted-weektime .calendar .calendar-table tr .calendar-atom-time:hover {
background: #ccc;
}
.byted-weektime .calendar .calendar-table tr .ui-selected {
background: #2f88ff;
}
.byted-weektime .calendar .calendar-table tr .ui-selected:hover {
background: #2f88ff;
}
.byted-weektime .calendar .calendar-table tr,
.byted-weektime .calendar .calendar-table td,
.byted-weektime .calendar .calendar-table th {
border: 1px solid #ccc;
font-size: 12px;
text-align: center;
min-width: 11px;
line-height: 1.8em;
-webkit-transition: background 200ms ease;
-moz-transition: background 200ms ease;
-ms-transition: background 200ms ease;
transition: background 200ms ease;
}
.byted-weektime .calendar .calendar-table tbody tr {
height: 30px;
}
.byted-weektime .calendar .calendar-table tbody tr td:first-child {
background: #f8f9fa;
}
.byted-weektime .calendar .calendar-table thead th,
.byted-weektime .calendar .calendar-table thead td {
background: #f8f9fa;
}
.byted-weektime .calendar .calendar-table .td-table-tip {
line-height: 2.4em;
padding: 0 12px 0 19px;
background: #fff !important;
}
.byted-weektime .calendar .calendar-table .td-table-tip .clearfix {
height: 46px;
line-height: 46px;
}
.byted-weektime .calendar .calendar-table .td-table-tip .pull-left {
font-size: 14px;
color: #333333;
}
.byted-weektime .week-td {
width: 75px;
padding: 20px 0;
}
.byted-weektime a {
cursor: pointer;
color: #2f88ff;
font-size: 14px;
}
#kuang {
position: absolute;
background-color: blue;
opacity: 0.3;
}
</style>

文章作者: 雪碧爱可乐
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 雪碧爱可乐 !