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

種甘蔗

爸前幾天提到, 阿泉伯他們建議本期紅豆採收後, 將菜園縮減, 一半併入所連接的上方田, 免得沒種菜卻要時常除草. 我認為還是先保留原狀好, 雖說母親走後沒人會將種菜, 但若菜園縮減一半, 久之母親昔日所經營之菜園景象便不復記憶矣.

小舅之前曾建議菜園眼前先種些果樹, 可供四季水果無虞. 但我一直沒有時間去找秧苗. 週日下午為了做冬瓜封, 又去砍了一棵甘蔗回來, 心想每兩三個禮拜砍一棵, 這樣遲早把母親所種的甘蔗全用完啦. 因此削完甘蔗先不做冬瓜封, 先把剩下的幾節有芽眼的嫩莖拿到菜園東南角整地斜插種下, 順便把同事給我的一株木瓜也從盆栽中取出種在旁邊.

才勞動了三十分鐘就氣喘吁吁, 做農還真不是想像中簡單.


2014年12月12日 星期五

鄉民的由來

今天同事在問, 甚麼是鄉民? 為什麼叫鄉民? 我找了一下, 原來出自周星馳的九品芝麻官 :

# 鄉民 (網路用語)

鄉民=愛湊熱鬧的網民


補助通

今天在公司福利社看到 "補助通" 這個網站, 設計簡潔又實用, 對於家有獨居老人, 或身心障礙家人, 卻又搞不清楚到底政府有那些福利補助者, 可以快速取得相關補助與申請資訊.

日本人對於老人殘障者所提供的服務真是細緻周到.

# 日商知恩思設計的 補助通
# 微笑看護
# 慢性病藥物宅配服務 iHealth
# 爸媽 Home


2014年12月10日 星期三

母親的護照

今晚載菁菁去補課回來後, 閒著沒事突然想看之前寫的日記. 回顧了六月以來的記事, 回想一下媽住院前我都在忙啥? 如果知道母親會走的這麼快, 我要把一整年的假都請掉來陪她, 但事實上, 我們永遠都覺得來日方長, 唉.

打開抽屜看到媽這本五年前我幫她辦的護照, 遺憾的是, 這本護照她只用了一次而已. 六年前媽經歷難熬的化療病癒後, 我就決定一定要帶她出國去玩, 所以我就跑去成功路的領務局, 跟小狐狸們一起辦了四本新護照. 如果要她為了辦護照去拍張相片, 她鐵定會覺得麻煩而推三阻四不去拍. 所幸我在戶口名簿袋裡找到一張跟身分證不一樣的近照才順利地辦好. 這張面帶微笑的照片我忘記當時她說是為辦甚麼而拍的了, 因為拍得好, 被我選做她的遺照.

護照辦好也只是放著, 每天忙著忙著, 輾轉過了兩年, 姊姊小學畢業了, 終於在暑假前最後一檔, 敲定要去東京迪士尼玩, 但媽卻說她腳會痠痛, 要我帶小朋友去就好. 前年暑假我想帶媽去看京都的楓紅, 但是我一忙就耽誤了預定旅行社的行程, 加上颱風的不確定因素, 最後不了了之. 所幸去年四月中小舅與小舅媽邀她去黑部立山, 她原先說不, 但我說錢已經交給旅行社了, 她只好答應. 我本想陪他們一起去, 但可惜就是少一個機位, 只能開車送他們去機場. 這趟旅行帶給她滿是驚奇的美好回憶, 不僅是第一次坐飛機, 而且第一次出國就在東京遇到降雪, 每次提到黑部立山, 她就滔滔不絕. 光是這一點, 我就無限感激小舅.

即使如此, 沒能親自陪媽出國玩, 仍然是一個遺憾.

2014年12月6日 星期六

忙碌的一天

前天下班回來看到門口貼了一張衛生所的公告, 說星期六要噴登革熱噴藥. 據主委說, 是七樓住戶有人感染, 上下一層樓的住戶都要噴, 我說不是整棟噴嗎? 她說市府噴到快沒錢了. 也許是吧, 今年真的太嚴重.

昨天犧牲午覺, 跑去同事介紹的武廟福德路口的五金行買塑膠紙, 昨晚貼到 11 點才把書房, 房間, 廚房,客廳的書架, 餐具等覆蓋完畢.

早上先載菁菁去科工館敦煌參加劍橋英語檢定, 回來後又再補強一些沒蓋到的地方. 10 點衛生局的人就來按門鈴了. 每一個房間都放一個噴霧罐, 門窗緊閉悶一個小時, 再開窗流通空氣.

