2020年8月18日 星期二

jQuery UI SPA 應用程式 (一) : 環境配置與程式架構

最近因為工作整併, 負責的機器變多了, 需要寫一套以 WSH 技術為基礎的 Web 版維運資訊網頁, 以前本來想用 Python 技術來寫, 但由於我司資安要求越來越難搞, 連 Python 開發環境都被掃出版本太舊問題, 為了避免麻煩, 用瀏覽器執行本機 HTML5 + jQuery +WSH + Javascript 單頁應用程式 (Single Page Application, SPA) 大概是唯一的辦法了.

由於之前的 WSH 專案應用程式所使用的 jQuery UI 版本太舊了, UI 介面與函數用法也有些改變, 所以我對此 SPA 專案網頁架構進行了全新設計, 要點如下 :
  1. 以本機 IE/Edge 瀏覽器執行 (Active X + WSH 技術) 
  2. 無須伺服器, 以微軟 Active X 檔案處理取代資料庫 (避免資安問題)
  3. HTML5 + jQuery UI  + Bootstrap + WSH + Active X 技術堆疊
  4. 以頁籤面板 (tabs) 作為網頁應用程式主架構
關於 WSH 參考清大張智星老師的 "JavaScript 程式設計與應用" 這本書.

Source : 博客來

首先進行環境配置, 因為是要在封閉系統內的本機執行, 因此無法使用 CDN 獲取函式庫資源, 必須配置所需的 Javascript 函式庫, 包括本專案的主力 jQuery UI 與其動態表格外掛 DataTable, 以及配置網頁版面很好用的 Bootstrap 等等. jQuery UI 的最新版是 2016 年 9 月的 1.12.1 版 (似乎已停止更新), 下載位址為 :

https://jqueryui.com/download/all/

請下載 1.12.1 版的 jQuery UI 與其主題布景 (themes) 之 zip 壓縮檔 :




解壓縮 jQuery UI 後, 將其中的 jquery-ui.js (或 jquery-ui.min.js) 以及 external 資料夾下的 jquery.js (此為搭配此版 jQuery UI 的 1.12.4 版) 複製到專案根目錄的 jquery 資料夾下. 然後解壓縮 jquery-ui-themes-1.12.1.zip 檔, 將解出的 themes 資料夾整個複製到專案根目錄的 jquery 資料夾下即可 :



初步目錄結構如下 :




基本上這樣就可以開始建構本機網頁系統了, 但我想用 Bootstrap 框架來呈現靜態表格資料以及資料排版, Bootstrap 目前最新是 4.5 版 :

https://getbootstrap.com/docs/4.5/getting-started/download/

下載 Bootstrap 後解壓縮 zip 檔, 裡面有 js 與 css 兩個資料夾, 將 js 目錄底下的 bootstrap.js, bootstrap.js.map 以及 css 目錄底下的 bootstrap.css, bootstrap.css.map 與 bootstrap-grid.css 複製到專案根目錄下的 bootstrap 資料夾下 (注意, .map 檔雖然不影響主要功能, 但 bootstrap.js 會載入該檔, 若缺此兩檔案在瀏覽器 console 會出現錯誤) :




最後使用 jQuery UI 的頁嵌面板作為網頁結構, 寫一個首頁 index_1.htm, 放在根目錄下 :




範例 1 : 單純的頁嵌面板網頁 

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery UI Single Page Application</title>
  <script src="jquery/jquery.js"></script>
  <script src="jquery/jquery-ui.js"></script>
  <script src="bootstrap/bootstrap.js"></script>
  <link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <link href="bootstrap/bootstrap.css" rel="stylesheet">
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      font-size:10px;
      }
  </style>
</head>
<body>
  <div id="tabs">
    <ul>
      <li><a href="#tabs-1">tab1</a></li>
      <li><a href="#tabs-2">tab2</a></li>
      <li><a href="#tabs-3">tab3</a></li>
    </ul>
    <div id="tabs-1">tab1</div>
    <div id="tabs-2">tab2</div>
    <div id="tabs-3">tab3</div>
  </div>
  <script>
    $(function(){
      $("#tabs" ).tabs();
    }); 
  </script>
</body>
</html>

這裡使用了我最喜歡的 hot-sneaks 主題佈景, 結果如下 :




jQuery UI 提供了 25 種主題佈景, 我們可以為網頁加上一個下拉式選單 selectmenu, 透過 jQuery 強大的 DOM 操作功能隨意更換主題佈景, 參考之前的文章 :

