此文章是基於 EasyUI+Knockout實現經典表單的查看、編輯
一. 相關文件介紹
1. menu.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-save" title="保存" data-bind="click:saveClick">保存</a> </div> <table id="gridlist" data-bind="treegrid:grid"> <thead> <tr> <th field="id" hidden="true"></th> <th field="menuName" align="left" width="150" editor="{type:'validatebox',options:{required: true }}">菜單名稱 </th> <th field="parentId" align="left" width="150" editor="combotree" formatter="formatterParent">上級菜單 </th> <th field="iconClass" align="left" width="180" editor="{type:'lookup'}">圖標 </th> <th field="url" align="left" width="180" editor="text">鏈接地址 </th> <th field="isVisible" align="center" width="60" editor="{type: 'checkbox',options: {on: 1,off: 0}}" formatter="formatterEnable" >是否可見</th> <th field="isEnable" align="center" width="60" editor="{type: 'checkbox',options: {on: 1,off: 0}}" formatter="formatterEnable" >是否啟用</th> <th field="menuSeq" align="right" width="50" editor="text">排序</th> <th field="button" align="center" width="100" formatter="formatterButton">頁面按鈕 </th> </tr> </thead> </table> <script type="text/html" id="button-template"> <div style="margin:5px;height:320px;overflow:auto;"> <style type="text/css"> .listview{ margin:0 !important;} .listview li{width:100px !important;background-color:#ECECFF !important;float:left;margin:3px;overflow:hidden;} .listview span{ font-size:14px !important;height:auto !important; white-space: nowrap;} .listview .icon:before{content:"" !important} </style> <div style="border-bottom:1px solid #CCC; margin-bottom:5px;"> <span class="icon32 icon-settings32" style="padding-left:48px;font-weight:bold; font-size:14px;color:#666;">請選擇頁面按鈕</span> </div> <div class="metrouicss"> <label class="input-control checkbox" style="margin-top:6px;margin-left:3px;"> <input type="checkbox" data-bind="checked:checkAll"><span class="helper">全選</span> </label> <button class="image-button standart fg-color-white" style="float:right" data-bind="click:manageClick"> <i class="icon-grid-view bg-color-green"></i> 管理按鈕庫 </button> <ul class="listview" data-bind="foreach: buttons" style="clear:both"> <li data-bind="click:$parent.buttonClick,css:{selected:selected()>0}"><span class="icon" data-bind="text:buttonName,css:buttonIcon"></span></li> </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> <script type="text/html" id="manage-template"> <style type="text/css"> .datagrid-wrap{border-width:0 0 1px 0;} .datagrid-toolbar{background-color:#E0ECFF !important} </style> <table data-bind="datagrid:grid"> <thead> <th field="id" hidden="true"></th> <th field="buttonCode" align="left" editor="{type:'validatebox',options:{required:true}}" width="80" >編碼 </th> <th field="buttonName" align="left" editor="{type:'validatebox',options:{required:true}}" width="70" >名稱 </th> <th field="buttonIcon" align="left" editor="{type:'validatebox',options:{required:true}}" width="120" >圖標 </th> <th field="buttonSeq" align="left" editor="text" width="50" >排序 </th> <th field="description" align="left" editor="text" width="200" >備注說明 </th> </thead> </table> <div style="text-align:center;margin:5px;"> <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 src="viewModel/sys/menu.js"></script> <script type="text/javascript"> using(['lookup', 'numberspinner'], easyuifix.datagrid_editor_extend); ko.bindingViewModel(new viewModel()); using(['validatebox','combotree','dialog','messager']); var formatterEnable = function (value,row) {return '<img src="content/images/' + ((value||'').toString()=="1" ? "checkmark.gif" : "checknomark.gif") + '"/>';}; var formatterParent = function (value, row) { return row.parentName }; var formatterButton = function (value, row) { return (row.url&&row.url!='#')?'<a href="javascript:;" onclick="setButton(\'' + row.id + '\')"><span class="icon icon-set2"> </span>[設置按鈕]</a>':''; }; </script> </body> </html>
2. menu.js:實現菜單導航功能、列表,設置按鈕功能

