高校教務系統中,簡單課程表的實現方式

高校教務系統課程表的簡單實現方式


零、需求分析:拋開需求談教程就是耍流氓


在許多大學的教務系統或者第三方教務軟件中,都有一個重要的、不可或缺的功能——課程表,做爲整個教務軟件中使用頻率最高的功能,課程表的好壞直接決定了這個系統的用戶體驗。
然而對於剛剛初學軟件開發的我來講,不管是前端界面仍是後端的代碼彷佛都有一些難度。想着給和我同樣的又剛好也在開發教務系統相關軟件的小夥伴們一些思路,這篇博客就誕生了。

如圖,就是某款教務軟件的課表界面:
課程表.png前端


1、E-R——數據應該怎麼存(本文以MySQL爲例)


一、第一個問題:數據表

一門課是一個實體,由於一門課中含有不少節課,因此會有許多條數據,若是隻有一張數據表,把每節課都從做爲一條單獨的數據寫入表中,無疑,會產生許多冗餘的數據(由於對於某一門課中的每一節課來講,他們的課程名稱、上課班級、任課教師都是相同的),
圖片.png
而且還會出現其餘問題,好比,一門課程是一個對象,同這種方式儲存的數據,一門課程被拆分紅了多個對象,在數據的存取時很不方便。
所以,應該怎麼寫呢?——一門課程建一張數據表(命名爲Course表),一節課建一張數據表(命名爲Course_info表)。這樣,一門課程與一節課就是多對一的實體關係。

數據庫

二、第二個問題:課程信息有哪些字段

首先聲明一下關於時間表示的關鍵字段:後端

  • 周次(第一週、第二週...)week,int型
  • 星期(星期1、星期二...)weekday,int型
  • 上課時間(第一節課、第二節課...)begin,int型

對於同一門課程來講,不一樣的周次、不一樣的時段,上課的教室都會不同,也就是說,對於同一門課程的每節課,周次、時段、教室這三個值中任意兩個隨第三個值的變化而變化,而且每節課這三個值都至少會有一個改變;而對於同一門課程來講,課程名、任課教師、班級這三個值都是同樣的。
因此,這兩張數據表該分別儲存什麼內容,就顯而易見了——把不變量儲存進Course表,變化量儲存到Course_info表中。
Course表的字段:圖片.png
Course_info表的字段:圖片.png
Course表數據:圖片.png
Course_info表數據:圖片.png數組

可見,只要course_id這個字段正確指向Course表的對象,不管課程的時間、教室怎麼變,課程都仍是那個課程,教師都仍是那個教師。


三、第三個問題:課程的大小節問題

大學的課程,一大節課,會連續持續幾小節(請看本文的第一張圖片),因此數據庫中,每一節課存大節、仍是小節,是個問題。session

  • 一開始的思路是,所有存爲大節,每大節兩小節,把一天的時間分爲五大節,可隨之而來的問題就是,若是一節課持續三個小節,該怎麼辦?
  • 因而,把思路改爲存小節,把一節課分紅11個小節,課這樣有出現了問題:一大節是一個對象,若是存小節,就硬生生的把一個大節拆成了兩個或三個對象。或許你認爲這樣也無傷大雅,可是,試想,一旦須要記錄簽到情況,三個小節是三個對象。這樣,在第二節課的時候,第一節課的簽到信息就失效了,若是再想看第二節課的簽到信息,就須要從新簽到,影響用戶體驗。
  • 所以,一種合理的方案是這樣,把一天分紅11個小節,對於每節課,儲存一個起點(啓示的小節是第幾節課)和一個長度(這節課持續幾個小節)。這樣就解決了上述的全部問題:一節課就是一個對象,同時也讓時間段足夠細化。

對Course_info表作以下修改,加入持續長度:圖片.png
修改後的數據:圖片.pngapp


2、後端