下午又跟菁菁一起把兩個浴室的水龍頭換新, 今天太累, 明天再記好了.


2014年12月3日 星期三

jQuery EasyUI 測試 : Tree 樹

Tree 適合用來表達從屬結構, 例如組織架構, 檔案系統等等, 是非常好用的使用者介面. EasyUI 提供的 tree 元件 API 如下 :

http://www.jeasyui.com/documentation/tree.php
# Tree Demo

EasyUI 的樹使用多層 ul-li 結構製作, 最外層 ul 必須賦予 "easyui-tree" 的樣式類別, 而子目錄則使用 span 元素串接下一層 ul 來製作, 如範例 1 所示 :

範例 1 http://tony1966.16mb.com/easyuitest/easyui-tree-1.htm [看原始碼]

  <ul class="easyui-tree">
    <li>
      <span>總統府</span>
      <ul>
        <li>
          <span>行政院</span>
          <ul>
            <li>內政部</li>
            <li>外交部</li>
            <li>國防部</li>
          </ul>
        </li>
        <li>
          <span>立法院</span>
          <ul>
            <li>內政委員會</li>
            <li>外交委員會</li>
            <li>國防委員會</li>
          </ul>
        </li>
        <li>
          <span>司法院</span>
          <ul>
            <li>最高法院</li>
            <li>高等法院</li>
            <li>地方法院</li>
          </ul>
        </li>      
      </ul>
    </li>
  </ul>


可見預設情況下每一個˙子目錄都是打開的. 我們可以為 Tree 添加節點之間的連線, 只要在最外層 ul 元素的 data-options 屬性中, 將 lines 設為 true 即可, 如範例 1-1 所示 :

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

<ul id="tree1" class="easyui-tree" data-options="lines:true">


除了把樹狀結構的內容直接寫在網頁外, 也可以從遠端伺服器上取得外部資料來源, 例如 JSON 檔或 PHP 等能產出 JSON 輸出的後端程式, 只要在最外層 ul 元素的 data-options 屬性中指定 url 位址即可 :

  <ul class="easyui-tree" data-options="url:'government.json'"></ul>

Tree 元件需要的是一個格式如下的 JSON 物件陣列 :

[{
  "id":1,
  "text":"樹葉1"
  },{
  "id":2,
  "text":"樹葉2"
  },{
  "id":3,
  "text":"節點1",
  "children":[{
    "id":31,
    "text":"樹葉31"
    },{
    "id":32,
    "text":"樹葉32"
    }]
  }]

可見樹狀結構中的樹葉只需要 id 與 text, 而節點則需要 children 開啟下一層分支. 因此範例 1 中的樹可依此格式寫成 JSON 檔如下 :

[{
  "id":1,
  "text":"總統府",
  "children":[{
    "id":11,
    "text":"行政院",
    "children":[{
      "id":111,
      "text":"內政部"
      },{
      "id":112,
      "text":"外交部"
      },{
      "id":113,
      "text":"國防部"
      }]
    },{
    "id":12,
    "text":"立法院",
    "children":[{
      "id":121,
      "text":"內政委員會"
      },{
      "id":122,
      "text":"外交委員會"
      },{
      "id":123,
      "text":"國防委員會"
      }]
    },{
    "id":13,
    "text":"司法院",
    "children":[{
      "id":131,
      "text":"最高法院"
      },{
      "id":132,
      "text":"高等法院"
      },{
      "id":133,
      "text":"地方法院"
      }]
    }]
  }]

# government.json

將此 JSON 檔以 UTF-8 格式存成 government.json, 然後在最外層 ul 元素加上 data-options 屬性, 指定 url 為此 JSON 檔即可, 如範例 2 所示 :

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

可見效果與範例 1 完全一樣. 注意, 外部資源檔須符合 JSON 格式要求, 每一個屬性名稱如 id, text, children 等均需要用雙引號括起來 (不要用單引號), 且其值除數值外, 也都要用雙引號括起來. 如果 JSON 格式錯誤, 將無法呈現正確結果, 驗證 JSON 格式可使用 JSONLint 網站 :

JSONLint-The JSON Validator

上面範例中的樹預設都是全部展開, 能否控制讓目錄預設為縮合呢? 可以的, 只要將目錄節點的 state 屬性設為 "closed" 即可 (預設為 "open"). 如下列範例 2-1 所示 :

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