# jQuery UI 學習筆記 (一) : 主題布景 (Themes)
jQuery UI 學習筆記 (十) : 下拉式選單 (Selectmenu)

在上面範例 1 的基礎上添加一個 jQuery UI 的 SelectMenu 元件用來選擇主題佈景 (預設是 hot-sneaks), 例如 :


範例 2 : 可選擇主題布景的頁嵌面板網頁

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery UI Single Page Application</title>
  <script src="jquery/jquery.js"></script>
  <script src="jquery/jquery-ui.js"></script>
  <script src="bootstrap/bootstrap.js"></script>
  <link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <link href="bootstrap/bootstrap.css" rel="stylesheet">
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      font-size:10px;
      }
  </style>
</head>
<body>
  <select id="themes">
    <option value="base">base</option>
    <option value="black-tie">black-tie</option>
    <option value="blitzer">blitzer</option>
    <option value="cupertino">cupertino</option>
    <option value="dark-hive">dark-hive</option>
    <option value="dot-luv">dot-luv</option>
    <option value="eggplant">eggplant</option>
    <option value="excite-bike">excite-bike</option>
    <option value="flick">flick</option>
    <option value="hot-sneaks">hot-sneaks</option>
    <option value="humanity">humanity</option>
    <option value="le-frog">le-frog</option>
    <option value="mint-choc">mint-choc</option>
    <option value="overcast">overcast</option>
    <option value="pepper-grinder">pepper-grinder</option>
    <option value="redmond">redmond</option>
    <option value="smoothness">smoothness</option>
    <option value="south-street">south-street</option>
    <option value="start">start</option>
    <option value="sunny">sunny</option>
    <option value="swanky-purse">swanky-purse</option>
    <option value="trontastic">trontastic</option>
    <option value="ui-darkness">ui-darkness</option>
    <option value="ui-lightness">ui-lightness</option>
    <option value="vader">vader</option>
  </select>
  <div id="tabs">
    <ul>
      <li><a href="#tabs-1">tab1</a></li>
      <li><a href="#tabs-2">tab2</a></li>
      <li><a href="#tabs-3">tab3</a></li>
    </ul>
    <div id="tabs-1">tab1</div>
    <div id="tabs-2">tab2</div>
    <div id="tabs-3">tab3</div>
  </div>
  <script>
    $(function(){
      $("#themes").selectmenu();
      $("#themes").val("hot-sneaks");
      $("#themes").selectmenu("refresh");
      $('#themes').on('selectmenuchange', function() {
        var theme=$(this).val();
        var href="jquery/themes/" + theme + "/jquery-ui.min.css";
        $("#theme").attr("href", href);
        });
      $("#tabs" ).tabs();
      });
  </script>
</body>
</html>

上面藍色部分為因應主題佈景選項而加入的網頁與程式碼, 結果如下 :




下面範例 3 則是將主題佈景選擇器與頁籤面板兩個區塊分開, 如果不需要主題佈景部分, 只要將該區塊直接刪除即可 (以下測試為了簡單將固定使用 hot-sneaks 主題佈景) :


範例 3 : 可選擇主題布景的頁籤面板網頁 (選擇器與頁籤面板分離)

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery UI Single Page Application</title>
  <script src="jquery/jquery.js"></script>
  <script src="jquery/jquery-ui.js"></script>
  <script src="bootstrap/bootstrap.js"></script>
  <link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <link href="bootstrap/bootstrap.css" rel="stylesheet">
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      font-size:10px;
      }
  </style>
