2014年12月29日 星期一

做百日

今天是農曆11月初8, 請假一天回鄉下給母親做百日. 母親離世已三個月又 10 天矣. 因舅媽趕不上, 阿泉伯要去探病, 所以這次只有我跟爸兩人而已. 好像該叫菁菁請假才對.

到慈恩塔時只見另一戶在做對參年, 那伯母很是健談, 來自新威庄, 我聽她向其亡夫稟告了一堆莊稼事務, 像是五厘田豆子賣了五萬元等等, 我想都做神仙了哪還管這麼多家務事呢. 爸說所謂對參年是滿一年跟三年一起做之意, 古時須各做一次. 現在禮俗一切精簡了, 但我覺得還是很多規矩須注意. 昨日去買豬肉, 榮發舅媽還特別叮囑百日與對參年須庫銀, 分年則不可, 須特別記下, 不然易忘.

下午4點半趁著天還沒暗去慢跑, 回來時經過對面的張家夥房, 見榮彩伯母在水圳洗菜, 我跟她打了招呼, 她說拿顆高麗菜回去吧, 爸說前天才給我們一大把小白菜哩. 以前媽種很多菜的時候常拿菜給她, 所以現在我還能感受到母親慷慨的餘蔭. 其實我覺得她們應該是互通有無啦, 只是多與少而已.






2014年12月27日 星期六

用 jQuery EasyUI 打造輕量級 CMS (七)

今天繼續寫系統設定表單管理, 發現欄位 system_state 與 auth_policy 所用的 radio 元件切換選項後卻無法取出選取的資料.

在 sys.php 的 case 'admin' 中這兩個圓鈕我是這樣寫 :
       
       //製作系統狀態 radio 群組
       $radio="<input type='radio' name='system_state' "; //共同部分
       if ($system_state=="active") { 
         $system_state_radio=$radio." value='active' checked>工作中 ".
                             $radio." value='inactive'>維護中"; 
         } //end of if
       else { 
         $system_state_radio=$radio." value='active'>工作中 ".
                             $radio." value='inactive' checked>維護中";
         } //end of else
       //製作使用者認證政策 radio 群組
       $radio="<input type='radio' name='auth_policy' "; //共同部分
       if ($auth_policy=="normal") { 
         $auth_policy_radio=$radio." value='normal' checked>普通 ".
                            $radio." value='strict'>嚴格"; 
         } //end of if
       else { 
         $auth_policy_radio=$radio." value='normal'>普通 ".
                            $radio." value='strict' checked>嚴格";
         } //end of else

然後寫到 PHP 的長字串中 :

        <td style="width:50%;padding:3px;">
          <label style="width:100px;display:inline-block;">系統運行狀態 : </label>
          {$system_state_radio}
        </td>

        <td style="width:50%;padding:3px;">
          <label style="width:100px;display:inline-block;">使用者認證政策 : </label>
          {$auth_policy_radio}
        </td>

這個設定表單會回傳給 home.php 的管理標籤的 href 顯示 :

  <div class="tab" title="管理">
    <div id="sys_settings" class="easyui-panel" title="系統設定" style="width:100%;padding:5px" data-options="href:'sys.php?op=admin',tools:'#settings-tools'">
    </div>
    <div id="settings-tools">
      <a href="#" id="reload-settings" class="icon-reload" title="重新載入"></a>
      <a href="#" id="save-settings" class="icon-save" title="儲存"></a>
    </div>
  </div>

按下 save-settings 按鈕圖示時, 就將表單內容以 Ajax 方式傳給 sys.php 的 case 'update_sys_settings' 區段更新 sys_settings 資料表 :

      $("#save-settings").bind("click",function(){
        var params={
          op:"update_sys_settings",
          site_title:$("[name=site_title]").val(),
          sys_theme:$("[name=sys_theme]").val(),
          system_state:$("[name=system_state]").val(),
          auth_policy:$("[name=auth_policy]").val(),
          min_password_length:$("[name=min_password_length]").val(),
          password_type:$("[name=password_type]").val()
          };
        alert($.param(params));
        var callback=function(data,textStatus){
          if (data.status==="success"){
            $.messager.alert("訊息","系統設定更新成功!");
            }
          else {$.messager.alert("訊息","系統設定更新失敗!");}
          }
        $.post("sys.php",params,callback,"json");
        });

後端 sys.php 處理區塊為 :

  case "update_sys_settings" : {
       $data_array["sys_theme"]=$_REQUEST["sys_theme"];
       $data_array["site_title"]=$_REQUEST["site_title"];
       $data_array["system_state"]=$_REQUEST["system_state"];
       $data_array["auth_policy"]=$_REQUEST["auth_policy"];
       $data_array["min_password_length"]=
          $_REQUEST["min_password_length"];
       $data_array["password_type"]=$_REQUEST["password_type"];
       $result=update_all("sys_settings", $data_array);
       if ($result===TRUE) {$status="success";}
       else {$status="failure";}
       $arr["status"]=$status;
       echo json_encode($arr);
       break;
       }    

問題來了, 這兩個單選圓鈕傳不出去選定的值, 一直都固定傳出 normal 與 active :



左看右看就是沒問題啊, 怎會擷取不到選定值呢? 用 select 拉霸都正常.

註 : 原因找到了, radio 不能只用 [name=system_state] 來選取, 因為它一組有兩個 radio, 必須再加上 :checked 來過濾才行 :

          system_state:$("[name=system_state]:checked").val(),
          auth_policy:$("[name=auth_policy]:checked").val(),

這樣就會取得選取的那個 radio 了. 其實不用這麼麻煩自行將欄位值製作成 params 物件, 直接使用 jQuery 為 form 提供的 serialize() 函數就可以了 :

      $("#save-settings").bind("click",function(){
        var params=$("#sys-settings-form").serialize();
        params="op=update_sys_settings&" + params;
        var callback=function(data,textStatus){
          if (data.status==="success"){
            $.messager.alert("訊息","系統設定更新成功!");
            }
          else {$.messager.alert("訊息","系統設定更新失敗!");}
          }
        $.post("sys.php",params,callback,"json");
        });


2014年12月26日 星期五

用 jQuery EasyUI 打造輕量級 CMS (六)

今天寫系統設定部分, 最短密碼長度欄位用 spinner, 卻發現 spinner 很奇怪不會上下調整 :

        <td style="width:50%;padding:3px;">
          <label style="width:100px;display:inline-block;">密碼最短長度 : </label>
          <input id="min_password_length" name="min_password_length" style="min-width:120px;">
        </td>

    $("#min_password_length").spinner({
      min:4,
      max:12,
      value:6,
      increment:1,
      required:true,
      missingMessage:'此欄位為必填'
      });