<ul class="easyui-tree" data-options="url:'government-2.json'"></ul>

這裡我們將後端 JSON 檔修改為 government-2.json 如下 :

[{
  "id":1,
  "text":"總統府",
  "state":"closed",
  "children":[{
    "id":11,
    "text":"行政院",
    "state":"closed",
    "children":[{
      "id":111,
      "text":"內政部"
      },{
      "id":112,
      "text":"外交部"
      },{
      "id":113,
      "text":"國防部"
      }]
    },{
    "id":12,
    "text":"立法院",
    "state":"closed",
    "children":[{
      "id":121,
      "text":"內政委員會"
      },{
      "id":122,
      "text":"外交委員會"
      },{
      "id":123,
      "text":"國防委員會"
      }]
    },{
    "id":13,
    "text":"司法院",
    "state":"open",
    "children":[{
      "id":131,
      "text":"最高法院"
      },{
      "id":132,
      "text":"高等法院"
      },{
      "id":133,
      "text":"地方法院"
      }]
    }]
  }]

government-2.json


我們在每一個目錄都添加了一個 state 屬性, 除司法院設為 "open" 外, 其他均設為 "closed". 可見網頁初載入時根節點是縮合的, 點開後發現只有司法院是打開的. 其實 "state":"open" 不用設, 因為預設就是"open".

注意, 設定目錄預設狀態為縮合只能在用 JSON 或 Javascript 建立樹時才行, 像範例 1 與 1-1 直接寫在網頁的樹一定會全部展開.

上面範例 2 是直接取用  JSON 檔, 當然也可以用 PHP 程式來產出, 不過 EasyUI 使用稱為 Async Tree (非同步樹) 的方式來製作, 它是利用 Ajax 技術以非同步方式動態地自後端擷取被打開的節點的子節點, 據此更新樹狀結構, 而不是像上面兩個範例那樣一次取得整棵樹.

首先到 phpMyAdmin 中定義樹狀結構的資料表 government, 我們需要三個欄位 :

id: 節點索引
text: 節點名稱
parentId: 父節點索引

父節點索引是用來串接上一層節點用的. 建好後如下圖所示 :


要建立此資料表所需的 SQL 指令如下 :

