效果:
QQ录屏20220718094326
index.vue
<template>
<plan-time :planData="planData" @changePlanData="changePlanData" />
</template>
<script>
import PlanTime from "@/components/PlanTime";
const pd = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
];
export default {
components: { PlanTime },
data() {
return {
planData: pd,
};
},
methods: {
changePlanData(id, fv) {
this.planData = this.planData.map((v, index) => {
return index === id ? fv : v;
});
},
},
};
</script>
PlanTime.vue
<template>
<div class="plans-div" title="" style="user-select: none">
<span style="color: #8e8b8b; font-style: italic; line-height: 24px">
录像时段 蓝色表示保留 灰色表示删除 单击选择或取消
<!-- 录像时段 蓝色表示保留 灰色表示删除 单击选择或取消 单击不放滑动可快速选择 单击标题可反选行或列-->
</span>
<div class="plans" @mousedown="handleMouseDown">
<div class="plan-row-header">
<div class="plan-day plan-header" style="cursor: pointer">
<i title="点击还原" class="fa fa-refresh"></i>
</div>
<div class="plan-hours">
<!-- <div
v-for="(item, i) in time"
:key="i"
title="单击反选该列"
class="plan-hour plan-header"
@click="timeClick(item)"
>
{{ i }}时
</div> -->
<div v-for="(item, i) in time" :key="i" class="plan-hour">
<div class="plan-title">{{ i }}时</div>
</div>
</div>
</div>
<div v-for="(item, wi) in week" class="plan-row" :key="wi">
<!-- <div title="单击反选该行" class="plan-day" @click="dayClick(item.id)">
{{ item.name }}
</div> -->
<div class="plan-day">{{ item.name }}</div>
<div class="plan-hours">
<div v-for="(v, ti) in time" class="plan-hour" :key="ti">
<div
class="plan"
:id="'hour-' + getHourId(wi, ti)"
:class="
isCheck(getHourId(wi, ti))
? 'plan-hour-half'
: 'plan-hour-half-no'
"
:title="setHourTitle(item, ti, isCheck(getHourId(wi, ti)))"
@click="hourClick(getHourId(wi, ti))"
/>
<div
class="plan"
:id="'hour-' + getHourId(wi, ti, true)"
:class="
isCheck(getHourId(wi, ti, true))
? 'plan-hour-half'
: 'plan-hour-half-no'
"
:title="setHourTitle(item, ti, isCheck(getHourId(wi, ti, true)))"
@click="hourClick(getHourId(wi, ti, true))"
/>
</div>
</div>
</div>
<div
class="mask"
v-show="positionList.is_show_mask"
:style="
'width:' +
mask_width +
'left:' +
mask_left +
'height:' +
mask_height +
'top:' +
mask_top
"
></div>
</div>
</div>
</template>
<script>
const pd = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
];
export default {
name: "PlanTime",
props: {
planData: {
type: Array,
default: () => {
return pd;
},
},
},
data() {
return {
week: [
{ id: 1, name: "星期一" },
{ id: 2, name: "星期二" },
{ id: 3, name: "星期三" },
{ id: 4, name: "星期四" },
{ id: 5, name: "星期五" },
{ id: 6, name: "星期六" },
{ id: 7, name: "星期日" },
],
time: 24,
clickX: null,
clickY: null,
positionList: {
is_show_mask: false,
box_screen_left: 0,
box_screen_top: 0,
start_x: 0,
start_y: 0,
end_x: 0,
end_y: 0,
},
plans: [],
plansDom: null,
flag: true, //判断是执行点击事件还是mousedown事件
firstTime: 0,
lastTime: 0,
};
},
computed: {
mask_width() {
return `${Math.abs(
this.positionList.end_x - this.positionList.start_x
)}px;`;
// return '100px'
},
mask_height() {
return `${Math.abs(
this.positionList.end_y - this.positionList.start_y
)}px;`;
},
mask_left() {
return `${
Math.min(this.positionList.start_x, this.positionList.end_x) -
this.positionList.box_screen_left
}px;`;
},
mask_top() {
return `${
Math.min(this.positionList.start_y, this.positionList.end_y) -
this.positionList.box_screen_top
}px;`;
},
},
mounted() {
this.plansDom = document.getElementsByClassName("plans")[0];
this.positionList.box_screen_left = this.plansDom.getClientRects()[0].left;
this.positionList.box_screen_top = this.plansDom.getClientRects()[0].top;
this.plans = document.getElementsByClassName("plan");
this.plans = Array.from(this.plans);
},
methods: {
getHourId(wi, ti, f) {
let id = wi * 48 + ti * 2;
if (f) {
id = wi * 48 + ti * 2 + 1;
}
return id;
},
setHourTitle(item, ti, isCheck) {
let title =
"蓝色表示保留(" +
item.name +
"(" +
this.format(ti * 60) +
"-" +
this.format(ti * 60 + 30) +
")录像";
if (!isCheck) {
title =
"灰色色表示删除(" +
item.name +
"(" +
this.format(ti * 60 + 30) +
"-" +
this.format(ti * 60 + 60) +
")录像";
}
return title;
},
isCheck(id) {
return this.planData[id];
},
// timeClick(v) {
// console.log(v);
// },
// dayClick(v) {
// console.log(v);
// },
hourClick(id) {
if (!this.flag) {
this.$emit("changePlanData", id, this.isCheck(id) ? 0 : 1);
}
this.flag = true;
},
format(seconds) {
let min =
Math.floor(seconds / 60) >= 10
? Math.floor(seconds / 60)
: "0" + Math.floor(seconds / 60);
seconds -= 60 * min;
let sec = seconds >= 10 ? seconds : "0" + seconds;
return min + ":" + sec;
},
handleMouseDown(event) {
this.firstTime = new Date().getTime;
this.lastTime = new Date().getTime;
if (this.lastTime - this.firstTime < 1000) {
this.flag = false;
}
this.positionList.is_show_mask = true;
this.positionList.start_x = event.clientX;
this.positionList.start_y = event.clientY;
this.positionList.end_x = event.clientX;
this.positionList.end_y = event.clientY;
this.positionList.box_screen_left =
this.plansDom.getClientRects()[0].left;
this.positionList.box_screen_top = this.plansDom.getClientRects()[0].top;
document.body.addEventListener("mousemove", this.handleMouseMove); //监听鼠标移动事件
document.body.addEventListener("mouseup", this.handleMouseUp); //监听鼠标抬起事件
},
handleMouseMove(event) {
this.positionList.end_x = event.clientX;
this.positionList.end_y = event.clientY;
},
handleMouseUp() {
document.body.removeEventListener("mousemove", this.handleMouseMove);
document.body.removeEventListener("mouseup", this.handleMouseUp);
this.positionList.is_show_mask = false;
this.handleDomSelect();
this.resSetXY();
},
handleDomSelect() {
const dom_mask = document.querySelector(".mask");
//getClientRects()每一个盒子的边界矩形的矩形集合
const rect_select = dom_mask.getClientRects()[0];
this.plans.forEach((node, index) => {
const rects = node.getClientRects()[0];
if (this.collide(rects, rect_select) === true) {
this.$emit("changePlanData", index, this.isCheck(index) ? 0 : 1);
}
});
console.log(this.plans);
},
//比较checkbox盒子边界和遮罩层边界最大最小值
collide(rect1, rect2) {
const maxX = Math.max(rect1.x + rect1.width, rect2.x + rect2.width);
const maxY = Math.max(rect1.y + rect1.height, rect2.y + rect2.height);
const minX = Math.min(rect1.x, rect2.x);
const minY = Math.min(rect1.y, rect2.y);
return (
maxX - minX <= rect1.width + rect2.width &&
maxY - minY <= rect1.height + rect2.height
);
},
//清除
resSetXY() {
this.positionList.start_x = 0;
this.positionList.start_y = 0;
this.positionList.end_x = 0;
this.positionList.end_y = 0;
},
},
};
</script>
<style lang="scss">
.plans-div {
.plans {
font-size: 12px;
text-align: center;
border: 0;
width: 100%;
min-width: 869px;
margin: auto;
position: relative;
.mask {
position: absolute;
background: blue;
opacity: 0.2;
z-index: 100;
left: 0;
top: 0;
}
}
.plan-header {
font-weight: 700;
height: 24px !important;
color: #eee;
line-height: 24px !important;
}
.plan-header:hover {
background-color: #1d5d94 !important;
}
.plan-row-header {
height: 24px;
width: 100%;
}
.plan-row {
height: 60px;
width: 100%;
border-top: 1px;
border-style: solid none none;
border-color: transparent;
}
.plan-day {
width: 4%;
height: 100%;
float: left;
background-color: #515a6e;
color: #eee;
line-height: 60px;
}
.plan-day:hover {
background-color: #226dae;
}
.plan-hours {
width: 92%;
height: 100%;
float: left;
}
.plan-hour {
height: 100%;
width: 4%;
float: left;
font-size: 12px;
border-style: none none none solid;
border-color: transparent;
border-left-width: 1px;
}
.plan-title {
font-weight: 700;
height: 24px !important;
color: #eee;
line-height: 24px !important;
background-color: #515a6e;
&:hover {
background-color: #1d5d94;
}
}
.plan-hour-half {
width: 100%;
height: 50%;
background-color: #009fe7;
font-size: 10px;
line-height: 30px;
cursor: pointer;
}
.plan-hour-half-no {
width: 100%;
height: 50%;
background-color: #ddd;
font-size: 10px;
line-height: 30px;
cursor: pointer;
}
.plan-hour-half-no:hover {
background-color: #f7f7f7;
}
.plan-hour-half:hover {
background-color: #23d4ff;
}
}
</style>