2015年1月22日 星期四

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

最近系統的工作突然增多, 所以這兩天在公司的程式進度幾乎是零, 只好帶回家繼續寫. 下午同事反映工作日誌無法上傳檔案, 我想可能又是該死的 IE 搞鬼, 只要一升版就會讓某些功能失靈. 所以架站機要趕快寫好才行, 這樣新版工作日誌才能開工.

花了一個禮拜才把留言板功能搞定, 其實邏輯不難, 時間主要花在呈現方式, 原先是想要用 EasyUI 的 tooltip 元件來呈現留言, 試了兩天還是放棄了, 原因是 tooltip 預設就是隱藏, 要等游標移到上面時才會現身, 而留言板是進入頁面就顯示最近幾筆留言, 若還要使用者移到人名或日期上才顯示內容就有點怪了. 最後在網路上看到交談框 (speech bubble) :

# 那些 CSS 偽元素可以幫你做的 10 個效果
# Pure CSS speech bubbles
# How to Create CSS3 Speech Bubbles Without Images

於是我就突發奇想, 乾脆用交談框來製作留言板吧! 前兩天為此還研究了一下其製作原理, 詳見  :

# CSS3 的魔力 : 如何製作交談氣泡 (Speech bubble)

首先要在安裝程式 install.php 中添加資料表 sys_board :

    //建立 sys_board 資料表
    $data_array["id"]="smallint(6) NOT NULL AUTO_INCREMENT PRIMARY KEY";
    $data_array["poster"]="varchar(255)";             //作者
    $data_array["subject"]="varchar(255)";            //主題
    $data_array["content"]="varchar(255)";            //內容
    $data_array["post_time"]="datetime";              //時間
    $result=create_table("sys_board",$data_array);
    if ($result) {$msg .= "建立資料表 sys_board ... 完成!<br>";}

留言板的內容放在一個 panel 元件裡, 包括右上角的工具 tools 以及下方的分頁工具列 pagination :

  <div id="board" class="easyui-panel" title="留言板" style="padding:15px;height:100%" data-options="href:'sys.php?op=list_board',tools:'#board_tools',footer:'#board_pager',fit:true">
  </div>
  <div id="board_tools">
    <a href="#" id="add_post" class="icon-add" title="新增留言"></a>
    <a href="#" id="reload_board" class="icon-reload" title="重新載入"></a>
  </div>
  <div id="board_pager" class="easyui-pagination" data-options="total:<?php echo $total ?>,pageSize:10">
  </div>

注意這裡 Panel 的 fit 被設成 true, 表示其大小會擴大為與母元件同樣大 (這時 width 與 height 可以不設定).

分頁工具列需要給予 total 與 pageSize 這兩個屬性以便計算總頁數, 這要在一開始時便查詢 board 資料表, 取得總留言數 $total :

    $SQL="SELECT COUNT(*) FROM `sys_board`";
    $RS=run_sql($SQL);
    $total=$RS[0][0]; //紀錄總筆數

