问题背景
接了一个学校的项目,做一个机房实验室预约系统,首先就是要实现一个课表,效果如图:
问题分析
问题初期,查了CSDN和B站上的很多资料,看了很多的类似项目。但该课表与普通课表不同的是,在同一时间同一位置有多门课程。这就产生了一个难题——确定每条数据在课表中的位置。
我自己想了两种解决方法:
- 1.v-for循环遍历。 存在问题:接口返回参数的数据结构定义较为复杂。
- 2.绝对定位,一条数据放在一个div中,对div做绝对定位。 存在问题:无法实现时间栏和位置栏的高度自适应
后来问了一个前端大神,他给我的方法是:
- 拿到后端数据后,先构建二维表,
- 对时间列进行遍历,可以获取到数据的行索引,对相同的时间行进行合并
- 同理,在2的基础上,对位置列进行合并,将相同的位置行进行合并
并且他还帮我定义了后端返回的数据结构:
const data = [
{
time: "8:00-10:00",
position: "1号机房",
class_list: [
{
week: "1",
class: "李1-A设计-40人",
},
{
...
},
],
},
{
...
}
]
对于该结构,简单理解就是一张二维表,data数组中的一个元素就是二位表的一行
具体实现
接下来就是具体实现,因为本人写vue项目还在小白阶段,很多东西还没有具体掌握,于是对于js代码的实现,具体参照的是以下文章:
但在实现过程中,我发现了该文章的一个问题:
for (var i=0;i<result.massage.length;i++){
var stationName = result.massage[i].stationName; //这里我们在一次循环数据,获取站点的名字
console.log(stationName)
var id="."+stationName;
console.log(id)//这里是拼接字符串
var num=$(id).length;//然后获取这个class的长度
console.log(num)
$(id).not(":eq(0)").remove();//然后在这个class中除了第一个全部删除掉
$(id).attr('rowspan',num);//然后合并class长度的单元格
}
在这段代码的最后一句前要加一条if判断,不然for循环在遍历时会覆盖前一趟循环设置的rowspan属性,即将这段代码改为:
for (var i=0;i<result.massage.length;i++){
var stationName = result.massage[i].stationName; //这里我们在一次循环数据,获取站点的名字
console.log(stationName)
var id="."+stationName;
console.log(id)//这里是拼接字符串
var num=$(id).length;//然后获取这个class的长度
console.log(num)
$(id).not(":eq(0)").remove();//然后在这个class中除了第一个全部删除掉
if (num != 1) $(id).attr('rowspan',num);//然后合并class长度的单元格
}
接下来就是按照大神提供的步骤来实现,具体js代码如下:
const courseList = reactive([
{
time: "8:00-10:00",
position: "1号机房",
class_list: [
{
week: "1",
class: "李1-A设计-40人",
},
{
week: "2",
class: "李3-A设计-40人",
},
{
week: "3",
class: "李3-A设计-40人",
},
{
week: "4",
class: "李4-A设计-40人",
},
{
week: "5",
class: "李5-A设计-40人",
},
{
week: "6",
class: "李6-A设计-40人",
},
{
week: "7",
class: "李7-A设计-40人",
},
],
}
onMounted(() => {
//合并日期列
for (var i = 0; i < courseList.length; i++) {
var id_time = courseList[i].time.split(":")[0]; //这里循环数据,获取自己起的class名
console.log(id_time);
var id = "." + id_time;
console.log(id); //这里是拼接字符串
var num = $(id).length; //然后获取这个class元素的个数
console.log(num);
$(id).not(":eq(0)").remove(); //然后在这个class中除了第一个全部删除掉
if (num != 1) $(id).attr("rowspan", num); //然后合并相同class的单元格
}
// 合并位置列
for (var n = 0; n < courseList.length; n++) {
var id_position =
courseList[n].time.split(":")[0] + courseList[n].position.match(/\d+/)[0];
//str..match(/\d+/)[0]
var id = "." + id_position;
var num = $(id).length;
$(id).not(":eq(0)").remove();
if (num != 1) $(id).attr("rowspan", num);
}
});
实现效果如图:
实现过程中遇到的问题以及解决方案
事先声明:实现过程中遇到的问题主要在于我个人对js代码的不熟悉。
首先就是jQuery的语法问题,在此项目之前我还没有学过jQuery,以至于连“$”的含义都不懂,于是就去查了很多资料,在结项后,需要将这部分学习下来。
其次在合并单元格时,采用的是jQuery的.class选择器(经测试,#id选择器的num有异常,但没有具体深究),原博主直接将" '+'+车站名"作为class名。但在该项目中,时间和位置这两个信息都是以数字开头,且一个有':',一个有汉字都不满足class的命名要求。该解决方法如下:对于时间栏,采用str.split(':')函数,将时间信息切割成一个含三个元素的数组,取其第一个元素值作为时间栏class的名称;对于位置栏,使用str.match(/\d+/)[0] 正则表达式,来从字符串中提取数字,作为位置栏的class名称,与此同时,单用一个信息对位置栏<td>标签的class命名是不够的,因为对于不同时间的同一位置不能进行合并,所以又将该位置栏对应的时间栏的class名加了进去,就如代码中所写的 :class="item1.time.split(':')[0] + item1.position.match(/\d+/)[0]" 。
最后,自己的vue3的特性还不是很熟悉,需要结项后和jQuery一起再学习学习。
结语
对于“实验室排课表设计”的分享就到这里,后续还会对这张表进行改进,如果有时间的花还会再写几篇博客记录。