</head>
<body>
  <!-- 主題佈景選擇器 -->
  <select id="themes">
    <option value="base">base</option>
    <option value="black-tie">black-tie</option>
    <option value="blitzer">blitzer</option>
    <option value="cupertino">cupertino</option>
    <option value="dark-hive">dark-hive</option>
    <option value="dot-luv">dot-luv</option>
    <option value="eggplant">eggplant</option>
    <option value="excite-bike">excite-bike</option>
    <option value="flick">flick</option>
    <option value="hot-sneaks">hot-sneaks</option>
    <option value="humanity">humanity</option>
    <option value="le-frog">le-frog</option>
    <option value="mint-choc">mint-choc</option>
    <option value="overcast">overcast</option>
    <option value="pepper-grinder">pepper-grinder</option>
    <option value="redmond">redmond</option>
    <option value="smoothness">smoothness</option>
    <option value="south-street">south-street</option>
    <option value="start">start</option>
    <option value="sunny">sunny</option>
    <option value="swanky-purse">swanky-purse</option>
    <option value="trontastic">trontastic</option>
    <option value="ui-darkness">ui-darkness</option>
    <option value="ui-lightness">ui-lightness</option>
    <option value="vader">vader</option>
  </select>
  <script>
    $(function(){
      $("#themes").selectmenu();
      $("#themes").val("hot-sneaks");
      $("#themes").selectmenu("refresh");
      $('#themes').on('selectmenuchange', function() {
        var theme=$(this).val();
        var href="jquery/themes/" + theme + "/jquery-ui.min.css";
        $("#theme").attr("href", href);
        });
      }); 
  </script>
  <!-- 頁嵌面板 -->
  <div id="tabs">
    <ul>
      <li><a href="#tabs-1">tab1</a></li>
      <li><a href="#tabs-2">tab2</a></li>
      <li><a href="#tabs-3">tab3</a></li>
    </ul>
    <div id="tabs-1">tab1</div>
    <div id="tabs-2">tab2</div>
    <div id="tabs-3">tab3</div>
  </div>
  <script>
    $(function(){
      $("#tabs" ).tabs();
      });
  </script>
</body>
</html>

以上就是這個網頁應用程式的基本架構, 在此基礎上可在各個頁籤顯示不同的資料, 如果網頁是放在伺服器上, jQuery UI 的 tabs 元件各頁籤的內容不一定要寫在網頁中, 也可以指定外部網頁, 例如我們可以將上面第一個頁籤的 href 改為 href="tabs-1.htm", 這樣 id="tabs-1" 的 div 元件就不需要了, div 的內容可以寫在外部檔案 tabs-1.htm 中, 效果一樣, 但這是利用 Ajax 技術從伺服器下載 tabs-1.htm 檔案內容到頁籤中, 網頁若放在伺服器上是沒問題, 但若在本機則無 Ajax 載入效果, 頁籤 1 將不會顯示 tabs-1.htm 的內容, 解決辦法是必須透過檔案函式庫讀取, 再用 jQuery 物件的 html() 函數填入 DOM 中.

有了上面建立的網頁應用程式基礎架構後, 接著測試如何用 Bootstrap 呈現靜態表格, 參考之前的文章 :

Bootstrap 4 學習筆記 (三) : 表格

Bootstrap 的表格其實是透過下列五個表格類別來控制 table 元素的樣式, 不過為了在表格長度或寬度超過螢幕時產生卷軸 (自適應效果), 通常會在 table 元素外面套一個 div 容器 (套用 container 與 table-responsive 樣式類別) :


 table 元素樣式類別 說明
 table 套用在 table 元素上的 Bootstrap 表格預設樣式 
 table-striped 在 table 類別基礎上加上奇偶列背景顏色交替變換效果 
 table-bordered 在 table 類別基礎上加上顯示儲存格邊框樣式
 table-hover 在 table 類別基礎上加上滑鼠滑過時變換背景色效果
 table-condensed 在 table 類別基礎上加上儲存格大小緊縮效果


下面範例 4 的三個頁籤分別套用這些表格樣式類別來呈現相同的資料 :


範例 4 : 套用 Bootstrap 的表格樣式類別

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery UI Single Page Application</title>
  <script src="jquery/jquery.js"></script>
  <script src="jquery/jquery-ui.js"></script>
  <script src="bootstrap/bootstrap.js"></script>
  <link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <link href="bootstrap/bootstrap.css" rel="stylesheet">
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      font-size:10px;
      }
  </style>
