实现效果:
说明:
支持星期切换、支持上下滚动以及左右滚动
1 模板内容
<template>
<van-tabs v-model="active" @change="tabChange" class="order">
<van-tab v-for="(item, index) in weekDateList" :key="item.title" :title="item.title" >
<div style="width: 100%;">
<div style="position: relative;">
<div class="mask"></div>
<div :id="'top-tab' + index" class="top-tab">
<div v-for="room in rooms" :key="room.room_uuid">{{ room.room_name }}</div>
</div>
</div>
<div id="bom-con" @scroll="syncScroll">
<div class="con-left">
<div v-for="timeSlot in timeSlots" :key="timeSlot">{{ timeSlot }}</div>
</div>
<div id="tableContainer" style="width: calc(100% - 71px);" >
<table v-show="!loading" id="order-table" class="order-table">
<tbody>
<tr v-for="timeSlot in timeSlots.slice(0, -1)" :key="timeSlot">
<td v-for="room,index in rooms" :key="room.room_uuid" class="border" :class="{isorder:getAppointment(room.room_uuid, timeSlot)=='已预约'}">
<span >{{ getAppointment(room.room_uuid, timeSlot) }}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</van-tab>
</van-tabs>
</template>
2 js部分
import Vue from 'vue'
import moment from 'moment';
import {Toast} from 'vant';
export default {
name:'mtOrderView',
data() {
return {
active: 0,
currentDate: new Date(),
loading: false,
rooms: [],
appointments: {},
roomTimeOrder:{},
show:true,
};
},
computed:{
weekDateList() {
const currentDayOfWeek = this.currentDate.getDay();
this.active = currentDayOfWeek - 1
const daysOffset = currentDayOfWeek > 0 ? currentDayOfWeek - 1 : 6;
const weekStartDateTimestamp = this.currentDate.getTime() - (daysOffset * 24 * 60 * 60 * 1000);
const weekDateList = [];
for (let i = 0; i < 7; i++) {
const date = new Date(weekStartDateTimestamp + (i * 24 * 60 * 60 * 1000));
const weekdays = [ '周一', '周二', '周三', '周四', '周五', '周六','周日'];
let day = {
title:weekdays[i]+'('+moment(date).format('MM-DD')+')',
date:date
}
if(currentDayOfWeek-1 == i){
day = {
title:'今天'+'('+moment(date).format('MM-DD')+')',
date:date
}
}
weekDateList.push(day);
}
return weekDateList;
},
queryDate(){
return this.weekDateList[this.active].date
},
timeSlots() {
// 根据需求定义每天的时间段
const startTime = new Date();
startTime.setHours(7, 0); // 设置开始时间为7点
const endTime = new Date();
endTime.setHours(20, 0); // 设置结束时间为19点
const timeSlots = [];
while (startTime < endTime) {
timeSlots.push(this.formatTime(startTime));
startTime.setMinutes(startTime.getMinutes() + 60); // 每个时间段为30分钟
}
return timeSlots;
},
},
created(){
this.userInfo = Vue.ls.get('userInfo');
},
mounted(){
this.getData()
},
methods: {
syncScroll(e) {
let { active = 0 } = this
var targetDiv = document.getElementById(`top-tab${active}`);
// 将目标 div 的滚动位置设置为源 div 的滚动位置 sourceDiv.scrollLeft
targetDiv.scrollLeft = e.target.scrollLeft;
},
tabChange(e){
this.getData()
},
//刷新
onRefresh() {
this.getData()
},
formatTime(date) {
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
},
getData(){
let currentDate = this.queryDate
Toast.loading({
message: '加载中...',
forbidClick: true,
loadingType: 'spinner',
});
this.loading = true
const currentTimestamp = currentDate.getTime();
// 计算后一天的时间戳(加上一天的毫秒数)
const nextDayTimestamp = currentTimestamp + (24 * 60 * 60 * 1000);
// 创建后一天的 Date 对象
let nextDay = new Date(nextDayTimestamp);
let startTime = moment(currentDate).format('YYYY-MM-DD')
let endTime = moment(nextDay).format('YYYY-MM-DD')
gaService(
'service.ga.mtRoomApplyService',
"queryByAttr2",
{
"queryFields": {
"start_time":startTime,
"end_time":endTime,
},
"userInfo": this.userInfo,
"resultFields": "",
"orderFields": "syscreatedate desc"
}
).then(d => {
d = d.data.data
this.rooms = d.resultset.map(item=>{
return {
apply_detail:item.apply_detail,
room_name:item.room_name,
room_uuid:item.room_uuid
}
})
this.roomTimeOrder = {}
this.rooms.forEach(item=>{
if(item.apply_detail){
let key = `${item.room_uuid}`;
this.roomTimeOrder[key] = item.apply_detail
}
})
this.loading = false
Toast.clear();
}).catch(err => {
console.log('err',err)
this.loading = false
Toast.clear();
Toast.fail('获取数据失败',err);
})
},
getAppointment(roomId, timeSlot) {
if(this.roomTimeOrder[roomId]){
let keys = Object.keys(this.roomTimeOrder[roomId])
return keys.includes(timeSlot)?'已预约':'未预约'
}else{
return '未预约'
}
}
}
3 样式
<style lang="less" scoped>
.order{
height: 100vh;
}
.order-table{
// margin-left: 70px;
width: 100%;
border-collapse: collapse;
table-layout: fixed; /* 强制表格使用固定布局 */
td{
width: 134px;
height: 40px;
text-align: center;
padding: 8px;
border-right: 1px solid #ddd;
// background-color: #d9d9d9;
span{
color: rgb(129, 255, 127);
}
}
th:not(:first-child),td:not(:first-child) {
border-left: none;
}
td.border {
// border-right: none;
border: 1px solid #dddddd45;
}
thead th {
position: sticky; /* 将表头设置为 sticky */
top: 0; /* 设置固定位置在顶部 */
background-color: #fff; /* 可选:添加背景色以区分固定行 */
}
th:first-child {
z-index: 2; /* 提高层级以覆盖表头 */
}
.isorder{
span{
color: red;
}
}
}
.order {
/deep/.van-tab{
font-size: 18px;
}
/deep/.van-tabs__content{
width: 100%;
height: calc(100% - 70px);
overflow: auto;
// padding-top: 20px;
background: #fff;
}
/deep/.van-tab__pane{
display: flex;
height: 100%;
}
}
.con-left{
// width: 70px;
// margin-top: 40px;
// border-right: 1px solid #ddd;
position: sticky;
left: 0;
background-color: #fff;
z-index: 99;
div{
width: 70px;
height:57px;
text-align: center;
border-right: 1px solid #ddd;
background-color: #fff;
}
div:last-child{
border-right:none
}
div::after{
content: '';
width: 5px;
position: absolute;
/* top: 0; */
right: 0;
border-top: 1px solid #ddd;
}
}
.top-tab{
position: relative;
display: flex;
margin-left: 71px;
overflow: auto hidden;
scrollbar-width: none; /* Firefox 隐藏滚动条 */
-ms-overflow-style: none; /* IE 和 Edge 隐藏滚动条 */
div{
width: 151px;
height: 40px;
text-align: center;
flex-shrink: 0
}
}
#bom-con{
display: flex;
height: calc(100% - 40px);
overflow-y: auto;
width: 100%;
}
.top-tab::-webkit-scrollbar,#bom-con::-webkit-scrollbar{
display: none !important;
width: 0 !important
}
.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: transparent; /* 设置遮罩层的颜色和透明度 */
z-index: 1; /* 确保遮罩层位于内容之上 */
overflow: hidden;
}
</style>