而 Panel 的資料來源是透過 Ajax 從 sys.php?op=list_board 取得 :

  case "list_board" : {
    $page=isset($_REQUEST['page']) ? intval($_REQUEST['page']) : 1;
    $rows=isset($_REQUEST['rows']) ? intval($_REQUEST['rows']) : 10;
    $start=($page-1) * $rows;  //本頁第一個列索引 (0 起始)
    $SQL="SELECT COUNT(*) FROM `sys_board`";
    $RS=run_sql($SQL);
    $total=$RS[0][0]; //紀錄總筆數
    $SQL="SELECT * FROM sys_board ORDER BY post_time DESC ".
         "LIMIT ".$start.",".$rows;
    $RS=run_sql($SQL);
    if (is_array($RS)) {
      $icon=Array();
      $icon[]="ball";
      $icon[]="bambi";
      $icon[]="bear";
      $icon[]="corgi";
      $icon[]="cow";
      $icon[]="denchi";
      $icon[]="dorayaki";
      $icon[]="duck";
      $icon[]="kaeru";
      $icon[]="kuma";
      $icon[]="momo";
      $icon[]="saru";
      for ($i=0; $i<count($RS); $i++) {
        $gif=$icon[mt_rand(0,11)].".gif";
?>
  <div>
    <img src="images/<?php echo $gif ?>" style="margin-top:20px;border-width:0px;">  
    <?php echo $RS[$i]["poster"] ?> 張貼於 <?php echo $RS[$i]["post_time"] ?>
    主題 : <?php echo $RS[$i]["subject"] ?>
<?php
    if ($_SESSION["user_level"]==9) {
?>
    <a href="#" id="remove_post" class="easyui-linkbutton" data-options="iconCls:'icon-clear'" title="刪除留言" onclick="javascript:remove_post(<?php echo $RS[$i]['id'] ?>)"></a>
<?php
      }
?>
    <div class="arrow_box"><?php echo $RS[$i]["content"] ?></div>
  </div>
<?php
        }
      }
    else {
?>
  <div>沒有留言</div>
<?php
      }
    break;
    }


這程式會擷取前端分頁工具列傳出的 page 與 row 這兩個參數, 預設是顯示第一頁, 每頁 10 筆留言. 然後我在網路上下載了一組共 12 個 gif 圖檔 (放在系統根目錄的 images 子目錄下), 並用 PHP 的 mt_rand() 函式來隨機顯示在每筆留言的最前面. 若使用者為管理員, 還會顯示一個刪除按鈕以便移除不宜之留言 (傳入 id  給一個最上層的 remove_post 方法). 這個網頁排版參見 :

CSS3 的魔力 : 如何製作交談氣泡 (Speech bubble)

按下新增留言按鈕會顯示對話框, 同時清除裡面可能的舊資料 :

    $("#add_post").bind("click",function(){
      $("#board_dialog").dialog("open").dialog("setTitle","新增留言");
      $("#board_form").form("clear");
      });

這對話框預設是隱藏的 :

  <!--新增留言對話框-->
  <div id="board_dialog" class="easyui-dialog" title="新增留言" style="width:360px;" data-options="closed:'true',buttons:'#board_buttons'">
    <form id="board_form" style="padding:10px">
      <div style="margin:10px">
        <label style="width:50px;display:inline-block;">主題 : </label>
        <input name="subject" type="text" class="easyui-textbox" required="true" data-options="missingMessage:'此欄位為必填'"  style="width:230px">
      </div>
      <div style="margin:10px">
        <label style="vertical-align:top;width:50px;display:inline-block;">內容 : </label>
        <textarea name="content" class="easyui-validatebox" data-options="required:true" style="resize:none;width:225px;height:80px;border-radius:5px;"></textarea>
      </div>
    </form>
  </div>
  <div id="board_buttons" style="padding-right:15px;">
    <a href="#" id="clear_post" class="easyui-linkbutton" iconCls="icon-clear" style="width:90px">重設</a>
    <a href="#" id="save_post" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">確定</a>
  </div>


由於 EasyUI 並沒有推出自己的多行文字欄位元件, 這對話框中的留言內容使用 textarea 元素, 為了能進行欄位驗證, 必須將其樣式類別指定為 easyui-validatebox, 這樣還有個用處, 就是外觀會一致, 跟 easyui-textbox 一樣. 當按下確定鈕時, 就用 Ajax 上將表單提交給遠端執行 :

    $('#save_post').bind('click',function(){
      if (!$('#board_form').form('validate')){
        $.messager.alert('訊息','必填欄位未輸入!','warning');
        return;
        }
      var params=$('#board_form').serialize();
      params='op=add_post&' + params;
      var callback=function(data,textStatus){
        if (data.status==='success'){
          $('#board_dialog').dialog("close");
          $('#board').panel('open').panel('refresh');  //更新留言列表
          $('#board_pager').pagination('select',1);   //分頁條顯示第一頁
          $('#board_pager').pagination('refresh',{total:data.total}); //更新總數
          }
        else {$.messager.alert('訊息','留言失敗!','error');}
        }
      $.post('sys.php',params,callback,'json');
      });
    $("#clear_post").bind("click",function(){
      $("#board_form")[0].reset();
      });