</head>
<body>
  <!-- 頁嵌面板 -->
  <div id="tabs">
    <ul>
      <li><a href="#tabs-1">預設樣式 table</a></li>
      <li><a href="#tabs-2">表格邊框 table-bordered</a></li>
      <li><a href="#tabs-3">奇偶交替 table-striped</a></li>
      <li><a href="#tabs-4">滑鼠掠過 table-hover</a></li>
      <li><a href="#tabs-5">緊實儲存格 table-condensed</a></li>
      <li><a href="#tabs-6">混合效果與自適應容器</a></li>
    </ul>
    <div id="tabs-1">
      <div class="container">
        <table class="table">
          <thead>
            <tr>
              <th>姓名</th>
              <th>國文</th>
              <th>英文</th>
              <th>數學</th>
              <th>物理</th>
              <th>化學</th>
              <th>歷史</th>
              <th>地理</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>王小二</td>
              <td>56</td>
              <td>78</td>
              <td>88</td>
              <td>45</td>
              <td>89</td>
              <td>65</td>
              <td>73</td>
            </tr>
            <tr>
              <td>李大明</td>
              <td>86</td>
              <td>93</td>
              <td>79</td>
              <td>85</td>
              <td>90</td>
              <td>87</td>
              <td>75</td>
            </tr>
            <tr>
              <td>張阿花</td>
              <td>100</td>
              <td>98</td>
              <td>75</td>
              <td>95</td>
              <td>89</td>
              <td>100</td>
              <td>95</td>
            </tr>
           </tbody>
        </table>
      </div> 
    </div>
    <div id="tabs-2">
      <div class="container">
        <table class="table table-bordered">
          <thead>
            <tr>
              <th>姓名</th>
              <th>國文</th>
              <th>英文</th>
              <th>數學</th>
              <th>物理</th>
              <th>化學</th>
              <th>歷史</th>
              <th>地理</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>王小二</td>
              <td>56</td>
              <td>78</td>
              <td>88</td>
              <td>45</td>
              <td>89</td>
              <td>65</td>
              <td>73</td>
            </tr>
            <tr>
              <td>李大明</td>
              <td>86</td>
              <td>93</td>
              <td>79</td>
              <td>85</td>
              <td>90</td>
              <td>87</td>
              <td>75</td>
            </tr>
            <tr>
              <td>張阿花</td>
              <td>100</td>
              <td>98</td>
              <td>75</td>
              <td>95</td>
              <td>89</td>
              <td>100</td>
              <td>95</td>
            </tr>
           </tbody>
        </table>
      </div> 
    </div>
    <div id="tabs-3">
      <div class="container">
        <table class="table table-striped">
          <thead>
            <tr>
              <th>姓名</th>
              <th>國文</th>
              <th>英文</th>
              <th>數學</th>
              <th>物理</th>
              <th>化學</th>
              <th>歷史</th>
              <th>地理</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>王小二</td>
              <td>56</td>
              <td>78</td>
              <td>88</td>
              <td>45</td>
              <td>89</td>
              <td>65</td>
              <td>73</td>
            </tr>
            <tr>
              <td>李大明</td>
              <td>86</td>
              <td>93</td>
              <td>79</td>
              <td>85</td>
              <td>90</td>
              <td>87</td>
              <td>75</td>
            </tr>
            <tr>
              <td>張阿花</td>
              <td>100</td>
              <td>98</td>
              <td>75</td>
              <td>95</td>
              <td>89</td>
              <td>100</td>
              <td>95</td>
            </tr>
           </tbody>
        </table>
      </div> 
    </div>
    <div id="tabs-4">
      <div class="container">
        <table class="table table-hover">
          <thead>
            <tr>
              <th>姓名</th>
              <th>國文</th>
              <th>英文</th>
              <th>數學</th>
              <th>物理</th>
              <th>化學</th>
              <th>歷史</th>
              <th>地理</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>王小二</td>
              <td>56</td>
              <td>78</td>
              <td>88</td>
              <td>45</td>
              <td>89</td>
              <td>65</td>
              <td>73</td>
            </tr>
            <tr>
              <td>李大明</td>
              <td>86</td>
              <td>93</td>
              <td>79</td>
              <td>85</td>
              <td>90</td>
              <td>87</td>
              <td>75</td>
            </tr>
            <tr>
              <td>張阿花</td>
              <td>100</td>
              <td>98</td>
              <td>75</td>
              <td>95</td>
              <td>89</td>
              <td>100</td>
              <td>95</td>
            </tr>
           </tbody>
        </table>
      </div> 
    </div>
    <div id="tabs-5">
      <div class="container">
        <table class="table table-condensed">
          <thead>
            <tr>
              <th>姓名</th>
              <th>國文</th>
              <th>英文</th>
              <th>數學</th>
              <th>物理</th>
              <th>化學</th>
              <th>歷史</th>
              <th>地理</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>王小二</td>
              <td>56</td>
              <td>78</td>
              <td>88</td>
              <td>45</td>
              <td>89</td>
              <td>65</td>
              <td>73</td>
            </tr>
            <tr>
              <td>李大明</td>
              <td>86</td>
              <td>93</td>
              <td>79</td>
              <td>85</td>
              <td>90</td>
              <td>87</td>
              <td>75</td>
            </tr>
            <tr>
              <td>張阿花</td>
              <td>100</td>
              <td>98</td>
              <td>75</td>
              <td>95</td>
              <td>89</td>
              <td>100</td>
              <td>95</td>
            </tr>
           </tbody>
        </table>
      </div> 
    </div>
    <div id="tabs-6">
      <div class="container table-responsive">
        <table class="table table-bordered table-striped table-hover table-condensed">
          <thead>
            <tr>
              <th>姓名</th>
              <th>國文</th>
              <th>英文</th>
              <th>數學</th>
              <th>物理</th>
              <th>化學</th>
              <th>歷史</th>
              <th>地理</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>王小二</td>
              <td>56</td>
              <td>78</td>
              <td>88</td>
              <td>45</td>
              <td>89</td>
              <td>65</td>
              <td>73</td>
            </tr>
            <tr>
              <td>李大明</td>
              <td>86</td>
              <td>93</td>
              <td>79</td>
              <td>85</td>
              <td>90</td>
              <td>87</td>
              <td>75</td>
            </tr>
            <tr>
              <td>張阿花</td>
              <td>100</td>
              <td>98</td>
              <td>75</td>
              <td>95</td>
              <td>89</td>
              <td>100</td>
              <td>95</td>
            </tr>
           </tbody>
        </table>
      </div> 
    </div> 
  </div>
  <script>
    $(function(){
      $("#tabs" ).tabs();
      });
  </script>
