JavaScript代码绘制课程表

2022-07-26,,,

任务要求

要求:把一个json字符串格式的课程表数据,在页面上绘制成一周的课表
数据样例:

var data = [
    {
        XKBH: "R0101",
        KCMC: "毛泽东思想和中国特色社会主义理论体系概论",
        JSXM: "任XX",
        SJDD: "周一3-5节,尚农楼1-17;..周四6-7节,尚农楼1-17;",
    },
    {
        XKDM: "R0187",
        KCMC: "中国近现代史纲要",
        JSXM: "李X",
        SJDD: "周二6-7节,普洱善御楼1-02;..周四1-2节,普洱尚射楼3-01;",
    },
    {
        XKBH: "R0199",
        KCMC: "管理信息系统",
        JSXM: "王XX",
        SJDD: "周一6-7节,普洱善御楼1-02;..周四8-9节,普洱尚射楼3-01;..周五1-2节,普洱尚射楼3-01;",
    }
];

绘制结果样例

解决思路

  1. 绘制课程表样式的表格
  2. 解析json字符串数据,分离出各个信息
  3. 给每个表格格子命名
  4. 把相应的信息添加到对应的表格位置
  5. 解析该堂课占用的课节数,合并相应数量的单元格
  6. 把被合并的单元格元素删除
  7. 解决课程表时间冲突问题(怎样放置相应信息使表格不错乱,并且得出课程冲突的具体信息)

实现所使用到的框架技术:jquery

具体实现

一、绘制课程表表格
绘制表格元素的js方法。
注意:给表格、每一个格子元素和行元素都添加上唯一的标识符,即(class名称),方便之后添加对应课程信息使用,这里建议使用字母和数字,对周信息和课节信息进行唯一标识,比如(w1_j1)标识周一第一节课。

function createCourseTable(idName) {
    var div_ = $("." + idName);
    div_.css({ margin: "30px auto" });//表格居中,添加上下边距
    var remind = '<h3 class="course_remind">课程表时间发生冲突,以下课表数据将不再准确!</h3>';
    div_.html(remind);//添加记录信息
    div_.append('<table class="tableCourse"></table>');//添加表格
    var table_ = $(".tableCourse");
    var tr_;
    for (var i = 0; i <= 13; i++) {
        if (i == 0) {
            //添加周信息
            table_.append('<tr class="headCourse"><th class="th_course"></th><th class="th_course">周一</th><th class="th_course">周二</th><th class="th_course">周三</th><th class="th_course">周四</th><th class="th_course">周五</th><th class="th_course">周六</th><th class="th_course">周天</th></tr >');
        } else {
            //添加时段信息
            //<tr class="j1"></tr>
            table_.append('<tr class="j' + i + '"></tr>');
            tr_ = $(".j" + i);
            //<th>1</th>
            tr_.append('<th class="th_course">' + i + '</th>');
            for (var j = 1; j <= 7; j++) {
                //<td id="w1_j1"></td>
                tr_.append('<td id="w' + j + '_j' + i + '" class="td_course"></td>');
            }
        }
    }
}

使用方法
创建一个div块元素,给定一个class名称。

在js代码块中执行表格创建方法

该绘制方法的执行结果的部分样例代码

执行结果

给表格添加相应的样式,使之美观

/*课程表样式*/
.class_schedule {
    width: 1000px;
}

.tableCourse {
    table-layout: fixed;
    width: 100%;
}

.th_course, .td_course, .tableCourse {
    border-style: solid;
    border-width: 2px;
    border-color: rgb(156, 202, 213);
    padding: 10px 8px;
}

.headCourse {
    background-color: rgb(193, 235, 248);
}

.th_course {
    text-align: center;
}

.j1, .j2, .j3, .j4, .j5 {
    background-color: aquamarine;
}

.j6, .j7, .j8, .j9, .j10 {
    background-color: rgb(235, 204, 163);
}

.j11, .j12, .j13 {
    background-color: rgb(104, 115, 197);
}

.course_location {
    font-weight: bold;
}

/*提示文字样式*/
.course_remind {
    color: red;
    display: none;
}