CREATE TABLE IF NOT EXISTS `government` (
  `id` int(11) NOT NULL,
  `text` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `parentId` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `government` (`id`, `text`, `parentId`) VALUES
(1, '總統府', 0),
(11, '行政院', 1),
(111, '內政部', 11),
(112, '外交部', 11),
(113, '國防部', 11),
(12, '立法院', 1),
(121, '內政委員會', 12),
(122, '外交委員會', 12),
(123, '國防委員會', 12),
(13, '司法院', 1),
(131, '最高法院', 13),
(132, '高等法院', 13),
(133, '地方法院', 13);

注意, 這裡除了根節點的 parentId 必須設為 0 之外 (配合後端 PHP 程式), 其餘的索引可隨意編, 只要 parentId 正確串接即可.

# 下載 government.sql

接下來我們要撰寫能從 MySQL 資料表 government 產出 JSON 資料的 PHP 程式. 如果要產生上面範例 2 的 JSON 資料, 乍看之下似乎不難, 結構很規律, 但實際要寫時你會發現沒這麼簡單, 因為有些節點可能有子節點, 需要使用遞迴函式才能拜訪每一個節點, 請參考下面相關資料 :

# create hierarchical json data from MySQL in php without loadFilter
Need to Convert MySQL records to JSON tree
create json data for tree structure data with php array using recursion function

但 EasyUI 既然名為 Easy, 處理這問題當然不用這麼麻煩. 它使用 Ajax 方式來建立一個非同步樹, 其作法請參考 Tutorial 這篇文章 :

http://www.jeasyui.com/tutorial/tree/tree2.php

事實上它不是一次全部呈現全部樹狀結構, 而是利用 Tree 元件的 state 屬性, 預設只向後端擷取樹的最上層元素, 因此所有最上層目錄的 state 屬性都會被設為 "closed", 當使用者開啟目錄時, 會以 Ajax 方式向後端傳送此節點之id (method 屬性預設為 post), 後端程式只要傳回此節點的所有子節點的 JSON 資料, 以便前端的 EasyUI Tree 元件更新該節點為開啟狀態即可.

根據該篇教學改寫所需的 PHP 程式 get-government-1.php 如下 :

<?php
header('Content-Type: text/html;charset=UTF-8');
$host="abc.xyz.com"; //本機改為 localhost 或 127.0.0.1
$username="test";
$password="123";
$database="testdb";
$conn=mysql_connect($host, $username, $password); //建立連線
mysql_query("SET NAMES 'utf8'"); //設定查詢所用之字元集為 utf-8
mysql_select_db($database, $conn); //開啟資料庫
$result=array();
$id=isset($_POST['id']) ? intval($_POST['id']) : 0; //根節點 parentId=0
$rs=mysql_query("SELECT * FROM government WHERE parentId=$id");
while ($row=mysql_fetch_array($rs)){ //若節點存在
  $node=array();
  $node["id"]=$row["id"];
  $node["text"]=$row["text"];
  $node["state"]=has_child($row["id"]) ? "closed" : "open";
  array_push($result,$node);
  }
echo json_encode($result);
function has_child($id){
  $SQL="SELECT count(*) FROM government WHERE parentId=$id";
  $rs=mysql_query($SQL);
  $row=mysql_fetch_array($rs);
  return $row[0] > 0 ? true : false;
  }
?>

此處我們先判斷有無傳出 id 參數, 第一次載入網頁時沒有傳出, 這時 $id 預設為 0, 當點擊節點時會傳出 id, 這時就去搜尋它有哪些子節點 (parentId=$id), 將所有子節點紀錄在陣列中, 並且透過呼叫 has_child() 方法判別這些子節點是否亦有子節點, 是的話將其 state 設為 "closed", 否則設為 "open". 最後呼叫 json_encode 函數轉成 JSON 格式傳回. 如果這些子節點也有子節點, 那麼其 state 屬性會被設為 closed, 否則為 open.

如果向伺服器要求 get-government-1.php 檔, 會傳回最上層節點, 也就是根節點-總統府, 其預設狀態是縮合的 :

[{"id":"1","text":"\u7e3d\u7d71\u5e9c","state":"closed"}] :

http://mybidrobot.allalla.com/easyuitest/get-government-1.php

最後把網頁中的 url 改成後端 PHP 程式即可 :

<ul class="easyui-tree" data-options="url:'get-government-1.php'"></ul>

如下面範例 3 所示 :

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


可見, 網頁初載入時, 預設只顯示根節點, 因為這時網頁不會傳出 id, 因此在 PHP 程式中, $id 變數初始值為 0, 在資料表 government 中搜尋父節點 id=0 當然只找到根節點而已. 當點選根節點時, Tree 元件會觸發 Select 事件, 向 url 指定之遠端 PHP 程式發出 Ajax 要求, 並傳出該節點之 id 參數 1, 在 Chrome 中按 F12, 切到 Network 即可看到 Form Data 中傳出的參數 id 為 1 :


除了直接用網頁或外部資源建立樹狀結構外, 也可以用 Javascript 來產生, 這要用到 Tree 元件的 data 屬性. 這時必須給最外層 ul 元素添加  id 屬性, 如範例 4 所示 :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        data:[{
          "id":1,
          "text":"總統府",
          "state":"closed",
          "children":[{
            "id":11,
            "text":"行政院",
            "state":"closed",
            "children":[{
              "id":111,
              "text":"內政部"
              },{
              "id":112,
              "text":"外交部"
              },{
              "id":113,
              "text":"國防部"
              }]
            },{
            "id":12,
            "text":"立法院",
            "state":"closed",
            "children":[{
              "id":121,
              "text":"內政委員會"
              },{
              "id":122,
              "text":"外交委員會"
              },{
              "id":123,
              "text":"國防委員會"
              }]
            },{
            "id":13,
            "text":"司法院",
            "state":"open",
            "children":[{
              "id":131,
              "text":"最高法院"
              },{
              "id":132,
              "text":"高等法院"
              },{
              "id":133,
              "text":"地方法院"
              }]
            }]
          }]
        });
      });
  </script>

可見這根本就是將 JSON 檔的內容直接貼到 data 屬性內嘛! 當然, 我們也可以用 url 屬性指定外部資料來源為 JSON :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"government-2.json"
        });
      });
  </script>

也可以從 PHP 取得資料 :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"get-government-1.php"
        });
      });
  </script>

