演示地址:
点击右下角布防时间查看效果
组件
<template>
<div class="c-weektime">
<div class="c-schedue"></div>
<div
:class="{ 'c-schedue': true, 'c-schedue-notransi': mode }"
:style="styleValue"
></div>
<table class="c-weektime-table" :class="{ 'c-min-table': colspan < 2 }">
<thead class="c-weektime-head">
</thead>
<tbody class="c-weektime-body">
<div style="width: 100%;margin-bottom: 10px" v-for="t in data" :key="t.row">
<tr class="tableinfo1">
<td></td>
<td v-for="t in theadArr" :key="t">{{ t }}</td>
</tr>
<tr class="tableinfo1">
<td></td>
<td v-for="t in theadArr" :key="t">
<div class="linex"></div>
</td>
</tr>
<tr class="tableinfo2">
<td class="xingqi">{{ t.value }}</td>
<td
v-for="n in t.child"
:key="`${n.row}-${n.col}`"
:data-week="n.row"
:data-time="n.col"
@mouseenter="cellEnter(n)"
@mousedown="cellDown(n)"
@mouseup="cellUp(n)"
:class="selectClasses(n)"
class="weektime-atom-item"
></td>
</tr>
</div>
<tr>
<td colspan="49" class="c-weektime-preview">
<div class="g-clearfix c-weektime-con">
<span class="g-pull-left">{{
selectState ? '已选择时间段' : '可拖动鼠标选择时间段'
}}</span>
<a class="g-pull-right" @click.prevent="$emit('on-clear')"
>清空选择</a
>
</div>
<div v-if="selectState" class="c-weektime-time">
<div v-for="t in selectValue" :key="t.id">
<p v-if="t.value">
<span class="g-tip-text">{{ t.week }}:</span>
<span>{{ t.value }}</span>
</p>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
const createArr = (len) => {
return Array.from(Array(len)).map((ret, id) => id)
}
export default {
name: 'DragWeektime',
props: {
value: {
type: Array,
},
data: {
type: Array,
},
colspan: {
type: Number,
default () {
return 2
},
},
},
computed: {
styleValue () {
return {
width: `${this.width}px`,
height: `${this.height}px`,
left: `${this.left}px`,
top: `${this.top}px`,
}
},
selectValue () {
return this.value
},
selectState () {
return this.value.some((ret) => ret.value)
},
selectClasses () {
return (n) => (n.check ? 'ui-selected' : '')
},
},
methods: {
cellEnter (item) {
const ele = document.querySelector(
`td[data-week='${item.row}'][data-time='${item.col}']`
)
if (ele && !this.mode) {
this.left = ele.offsetLeft
this.top = ele.offsetTop
} else {
if (item.col <= this.col && item.row <= this.row) {
this.width = (this.col - item.col + 1) * ele.offsetWidth
this.height = (this.row - item.row + 1) * ele.offsetHeight
this.left = ele.offsetLeft
this.top = ele.offsetTop
} else if (item.col >= this.col && item.row >= this.row) {
this.width = (item.col - this.col + 1) * ele.offsetWidth
this.height = (item.row - this.row + 1) * ele.offsetHeight
if (item.col > this.col && item.row === this.row) {
this.top = ele.offsetTop
}
if (item.col === this.col && item.row > this.row) {
this.left = ele.offsetLeft
}
} else if (item.col > this.col && item.row < this.row) {
this.width = (item.col - this.col + 1) * ele.offsetWidth
this.height = (this.row - item.row + 1) * ele.offsetHeight
this.top = ele.offsetTop
} else if (item.col < this.col && item.row > this.row) {
this.width = (this.col - item.col + 1) * ele.offsetWidth
this.height = (item.row - this.row + 1) * ele.offsetHeight
this.left = ele.offsetLeft
}
}
},
cellDown (item) {
const ele = document.querySelector(
`td[data-week='${item.row}'][data-time='${item.col}']`
)
this.check = Boolean(item.check)
this.mode = 1
if (ele) {
this.width = ele.offsetWidth
this.height = ele.offsetHeight
}
this.row = item.row
this.col = item.col
},
cellUp (item) {
if (item.col <= this.col && item.row <= this.row) {
this.selectWeek(
[item.row, this.row],
[item.col, this.col],
!this.check
)
} else if (item.col >= this.col && item.row >= this.row) {
this.selectWeek(
[this.row, item.row],
[this.col, item.col],
!this.check
)
} else if (item.col > this.col && item.row < this.row) {
this.selectWeek(
[item.row, this.row],
[this.col, item.col],
!this.check
)
} else if (item.col < this.col && item.row > this.row) {
this.selectWeek(
[this.row, item.row],
[item.col, this.col],
!this.check
)
}
this.width = 0
this.height = 0
this.mode = 0
},
selectWeek (row, col, check) {
const [minRow, maxRow] = row
const [minCol, maxCol] = col
this.data.forEach((item) => {
item.child.forEach((t) => {
if (
t.row >= minRow &&
t.row <= maxRow &&
t.col >= minCol &&
t.col <= maxCol
) {
this.$set(t, 'check', check)
}
})
})
},
},
data () {
return {
width: 0,
height: 0,
left: 0,
top: 0,
mode: 0,
row: 0,
col: 0,
theadArr: [],
}
},
created () {
this.theadArr = createArr(24)
},
}
</script>
<style lang="scss" scoped>
.c-weektime {
width: 100%;
position: relative;
display: inline-block;
}
.c-schedue {
background: #598fe6;
position: absolute;
width: 0;
height: 0;
opacity: 0.6;
pointer-events: none;
}
.c-schedue-notransi {
transition: width 0.12s ease, height 0.12s ease, top 0.12s ease,
left 0.12s ease;
}
.c-weektime-table {
border-collapse: collapse;
width: 100%;
th {
vertical-align: inherit;
font-weight: bold;
}
//tr {
// height: 30px;
//}
tr,
td,
th {
user-select: none;
border: 0;
text-align: center;
min-width: 12px;
line-height: 1.8em;
transition: background 0.2s ease;
}
.c-weektime-head {
font-size: 12px;
.week-td {
width: 70px;
}
}
.c-weektime-body {
font-size: 12px;
td {
&.weektime-atom-item {
user-select: unset;
background-color: #f5f5f5;
}
&.ui-selected {
background-color: #598fe6;
}
}
}
.c-weektime-preview {
line-height: 2.4em;
padding: 0 10px;
font-size: 14px;
.c-weektime-con {
line-height: 46px;
user-select: none;
}
.c-weektime-time {
text-align: left;
line-height: 2.4em;
p {
max-width: 625px;
line-height: 1.4em;
word-break: break-all;
margin-bottom: 8px;
}
}
}
}
.c-min-table {
tr,
td,
th {
min-width: 24px;
}
}
.g-clearfix {
&:after,
&:before {
clear: both;
content: " ";
display: table;
}
}
.g-pull-left {
float: left;
}
.g-pull-right {
float: right;
}
.g-tip-text {
color: #999;
}
.tableinfo1 {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
height: 15px;
td {
flex: 1;
font-size: 12px;
font-family: PingFang SC Medium;
font-weight: 400;
color: #FFFFFF;
display: flex;
height: 10px;
line-height: 10px;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
}
.linex {
width: 1px;
height: 4px;
background: #fff;
}
td:nth-child(1) {
flex: 2;
}
.weektime-atom-item {
height: 20px;
}
}
.tableinfo2 {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
td {
flex: 1;
}
td:nth-child(2) {
border-radius: 5px 0 0 5px;
}
td:last-child {
border-radius: 0px 5px 5px 0px;
}
.weektime-atom-item {
height: 20px;
}
.xingqi {
font-size: 16px;
font-family: PingFang SC Bold;
font-weight: 400;
color: #FFFFFF;
flex: 4;
}
}
</style>
引用组件
<template>
<div>
<el-dialog class="sp-dialog" title="布防时间" :visible.sync="dialogVisible" width="50%">
<div class="gongzuomoshi">
<div class="operation">
<div class="delete cur">
<img src="../../../assets/icon_timedelete.png" alt="">
删除
</div>
<div @click="clearWeektime" class="deleteAll cur"><img src="../../../assets/icon_timedelete.png" alt="">
删除全部
</div>
</div>
<drag-weektime
v-model="mult_timeRange"
:data="weektimeData"
@on-clear="clearWeektime"
>
</drag-weektime>
</div>
<div slot="footer" class="dialog-footer">
<el-button class="chonghui">保存</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import DragWeektime from '@/components/drag-weektime'
import weektimeData from '@/data/weektime_data'
function splicing (list) {
let same
let i = -1
let len = list.length
let arr = []
if (!len) return
while (++i < len) {
const item = list[i]
if (item.check) {
if (item.check !== Boolean(same)) {
arr.push(...['、', item.begin, '~', item.end])
} else if (arr.length) {
arr.pop()
arr.push(item.end)
}
}
same = Boolean(item.check)
}
arr.shift()
return arr.join('')
}
export default {
name: '',
props: [''],
data () {
return {
dialogVisible: true,
checkAll: false,
checkedCities: ['上海', '北京'],
cities: ['上海', '北京'],
weektimeData: weektimeData,
isIndeterminate: true,
}
},
components: { DragWeektime },
computed: {
mult_timeRange () {
return this.weektimeData.map((item) => {
return {
id: item.row,
week: item.value,
value: splicing(item.child),
}
})
},
},
beforeMount () {
},
mounted () {
},
methods: {
// 清空时间段
clearWeektime () {
this.weektimeData.forEach((item) => {
item.child.forEach((t) => {
this.$set(t, 'check', false)
})
})
},
getshow () {
this.dialogVisible = true
},
handleClose (done) {
this.$confirm('确认关闭?')
.then((_) => {
done()
})
.catch((_) => {
})
},
},
watch: {},
}
</script>
<style lang='scss' scoped>
.gongzuomoshi {
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: nowrap;
flex-direction: column;
}
.dialog-footer {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
.chonghui {
width: 100px;
height: 40px;
background: linear-gradient(180deg, #0A7E57 0%, #135C68 100%);
color: #fff;
border: none;
}
.save {
width: 100px;
height: 40px;
background: linear-gradient(180deg, #0A447E 0%, #133A68 100%);
margin-left: 15px;
margin-right: 15px;
color: #fff;
border: none;
}
.quxiao {
width: 100px;
height: 40px;
background: #1E2B44;
color: #fff;
border: none;
}
.hzgj {
background: linear-gradient(180deg, #0A447E 0%, #133A68 100%);
border: none;
color: #fff;
}
.hzgj:hover, .chonghui:hover, .save:hover, .quxiao:hover {
color: #fff !important;
}
}
.operation {
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
margin-bottom: 50px;
width: 100%;
.delete {
width: 90px;
height: 40px;
background: linear-gradient(180deg, #0A447E 0%, #133A68 100%);
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
font-size: 14px;
font-family: PingFang SC Medium;
font-weight: 400;
color: #FFFFFF;
margin-right: 10px;
img {
width: 16px;
height: 16px;
margin-right: 10px;
}
}
.deleteAll {
width: 110px;
height: 40px;
background: linear-gradient(180deg, #760A7E 0%, #681346 100%);
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
font-size: 14px;
font-family: PingFang SC Medium;
font-weight: 400;
color: #FFFFFF;
img {
width: 16px;
height: 16px;
margin-right: 10px;
}
}
}
</style>
下载链接
下载链接 提取码: nc2l