无数据的表格样式
到此时,空表就绘制完成了,接下来只需要往里面添加相应的数据,即可使它成为一张完整的课程表。

二、解析json字符串中包含的数据
1、解析基本数据
解析出可以直接得出的信息
下面是执行解析、添加数据的总调用方法

//数据模型
//var data = [
//    {
//        XKBH: "R0101",
//        KCMC: "毛泽东思想和中国特色社会主义理论体系概论",
//        JSXM: "任XX",
//        SJDD: "周一3-5节,尚农楼1-17;..周四6-7节,尚农楼1-17;",
//    },
//    {
//        XKBH: "R0199",
//        KCMC: "管理信息系统",
//        JSXM: "王XX",
//        SJDD: "周一6-7节,普洱善御楼1-02;..周五1-2节,普洱尚射楼3-01;",
//    }
//];
//根据json对象解析出对应的数据,并在表格中展示
function classSchedule(data) {
    data = JSON.parse(data);
    //先清空record
    record = "";
    var temp;//课程对象
    var id;//课程id
    var lesson;//课程名称
    var teacher;//教师
    var date;//课程时间
    for (var i = 0; i < data.length; i++) {
        temp = data[i];
        id = temp.XKBH;
        lesson = temp.KCMC;
        teacher = temp.JSXM;
        //解析时间地点信息
        date = resolveDate(temp.SJDD.toString());
        //把数据添加到相应的位置
        addHtml(id, lesson, teacher, date);
    }
};

在这个主要方法中,把json对象转换成json对象,而且这个对象所包含的是多个课程信息的数组,通过循环可以遍历出每一个课程的相应信息。
这样就可以很容易的得出每一个课程的课程id、课程名称、任课教师的信息。
但是时间地点信息在一串数据里。
这里的难点是解析出这串数据里所包含的各个周信息,课节信息,位置信息。

我们需要将它所包含的时间和地点信息逐一解析出来。
在这,接着调用解析时间地点的方法,传入这一整个时间地点的数据。

这里的思路是把解析出来的各个数据封装到一个数组里,返回出来,和其它已经解析出来的信息统一的一起使用。
这里自定义一个对象,包含着周、开始课节、结束课节和位置的相应属性。
下面是记录信息的对象

///自定义记录解析完的上课时间、地点的类(对象)
function ReDate(week, timeBegin, timeEnd, location) {
    this.week = week;
    this.timeBegin = timeBegin;
    this.timeEnd = timeEnd;
    this.location = location;
}

接着是具体的解析数据代码。
下面是解析时间地点的主要方法

//数据样例
//"周二6-8节,普洱善御楼1-02;..周四1-2节,普洱尚射楼3-01;"
//根据课程时间、地点信息的数据解析出每一项结果
function resolveDate(date) {
    date = date.split(";");//拆分每一个课程信息
    var reDate = new Array();//存放解析完的数据对象
    //"周二6-8节,普洱善御楼1-02"
    for (var i = 0; i < date.length - 1; i++) {
        // alert("d="+date[i]);
        var temp = date[i].split(",");
        // alert("temp="+temp);
        if (i == 0) {
            week = temp[0].substr(0, 2);
        } else {
            week = temp[0].substr(2, 2);
        }
        week = resolveWeek(week);
        var patt = /\d{1,2}[-]{1}\d{1,2}/;
        var time = patt.exec(temp[0]);
        // alert("time="+time);
        time = time[0].toString().split("-");
        reDate[i] = new ReDate(week, time[0], time[1], temp[1]);
    }
    return reDate;
}

先观察信息的组成架构,每一个信息都是通过分号(;)分隔。
通过string的分割字符串方法,通过分号分割出一个一个分离的数据,并以数组的形式返回。

接着创建一个存放数据对象的数组,把数据解析完成放到对象里,然后把对象放到数组里,返回,方便统一使用。
因为数据拆分成一个个分开的数据,且存放于数组中,我们遍历数组,再次接着解析数组中的每个信息。
注意:最后一个通过分号拆分出的字符串信息为空,我们遍历该数组数据时,需要免除对最后一个数据的遍历。
接着是对数据里的每一条记录进行解析

观察数据发现,数据由逗号(,)分隔开时间和地点的数据。
我们通过逗号拆分数据。

