此文章是基於 EasyUI+Knockout實現經典表單的查看、編輯
一. 相關文件介紹
1. organize.jsp:組織結構樹的主界面

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>組織結構樹</title> <%@ include file="/common/head.jsp"%> </head> <body> <div class="toolbar"> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-arrow_refresh" title="刷新" data-bind="click:refreshClick">刷新</a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-add" title="新增" data-bind="click:addClick">新增</a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-edit" title="編輯" data-bind="click:editClick" >編輯</a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-cross" title="刪除" data-bind="click:deleteClick">刪除</a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-group" title="設置角色" data-bind="click:roleClick">設置角色</a> </div> <div id="gridlist" class="wrapper" style="width: 100%; height: 100%; margin-top:15px;"></div> <script type="text/html" id="edit-template"> <div class="container_16" style="width:90%;margin:5%;"> <div class="grid_3 lbl" >上級機構</div> <div class="grid_13 val" ><input class="txtBox" data-bind="easyuiCombotree:combotree,combotreeValue:form.parentId" /></div> <div class="grid_3 lbl">機構名稱</div> <div class="grid_13 val"><input class="txtBox easyui-validatebox" style="width:220px;" data-bind="value:form.organizeName" data-options="required:true,validateOnCreate:false,validateOnBlur:true" /></div> <div class="grid_3 lbl">備注</div> <div class="grid_13 val"><textarea class="txtBox" style="width:220px;height:50px;" data-bind="value:form.description" ></textarea></div> <div class="clear"></div> </div> <div style="text-align:center;"> <a class="easyui-linkbutton" data-options="iconCls:'icon-ok'" data-bind="click:confirmClick" href="javascript:void(0)" >確定</a> <a class="easyui-linkbutton" data-options="iconCls:'icon-cancel'" data-bind="click:cancelClick" href="javascript:void(0)">取消</a> </div> </script> <script type="text/html" id="setrole-template"> <style type="text/css"> .listview{ margin:0 !important;} .listview li{width:100px !important;background-color:skyblue !important;float:left;margin:3px;} </style> <div style="margin:5px;height:370px;overflow:auto;" > <div style="border-bottom:1px solid #CCC; margin-bottom:5px;"> <span class="icon32 icon-org32" style="padding-left:48px;font-weight:bold; font-size:14px;color:#666;" data-bind="text:organizeName">機構名稱</span> </div> <div> 擁有角色(請點擊勾選):</div> <div class="metrouicss"> <ul class="listview"></ul> </div> </div> <div style="text-align:center;"> <a class="easyui-linkbutton" data-options="iconCls:'icon-ok'" data-bind="click:confirmClick" href="javascript:void(0)" >確定</a> <a class="easyui-linkbutton" data-options="iconCls:'icon-cancel'" data-bind="click:cancelClick" href="javascript:void(0)">取消</a> </div> </script> <%@ include file="/common/foot.jsp"%> <script type="text/javascript" src="viewModel/sys/organize.js"></script> <script type="text/javascript" src="viewModel/sys/organizeTreeGraph.js"></script> <script type="text/javascript"> var data = ${data}; ko.bindingViewModel(new viewModel()); using(['dialog','combotree','messager']) </script> </body> </html>
2. organize.js:實現界面按鈕的功能