API 上有說明 spinner 不能用 markup 方式設定, 必須用 Javascript 才可以, 我也改了, 但還是不 work, 不知何故?

2014-12-26 : 但如果將 spinner 改成 numberspinner 就可以了.



1freehosting 的限制

今天查看我的 Yahoo 信箱發現, 1freehosting 來一封信表示我的帳號已被暫停, 連到後端管理介面才知, 由於 MySQL 查詢超過 90000 次以上, 所以被視為濫用免費帳號資源, 必須停止使用權 :


這家免費主機限制比較多, 還好我的測試網站沒這麼多存取, 目前還 OK.


2014年12月25日 星期四

用 jQuery EasyUI 打造輕量級 CMS (五)

這幾天趕進度所以沒空詳細紀錄修改過程. 昨晚將主題布景選擇功能搞定, 使用者隨時可選擇主題, 除了修改網頁本身連結的 css 設定檔路徑外, 同時也會以 Ajax 方式更新 sys_user 資料表中的 theme 欄位值, 這樣下一次登入時就會套用最近一次選擇的主題.

首先從資料庫取得使用者布景 :

//擷取系統設定布景
$RS=search("sys_settings");
if (is_array($RS)) {$sys_theme=$RS[0]["sys_theme"];}
else {$sys_theme="default";}
//擷取使用者布景
$RS=search("sys_users","account",$_SESSION["user_account"]);
if (is_array($RS)) {$user_theme=$RS[0]["theme"];}
else {$user_theme=$sys_theme;}

原先在 main.php 中的 header 中的主題布景是固定預設的 default :

  <link id="theme" rel="stylesheet" type="text/css" href="jquery/themes/default/easyui.css">

現在改成由 PHP 控制 :

  <link id="theme" rel="stylesheet" type="text/css" href="jquery/themes/<?php echo $user_theme ?>/easyui.css">

這裡 id 是用來給 jQuery 存取修改 href 屬性用的, 而右上角用來選主題的拉霸是用 Combobox 做的 :

            <select id="theme_sel" name="theme" class="easyui-combobox" style="width:120px;height:18px" panelHeight="320">
              <option value="default">主題布景</option>
<?php
$SQL="SELECT * FROM `sys_themes`";
$RS=run_sql($SQL);
if (is_array($RS)) {
  for ($i=0; $i<count($RS); $i++) {
    $theme=$RS[$i]["theme"];
?>
              <option value="<?php echo $theme ?>"<?php if ($theme==$user_theme) {echo " selected";}?>><?php echo $theme ?></option>
<?php
    }
  }
?>
            </select>

這裡 combobox 的 id 是用來以 jQuery 存取拉霸的, 而 name 用來將被選的主題傳給後端程式更新資料庫. 當然這 select 必須包在 form 元素中才能被傳送出去. 拉霸的事件處理程式如下 :

    $(document).ready(function(){
      $("#theme_sel").combobox({
        onSelect:function(rec){
          var css="jquery/themes/" + rec.value + "/easyui.css";
          $("#theme").attr("href", css);
          $.get("sys.php?op=change_theme",{theme:rec.value});
          }
        });
      });

當選擇主題時, combobox 會觸發 onSelect 事件, 並傳回 rec 物件, 選擇值放在 rec.value 中, 可利用 jQuery 的 attr 方法據此修改網頁的 link 元素所連結的主題布景 css 檔之位置, 然後用 ajax 的 get 方法更新後端資料庫中 sys_users 資料表裡使用者的布景設定 :

  case "change_theme" : {
       $theme=$_REQUEST["theme"];
       $SQL="UPDATE sys_users SET theme='".$theme."' WHERE account='".
            $_SESSION["user_account"]."'";
       $result=run_sql($SQL);
       break;
       }  


在後端的 sys.php 裡以 $_REQUEST["op"] 擷取操作碼, 再用 switch case 選擇執行區段.

除此之外的架構變動就是將 login.php, logout.php, 以及 change_theme.php 全整合於 sys.php 中, 用 op 參數決定要執行哪一種功能. 這樣能避免程式凌亂, 我打算架站系統本身的功能像留言, 新聞等都放在 sys.php 好了.

另外考量應用子系統是否該用獨立資料庫問題, 結論是要, 這樣才不會有各子系統資料表混在一起難以管理的問題, 一個系統就一個資料庫, 所以將 dbsettings.php 改名為 sysdb.php, 以後工作日誌子系統的資料庫設定檔就用 jlogdb.php ....

2014年12月24日 星期三

MySQL 不可用 order 做欄位名稱

這幾天在寫 EasyuiCMS 時, 把頁籤超連結的順序欄位名稱定為 order, 結果怪異的現象發生了, create table 可以順利建立這個欄位, 但要 insert 紀錄時卻沒辦法新增進去, 更討厭的是不會該. 誤了好久才驚覺 order 該不會是保留字吧? 一查還真的是咧. 參考 :

# MySQL保留字


2014年12月21日 星期日

買菜記

可能是冬至將屆的關係, 昨晚非常冷, 但今早的太陽卻非常強烈. 我在菜園空地對著東邊的暖陽做完八段錦已經八點半, 臉曬得熱烘烘的非常舒服. 吃完早餐才去市場, 已經九點半了, 有點晚.

母親不在後菜園暫時不種菜了, 上鎮裡的市集買菜成為每周日上午的行程, 因為要幫爸買一周的青菜跟魚. 每次菁菁都要跟, 因為她小時候每天都跟阿嬤上市場, 市集裡有許多試吃的攤位, 可以吃到許多東西. 她說印象最深刻的是魚丸伯母, 看她很可愛就給她很多試吃魚丸, 這就是她的丸子外號的來源.

高橋邊的豆腐攤是必買之點, 我現在都是一次買五塊, 回來時順路兩塊給大阿姨. 其次是開發財車來的賣菜阿伯, 他有賣現成的雞胸肉, 煮咖哩飯很方便, 還有白頭翁粄, 晚一點去就賣光了.



2014年12月20日 星期六

水車

今年過年時去岳父的吉洋農舍, 在附近農家門口看見屋主設計巧妙的水車, 我特地下車查看, 照相並錄影, 以備參考.

上週整理菜園時想到可以利用水車將圳裡的河水自動導入菜園, 這樣就不需要提水了. 於是從舊手機裡找出水車的相片與影片 :






屋主是用兩個廢棄的腳踏車輪子, 以電焊方式焊上打水板, 再於邊緣裝上養樂多空瓶組合而成水車, 水車轉動同時, 養樂多瓶子也順勢舀水, 下來時倒在旁邊的塑膠水管上, 設計真是巧妙啊! 


2014 運動會