EasyUI 還有一個建立 Tree 的方式, 即透過呼叫 loadData 方法, 此方法須傳入一個陣列, 就是與 JSON 一模一樣的陣列, 如下列範例 5 所示 :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      var data=[{
        "id":1,
        "text":"總統府",
        "state":"closed",
        "children":[{
          "id":11,
          "text":"行政院",
          "state":"closed",
          "children":[{
            "id":111,
            "text":"內政部"
            },{
            "id":112,
            "text":"外交部"
            },{
            "id":113,
            "text":"國防部"
            }]
          },{
          "id":12,
          "text":"立法院",
          "state":"closed",
          "children":[{
            "id":121,
            "text":"內政委員會"
            },{
            "id":122,
            "text":"外交委員會"
            },{
            "id":123,
            "text":"國防委員會"
            }]
          },{
          "id":13,
          "text":"司法院",
          "state":"open",
          "children":[{
            "id":131,
            "text":"最高法院"
            },{
            "id":132,
            "text":"高等法院"
            },{
            "id":133,
            "text":"地方法院"
            }]
          }]
        }];
      $("#tree1").tree(); //初始化
      $("#tree1").tree("loadData",data);

注意, 在呼叫 loadData 方法之前必須先初始化 Tree 元件, 否則會出現 cannot read options 錯誤 (初始化後才會讀得到). 要不然就要先添加 class="easyui-tree" 的樣式, 如範例 5-1 所示 :

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

  <ul id="tree1" class="easyui-tree"></ul>
  <script language="javascript">
    $(document).ready(function(){
      var data=[{
        "id":1,
        "text":"總統府",
        "state":"closed",
        "children":[{
          "id":11,
          "text":"行政院",
          "state":"closed",
          "children":[{
            "id":111,
            "text":"內政部"
            },{
            "id":112,
            "text":"外交部"
            },{
            "id":113,
            "text":"國防部"
            }]
          },{
          "id":12,
          "text":"立法院",
          "state":"closed",
          "children":[{
            "id":121,
            "text":"內政委員會"
            },{
            "id":122,
            "text":"外交委員會"
            },{
            "id":123,
            "text":"國防委員會"
            }]
          },{
          "id":13,
          "text":"司法院",
          "state":"open",
          "children":[{
            "id":131,
            "text":"最高法院"
            },{
            "id":132,
            "text":"高等法院"
            },{
            "id":133,
            "text":"地方法院"
            }]
          }]
        }];
      $("#tree1").tree("loadData",data);
      });
  </script>

參考資料 :

How to use loadData for a tree with data contained in a variabble

Tree 還有許多屬性與方法, 下面先來測試 animate. 此屬性預設為 false, 若設為 true, 則展開與縮合節點時會以動畫呈現, 如範例 6 所示 :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"government-2.json",
        animate:true
        });
      });
  </script>

可見節點展開時,  子節點會由上而下依次出現, 縮合時由下而上依次消失, 產生動畫效果.

Tree 元件繼承 drag and drop, 只要將 dnd 屬性設為 true, 每一個節點都可以拖放, 如範例 7 所示 :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"government-2.json",
        dnd:true
        });
      });
  </script>

此例中的節點都可以拖曳到任一節點上, 一放開就成為其子節點, 例如下圖立法院變成行政院的子節點 :


每一個節點前面還可以放置一個核取方塊, 只要將 checkbox 屬性設為 true 即可, 如範例 8 所示 :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"government-2.json",
        checkbox:true
        });
      });
  </script>


可見每一個節點都有核取方塊. 如果只要樹葉有核取方塊, 那麼除了 checkbox 外, 還要將 onlyLeafCheck 設為 true, 如範例 8-1 所示 :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"government-2.json",
        checkbox:true,
        onlyLeafCheck:true
        });
      });
  </script>


可見僅樹葉才會有核取方塊了. 與 checked 有關的事件為 onCheck, 此事件會傳出兩個參數 : node (被核取之節點物件) 與 checked (核取狀態布林值, true 為核取, false 為取消核取), 如下列範例 8-2 所示 :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"government-2.json",
        checkbox:true,
        onCheck:function(node,checked){
          var msg="id=" + node.id + " " + node.text" checked=" + checked;
          $.messager.alert("Info",msg);
          }
        });
      });
  </script>


核取地方法院後, 顯示 idd 133 核取狀態為 true. 此處用 node.id 與 node.text 即可分別取得節點之索引與文字.

Tree 允許透過 attributes 屬性來自訂節點的屬性, 其值為一個物件實體. 例如政府各單位幾乎都有網站, 就可以在 Tree 的每個節點的 attributes 屬性中自訂一個 url 屬性, 如下面的 government-3.json :

