css 代码
.week-table {
width: 660px;
position: relative;
}
.week-table .thead-tr,
.week-table .tbody-tr {
display: flex;
}
.week-table .thead-tr div,
.week-table .tbody-tr div {
text-align: center;
flex: 0 0 87px;
height: 25px;
border-right: 1px solid #ddd;
border-top: 1px #ddd dashed;
line-height: 25px;
user-select: none;
position: relative;
}
.week-table .thead-tr div:last-child,
.week-table .tbody-tr div:last-child {
border-right: unset;
}
.week-table .thead-tr div:first-child,
.week-table .tbody-tr div:first-child {
border-left: 1px #ddd dashed;
flex: 0 0 50px;
}
.week-table .thead-tr div:last-child,
.week-table .tbody-tr div:last-child {
border-right: 1px #ddd dashed;
}
.week-table .tbody-tr:last-child {
border-bottom: 1px #ddd dashed;
}
.week-table .tr div:first-child {
border-left: 1px #ddd dashed;
}
.week-table .thead-tr div {
font-weight: bold;
background-color: #f5f7fa;
}
.week-table .tbody-tr div:first-child {
color: #fff;
}
.week-table .tbody-tr:nth-child(-n + 6) div:first-child {
background-color: #3071a9;
}
.week-table .tbody-tr:nth-child(n + 7) div:first-child {
background-color: #45b6b0;
}
.week-table .tbody-tr:nth-child(n + 11) div:first-child {
background-color: #65c3df;
}
.week-table .tbody-tr .forbid {
color: red;
user-select: none;
cursor: not-allowed;
}
.week-table .tbody-tr .card {
background-color: #3071a9 !important;
color: #333 !important;
position: absolute;
left: 1px;
top: 1px;
z-index: 2;
user-select: none;
border-radius: 4px;
}
.week-table .tbody-tr .highlight {
background-color: #65c3df;
}
HTML 代码
<div class="week-table">
<div class="thead-tr">
<div>div>
<div>星期一div>
<div>星期二div>
<div>星期三div>
<div>星期四div>
<div>星期五div>
<div>星期六div>
<div>星期日div>
div>
<div class="tbody-tr">
<div>1div>
<div>div>
<div>
<span>文字span>
div>
<div data-forbid="true">
<span class="forbid">禁止放置span>
div>
<div>
<div class="card" data-x="3" data-y="0" style="height: 72px; width: 84px">星期四 1~3格div>
div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>2div>
<div><span>文字span>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>3div>
<div><span>文字span>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>4div>
<div><span>文字span>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>5div>
<div><span>文字span>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>6div>
<div><span>文字span>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>7div>
<div><span>文字span>div>
<div>div>
<div data-forbid="true">
<span class="forbid">禁止放置span>
div>
<div data-forbid="true">
<span class="forbid">禁止放置span>
div>
<div data-forbid="true">
<span class="forbid">禁止放置span>
div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>8div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>9div>
<div><span>文字span>div>
<div data-forbid="true">
<span class="forbid">禁止放置span>
div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>10div>
<div><span>文字span>div>
<div>div>
<div data-forbid="true">
<span class="forbid">禁止放置span>
div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>11div>
<div><span>文字span>div>
<div data-forbid="true">
<span class="forbid">禁止放置span>
div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
<div class="tbody-tr">
<div>12div>
<div><span>文字span>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
<div>div>
div>
div>
JavaScript 代码
//----------- 点击移动卡片 ------------------
var $card = $('.week-table .card')
var $weekTable = $('.week-table')
var cardVerticalNumber = 3
var xNumber = 7
var yNumber = 12
positionCard($card, $weekTable, cardVerticalNumber, xNumber, yNumber)
function positionCard($card, $weekTable, cardVerticalNumber, xNumber, yNumber) {
//-------- 拖动卡片 --------
var data = {}
var tdHeight = 26 // 每一个 div 的高度
var tdWidth = 88 // 每一个 div 的宽度
var xNumber = xNumber // X 轴div数量,不含序列
var yNumber = yNumber // Y轴div数量,不含星期列
var xMoveMini = Math.floor(tdWidth / 4) // x轴最小移动距离,才开始计算方向
var yMoveMini = Math.floor(tdHeight / 2) // y轴最小移动距离,才开始计算方向
var cardHeight = $card.height()
var cardWidth = $card.width()
var cardVerticalNumber = cardVerticalNumber // 卡片占据的td数量
var topLimit = 0 //顶部最大移动距离
var bottomLimit = yNumber * tdHeight - cardHeight // 底部最大移动距离
var leftLimit = 0 // 左边最大移动距离
var rightLimit = xNumber * tdWidth - cardWidth // 右边最大移动距离
var weekNameZh = ['一', '二', '三', '四', '五', '六', '日']
$weekTable
.find('.tbody-tr>div:not(:first-child)')
.not('[data-forbid="true"]') // 要不要都行,isPutHere 也做了处理
.on('click', function () {
var $this = $(this)
var xIndex = $this.index() - 1
var yIndex = $this.parent().index() - 1
var isPut = isPutHere(xIndex, yIndex)
if (!isPut) return true
data.newIndexX = xIndex
data.newIndexY = yIndex
moveCard(xIndex, yIndex, $this)
})
$card.mousedown(function (e) {
var $this = $(this)
var moveFlag = false // 防止别的事件触发
data.oldIndexX = Number($this.attr('data-x'))
data.oldIndexY = Number($this.attr('data-y'))
data.left = e.pageX
data.top = e.pageY
data.sPositionX = data.oldIndexX * tdWidth
data.sPositionY = data.oldIndexY * tdHeight
var tempNewLeft = data.left // 上次移动时的位置信息,用于判断移动方向
var tempNewTop = data.top // 上次移动时的位置信息,用于判断移动方向
$(document).mousemove(function (e) {
moveFlag = true
data.newLeft = e.pageX
data.newTop = e.pageY
data.x = data.newLeft - data.left
data.y = data.newTop - data.top
data.newPositionX = data.sPositionX + data.x
data.newPositionY = data.sPositionY + data.y
var xMove = data.newLeft - tempNewLeft
var yMove = data.newTop - tempNewTop
// 元素没移动
if (Math.abs(xMove) === 0 && Math.abs(yMove) === 0) return true
if (data.newPositionX > rightLimit) {
data.newPositionX = rightLimit
}
if (data.newPositionX < leftLimit) {
data.newPositionX = leftLimit
}
if (data.newPositionY < topLimit) {
data.newPositionY = topLimit
}
if (data.newPositionY > bottomLimit) {
data.newPositionY = bottomLimit
}
// 当前移动,不超过一定距离不代表要移动
if (Math.abs(xMove) > xMoveMini) {
data.newIndexX = Math.ceil(data.x / tdWidth) + data.oldIndexX - 1
// X index 的取值范围
data.newIndexX = data.newIndexX <= 0 ? 0 : data.newIndexX
data.newIndexX = data.newIndexX >= xNumber - 1 ? xNumber - 1 : data.newIndexX
} else {
data.newIndexX = data.oldIndexX
}
if (Math.abs(yMove) > yMoveMini) {
data.newIndexY = Math.ceil(data.y / tdHeight) + data.oldIndexY - 1
// Y index 的取值范围
data.newIndexY = data.newIndexY <= 0 ? 0 : data.newIndexY
data.newIndexY = data.newIndexY >= yNumber - cardVerticalNumber ? yNumber - cardVerticalNumber : data.newIndexY
} else {
data.newIndexY = data.oldIndexY
}
// 移动时,给将要移动到的地方一个高亮
setHighlight(false, data.newIndexX, data.newIndexY)
$card.css({
left: data.newPositionX - data.sPositionX + 'px',
top: data.newPositionY - data.sPositionY + 'px'
})
})
$(document).mouseup(function (e) {
// 下面这句话放在 if (!moveFlag) return 后面,会导致点击卡片,卡片随着鼠标指针移动
$(document).off('mousemove')
if (!moveFlag) return
moveFlag = false
tempNewLeft = data.newLeft
tempNewTop = data.newTop
setHighlight(true, data.newIndexX, data.newIndexY)
$card.css({
top: '1px',
left: '1px'
})
// 不允许放置,放到上一个可用的位置
if (isPutHere(data.newIndexX, data.newIndexY)) {
moveCard(data.newIndexX, data.newIndexY)
} else {
moveCard(data.tempIndexX, data.tempIndexY)
}
})
})
function moveCard(x, y, $target) {
setCardIndex(x, y)
setCardInfo(x, y)
var $parent = $target ? $target : getParent(x, y)
$parent.append($card)
}
// 判断是否当前位置是否可放置
function isPutHere(newX, newY) {
var isPut = true
// 当前 Y 轴 加上卡片高度超出Y 轴则直接返回 false
if (newY + cardVerticalNumber > yNumber) return false
for (var i = 0; i < cardVerticalNumber; i++) {
var $parent = getParent(newX, newY + i)
// 不允许放置的位置,高亮上一个可用的位置
if ($parent.length <= 0 || isTdForbid($parent)) {
isPut = false
}
}
return isPut
}
function setHighlight(isRemoveClass, newX, newY) {
$weekTable.find('.tbody-tr div').removeClass('highlight')
var isPut = isPutHere(newX, newY)
if (isPut) {
// 存储上一个可用的位置
data.tempIndexX = newX
data.tempIndexY = newY
}
if (!isRemoveClass) {
// 判断当前位置是否有禁用的 td
for (var i = 0; i < cardVerticalNumber; i++) {
if (isPut) {
$parent = getParent(newX, newY + i)
} else {
$parent = getParent(data.tempIndexX, data.tempIndexY + i)
}
$parent.addClass('highlight')
}
}
}
function getParent(x, y) {
return $weekTable
.find('.tbody-tr')
.eq(y)
.find('>div')
.eq(x + 1)
}
function isTdForbid($el) {
return $el.attr('data-forbid')
}
function setCardIndex(x, y) {
$card.attr('data-x', x)
$card.attr('data-y', y)
}
function setCardInfo(x, y) {
var dayName = weekNameZh[x]
$card.text(`星期${dayName} ${y + 1}~${y + cardVerticalNumber}格`)
}
}