vue2 日历上通过鼠标按下滑动实现任意日期多选
工作需求是实现一个在日历按下鼠标后随意移动鼠标实现多选,同时单击单选,网上搜了一圈都是点击两个日期然后这个范围全选,不能实现滑动鼠标多选,随意点击多选
效果图:
Ps:
从别人的代码改动而来的,找了一个个人看起相对接受度高的,改动之后实现了我的功能需求,原作者留下有一个多选方式不适合我的需求,但是相关功能代码我就懒得删除了,留放其中了,道友自取改动使用。子组件全部代码放在下边了,原文链接也在下方。
父组件代码:
<Calendar @date-selecteds = "handleDateSelected" ref="childRef" ></Calendar>
两个事件,一个是把多选的值传给父组件,可以把子组件中的方法写这里,我是调试两下后懒得弄过来了,道友们就自己随心了。
//子组件日期列表返回值
handleDateSelected(selectedDates) {
this.form.listDate = selectedDates;
},
一个是父组件的弹出框取消时清除下子组件中存储的多选值,放到你想调用清除的方法里。
this.$refs.childRef.handleClear();
子组件代码:
<template>
<div class="w-calender-container">
<div class="w-top">
<div class="w-chooseMonth">
<slot name="left"></slot>
</div>
<div class="w-top-date">
{{ year }}年{{ `${month > 9 ? month : '0' + month}` }}月
</div>
<div class="w-btn-wrap">
<span @click="handleShowLastMonth">上一月</span>
<span @click="handleShowToday">今天</span>
<span @click="handleShowNextMonth">下一月</span>
<span v-if="isShowClear" @click="handleClear">清除</span>
</div>
</div>
<div class="w-date_wrap" >
<ul class="w-week" >
<li style="margin-left: 0px">日</li>
<li style="margin-left: -3px">一</li>
<li style="margin-left: -3px">二</li>
<li style="margin-left: -3px">三</li>
<li style="margin-left: -3px">四</li>
<li style="margin-left: -3px">五</li>
<li style="margin-left: -3px">六</li>
</ul>
<ul class="w-day" >
<li v-for="(item,index) in days "
:class="{'c-isCurMonth':item.isNextMonth||item.isLastMonth,'c-isCurToday':item.isCurToday,'c-isActive':item.isActive,'c-is-previous-date':item.isPreviousDate,'user-select-none':true}"
:key="index"
@mousedown="startSelection(item)"
@mouseover="handleMouseover(item)"
@mouseup = "endSelection(item)">
<span style="user-select: none;pointer-events: none">
{{ item.day }}
</span>
<span v-if="item.isShowPoint" class="w-point" style="user-select: none;pointer-events: none"></span>
</li>
</ul>
</div>
</div>
</template>
<script>
import Da from 'element-ui/src/locale/lang/da'
export default {
name: 'calender',
props: {
//是否是多选
isMultipleChoice: {
type: Boolean,
default: false,
},
//是否显示清除按钮
isShowClear: {
type: Boolean,
default: true
},
//选择的月份
Month: {
type: String,
},
//需要加圆点的数据
//数据格式为map { new Date(2022-02-11).getTime():true}: 用年月日的时间戳为key
pointMap:{
type:Object,
default:()=>{
return {}
}
},
//是否禁止选择以前的日期 默认为空表示没有限制 注:如果只有数字则 默认时间是当天时间
//{1:['2022-02-11']}表示2022-02-11以前的日期不能选择;1:表示小于该时间的禁止选择,必填
//{2:['2022-02-11']}表示2022-02-11以后的日期不能选择;2:表示大于该时间的禁止选择,必填
//{3:['2022-02-11','2022-03-11']}表示2022-02-11到2022-03-11之间的日期可以选择;3:表示两个时间内的时间可以选择,必填
//{4:['2022-02-11','2022-03-11']}表示2022-02-11到2022-03-11之外的日期可以选择;4:表示两个时间之外的可以选择,必填
restrictDate: {
type: Object,
default: () => {
return null
}
}
},
watch: {
Month: {
handler(val) {
if (val) {
const date = val.split('-').map(Number);
this.year = date[0];
this.month = date[1]
this.days = [];
//选择月份后重新跟更新时间
this.dealDate();
}
},
immediate: true
},
pointMap: {
handler(val) {
if (val) {
this.dealDate();
}
},
immediate: true
}
},
data() {
return {
year: '',//年
month: '',//月
days: [],//日期
//是否已经选择了开始时间
isChooseOne: false,
endTime: null,
startTime: null,
monthValue: '',//选择的月份
chooseDateList: {},//如果是单选 则把选择的保存
isStart : false,
}
},
methods: {
startSelection(item){
//console.log("开始")
this.isStart = true
this.handleMouseover(item);
},
handleMouseover(item){
if (this.isStart){
//console.log(item.date)
this.handleChooseDay(item);
}
},
endSelection(item){
//console.log("结束")
this.isStart = false
const map = {...this.chooseDateList}
const dateList = Object.keys(map)
//const dateList = Object.values(map)
const datesArray = []
for (let i = 0; i < dateList.length; i++) {
datesArray[i] = new Date( parseInt(dateList[i], 10))
//datesArray[i] = dateList[i].date
}
console.log(datesArray)
this.$emit('date-selecteds', datesArray)
},
getCurDay(){
var datetime = new Date();
var year = datetime.getFullYear();
var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
return `${year}-${month}-${date}`
},
//得到当前年这个月分有多少天
getDays(Y, M) {
return new Date(Y, M, 0).getDate();
},
//得到当前年,这个月的一号是周几
getWeek(Y, M) {
let now = new Date()
now.setFullYear(this.year)
now.setMonth(this.month - 1)
now.setDate(1);
return now.getDay();
},
/**
* 获取本月日期
*/
pushDays() {
//将这个月多少天加入数组days
const m = `${this.month > 9 ? this.month : '0' + this.month}`;
for (let i = 1; i <= this.getDays(this.year, this.month); i++) {
const d = `${i > 9 ? i : '0' + i}`,
date = `${this.year}-${m}-${d}`;
this.days.push({
day: d,
isActive: false,
month: m,
year: `${this.year}`,
date,
timestamp: new Date(date).getTime(),//转换时间戳
})
}
//获取上个月的日期
this.getLastMonthDays()
//获取下个月的日期
this.getNextMonthDays()
},
/**
* 获取下个月的日期
*/
getNextMonthDays() {
const m = this.month < 12 ? this.month + 1 : 1,
y = this.month < 12 ? this.year : this.year + 1,
len = 42 - this.getDays(this.year, this.month) - this.getWeek(this.year, this.month),
_m = `${m > 9 ? m : '0' + m}`;
//将下个月要显示的天数加入days
for (let i = 1; i <= len; i++) {
const _d = `${i > 9 ? i : '0' + i}`,
date = `${y}-${_m}-${_d}`
this.days.push({
day: _d,
month: _m,
year: `${y}`,
isActive: false,
isNextMonth: true,
date,
timestamp: new Date(date).getTime()
})
}
},
/**
* 获取上个月的日期
*/
getLastMonthDays() {
const m = this.month > 1 ? this.month - 1 : this.year > 1970 ? 12 : 1,
y = this.month > 1 ? this.year : this.year > 1970 ? this.year - 1 : 1970,
len = this.getWeek(this.year, this.month),
lastMonthDays = this.getDays(this.year, this.month - 1)
//将上个月要显示的天数加入days
for (let i = 0; i < len; i++) {
const _m = `${m > 9 ? m : '0' + m}`,
date = `${y}-${_m}-${lastMonthDays - i}`
this.days.unshift({
day: `${lastMonthDays - i}`,
month: _m,
year: `${y}`,
isActive: false,
isLastMonth: true,
date,
timestamp: new Date(date).getTime(),
})
}
},
/**
* 上个月
*/
handleShowLastMonth() {
if (this.month > 1) {
this.month = this.month - 1;
} else if (this.year > 1970) {
this.month = 12;
this.year = this.year - 1;
}
this.dealDate();
},
/**
* 下个月
*/
handleShowNextMonth() {
this.days = [];
if (this.month < 12) {
this.month = this.month + 1;
} else {
this.month = this.month = 1;
this.year = this.year + 1;
}
this.dealDate();
},
/**
* 当天
*/
handleShowToday() {
let now = new Date();
this.year = now.getFullYear();
this.month = now.getMonth() + 1;
this.dealDate()
},
/**
* 处理时间
*/
dealDate() {
this.days = [];
const curDate = this.getCurDay()
this.pushDays();
// 判断 是否需要禁止选择某些时间段的时间
if (this.restrictDate) {
const keys = Object.keys(this.restrictDate);
let day, timestamp;
switch (keys[0]) {
case '1':
day = this.restrictDate[keys[0]] && this.restrictDate[keys[0]] || curDate;
timestamp = new Date(day).getTime();//转换时间戳
this.days.forEach(item => {
item.isCurToday = item.date === curDate
item.isPreviousDate = item.timestamp < timestamp
})
break;
case '2':
day = this.restrictDate[keys[0]] && this.restrictDate[keys[0]] || curDate;
timestamp = new Date(day).getTime();//转换时间戳
this.days.forEach(item => {
item.isCurToday = item.date === curDate
item.isPreviousDate = item.timestamp > timestamp
})
break;
case '3':
const s_d = this.restrictDate[keys[0]] && this.restrictDate[keys[0]][1] || curDate
const e_d = this.restrictDate[keys[0]] && this.restrictDate[keys[0]][2] || curDate
let s = new Date(s_d).getTime(),
e = new Date(e_d).getTime();
if (s > e) {
[s, e] = [e, s]
}
this.days.forEach(item => {
item.isCurToday = item.date === curDate
item.isPreviousDate = item.timestamp > s && item.timestamp < e
})
break
case '4':
const st_d = this.restrictDate[keys[0]] && this.restrictDate[keys[0]][1] || curDate
const en_d = this.restrictDate[keys[0]] && this.restrictDate[keys[0]][2] || curDate
let st = new Date(st_d).getTime(),
en = new Date(en_d).getTime();
if (st > en) {
[st, en] = [en, st]
}
this.days.forEach(item => {
item.isCurToday = item.date === curDate
item.isPreviousDate = item.timestamp < st || item.timestamp > en
})
break;
default:
this.days.forEach(item => {
item.isCurToday = item.date === curDate
})
}
} else {
this.days.forEach(item => {
item.isCurToday = item.date === curDate
})
}
this.getActiveDay()
},
/**
* 清空选择
*/
handleClear() {
this.isChooseOne = false;
this.startTime = null;
this.endTime = null;
this.chooseDateList = {};
this.days.forEach(item => {
item.isActive = false
})
},
/**
* 选择时间
* @param time
*/
handleChooseDay(time = {}) {
// 判断 是否是禁止选择的日期 是否是
if (this.restrictDate && time.isPreviousDate) {
return
}
//是否是多选
if (this.isMultipleChoice) {
this.chooseDateList = {}
//选择开始时间-结束时间
if (this.isChooseOne) {
this.endTime = time;
const {timestamp} = this.startTime || {};
//如果 选择的开始日期大于结束日期 则调换开始日期与结束日期
if (timestamp > time.timestamp) {
[this.startTime, this.endTime] = [this.endTime, this.startTime]
}
this.getActiveDay();
this.isChooseOne = false
this.$emit('chooseDays', this.chooseDateList)
} else {
this.isChooseOne = true
this.startTime = time;
//给选择的时间范围选中
this.days.forEach(item => {
item.isActive = item.timestamp === this.startTime.timestamp;
})
}
} else {
this.days.forEach(item => {
if (item.timestamp === time.timestamp) {
item.isActive = !time.isActive;
if (time.isActive) {
const {date, day, month, timestamp, year} = item
this.chooseDateList[time.timestamp] = {
date,
day,
month,
timestamp,
year
}
} else {
delete this.chooseDateList[time.timestamp]
}
}
})
this.$emit('chooseDays', this.chooseDateList)
}
},
/**
* 给选择的日期范围加上选中状态
*/
getActiveDay() {
if (this.isMultipleChoice) {
if (!this.startTime || !this.endTime) {
return
}
//给选择的时间范围选中
const {timestamp} = this.startTime || {};
const {timestamp: endTimestamp} = this.endTime || {};
this.days.forEach(item => {
item.isActive = item.timestamp >= timestamp && item.timestamp <= endTimestamp && !item.isPreviousDate;
//是否显示点
item.isShowPoint=this.pointMap[item.timestamp]
if (item.timestamp >= timestamp && item.timestamp <= endTimestamp && !item.isPreviousDate) {
const {date, day, month, timestamp, year} = item
this.chooseDateList[item.timestamp] = {
date,
day,
month,
timestamp,
year
}
} else {
delete this.chooseDateList[item.timestamp];
}
})
} else {
this.days.forEach(item => {
//是否显示点
item.isShowPoint=this.pointMap[item.timestamp]
//已选择的数据加上状态
item.isActive=!!this.chooseDateList[item.timestamp]
})
}
}
},
mounted() {
this.handleShowToday()
}
}
</script>
<style>
ul {
list-style: none;
}
.w-calender-container {
width: 90%;
min-width: 400px;
border: 1px solid #ddd;
padding: 5px;
box-sizing: border-box;
}
.w-top {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.1% 0;
border-bottom: 1px solid #ddd;
box-sizing: border-box;
}
.w-top .w-top-date {
white-space: nowrap;
font-size: 18px;
font-weight: bold;
}
.w-top .w-btn-wrap {
min-width: 200px;
display: flex;
justify-content: space-around;
color: #409EFF;
}
.w-btn-wrap span {
flex: 1;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
}
.w-btn-wrap span:last-child {
color: #FF6200;
}
.w-date_wrap {
width: 100%;
height: auto;
}
.w-date_wrap .w-week {
width: 100%;
display: flex;
flex-direction: row;
padding: 5px 5px 5px 5px;
font-size: 16px;
box-sizing: border-box;
}
.w-date_wrap .w-week li {
width: 14.28%;
height: 10px;
padding: 0 0 0 30px ;
}
.w-date_wrap .w-day {
width: 100%;
display: flex;
flex-direction: row;
padding: 0 20px;
font-size: 16px;
flex-wrap: wrap;
box-sizing: border-box;
}
.w-day li {
position: relative;
cursor: pointer;
width: 14.28%;
padding: 4%;
border: 1px solid #ddd;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-sizing: border-box;
}
.w-day li .w-point {
position: absolute;
bottom: 26%;
background: #ff0000;
display: flex;
width: 4px;
height: 4px;
border-radius: 50%;
}
.w-day li:nth-child(n+8) {
border-top: none;
}
.w-day li:nth-child(n+1) {
border-right: none;
}
.w-day li:nth-child(7n) {
border-right: 1px solid #ddd
}
.date-tip {
height: 40px;
width: 100%;
display: flex;
}
.tip_row {
width: 100px;
height: 100%;
display: flex;
align-items: center;
}
.tip_row:first-child {
padding-left: 20px;
box-sizing: border-box;
}
.tip_row .square {
width: 20px;
height: 20px;
display: flex;
background: #fff;
border: 1px solid #c0c4cc;
}
.tip_row .chosen {
background: #409EFF;
border: 0;
}
.tip_row .title {
display: flex;
padding-left: 6px;
box-sizing: border-box;
white-space: nowrap;
}
.c-isCurMonth {
background: #fff;
color: #c0c4cc;
}
.c-isCurToday {
background: #fff;
color: #409EFF;
}
.c-isActive {
background: #409EFF;
color: #f2f8fe;
}
.chooseMonth {
width: 120px;
}
.isCurYearDay {
cursor: not-allowed !important;
opacity: 0.6;
}
.c-isCurMonth.c-isActive {
background: rgba(64, 158, 255, 0.56);
}
.c-is-previous-date {
background: rgba(192, 196, 204, 0.2);
color: #c0c4cc;
cursor: not-allowed !important;
}
.user-select-none {
-webkit-user-select: none; /* Chrome, Safari, Opera */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently supported by all major browsers */
}
</style>
链接: 原文章地址