</body>
</html>

效果如下 :




以上為用顯示靜態表格的方法, 測試檔案都寄存於 GitHub :

https://github.com/tony1966/test/tree/master/jqueryui-app

若要有欄位排序功能, 則必須使用 DataTable 外掛了.


2020-08-19 補充 :

今天在測試 jQuery UI 對話框時發現右上角的 close 圖示不見了, 仔細看版面似乎也跟單獨使用 jQuery UI 時稍有變化, 拿掉 Bootstrap 後一切恢復正常, 確認混用 jQuery UI 與 Bootstrap 會造成 CSS 樣式衝突 :


有 Bootstrap

無 Bootstrap


因此決定拿掉 Bootstrap, 純粹使用 jQuery UI, 內部版面還是使用無邊框的 TABLE 元素為宜.

程式主結構修改如下 (純 jQuery UI) :

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery UI Single Page Application</title>
  <script src="jquery/jquery.js"></script>
  <script src="jquery/jquery-ui.js"></script>
  <link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      font-size:10px;
      }
  </style>
</head>
<body>
  <!-- 主題佈景選擇器 -->
  <select id="themes">
    <option value="base">base</option>
    <option value="black-tie">black-tie</option>
    <option value="blitzer">blitzer</option>
    <option value="cupertino">cupertino</option>
    <option value="dark-hive">dark-hive</option>
    <option value="dot-luv">dot-luv</option>
    <option value="eggplant">eggplant</option>
    <option value="excite-bike">excite-bike</option>
    <option value="flick">flick</option>
    <option value="hot-sneaks">hot-sneaks</option>
    <option value="humanity">humanity</option>
    <option value="le-frog">le-frog</option>
    <option value="mint-choc">mint-choc</option>
    <option value="overcast">overcast</option>
    <option value="pepper-grinder">pepper-grinder</option>
    <option value="redmond">redmond</option>
    <option value="smoothness">smoothness</option>
    <option value="south-street">south-street</option>
    <option value="start">start</option>
    <option value="sunny">sunny</option>
    <option value="swanky-purse">swanky-purse</option>
    <option value="trontastic">trontastic</option>
    <option value="ui-darkness">ui-darkness</option>
    <option value="ui-lightness">ui-lightness</option>
    <option value="vader">vader</option>
  </select>
  <script>
    $(function(){
      $("#themes").selectmenu();
      $("#themes").val("hot-sneaks");
      $("#themes").selectmenu("refresh");
      $('#themes').on('selectmenuchange', function() {
        var theme=$(this).val();
        var href="jquery/themes/" + theme + "/jquery-ui.min.css";
        $("#theme").attr("href", href);
        });
      });
  </script>
  <!-- 頁嵌面板 -->
  <div id="tabs">
    <ul>
      <li><a href="#tabs-1">tab1</a></li>
      <li><a href="#tabs-2">tab2</a></li>
      <li><a href="#tabs-3">tab3</a></li>
    </ul>
    <div id="tabs-1">tab1</div>
    <div id="tabs-2">tab2</div>
    <div id="tabs-3">tab3</div>
  </div>
  <script>
    $(function(){
      $("#tabs" ).tabs();
      });
  </script>
</body>
</html>

沒有留言 :