有了合理的數據庫作支撐,後端的查詢方法就十分的好寫了:

  • 從session中取出此學生的登錄信息
  • 取出當前的時間信息:周次、星期、小節數
  • 根據登錄信息從學生與課程的關聯表中(Score表,本文沒有說起)取得此學生的全部課程的id,是一個int類型的數組
  • 根據獲得的數組,在Course表中取出此學生所上的全部課程
  • 循環,對於每一門課程,在Course_in表中,找到這個學生在本週次(week)、本星期(weekday)的全部課程
  • 新建一個二維數組,做爲課程表,由於每週7天,天天有11小節,因此二維數組的大小是7*11
  • 此時,剛剛查詢出的每一節課,都是此學生這周(week)的課程,因此只須要根據星期(weekday)和起始小節(begin)把數據填入課程表數組的對應位置就能夠了,而後就能夠把這個課程表傳入前端了。

(代碼以ThinkPHP爲例)this

// 獲取本學生id
        $studentId = session('studentId');

        //查詢本學期本本學生的全部課程,(Score表就是學生與課程、成績的關聯表)
        $score = new Score();
        $getScore =  $score->where(['course_id'=>$courseIds, 'student_id'=>$studentId])->select();
        
        //創建課程表數組
        $coursetable = array('1' => array('0','0','0','0','0','0','0','0') , 
        '2' => array('0','0','0','0','0','0','0','0') , 
        '3' => array('0','0','0','0','0','0','0','0') , 
        '4' => array('0','0','0','0','0','0','0','0') , 
        '5' => array('0','0','0','0','0','0','0','0') , 
        '6' => array('0','0','0','0','0','0','0','0') , 
        '7' => array('0','0','0','0','0','0','0','0') , 
        '8' => array('0','0','0','0','0','0','0','0') , 
        '9' => array('0','0','0','0','0','0','0','0') , 
        '10' => array('0','0','0','0','0','0','0','0') , 
        '11' => array('0','0','0','0','0','0','0','0')) ;
        
        //儲存本學期本學生全部課程的course_id
        $scoreIds = [];
        foreach ($getScore as $score) {
            array_push($scoreIds, $score->course_id);
        }
        
        //對於每一門課程,查詢全部的課(單位:節)
        $courseinfos = Courseinfo::where(['course_id'=>$scoreIds, 'week'=>$week])->select();
        
        //把每節課填入課表
        foreach ($courseinfos as $key => $acourseinfo) {
            {
                $coursetable[$acourseinfo->begin][$acourseinfo->weekday] = $acourseinfo;
            }
        }
        
        //傳入課程表
        $this->assign('coursetable',$coursetable);

3、前端


一、已知:後端傳入一個7*11的數組,在合適的位置有對應的課程,在沒有課程的位置,值爲零。

所以,只須要在前端也加一個7*11的循環便可spa

//經過循環生成表格
//$i爲表格的行,值爲小節(begin),$j爲表格的列,值爲星期幾(weekday)

for ($i=0; $i < 11; $i++) { 
    for ($j=0; $j < 7; $j++) { 
        if (課程表中存在 weekday==$j 而且 begin==$i 的值) {
            此單元格中顯示此課程的信息
        }
    }
}


二、解決單元格合併的問題

課程表.png
仍是這張圖,能夠看到,當課程持續兩小節或者三小節時,連續兩、三個單元格都會顯示這節課的信息,而實際上,數據庫中只存了第一小節的信息。所以須要合併單元格。利用HTML表格的rowspan標籤,能夠很方便的合併單元格。
合併前代碼:3d

<table border="1">
  <tr>
    <th>星期一</th>
    <th>星期二</th>
    <th rowspan="1">合併前</th>
  </tr>
  <tr>
    <td>123</td>
    <td>456</td>
    <th>合併前</th>
  </tr>
  <tr>
    <td>789</td>
    <td>012</td>
    <th>合併前</th>
  </tr>
</table>