function viewModel() { var self = this; this.editClick = function () { var row = self.grid.treegrid('getSelected'); if (row) { //取得父節點數據 var treeData = JSON.parse(JSON.stringify(self.grid.treegrid('getData')).replace(/menuName/g, "text")); treeData.unshift({ "id": 0, "text": "" }); //設置上級菜單下拉樹 var gridOpt = $.data(self.grid.$element()[0], "datagrid").options; var col = $.grep(gridOpt.columns[0], function (n) { return n.field == 'parentId' })[0]; col.editor = { type: 'combotree', options: { data: treeData } }; col.editor.options.onBeforeSelect = function (node) { var isChild = utils.isInChild(treeData, row.id, node.id); com.messageif(isChild, 'warning', '不能將自己或下級設為上級節點'); return !isChild; }; //開始編輯行數據 self.grid.treegrid('beginEdit', row.id); self.edit_id = row.id; var eds = self.grid.treegrid('getEditors', row.id); var edt = function (field) { return $.grep(eds, function (n) { return n.field == field })[0]; }; self.afterCreateEditors(edt); } }; this.grid = { size: { w: 4, h: 40 }, url: rootPath+'/sys/menu!getAll.do', idField: 'id', queryParams: ko.observable(), treeField: 'menuName', loadFilter: function (d) { d = utils.copyProperty(d.rows || d, ["iconClass"], ["iconCls"], false); return utils.toTreeData(d, 'id', 'parentId', "children"); } }; this.grid.onClickRow = function () { var edit_id = self.edit_id; if (!!edit_id) { if (self.grid.treegrid('validateRow', edit_id)) { //通過驗證 self.grid.treegrid('endEdit', edit_id); self.edit_id = undefined; } else { //未通過驗證 self.grid.treegrid('select', edit_id); return false; } } return true; }; this.grid.onDblClickRow = self.editClick; this.grid.OnBeforeDestroyEditor = function (editors, row) { row.parentName = editors['parentId'].target.combotree('getText'); row.iconClass = editors["iconClass"].target.lookup('textbox').val(); }; this.refreshClick = function () { window.location.reload(); }; this.addClick = function () { if (self.grid.onClickRow()) { var row = { id: '0', menuName:''}; self.grid.treegrid('append', { parent: '', data: [row] }); self.grid.treegrid('select', row.id); self.grid.$element().data("datagrid").insertedRows.push(row); self.editClick(); } }; this.afterCreateEditors = function (editors) { var iconInput = editors("iconClass").target; var onShowPanel = function () { iconInput.lookup('hidePanel'); com.dialog({ title: " 選擇圖標", iconCls: 'icon-node_tree', width: 700, height: 500, url: rootPath+"/view/sys/icon.html", viewModel: function (w) { w.find('#iconlist').css("padding", "5px"); w.find('#iconlist li').attr('style', 'float:left;border:1px solid #fff; line-height:20px; margin-right:4px;width:16px;cursor:pointer') .click(function () { iconInput.lookup('setValue',$(this).find('span').attr('class').split(" ")[1]); w.dialog('close'); }).hover(function () { $(this).css({ 'border': '1px solid red' }); }, function () { $(this).css({ 'border': '1px solid #fff' }); }); } }); }; iconInput.lookup({ customShowPanel: true, onShowPanel: onShowPanel, editable: true }); iconInput.lookup('resize', iconInput.parent().width()); iconInput.lookup('textbox').unbind(); }; this.deleteClick = function () { var row = self.grid.treegrid('getSelected'); if (row) { var childNodes = self.grid.treegrid('getChildren', row.id); if(childNodes.length>0){ com.message('warning', '請先刪除子菜單!'); return; } self.grid.$element().treegrid('remove', row.id); self.grid.$element().data("datagrid").deletedRows.push(row); } }; this.saveClick = function () { self.grid.onClickRow(); var post = {}; post.list = new com.editTreeGridViewModel(self.grid).getChanges(['id', 'menuName', 'parentId', 'iconClass', 'url', 'isVisible', 'isEnable', 'menuSeq']); if (self.grid.onClickRow() && post.list._changed) { com.ajax({ url: rootPath+'/sys/menu!edit.do', contentType: 'application/json', data: ko.toJSON(post), success: function (d) { com.message('success', '保存成功!'); self.grid.treegrid('acceptChanges'); self.grid.queryParams({}); } }); } }; } var setButton = function (menuId) { com.dialog({ title: "設置按鈕", width: 555, height: 400, html: "#button-template", viewModel: function (w) { var self = this; com.loadCss(rootPath+'/content/css/metro/css/modern.css', parent.document); this.buttons = ko.observableArray(); this.refresh = function () { com.ajax({ url: rootPath+'/sys/button!getMenuButtons.do?menuId=' + menuId, type: 'GET', async: false, success: function (d) { self.buttons(ko.mapping.fromJS(d)()); } }); }; this.refresh(); this.checkAll = ko.observable(false); this.checkAll.subscribe(function (value) { $.each(self.buttons(), function () { this.selected(value ? 1 : 0); }); }); this.buttonClick = function (row) { row.selected(row.selected() ? 0 : 1); }; this.confirmClick = function () { var data = utils.filterProperties($.grep(self.buttons(), function (row) { return row.selected() > 0; }), ['id']); com.ajax({ url: rootPath+'/sys/button!editMenuButtons.do?menuId=' + menuId, data: ko.toJSON(data), success: function (d) { com.message('success', '保存成功!'); self.cancelClick(); } }); }; this.manageClick = function () { com.dialog({ title: "管理按鈕庫", width: 600, height: 410, html: "#manage-template", viewModel: function (w_sub) { var that = this; this.grid = { width: 586, height: 340, pagination: false, pageSize: 10, url: rootPath+"/sys/button!getButtons.do", queryParams: ko.observable() }; this.cancelClick = function () { w_sub.dialog('close'); }; this.gridEdit = new com.editGridViewModel(this