今天菁菁學校運動會, 早上花了一些時間整理單據, 直到十一點才去看她們六年級的大隊接力. 這是小學最後一次運動會, 明年就要上國中啦.

菁菁她們班是穿黑色班服 (背上有 "才高八斗"), 她跑第三棒, 快交棒時差點就超越第二名跑者. 到最後第三棒終於再度超越, 逆轉勝!


可惜因為有一個同學轉彎時踩到線內, 總秒數被罰 5 秒, 從第一名變成第三名. 今年運動會規模較小, 中午 12 點過後就頒獎結束了, 而且最後也沒有家長與校友的大隊接力. 去年當我跑到司令台前時, 主任說已額滿, 明年再來, 哪知今年卻沒有, 殘念 ~~~.

以前運動會我都是一大早就跟小狐狸們一起到校, 而且全程參與, 今年卻似乎沒那麼起勁了, why? 或許是參加太多次了吧 ? 明年菁菁畢業後, 我就跟這個小學沒交集了, 好快呀, 十年就這樣過去了捏.

買到棉花糖要回去時, 遇到了菁菁三四年級的好朋友雨涵, 真是好久不見, 本來想把手中的棉花糖給她, 但想到已經 12 點半了還要再去排長龍, 只好算了, 早知道就要買兩個的說.


2014年12月19日 星期五

用 jQuery EasyUI 打造輕量級 CMS (四)

昨天搞定系統登入與安裝對話框後, 接下來要真的來處理 install.php 與 login.php 這兩個後端程式. 其實要移植系統只要專心兩三個禮拜應該就可以完成了, 從最早玩 PHP, AutoIT, 到 Javascript 與 Java 都是如此 (Python 還沒寫), 我都針對各語言寫一套常用函式庫來加速開發腳步. 但這回我要詳細記錄過程, 以免久了又淡忘了.

前三篇文章參考 :

用 jQuery EasyUI 打造輕量級 CMS (一)
用 jQuery EasyUI 打造輕量級 CMS (二)
用 jQuery EasyUI 打造輕量級 CMS (三)

在撰寫 install.php 之前, 必須擴充 mysql.php 函式庫, 因為系統安裝程式最主要的工作是新增系統資料表以及填入初始值. 首先要撰寫 create_database() 函式, 用來新增一個資料庫.

function create_database($database, $address=MYSQL_ADDRESS,
                         $username=MYSQL_USERNAME, $password=MYSQL_PASSWORD) {
  $conn=get_connection($address,$username,$password); //取得 MySQL 資料庫連線
  if (!$conn) {return FALSE;} //無法取得連線傳回 FALSE
  $SQL="CREATE DATABASE IF NOT EXISTS ".$database." DEFAULT CHARSET=utf8 ".
       "DEFAULT COLLATE=utf8_unicode_ci";
  $result=mysql_query($SQL, $conn); //成功傳回資源識別字, 失敗傳回 FALSE
  if (mysql_error($conn)) { //若失敗取得資料庫伺服器錯誤敘述
      if (SHOW_ERROR) {
          echo "MySQL CREATE DATABASE Error : ".mysql_error($conn)."<br>";
          }
     }
  return $result;
  }

此函式有四個參數, 後面三個為 MySQL 設定參數, 有預設值, 因此實務上只要傳入資料庫名稱這個參數. 此函式主要使用 CREATE DATABASE 指令來建立資料庫, 成功傳回資源識別字, 失敗傳回 FALSE.

範例 :

  $result=create_database("a000b_12345678_easyuicms");

事實上因為我們已經用 phpMyAdmin 建立資料庫 "a000b_12345678_easyuicms" 了, 所以這個函式應該在 install.php 中用不到, 但是寫加掛的應用子系統時會用到. 執行 CREATE TABLE 需要 MySQL 管理員帳號, 通常代管的虛擬主機只給我們使用者帳號, 因此呼叫 create_table() 函式會失敗, 但在本機的 localhost 則可以, 因為我們是用管理員帳號 root 執行.  

再來要定義 create_table() 函式, 用來新增資料表 :

function create_table($table, $field_array, $database=DATABASE) {
  $conn=get_connection(); //取得 MySQL 資料庫連線
  if (!$conn) {return FALSE;} //無法取得連線傳回 FALSE
  $result=mysql_select_db($database, $conn); //開啟資料庫
  if (!$result) {return FALSE;} //選擇資料庫失敗傳回 FALSE
  $fields_str=""; //欄位字串
  for ($i=0; $i<count($field_array); $i++) {
       list($key,$value)=each($field_array);
       $fields_str.="`".$key."` ".$value;
       if ($i!=count($field_array)-1) {$fields_str .= ",";}
       }
  $SQL="CREATE TABLE IF NOT EXISTS `".$table."` (".$fields_str.")";
  //echo $SQL;
  $result=mysql_query($SQL, $conn);
  if (mysql_error($conn)) { //若失敗取得資料庫伺服器錯誤敘述
      if (SHOW_ERROR) {
          echo "MySQL CREATE TABLE Error : ".mysql_error($conn)."<br>";
          }
      }
  return $result;
  }

此函式傳入三個參數, 但資料庫名稱已有預設值, 故實際上只要傳入資料表名稱以及欄位陣列即可. 這個 $field_array 是關聯式陣列, key 為欄位名稱, value 為欄位型態 :

可用型態如下  :
 VARCHAR(20)   : 可變長度字元 255 bytes (文字)
 CHAR(4)       : 固定長度字元 255 bytes (文字)
 TINYTEXT      : 255 Bytes (文字)
 TEXT          : 65535 bytes (文字)
 MEDIUMTEXT    : 16777215 bytes (文字)
 LONGTEXT      : 4294967295 bytes (文字)
 TINYBLOB      : 255 bytes (文字)
 BLOB          : 65535 bytes (文字,分大小寫)
 MEDIUMBLOB    : 16777215 bytes (文字,分大小寫)
 LONGBLOB      : 4294967295 bytes (文字,分大小寫)
 TINYINT(M)    : 1 bytes (最大顯示寬度 M<=255)
 SMALLINT(M)   : 2 bytes (最大顯示寬度 M<=255)
 MEDIUMINT(M)  : 3 bytes (最大顯示寬度 M<=255)
 INT,INTEGER(M): 4 bytes (最大顯示寬度 M<=255)
 BIGINT(M)     : 8 bytes (總位數 M<=65, 小數位數 D<=30&M-2)
 FLOAT(M,D)    : 4 bytes (總位數 M<=65, 小數位數 D<=30&M-2)
 DOUBLE(M,D)   : 8 bytes (總位數 M<=65, 小數位數 D<=30&M-2)
 DECIMAL(M,D)  : ? bytes (總位數 M<=65, 小數位數 D<=30&M-2)
 DATE          : 3 bytes (YY-MM-DD)
 DATETIME      : 8 bytes (YY-MM-DD HH:MM::SS)
 TIMESTAMP     : 4 bytes (1970-01-01 00:00:00)
 TIME          : 3 bytes (HH:MM:SS)
 YEAR(2|4)     : 1 byte (預設 4)
 ENUM          : 1~2 bytes (儲存單選 radio)
 SET           : 1~8 bytes (儲存多選 checkbox)