當rowspan=1時不進行合併,至關於不寫。效果以下圖片.pngcode

合併後代碼:

<table border="1">
  <tr>
    <th>星期一</th>
    <th>星期二</th>
    <th rowspan="3">合併前</th>
  </tr>
  <tr>
    <td>123</td>
    <td>456</td>

  </tr>
  <tr>
    <td>789</td>
    <td>012</td>

  </tr>
</table>

而當rowspan被改爲3的時候,就能夠發現,最右面的單元格被合併了。圖片.png
然而,其實說合並,這只是一種表面的說法,細心的你必定發現了,在第二行、第三行分別少了一個<td>標籤圖片.png
與其說rowspan是合併單元格,不如說:rowspan改變了原有單元格的高度。
那麼問題來了,爲何下面的行要少一個<td>?若是在上一行使用了rowspan的狀況下,下面幾行仍然保持不變,那麼會出現什麼現象呢?

<table border="1">
  <tr>
    <th>星期一</th>
    <th>星期二</th>
    <th rowspan="3">合併後</th>
  </tr>
  <tr>
    <td>123</td>
    <td>456</td>
    <td>234</td>
  </tr>
  <tr>
    <td>789</td>
    <td>012</td>
    <td>567</td>
  </tr>
</table>

圖片.png

單元格就是這麼強硬,一旦啓用了rowspan,上面的單元格就會把下面的單元格強硬的「擠走」——使其向右移動一個單位。可是這在課程表中是絕對不能出現的——不可能由於第一節課有兩節,就把原本是週三的課顯示到了週四吧?!

因此,就須要動態的判斷了。
咱們知道,一個單元格有兩種狀態:顯示、不顯示(不顯示能夠不寫這一行,也能夠用CSS來定義一個樣式「disappear」,當class等於這個樣式時,單元格就不顯示了。而一節課有三種狀態:持續2小節、3小節、4小節。因此對於每個單元格,都須要判斷它上面的單元格有沒有長度等於2的課、它上面的上面的單元格有沒有長度爲3的課、它上面的上面的上面的單元格有沒有長度爲四的課——以上三種狀況有任意一種知足,這個單元格就是隱藏狀態,反之爲顯示狀態
但還有一種狀況須要考慮,那就是數組下標不能超出範圍。好比,對因而第一行的單元格(第一小節),若是請求它上方的單元格(第零小節),會超出數組範圍,致使報錯。因此,第一節、第二節、第三節須要單獨對待,其餘的小節因爲確保不會超出索引,因此可使用循環。
因爲本人的實際項目中的代碼比較凌亂,就不貼出來了,僅提供實現此功能的邏輯,供你們參考。


4、總結


在我看來,最大的困難,一是在於數據庫方面,數據如何儲存、如何創建實體關係、一節課按大節仍是按小節儲存...這些都是須要考慮的問題。而數據庫的結構是一個系統的基石,不合理的結構會大大增長開發的難度,甚至可能因爲數據庫結構不合理致使項目失敗。
另外一個問題就是前端輸出信息時的判斷了,爲了防止使用rowspan致使單元格錯誤的問題,須要根據前幾行的數據來判斷當前單元格是否顯示,若是上面有課,就不顯示,這樣就完美解決了問題。惟一的不足就是,若是不借助後端語言,僅僅用HTML來寫循環和判斷,會很是很是麻煩,這是我沒有列出代碼的緣由——若是能使用JS或者在前端直接使用PHP代碼來循環,就能減輕複雜度了。
或許在大佬面前這種功能簡單的不值一提,可做爲一個菜雞初學者,實現這個功能的時候確實花費了不少精力,思考了很長的時間,大概想了四五天才想出這個方案。

路途漫漫,道阻且長,咱們都在成長。

終有一天,當咱們回望過去的本身時,必定會發現,這段時間本身在不斷的進步,臉上流露出欣慰的笑容。

圖片.png

相關文章
相關標籤/搜索