[{
  "id":1,
  "text":"總統府",
  "state":"closed",
  "attributes":{"url":"http://www.president.gov.tw/"},
  "children":[{
    "id":11,
    "text":"行政院",
    "state":"closed",
    "attributes":{"url":"http://www.ey.gov.tw/"},
    "children":[{
      "id":111,
      "text":"內政部",
      "attributes":{"url":"http://www.moi.gov.tw/"}
      },{
      "id":112,
      "text":"外交部",
      "attributes":{"url":"http://www.mofa.gov.tw/"}
      },{
      "id":113,
      "text":"國防部",
      "attributes":{"url":"http://www.mnd.gov.tw/"}
      }]
    },{
    "id":12,
    "text":"立法院",
    "state":"closed",
    "attributes":{"url":"http://www.ly.gov.tw/"},
    "children":[{
      "id":121,
      "text":"內政委員會"
      },{
      "id":122,
      "text":"外交委員會"
      },{
      "id":123,
      "text":"國防委員會"
      }]
    },{
    "id":13,
    "text":"司法院",
    "state":"open",
    "attributes":{"url":"http://www.judicial.gov.tw/"},
    "children":[{
      "id":131,
      "text":"最高法院"
      },{
      "id":132,
      "text":"高等法院"
      },{
      "id":133,
      "text":"地方法院"
      }]
    }]
  }]

下面範例 9 我們同時也來測試最常用的 onClick 事件, 點選樹中任何節點時均會觸發此事件, 並傳回一個參數, 即被點選之節點物件. 我們要在此事件中顯示其 URL :

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"government-3.json",
        onClick:function(node){
          var msg="id=" + node.id + " " + node.text + " clicked"; 
          if (node.attributes) {
            msg += "\r\nattributes:url=" + node.attributes.url;
            }
          $.messager.alert("Info",msg);
          }
        });
      });
  </script>


這裡因為不是每一個單位都賦予 attributes 屬性, 因此要先判斷是否 node.attributes 物件存在, 有的話再取得其 url 屬性值.

參考資料 :

easyui tree自定义属性的使用

當然, attributes 也可以寫在網頁的 li 中, 如範例 9-1 所示 :

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

  <ul id="tree1" class="easyui-tree">
    <li data-options="attributes:{'url':'http://www.president.gov.tw/'}">
      <span>總統府</span>
      <ul>
        <li data-options="attributes:{'url':'http://www.ey.gov.tw/'}">
          <span>行政院</span>
          <ul>
            <li data-options="attributes:{'url':'http://www.moi.gov.tw/'}">內政部</li>
            <li data-options="attributes:{'url':'http://www.mofa.gov.tw/'}">外交部</li>
            <li data-options="attributes:{'url':'http://www.mnd.gov.tw/'}">國防部</li>
          </ul>
        </li>
        <li data-options="attributes:{'url':'http://www.ly.gov.tw/'}">
          <span>立法院</span>
          <ul>
            <li>內政委員會</li>
            <li>外交委員會</li>
            <li>國防委員會</li>
          </ul>
        </li>
        <li data-options="attributes:{'url':'http://www.judicial.gov.tw/'}">
          <span>司法院</span>
          <ul>
            <li>最高法院</li>
            <li>高等法院</li>
            <li>地方法院</li>
          </ul>
        </li>      
      </ul>
    </li>
  </ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        onClick:function(node){
          var msg="id=" + node.id + " " + node.text + " clicked";
          if (node.attributes) {
            msg += "\r\nattributes:url=" + node.attributes.url;
            }
          $.messager.alert("Info",msg);
          }
        });
      });
  </script>


注意, 這裡因為各節點 li 的 data-options 中沒有指定 id 屬性, 因此為 undefined.

當快速雙擊節點時會觸發 onDblClick 事件, 它也是會傳回節點的 node 物件. 下列範例 9-2 中, 我們讓有 url 屬性的節點在雙擊後於新視窗開啟此 url, 沒有 url 屬性的就跳出訊息框.

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

  <ul id="tree1"></ul>
  <script language="javascript">
    $(document).ready(function(){
      $("#tree1").tree({
        lines:true,
        url:"government-3.json",
        onDblClick:function(node){
          var msg="id=" + node.id + " " + node.text + " double clicked";
          if (node.attributes) {
            window.open(node.attributes.url,"_blank");
            }
          else {
            $.messager.alert("Info",msg);
            }
          }
        });
      });
  </script>