可用屬性如下  :
 SIGNED,UNSIGNED : 是否有負值 (數值)
 AUTO_INCREMENT  : 自動增量編號 (數值)
 BINARY          : 字元有大小寫之分 (文字)
 NULL,NOT NULL   : 是否允許不填入資料 (全部)
 DEFAULT         : 預設值
 PRIMARY KEY     : 資料表之唯一主鍵

範例 :

  $field_array["id"]="VARCHAR(20)";
  $field_array["name"]="VARCHAR(255)";
  $field_array["login_counts"]="INT(2)";
  $field_array["serial_no"]="INT(4) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT".
                            " PRIMARY KEY";
  $result=create_table("users",$field_array);

接著要建立 insert() 函式, 用來將紀錄寫進指定的資料表內 :

function insert($table, $data_array, $database=DATABASE) {
  $conn=get_connection(); //取得 MySQL 資料庫連線
  if (!$conn) {return FALSE;} //無法取得連線傳回 FALSE
  $result=mysql_select_db($database, $conn); //開啟資料庫
  if (!$result) {return FALSE;} //選擇資料庫失敗傳回 FALSE
  foreach ($data_array as $key => $value) { //產生欄位名稱與值陣列
           $tmp_col[] = $key;
           $tmp_dat[] = "'$value'";
          }
  $columns=join(",", $tmp_col); //轉成字串
  $data=join(",", $tmp_dat); //轉成字串
  $SQL="INSERT INTO ".$table."(".$columns.") VALUES(". $data.")";
  $result=mysql_query($SQL, $conn); //成功傳回資源識別字, 失敗傳回 FALSE
  if (mysql_error($conn)) { //若失敗取得資料庫伺服器錯誤敘述
      if (SHOW_ERROR) {
          echo "MySQL INSERT Error : ".mysql_error($conn)."<br>";
          }
     }
  return $result;
  },

此函式實際上是執行 SQL 的 INSERT INTO 指令, 它具有三個參數, 除資料庫名稱有預設值外, 實際傳入兩個 : 資料表名稱 $table 以及資料陣列 $data_array, 這也是一個關聯式陣列, key 為欄位名稱, value 為欄位值, 傳入的陣列於函式中會用迴圈來產生 SQL 指令中要寫入的資料字串.

範例 :

  $data_array["user"]="tony";
  $data_array["age"]=18;
  $result=insert("users", $data_array); //預設資料庫
  $result=insert("users", $data_array, "stocks");  //指定資料庫

另外再寫一個比較 General 的函式 run_sql(), 可執行所有 SQL 指令. 由於 SQL 指令中的 SELECT 指令會傳回紀錄集, 因此必須分成兩部分處理 :

function run_sql($SQL, $database=DATABASE) {
  $conn=get_connection(); //取得 MySQL 資料庫連線
  if (!$conn) {return FALSE;} //無法取得連線傳回 FALSE
  $result=mysql_select_db($database, $conn); //開啟資料庫
  if (!$result) {return FALSE;} //選擇資料庫失敗傳回 FALSE
  $result=mysql_query($SQL, $conn); //執行 SQL 指令
  if (mysql_error($conn)) { //若失敗取得資料庫伺服器錯誤敘述
      if (SHOW_ERROR) {
          echo "MySQL SELECT Error : ".mysql_error($conn)."<br>";
       }
      return $result; //失敗傳回 FALSE
      } //end of if
  else { //判別是否為 SELECT 指令
      if (substr_count($SQL, "SELECT")==1) { //SELECT 指令
         //從紀錄集資源識別字擷取紀錄為紀錄集陣列
         for ($i=0; $i<mysql_numrows($result); $i++) {
              $RS[$i]=mysql_fetch_array($result);
              } //end of for
         //若紀錄集陣列為一列 (單筆記錄), 則回傳單列紀錄 (即第一列索引 0)
         //if (sizeof($RS)==1) {$RS=$RS[0];}    
         return $RS; //傳回紀錄集陣列
         } //end of else
      else {return $result;} //傳回 TRUE
      } //end of else
  }

此函數有兩個參數, 資料庫名稱有預設值, 故只需傳入 SQL 指令字串即可. 如果 SQL 是 SELECT 指令, 還需要判斷傳回的紀錄集是否只有一筆, 若只有一筆就傳回單筆紀錄, 若是多筆就傳回紀錄集陣列.