function viewModel() { var self = this; this.refreshClick = function () { window.location.reload(); }; this.save = function (vm,win) { var post = { form: ko.toJS(vm.form) }; com.ajax({ type: 'POST', url: rootPath+'/sys/organize!edit.do', data: JSON.stringify(post), success: function (d) { com.message('success', '保存成功!'); win.dialog('close'); window.location.reload(); } }); } this.addClick = function () { var defaults = { parentId: (self.selectNode || {}).id || 0 }; self.openDiloag("添加新機構", defaults, function (vm, win) { if (com.formValidate(win)) { vm.form._id = vm.form.id(); self.save(vm,win); } }); }; this.editClick = function () { if (!self.selectNode) return com.message('warning', '請先選擇一個機構!'); self.openDiloag("編輯機構-"+self.selectNode.organizeName,self.selectNode, function (vm, win) { if (com.formValidate(win)) { self.save(vm,win); } }); }; this.deleteClick = function () { if (!self.selectNode) return com.message('warning', '請先選擇一個機構!'); if (utils.isNotEmpty(self.selectNode.children)) return com.message('warning', '請先刪除子機構!'); com.message('confirm', '確認要刪除選中的機構嗎?', function (b) { if (b) { com.ajax({ type: 'DELETE', url: rootPath+'/sys/organize!delete.do?id=' + self.selectNode.id, success: function (d) { com.message('success', '刪除成功!'); window.location.reload(); } }); } }); }; this.roleClick = function () { if (!self.selectNode) return com.message('warning', '請先選擇一個機構!'); com.dialog({ title: "設置角色", width: 600, height: 450, html: "#setrole-template", viewModel: function (w) { var thisRole = this; this.organizeName = ko.observable(self.selectNode.organizeName); com.loadCss(rootPath+'/content/css/metro/css/modern.css', parent.document); com.ajax({ type: 'GET', url: rootPath+'/sys/role!getRoleWithOrganizeCheck.do?organizeId=' + self.selectNode.id, success: function (d) { var ul = w.find(".listview"); for (var i in d) ul.append(utils.formatString('<li role="{0}" class="{2}">{1}</li>', d[i].id, d[i].roleName, d[i].checked == 'true' ? 'selected' : '')); ul.find("li").click(function () { if ($(this).hasClass('selected')) $(this).removeClass('selected'); else $(this).addClass('selected'); }); } }); this.confirmClick = function () { var roles = []; w.find("li.selected").each(function () { roles.push({ id: $(this).attr('role') }); }); com.ajax({ url: rootPath+'/sys/role!editOrganizeRoles.do?organizeId=' + self.selectNode.id, data: ko.toJSON(roles), success: function (d) { thisRole.cancelClick(); com.message('success', '保存成功!'); } }); }; this.cancelClick = function () { w.dialog('close'); }; } }); }; this.openDiloag = function (title,node,fnConfirm) { com.dialog({ title: title, height: 250, width: 400, html: "#edit-template", viewModel: function (w) { var that = this; this.combotree = { width:228, data: self.combotreeData, onBeforeSelect: function (node) { var isChild = utils.isInChild(self.combotreeData, that.form._id, node.id); com.messageif(isChild, 'error', '不能將自己或下級設為上級機構'); return !isChild; } }, this.form = { _id:node.id, parentId: ko.observable(node.parentId), id: ko.observable(node.id), organizeName: ko.observable(node.organizeName), description: ko.observable(node.description) }; this.confirmClick = function () { fnConfirm(this,w); }; this.cancelClick = function () { w.dialog('close'); }; } }); }; this.initTreeData = function () { var list = utils.filterProperties(data, ['id as id', 'parentId as pid', 'organizeName as text']); var treeData = utils.toTreeData(list, "id", "pid", "children"); treeData.unshift({ "id": 0, "text": "==請選擇==" }); self.combotreeData = treeData; }; this.initGraph = function (data) { self.data = data; var wrapper = $("div.wrapper").empty(); var treeData = utils.toTreeData(data, "id", "parentId", "children"); var tb = renderTreeGraph(treeData); tb.appendTo(wrapper); //綁定事件 $(wrapper).find(".td-node").click(function () { $(".td-node").css({ "background-color": "#f6f6ff", "color": "" }); $(this).css({ "background-color": "#faffbe", "color": "#FF0000" }); self.selectNode = $(this).data("node"); }).dblclick(self.editClick); if (self.selectNode) { $("#td" + self.selectNode.id).css({ "background-color": "#faffbe", "color": "#FF0000" }); } }; this.initGraph(data); this.initTreeData(); }
3. organizeTreeGraph.js:實現結構樹的繪制

function renderTreeGraph(treeData) { //生成圖形 var tb = $('<table class="tb-node" cellspacing="0" cellpadding="0" align="center" border="0" style="border-width:0px;border-collapse:collapse;margin:0 auto;vertical-align:top"></table>'); var tr = $('<tr></tr>'); for (var i in treeData) { if (i > 0) $('<td> </td>').appendTo(tr); $('<td style="vertical-align:top;text-align:center;"></td>').append(createChild(treeData[i])).appendTo(tr); } tr.appendTo(tb); return tb; } //遞歸生成機構樹圖形 function createChild(node, ischild) { var length = (node.children || []).length; var colspan = length * 2 - 1; if (length == 0) colspan = 1; var fnTrVert = function () { var tr1 = $('<tr class="tr-vline"><td align="center" valign="top" colspan="' + colspan + '"><img class="img-v" src="'+rootPath+'/content/images/tree/Tree_Vert.gif" border="0"></td></tr>'); return tr1; }; //1.創建容器 var tb = $('<table class="tb-node" cellspacing="0" cellpadding="0" align="center" border="0" style="border-width:0px;border-collapse:collapse;margin:0 auto;vertical-align:top"></table>'); //var tb = $('<table style="BORDER-BOTTOM: 1px solid; BORDER-LEFT: 1px solid; BORDER-TOP: 1px solid; BORDER-RIGHT: 1px solid" border="1" cellspacing="0" cellpadding="2" align="center"></table>'); //2.如果本節點是子節點,添加豎線在節點上面 if (ischild) { fnTrVert().appendTo(tb); } // 3.添加本節點到圖表 var tr3 = utils.functionComment(function () {/* <tr class="tr-node"><td align="center" valign="top" colspan="{0}"> <table align="center" style="border:solid 2px" border="1" cellpadding="2" cellspacing="0"> <tr> <td class="td-node" id='td{3}' data-node='{2}' align="center" valign="top" style="background-color:#f6f6ff;cursor:pointer;padding:2px;">{1}</td> </tr> </table> </td></tr> */ }); tr3 = utils.formatString(tr3, colspan, node.organizeName, JSON.stringify(node),node.id); $(tr3).appendTo(tb); // 4.增加上下級的連接線 if (length > 1) { //增加本級連接下級的首節點豎線,在節點下方 fnTrVert().appendTo(tb); //增加本級連接下級的中間橫線 var tr4 = utils.functionComment(function () {/* <tr class="tr-hline"> <td colspan="1"> <table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0"> <tbody> <tr> <td width="50%" style="background:url(rootPath/content/images/tree/Tree_Empty.gif)"></td> <td width="3px" height="3px" ><img src="rootPath/content/images/tree/Tree_Dot.gif" border="0"></td> <td width="50%" style="background:url(rootPath/content/images/tree/Tree_Dot.gif)"></td> </tr> </tbody> </table> </td> <td style="background:url(rootPath/content/images/tree/Tree_Dot.gif)" colspan="{0}"></td> <td colspan="1"> <table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0"> <tbody> <tr> <td width="50%" style="background:url(rootPath/content/images/tree/Tree_Dot.gif)"></td> <td width="3px" height="3px" ><img src="rootPath/content/images/tree/Tree_Dot.gif" border="0"></td> <td width="50%" style="background:url(rootPath/content/images/tree/Tree_Empty.gif)"></td> </tr> </tbody> </table> </td> </tr>*/}); tr4 = tr4.replace(/rootPath/g,rootPath); tr4 = utils.formatString(tr4, colspan - 2); $(tr4).appendTo(tb); } //5.遞歸增加下級所有子節點到圖表 if (length > 0) { var tr5 = $('<tr></tr>'); for (var i in node.children) { if (i > 0) { $('<td> </td>').appendTo(tr5); } $('<td style="vertical-align:top;text-align:center;"></td>').append(createChild(node.children[i], true)).appendTo(tr5); } tr5.appendTo(tb); } return tb; }
二. 效果圖
1. 訪問:http://localhost:8080/ims/sys/organize.do
2. 選擇一個機構,點擊 設置角色