数据拆分为单个的信息放在数组里。且都是有两个元素,第一个是时间,第二个是地点。所以可以直接得出位置信息,但是时间和课节需要继续解析。
先对第一个信息继续解析出周信息和课节数信息。

发现开头两个是周的信息,可以截取开头两个字符作为周信息。
但是,有多组课程记录时,第二个记录开始是以两个符号接头的。

所以解析第一个时截取开头两个,其他的截取第三第四个字符。
但是这里一个难点是解析出课节信息。

为了方便使用的是正则表达式匹配这个信息。

        var patt = /\d{1,2}[-]{1}\d{1,2}/;//匹配:数字-数字

匹配出的结果是,例如:3-5

接着可以通过“-”拆分字符。
接着调用js中Number对象的格式转换功能解析出单个的开始课节和结束课节。
为了方便在表格里插入课程数据,把周信息也转换成对应的数字。
下面是解析周信息对应的代码

//根据周信息解析出对应代码
function resolveWeek(week) {
    switch (week) {
        case "周一": week = "1"; break;
        case "周二": week = "2"; break;
        case "周三": week = "3"; break;
        case "周四": week = "4"; break;
        case "周五": week = "5"; break;
        case "周六": week = "6"; break;
        case "周日": week = "7"; break;
    }
    return week;
}

然后把解析出来的周信息,开始课节,结束课节,位置信息,存放在自定义对象中,放入数组。等到循环结束,把数据返回到主要调用方法里调用添加信息的方法。

调用添加信息的方法。传入课程代码,课程名称,教师,周,开始结束课节,位置。

下面是调用课程信息添加到表格的方法,传入某节课的相关信息。
方法外是冲突信息的记录变量。直到所有方法运行结束可以一并返回到调用的位置。


var record = "";//记录课程冲突信息

//把对应数据添加到课程表里
//课程代码,课程名称,教师,周,开始时间,结束时间,位置
//问题:可能存在课程冲突情况
function addHtml(id, lesson, teacher, date) {
    var temp;
    for (var i = 0; i < date.length; i++) {
        temp = date[i];
        var l = $("#w" + temp.week + "_j" + temp.timeBegin);
        if (l.length > 0 && l[0].innerText == "") {
            //在表格中加入课程数据
            l.html("<span class='course_id'>" + id + "</span> <span class='course_lesson'>" +
                lesson + "</span><br><span class='course_teacher'>"
                + teacher + "</span><br>" + "<span class='course_location' >" +
                temp.location + "</span>"
            );
        } else {
            //说明该位置已经被其它课程占用,在冲突位置或者被合并位置
            //循环往上找到冲突信息所在位置
            var temp2;
            for (var j = temp.timeEnd; j > 0; j--) {
                temp2 = $("#w" + temp.week + "_j" + j);
                if (temp2[0] != null && temp2.text() != "") {
                    //添加原课程冲突信息
                    record += "<br>【《 " + temp2.children('.course_id').text() + " " + temp2.children('.course_lesson').text() + " " + temp2.children('.course_teacher').text() + " 》";
                    //添加现课程冲突信息
                    record += "和《 " + id + " " + lesson + " " + teacher + " 》】";
                }
            }
            //退出该层循环
            continue;
        }

        var s = temp.timeEnd - temp.timeBegin;
        //合并单元格
        l.attr('rowspan', s + 1);
        var object;//元素对象
        var token = false;//标记合并单元格时是否会有冲突:【false】没有时间冲突,【true】有时间冲突
        for (var j = 1; j <= s; j++) {
            object = $("#w" + temp.week + "_j" + (Number.parseInt(temp.timeBegin) + j));
            //该元素要合并的单元格所在的元素存在 并且元素的内容为空,则不冲突
            if (object.length > 0 && object[0].innerText == "") {
                object.remove();
            } else if (object.length > 0) {
                //若要合并的元素存在,但是内容不为空,则也冲突
                token = true;
                //记录冲突课程信息
                record += "<br>【《 " + object.children('.course_id').text() + " " + object.children('.course_lesson').text() + " " + object.children('.course_teacher').text() + " 》";
                //添加现课程冲突信息
                record += "《 " + id + " " + lesson + " " + teacher + " 》】";
                object.remove();
            }
            else {
                //上面删除冲突的信息后
                //可能存在单元格不存在的情况
                token = true;
            }
        }
        //有冲突则记录,并删除冲突的信息
        //这样删除有数据的单元格可能存在空缺的单元格
        //所有需要把空缺的单元格找回
        if (token == true) {
            //把丢失的单元格补充回来
            //往下循环看丢失的格子并找回
            var end = Number.parseInt(temp.timeEnd);
            for (var j = 13; j > end; j--) {
                object = $("#w" + temp.week + "_j" + (Number.parseInt(temp.timeEnd) + j));
                if (object.length == 0) {
                    var obj;
                    //循环找到丢失元素之前的元素
                    for (var k = Number.parseInt(week); k > 0; k--) {
                        obj = $("#w" + k + "_j" + (Number.parseInt(temp.timeEnd) + j));
                        //若找到则在元素之后添加丢失的元素,没找到则继续循环向前找
                        if (obj.length > 0) {
                            obj.after("<td id='w" + temp.week + "_j" + (Number.parseInt(temp.timeEnd) + j) + "'></td>");
                            break;
                        }
                    }
                } else {
                    //若该元素之下的第一个
                    break;
                }
            }
            token = false;
        }
    }
}