範例 :

  $result=run_sql("DELETE FROM users WHERE id='peter'"); //注意, DELETE 無 *
  $SQL="UPDATE users SET login_count=5,name='彼得' WHERE id='peter'";
  $SQL="INSERT INTO users(id,name) VALUES('peter','彼得')";
  $result=run_sql($SQL);
  $RS=run_sql("SELECT * FROM tabs ORDER BY tab_order); //多筆紀錄
  for ($i=0; $i<count($RS); $i++) { //走訪紀錄集陣列
       echo $RS[$i]["tab_name"].",".$RS[$i]["age"];
       }

把這四個函式放進 mysql.php 中就可以開始撰寫 install.php 安裝程式了. 系統安裝主要有下列 9 個資料表要建立, 並初始化 :
  1. sys_settings :
    包括 EasyUI 主題布景, 網站標題, 系統狀態, 以及資安有關的密碼型態等. 
  2. sys_tabs :
    首頁與管理等平台相關的頁籤.
  3. sys_nav_blocks :
    管理導覽列. 
  4. sys_nav_links :
    儲存導覽列超連結.
  5. sys_header_links :
    儲存標題列超連結.
  6. sys_themes :
    用來儲存 EasyUI 的主題布景種類. 
  7. sys_users :
    儲存使用者資料.
  8. sys_visitors :
    紀錄到訪者資料.
  9. sys_apps :
    紀錄所掛載的應用子系統. 
# 2014-12-24 修改版 (login/logout/change_theme 整合為 sys.php 前)



2014年12月18日 星期四

用 jQuery EasyUI 打造輕量級 CMS (三)

在前兩篇文章中, 我們已完成打版與虛擬主機申請, 接下來要規劃資料庫結構與撰寫架站系統的功能. 前兩篇參考 :

用 jQuery EasyUI 打造輕量級 CMS (一)
用 jQuery EasyUI 打造輕量級 CMS (二)

基本上我是將之前用 jQuery UI 寫的 AmyPHP 大部分移植過來, 因為去年寫那系統時早已將輪子造好, 也就是一些常用的 Library, 例如 mysql.php 專門處理資料庫存取, parse.php 負責文字處理與資料剖析等等. 但那時沒時間詳細記錄撰寫過程, 因此趁著移植機會複習一下也好.

函式庫 mysql.php 主要的目的就是簡化我們對 MySQL 的存取而已, 首先是定義資料庫參數 :

define('MYSQL_ADDRESS','sql313.a000.biz');
define('MYSQL_USERNAME','a000b_12345678');
define('MYSQL_PASSWORD','blabla');
define('DATABASE','a000b_12345678_easyuicms');

然後定義資料庫連線函式 get_connection() :

function get_connection($address=MYSQL_ADDRESS, $username=MYSQL_USERNAME,
                        $password=MYSQL_PASSWORD) {
  @$conn=mysql_connect($address, $username, $password); //抑制錯誤顯示
  //設定查詢所用之字元集為 utf-8
  if ($conn) {mysql_query("SET NAMES 'utf8'");}
  else {
      if (SHOW_ERROR) {
          echo "無法建立 MySQL 資料庫連線, 請檢查 dbsettings.php 設定!<br>";
          }
      } //end of else
  return $conn;
  }

若連線成功, 此函式會傳回連線物件. 接著定義 find_table() 函式, 用來尋找指定的資料表 :

function find_table($table, $database=DATABASE) {
  $conn=get_connection(); //取得 MySQL 資料庫連線
  if (!$conn) {return FALSE;} //無法取得連線傳回 FALSE
  $result=mysql_select_db($database, $conn); //開啟資料庫
  if (!$result) {return FALSE;} //選擇資料庫失敗傳回 FALSE
  $SQL="SHOW TABLES;";
  $result=mysql_query($SQL, $conn); //顯示資料表
  while ($data=mysql_fetch_row($result)) {
      if ($data[0]==$table) {
          mysql_free_result($result);
          mysql_close($conn);
          return TRUE;
          } //end of if
      } //end of while
  mysql_free_result($result);
  mysql_close($conn);
  return FALSE;
  }

此函式利用上面所定義的 get_connection() 函式來建立資料庫連線. 傳入參數為所要尋找的資料表名稱, 利用 SHOW TABLES 指令取得資料庫中的所有資料表, 再用迴圈與傳入參數比對, 一找到符合的名稱就傳回 TRUE, 否則傳回 FALSE.

把上面兩個函式連同常數定義存成 mysql.php, 放在 lib 目錄下, 就可以開始寫初次連線的首頁程式 index.php 了. 我們原先打好的首頁版面要改為 home.htm (之後可能要改為 home.php).

首先勾畫一下系統運作邏輯. 當使用者連線網站時 (index.php), 程式要先判斷系統是否已存在 (搜尋資料庫中是否有 sys_settings 資料表), 如果已經存在, 表示網站系統已安裝過, 就顯示登入視窗; 否則表示系統尚未安裝, 就顯示系統資料庫設定視窗, 如下所示 :

$installed=find_table("sys-settings");
if ($installed===FALSE) { //系統尚未安裝
   //顯示系統安裝對話框
  }
else { //系統已安裝
  //顯示系統登入對話框
  }

這裡的對話框使用 Dialog 元件, 參考下面這篇的範例 4 :

# jQuery EasyUI 測試 : 對話框

所以, 我們的 index.php 需要用 if else 分別製作兩張對話框, 一個是系統安裝對話框, 另外是登入對話框.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
  <link rel="stylesheet" type="text/css" href="jquery/themes/default/easyui.css">
  <link rel="stylesheet" type="text/css" href="jquery/themes/icon.css">
  <script type="text/javascript" src="jquery/jquery.min.js"></script>
  <script type="text/javascript" src="jquery/jquery.easyui.min.js"></script>
  <script type="text/javascript" src="jquery/locale/easyui-lang-zh_TW.js"></script>
</head>
<body>
<?php
require_once("lib/mysql.php"); //匯入資料庫函式
$installed=find_table("sys_settings"); //檢查資料庫是否已安裝
$installed=FALSE; //檢查資料庫是否已安裝
if ($installed===FALSE) { //系統尚未安裝, 先安裝系統資料表
  //設定資料庫參數
$username="a000b_12345678";
$password="blabla";
$address="sql313.000a.biz";
$db="a000b_12345678_easyuicms";
?>
  <div id="install-dialog" class="easyui-dialog" title="系統安裝" style="width:370px;height:290px;padding:10px" buttons="#install-button">
    <div class="form-title" style="margin:5px;border-bottom:1px solid #ccc;">
      <p>請輸入 MySQL 資料庫伺服器管理員帳號密碼安裝</p>
      <p>若無管理員帳號, 請先以 phpMyAdmin 建立資料庫</p>
    </div>
    <form id="install-form" method="post" style="padding:5px 10px;">
      <div style="margin:5px">
        <label style="width:80px;display:inline-block;">MySQL 位址 : </label>
        <input name="address" type="text" class="easyui-textbox" required="true" data-options="missingMessage:'此欄位為必填'" style="width:220px" value="<?php echo $address?>">
      </div>
      <div style="margin:5px">
        <label style="width:80px;display:inline-block;">MySQL 帳號 : </label>
        <input name="username" type="text" class="easyui-textbox" required="true" data-options="missingMessage:'此欄位為必填'" style="width:220px" value="<?php echo $username?>">
      </div>
      <div style="margin:5px">
        <label style="width:80px;display:inline-block;">MySQL 密碼 : </label>
        <input name="password" type="text" class="easyui-textbox" required="true" data-options="missingMessage:'此欄位為必填'" style="width:220px" value="<?php echo $password?>">
      </div>
      <div style="margin:5px">
        <label style="width:80px;display:inline-block;">資料庫名稱 : </label>
        <input name="database" type="text" class="easyui-textbox" required="true" data-options="missingMessage:'此欄位為必填'" style="width:220px" value="<?php echo $db?>">
      </div>
    </form>
  </div>
  <div id="install-button" style="padding-right:15px;">
    <a href="#" id="install" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">安裝</a>
  </div>
  <script type="text/javascript">
    $(document).ready(function(){
      $("#install").bind("click",function(){
        $("#install-form").submit();
        });
      $("#install-form").form({
        url:"install.php",
        success:function(data){
          var data=eval('(' + data + ')');   //將 JSON 轉成物件
          if (data.success) {
            $("#install-dialog").dialog("close");
            var msg=data.msg;
            }
          else {var msg="安裝失敗!"}
          $.messager.alert("訊息",msg,"info");
          }
        });
      });
  </script>
<?php
    } //end of if:uninstalled
else { //系統已安裝
?>
  <div id="login-dialog" class="easyui-dialog" title="系統登入" style="width:370px;height:230px;padding:10px" buttons="#login-buttons">
    <div style="margin:5px;border-bottom:1px solid #ccc;">
      <p>請輸入帳號密碼</p>
    </div>
    <form id="login-form" method="post" style="padding:10px 30px;">
      <div style="margin:5px">
        <label style="width:60px;display:inline-block;">帳號 : </label>
        <input name="account" type="text" class="easyui-textbox" required="true" data-options="iconCls:'icon-man',missingMessage:'此欄位為必填'"  style="width:200px">
      </div>
      <div style="margin:5px">
        <label style="width:60px;display:inline-block;">密碼 : </label>
        <input name="password" type="password" class="easyui-textbox" required="true" data-options="iconCls:'icon-lock',missingMessage:'此欄位為必填'" style="width:200px">
      </div>
    </form>
  </div>
  <div id="login-buttons" style="padding-right:15px;">
    <a href="#" id="login-cancel" class="easyui-linkbutton" iconCls="icon-cancel" style="width:90px">取消</a>
    <a href="#" id="login" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">登入</a>
  </div>
  <script type="text/javascript">
    $(document).ready(function(){
      $("#login").bind("click",function(){
        $("#login-form").submit();
        });
      $("#login-form").form({
        url:"login.php",
        success:function(data){
          var data=eval('(' + data + ')');  //將 JSON 轉成物件
          if (data.success) {
            $("#install-dialog").dialog("close");
            var msg=data.msg;
            }
          else {var msg="帳號或密碼錯誤!"}
          $.messager.alert("訊息",msg,"info");
          }
        });
      });
  </script>
<?php
    } //end of else:installed
?>
</body>
</html>

這裡有兩個 Ajax 呼叫執行的後端程式,  分別是安裝系統的 install.php 與登入系統用的 login.php, 我們暫時簡單地回傳成功即可, 傳回值是一個如下的 JSON 字串 :

{"success":true,"msg":"\u7cfb\u7d71\u5b89\u88dd\u5b8c\u6210!"}

它會傳回給表單的 success 回呼函式的參數 data, 利用 eval('(' + data + ')') 即可將 JSON 字串轉成物件了.

install.php 內容如下 :

<?php
header('Content-Type: text/html;charset=UTF-8');
$arr["success"]=true;
$arr["msg"]="系統安裝完成!";
echo json_encode($arr);
?>

而 login.php 內容如下 :

<?php
header('Content-Type: text/html;charset=UTF-8');
$arr["success"]=true;
$arr["msg"]="您已登入系統!";
echo json_encode($arr);
?>

這兩個簡單程式只是為了要測試 index.php 的兩個對話框排版是否正確, 以及邏輯是否正常而已, 真正的 install.php 需在資料庫中插入系統表單, 以及填入預設資料; 而 login.php 則要驗證帳密是否符合.



把 index.php 中的 $installed 直接改成 FALSE, 就可以測試登入對話框了 :



可見兩個對話框版面都 OK, 但是光是花在調 style 就花了一整天時間捏. 我把目前為止的結果壓縮成 easyuicms-1.zip 備考.

範例 1 : http://mybidrobot.allalla.com/easyuitest/easyuicms-1.zip 

這只是紀錄移植過程中的檔案, 程式還要繼續改.



2014年12月17日 星期三

用 jQuery EasyUI 打造輕量級 CMS (二)

昨天搞定新版工作日誌要用的迷你 CMS 的主要版面後, 今天接著要重新設計資料庫與檔案系統. 上篇文章參考 :

用 jQuery EasyUI 打造輕量級 CMS (一)

為了不干擾目前所使用的測試伺服器, 打算找個免費虛擬主機來測試 CMS. 當然用本機的 localhost 也是 OK, 但是要先安裝 Apache + MySQL (這我當然有), 而且系統終究是要架到主機上跑, 直接上架可以在開發階段就找到可能的問題.

我找了 000a.biz 這家, 兩年前申請的帳號目前都還有效, 可見公司營運 OK, 而且不會隨便砍帳號. 申請新帳號很簡單, 只要有 email 就可以了, 首先到其首頁按下 SIGNUP :


然後填寫 E-mail, 網址與密碼, 選擇網站類型與語言, 輸入圖示安全碼 :


按 Register, 順利的話即顯示  :

An activation email has now been sent to abc@blabla.com.tw, please click the activation link in the email to activate this account

然後去收認證信, 點信中所附的超連結即開始設定網站環境 :


完成後顯示下列網頁, 表示網站已開通 :


這張表要記下來保存, 注意, cPanel, MySQL, 以及 FTP 的帳號密碼都是一樣的. 完成申請後, 它會再寄一封新帳號相關訊息的信, 主要是後台登入帳號與申請時所填的密碼, FTP 與 POP 伺服器位址, 這比上面網頁多了 POP 收信主機位址, 也要記下來保存 :


然後就可以用信中的帳密登入 cPanel 進行後台管理了 :

cpanel.000a.biz


進入後台第一件事情當然是設定 MySQL 資料庫 :


在 create new database 欄中填入資料庫末尾名稱, 按 Create Database 即可 :


這樣在 "您現有的資料庫" 列表中就會出現此新增的資料庫 :


這裡必須將 MySQL DB Name 與 MySQL Host Name 抄記下來, 以備寫 PHP 資料庫存取程式之用. 由上可知, 000a.biz 提供 400 個資料庫給免費使用者.

資料庫設定完成後, 按左上角的 "Home" 就可以回 cPanel 主畫面. 接下來就可以將我們在 part-1 所打好的版面網頁上傳到伺服器了. 上傳檔案要用 Files 底下的檔案管理員 :


首先到根目錄下, 這裡有提醒不要將網頁上傳到此目錄下, 而是要點 htdocs 進去, 可見系統已經預先放了 index2.html 首頁在此, 可以先將其刪除 :


然後將我們在 part-1 打好的版 easyui-cms-7.htm 改名成 index.htm, 點左方的 upload 鈕來上傳 :


點選好檔案後, 一定要按左上角的勾勾才會執行上傳. 傳完後按左箭頭可回到檔案列表 :


瀏覽網頁, 結果如下 :


原因是 ~~~ 我們還沒布置 jQuery EasyUI 的執行環境 ! 所以先連到 EasyUI 網站, 發現 EasyUI 已經從 1.4 版升級為 1.4.1 版 :

# 下載 jQuery EasyUI 1.4.1 版

下載 GPL 版的 zip 檔後解壓縮, 然後在專案根目錄下建立一個子目錄 jquery, 將下列解開的 2 個檔案與 2 個目錄複製到 jquery 目錄下 :



然後回到根目錄下修改 index.htm 的 js 與 css 路徑 :

  <link rel="stylesheet" type="text/css" href="jquery/themes/default/easyui.css">
  <link rel="stylesheet" type="text/css" href="jquery/themes/icon.css">
  <script type="text/javascript" src="jquery/jquery.min.js"></script>
  <script type="text/javascript" src="jquery/jquery.easyui.min.js"></script>
  <script type="text/javascript" src="jquery/locale/easyui-lang-zh_TW.js"></script>

這樣就可以把專案上傳到 000a.biz 了, 當然, 若要一個個傳檔可不得了, 因為 EasyUI 檔案非常多, cPanel 的檔案管理器支援 zip 上傳再解壓縮的功能, 因此在根目錄下將 index.htm 與 jquery 目錄全部壓縮為 easyuicms.zip :


然後將此 zip 檔上傳到 htdocs 下 :


然後勾選 easyuicms.zip, 按 Unzip 鈕解壓縮 :



這樣就大功告成了, 這時重新載入 index.htm, 果然正常顯示版面 :


接下來就可以開始撰寫登入畫面了.


果汁三神器

自從買了隨手杯果汁機後, 早餐的飲料幾乎都是自打的新鮮果汁. 剛開始都買柳丁, 奇異果, 百香果, 偶而會加入鳳梨, 這些水果打完後都需要濾渣, 這樣喝起來口感較好. 經過兩三周實務經驗, 我發現打果汁只要木瓜, 香蕉, 芭樂這三種即可, 加上一湯匙蜂蜜, 味道就非常棒, 最重要的是不用濾渣. 當然芭樂不可以把芯放進去打, 只削果肉進去.


這三神器的好處是 :
  1. 木瓜 : 酵素與貝他胡蘿蔔素含量高.
  2. 香蕉 : 酵素多, 亦可增添香味與濃稠感.
  3. 芭樂 : 維他命 C 含量高 (奇異果的兩倍)
其實再加個蘋果更好, 因為平常削成一塊塊, 只有姊姊會老實吃掉, 菁菁跟二哥都不吃. 打進果汁裡面就神不知鬼不覺全喝掉, 嘿嘿.

另外, 奇異果不建議打果汁, 因為會讓果汁偏酸, 喝起來喉嚨還有微微的刺痛感. 百香果也是一樣, 而且仔太大一定要濾渣, 太麻煩.

關於酵素, 有三種水果壞掉時是不生蟲的 : 香蕉, 木瓜, 與鳳梨. 印象中好像這三種水果富含的酵素就是天然的抗生素, 雖然壞掉的水果上面也是會有蒼蠅等昆蟲盤旋下蛋, 但就是孵不出來哩.

# 芭樂盛產 維生素C含量大勝奇異果2倍



2014年12月16日 星期二

姊姊班排第三

週日晚上洗澡時, 姊姊很興奮地跑到浴室外告訴我說, 這次段考班排大躍進, 從第九進到第三, 我真為她感到高興, 因為這是讀書以來第一次進到前三名, 國中時都在 9~12 名. 上次段考後她就發奮這次要拚到前三名, 希望繁星能上.

二哥上周也去柏碩補數學, 這周數學就拿 100 了. 在家自己讀除非很有自制力, 不然很容易分心. 我雜事多, 要盯功課實在力有未逮, 雖然我不認同補習, 但是, 台灣的教育系統就是這樣.


用 Moo0 Voice Recorder 網頁錄音

有時候在網路上聽到不錯的音樂想要錄下來, 以前我有一款 mp3 Recorder 可以用, 但到 win8 時代後, 該軟體卻無法安裝. 今天找到這款免費的 Moo0 Voice Recorder, 發現比之前的好用, 安裝即可使用, 不需任何設定, 參見 :

免費串流音樂錄音的軟體Moo0 Voice Recorder 1.43
http://www.moo0.com/



只要按開始錄音, 再讓網頁中的播放器 Play 即可.


D 大調的卡農

我第一次聽到這首曲子是陪菁菁去靈糧堂學吉他時, 老師之一是高應大吉他社的社長, 他在休息時間常常用旁邊的鋼琴練習這首曲子, 我一聽馬上為之著迷, 馬上問他此為何曲, 從此百聽不厭.

我找了卡農的資料, 原來卡農是一種好幾個聲部依次出現的曲式, 不是樂曲的名字, 而三百餘年來深受喜愛的這首卡農是德國作曲家帕海貝爾所寫的 "Canon and Gigue in D (D大調的卡農與吉格舞曲)", 是帕海貝爾眾多教會樂曲中最短的小品之作, 但也是讓他名留青史的佳作. 帕海貝爾與德國作曲家巴哈家族淵源深厚, 巴哈的哥哥是帕海貝爾的門生.

我不懂音樂, 但根據 "卡農正解" 這篇的說明, 原來卡農令人著迷的原因來自於其精妙的樂曲結構, 帕海貝爾以簡單的旋律, 遵守嚴謹的對位法不斷地重複, 產生綿延不絕的感覺, 難怪聽完還覺得繞樑三日.

旋律優美的卡農最常被用在婚禮中彈奏, 象徵愛情綿延不絕. 卡農更被收錄在代表人類文明的 27 首世界名曲中, 隨著 1977 年升空的航海家一號送進太空, 或許有一天會成為人類與外星人溝通的語言之一呢.

參考資料 :

# 卡農【Canon】 piano cover 鋼琴版 by:Miemie
# Canon in D violin duet - Tiffany
卡農的由來
# 卡農正解
# 卡農(CANON)的由來(一個浪漫憂傷的故事) 
# 卡農 (電影「凡夫俗子」「我的野蠻女友」經典配樂)

HTML5 語法驗證器


今天在 W3C 網站找到它提供的 HTML5 語法驗證器, 只要在 Address 欄輸入網頁的 URL, 再按 Check 就會在下方顯示有哪些錯誤.

http://validator.w3.org/


2014年12月15日 星期一

Stem OS

今天在 SlideShare 上看到這個叫做 Stem 的作業系統, 這是以 Javascript 為基礎開發的 OS (事實上就是 Node.js), 使用 HTML5 與網頁技術, 不僅可用來開發桌面程式, 還讓 Javascript 能用在嵌入式系統, 參考 :
  1. Node.js 進攻桌面開發
  2. 使用 JAVASCRIPT 大搞桌面應用和嵌入式系統!
  3. 誰說 Node.js 程式不能編成 binary
  4. Node.js能幹嘛?   
  5. Node.js真的无所不能?那些不适用的应用领域分析


用 jQuery EasyUI 打造輕量級 CMS (一)

公司的工作日誌系統明年將運轉屆滿十年, 這套當初用 ASP+Javascript+ACCESS 寫的簡易型 CMS 是我參考 myPHPNuke 的功能拼拼湊湊寫出來的, 前後端都使用 Javascript 撰寫. 我在這個架站平台上掛上加寫的營運所需的各個子系統, 因為系統是自己寫的, 所以要殺要剮都能隨心所欲, 使用至今倒也便利. 不過使用十年的系統畢竟太舊了, 網頁技術突飛猛進, 早已不知越過幾個世代, 所以靜極思動, 想要用 jQuery EasyUI 來改寫. 目前主要構想如下 :
  1. 後端改用 PHP + MySQL 架構.
  2. 前端 Javascript 框架使用 EasyUI 做介面.
  3. 版面使用 Layout 元件排版 (不需 South), 子系統用 Tabs 管理內容.
  4. 架站機平台極簡化, 只含留言板, 會員管理與子系統管理.
  5. 主要子系統為工作日誌, 以及各型機器之營運資料庫查詢管理子系統.
  6. 符合資安要求, 以免被電.
這兩天對以上構想的可能性做了一番測試, 基本模型如下面範例 1 :

範例 1 : http://mybidrobot.allalla.com/easyuitest/easyui-cms-1.htm [看原始碼]

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>EasyUI 測試</title>
  <link rel="stylesheet" type="text/css" href="../jquery/easyui-themes/default/easyui.css">
  <link rel="stylesheet" type="text/css" href="../jquery/easyui-themes/icon.css">
  <script type="text/javascript" src="../jquery/jquery.js"></script>
  <script type="text/javascript" src="../jquery/jquery.easyui.min.js"></script>
  <script type="text/javascript" src="../jquery/easyui-lang-zh_TW.js"></script>
  <style>
    .navbar {
      width:180px;
      padding-left:5px;
      padding-right:5px;
      padding-bottom:5px;
      }
    .nav {
      margin-bottom:5px;
      }
    .content {
      overflow:auto;
      padding-bottom:5px;
      }
    .tab {
      padding:10px;
      overflow:auto;
      position:relative;
      }
    #header {
      padding-left:5px;
      padding-right:5px;
      padding-top:5px;
      }
  </style>