遠端 sys.php 的 add_post 模組執行新增到資料表作業 :

  case "add_post" : {
    $SQL="SELECT COUNT(*) FROM `sys_board`";
    $RS=run_sql($SQL);
    $total=$RS[0][0]; //紀錄總筆數
    $data_array["poster"]=$_SESSION["user_name"];
    $data_array["subject"]=$_REQUEST["subject"];
    $data_array["content"]=$_REQUEST["content"];
    $data_array["post_time"]=date("Y-m-d H:i:s");;
    $result=insert("sys_board", $data_array);
    if ($result===TRUE) {
      $status="success";
      ++$total;
      $msg="ok";
      }
    else {
      $status="failure";
      $msg="資料新增錯誤!";
      }
    $arr["status"]=$status;
    $arr["total"]=$total;
    $arr["msg"]=$msg;
    echo json_encode($arr);
    break;
    }  

這裡比較重要的是要將留言總數加 1 後傳回給網頁中的分頁條更新顯示的留言總數. 就是上面的 :

$('#board_pager').pagination('refresh',{total:data.total}); //更新總數

關於分頁工具列還有一個需要處理的部分, 就是當使用者選取頁碼 (傳出 pageNumber 參數) 與選取每頁大小 (傳出 pageSize 參數) , 以及按下 refresh 鈕時, 必須向後端的 list_board 模組要求新資料, 這必須替分頁工具列加上 onRefresh 與 onSelectPage 方法,  他們都會由事件觸發得到 pageNumber 與 pageSize 參數, 我們將其分別改名為 page 與 row 參數傳遞給後端的 list_board 模組 (這是因為後端分頁程式我都維持一貫使用 page 表示 pageNumber, 用 row 表示 pageSize 之故) :

    $("#board_pager").pagination({
      onRefresh:function(pageNumber,pageSize) {
        var url='sys.php?op=list_board&page=' + pageNumber + '&row=' + pageSize;
$('#board').panel('refresh',url);    
        },
      onSelectPage:function(pageNumber,pageSize){
        var url='sys.php?op=list_board&page=' + pageNumber + '&row=' + pageSize;
$('#board').panel('refresh',url);
        }
      });

不管是按下更新鈕還是選擇另一個頁都會依所選的頁與大小重新載入分頁.

最後, 如果是管理員按下刪除鈕, 這時會呼叫最上層方法 remove_post(), 並傳入要刪除的留言的 id :

  function remove_post(id){
    $.messager.confirm('確認','確定要刪除這筆留言?',function(r){
      if(r){
        var params='op=remove_post&id=' + id;
        var callback=function(data,textStatus){
          if (data.status==='success'){
            $('#board_dialog').dialog("close");
            $('#board').panel('open').panel('refresh');  //更新留言列表
            $('#board_pager').pagination('refresh',{total:data.total}); //更新總數
            }
          else {$.messager.alert('訊息',"刪除留言失敗",'error');}
          }
        $.post('sys.php',params,callback,'json');
}
      });
    }

然後跳出確認框, 按確定後就呼叫遠端的 remove_post 模組來處理 :

  case "remove_post" : {
    $SQL="SELECT COUNT(*) FROM `sys_board`";
    $RS=run_sql($SQL);
    $total=$RS[0][0]; //紀錄總筆數
    $id=$_REQUEST["id"];
    $result=delete_record("sys_board", "id", $id);
    if ($result===TRUE) {$status="success";--$total;}
    else {$status="failure";}
    $arr["status"]=$status;
    $arr["total"]=$total;
    echo json_encode($arr);
    break;
    }

同樣的, 也要傳回 total 屬性給前端網頁以便更新分頁工具列上顯示的留言總數. OK, 留言板搞定了.

參考資料 :

http://ckeditor.com/
# CKEditor的使用方法

沒有留言 :