首先是对传入的某天某次课的信息添加到对应的表格位置。
这里用到周信息和课节信息,进行拼接出格子对应的标识符,以便选中该格子,在该格子中进行信息的添加。

var l = $("#w" + temp.week + "_j" + temp.timeBegin);//例如:w1_j1

选中后即可在里面添加信息,同时根据课节信息,判断出这堂课要上几个课节,然后合并对应数量的列。
注意:合并后,被合并的对应位置格子会向右挤出,也就是会产生格子溢出。
这里的解决办法是:根据被合并的格子位置信息,选中并删除这个元素。
例如:格子添加数据的位置(w1_j1),有2个课节数,就要向下合并两个格子,即格子(w1_j2)被合并了,所以根据这个信息,把添加的数据的开始课节增加1,对应拼接出的信息就是被合并的格子位置,可以选中这个元素并删除它。
多个元素合并也是同理。
下面是jquery对象删除自身元素的方法。

接下来是整个算法最麻烦的地方:课程时间冲突处理。
添加课程信息时会有以下几个冲突情况:

  1. 添加课程时,该位置已经有数据在该位置上。
  2. 添加数据时,该位置不存在,即该位置已经被其它课程合并了。
  3. 添加信息并合并格子后,需要删除被合并的格子,但是要删除(即被合并)的格子已经有其它课程的信息。
  4. 第三条的处理,把该冲突信息记录下来,并把该有信息的格子删除,会导致删除的格子合并过的格子有空缺。
    解决办法:
    第一条处理:不添加该信息,但是记录改位置课程的冲突信息。
    第二条处理:也不添该信息,但是从该位置的列往上遍历,找到发生冲突的课程,记录冲突信息。
    第三条处理:在删除有信息的格子之前,记录冲突信息,再删除。
    第四条处理:从添加的信息中,找到最后课节信息,往下遍历,判断该位置是否有元素存在,查找到不存在时,通过逻辑找到该格子的横排的前一个格子,在前一个格子后面添加上该元素。直到遍历一整列。

这里具体冲突解决的措施就不再细讲,也不太好说了,上面方法里的代码和文字解决办法里写的很明确了。

最后:可以在调用表格创建方法时,就传入数据。表格创建完成即刻调用课程信息添加绘制的方法,接着在所有方法执行完毕,检测记录冲突信息的变量是否为空,不为空把提示信息变显眼,提示用户,接着把冲突信息层层返回到最外层的调用,供调用着对冲突信息进行处理。

最终执行结果:

下面是没有冲突的课程表展示

有冲突时,显示冲突信息(存在四个课时段的冲突)
课表显示

本文地址:https://blog.csdn.net/l210148678/article/details/111144933

《JavaScript代码绘制课程表.doc》

下载本文的Word格式文档,以方便收藏与打印。