</head>
<body>
  <div id="layout-1" class="easyui-layout" data-options="fit:true" style="">
    <div id="header" data-options="region:'north',border:false">
      <div class="easyui-panel" title="MySystem" data-options="tools:'#tools'"  style="height:60px;padding-top:5px;padding-left:5px;padding-right:5px;">
      歡迎光臨! 今天是 2014 年 12 月 12 日
      </div>
    </div>
    <div id="tools">
     <a href="#" id="nav-west" class="icon-remove"></a>
     <a href="#" id="nav-east" class="icon-remove"></a>
    </div>
    <div class="navbar" data-options="region:'east',border:false">
      <div class="easyui-panel nav" title="MyPanel">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
      <div class="easyui-panel nav" title="MyPanel">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
    </div>

    <div class="navbar" data-options="region:'west',border:false">
      <div class="easyui-panel nav" title="MyPanel-1">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
      <div class="easyui-panel nav" title="MyPanel-2">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
      <div class="easyui-panel nav" title="MyPanel-3">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
      <div class="easyui-panel nav" title="MyPanel-4">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
      <div class="easyui-panel nav" title="MyPanel-5">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
      <div class="easyui-panel nav" title="MyPanel-6">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
      <div class="easyui-panel nav" title="MyPanel-7">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
      <div class="easyui-panel nav" title="MyPanel-8">
        <ul>
          <li>Hello World!</li>
          <li>Hello World!</li>
          <li>Hello World!</li>
        </ul>
      </div>
    </div>
    <div class="content" data-options="region:'center',border:false">
      <div class="easyui-tabs">
        <div title="標題 1" class="tab">
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        內容 1<br>
        </div>
        <div title="標題 2" class="tab">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</div>
        <div title="標題 3" class="tab">內容 3</div>
      </div>
    </div>
  </div>
  <script language="javascript">
    $(document).ready(function(){
      $("#nav-west").bind("click",function(){
        if (this.className.indexOf("icon-remove") != -1){
          $("#layout-1").layout("collapse","west");
          this.className="icon-add";
          }
        else{
          $("#layout-1").layout("expand","west");
          this.className="icon-remove";
          }
        });
      $("#nav-east").bind("click",function(){
        if (this.className.indexOf("icon-remove") != -1){
          $("#layout-1").layout("collapse","east");
          this.className="icon-add";
          }
        else{
          $("#layout-1").layout("expand","east");
          this.className="icon-remove";
          }
        });
      });
  </script>
</body>
</html>


此例中我用 Layout 排版, 底下的 South 不需要, 只剩下東西北中四個區域. 北方用作網站標頭, 擺放登入訊息與子系統進入口. 東西兩方用作導覽連結框, 中間作為內容區. 為了畫面的美觀, 我捨棄了各區域的標題, 從而無法擺放展開縮合紐, 北方區域的標題上我放了兩個按鈕, 分別控制左右導覽框之縮合與展開. 注意, 這裡 this.className 的 this 為兩個按鈕之 DOM 物件, 使用 indexOf 判斷的原因是, 第一次按時其 className 不是只有 icon-remove 而已, 用 this.className=="icon-remove" 會傳回 false, 但按第二次就可以了.

上面範例中, 我特地在頁籤 1 與 2 中分別給予垂直與水平方向很長的內容, 可見它會自動出現捲軸.

另外我又陸陸續續做了幾個測試, 實在太雜了, 就不一一詳述, 如下列範例 :

範例 2 : http://mybidrobot.allalla.com/easyuitest/easyui-cms-2.htm [看原始碼]

範例 3 : http://mybidrobot.allalla.com/easyuitest/easyui-cms-3.htm [看原始碼]

範例 4 : http://mybidrobot.allalla.com/easyuitest/easyui-cms-4.htm [看原始碼]

範例 5 : http://mybidrobot.allalla.com/easyuitest/easyui-cms-5.htm [看原始碼]

範例 6 : http://mybidrobot.allalla.com/easyuitest/easyui-cms-6.htm [看原始碼]

最後進行微調, 將樣式集中, 單一性元素用 id, 重複性元素用 class, 這樣就清爽多了. 版面如下 :

範例 7 : http://mybidrobot.allalla.com/easyuitest/easyui-cms-7.htm [看原始碼]

接下來要考量資料庫了.

參考資料 :
  1. miniCMS
  2. A Micro CMS
  3. Controling Combobox "panel" height