datagrid在MVC中的运用07-实现Master-Detail(使用PartialView)

本文介绍如何使用jQuery EasyUI datagrid组件实现主次表(Master-Detail)功能,包括Category和Product两个Model的定义,服务类及方法的实现,以及在MVC应用中的Controller和视图展示。

本文主要体验用jQuery Easyui的datagrid来实现Master-Detail主次表。谢谢Kevin的博文,助我打开了思路。

主表显示所有的Category,当点击主表的展开按钮,显示该Category下的所有Product。

  涉及显示的2个Model

展开namespace DataGridInMVC2.Models
{
    public class Category
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }
}

namespace DataGridInMVC2.Models
{
    public class Product
    {
        public int CategoryID { get; set; }
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public int QuantityPerUnit { get; set; }
        public decimal UnitPrice { get; set; }
        public int UnitsInStock { get; set; }
        public int UnitOnOrder { get; set; }
    }
}

  定义一个服务类和方法用来显示Category列表

展开using System;
using System.Collections;
using System.Linq;
using DataGridInMVC2.Models;
using System.Collections.Generic;

namespace DataGridInMVC2.Helpers
{
    public class Service
    {
        //获取所有Category
        public IEnumerable<Category> LoadPageCategories(CategoryParam param, out int total)
        {
            var categories = InitializeCategory();

            //搜索逻辑
            if (!string.IsNullOrEmpty(param.Name))
            {
                categories = categories.Where(c => c.Name.Contains(param.Name)).ToList();
            }

            total = categories.Count();
            var result = categories.OrderBy(c => c.ID)
                .Skip(param.PageSize*(param.PageIndex - 1))
                .Take(param.PageSize);
            return result;

        }

        //初始化Category
        public IEnumerable<Category> InitializeCategory()
        {
            var categories = new List<Category>();
            for (int i = 0; i < 35; i++)
            {
                categories.Add(new Category()
                {
                    ID = i + 1,
                    Name = "CategoryName" + Convert.ToString(i+1),
                    Description = "DescriptionDescriptionDescriptionDescriptionDescriptionDescription" + Convert.ToString(i + 1)
                });
            }
            return categories;
        }

    }
}

CategoryParam 延续了以前文章的思路,是对应View Model的封装类,继承于包含分页信息的基类。

展开namespace DataGridInMVC2.Models
{
    public class PageParam
    {
        public int PageSize { get; set; }
        public int PageIndex { get; set; } 
    }
}

using System;

namespace DataGridInMVC2.Models
{
    public class CategoryParam : PageParam
    {
         public string Name { get; set; } //对应视图搜索条件
    }
}

  CategoryController

展开namespace DataGridInMVC2.Controllers
{
    public class CategoryController : Controller
    {

        public ActionResult Index()
        {
            return View();
        }

        public ActionResult GetData()
        {
            //接收datagrid传来的参数
            int pageIndex = int.Parse(Request["page"]);
            int pageSize = int.Parse(Request["rows"]);

            //接收搜索参数
            string name = Request["Name"];

            //构建服务方法所需要的参数实例
            var temp = new CategoryParam()
            {
                PageIndex = pageIndex,
                PageSize = pageSize,
                Name = name
            };

            var service = new Service();
            int totalNum = 0;
            var categories = service.LoadPageCategories(temp, out totalNum);

            var result = from category in categories
                select new {category.Name, category.ID, category.Description};

            //total,rows是前台datagrid所需要的
            var jsonResult = new { total = totalNum, rows = result };

            //把json对象序列化成字符串
            string str = JsonSerializeHelper.SerializeToJson(jsonResult);
            return Content(str);
        }
    }
}

page和rows是前台视图datagrid传来的参数。
当我们把一个json对象往前台传的时候,需要序列化json对象。定义了一个序列化/反序列化json对象的静态类。

展开using System;
using Newtonsoft.Json;
namespace DataGridInMVC2.Helpers
{
    public static class JsonSerializeHelper
    {
        /// <summary>
        /// 把object对象序列化成json字符串
        /// </summary>
        /// <param name="obj">序列话的实例</param>
        /// <returns>序列化json字符串</returns>
        public static string SerializeToJson(object obj)
        {
            return JsonConvert.SerializeObject(obj);
        }

        /// <summary>
        /// 把json字符串反序列化成Object对象
        /// </summary>
        /// <param name="json">json字符串</param>
        /// <returns>对象实例</returns>
        public static Object DeserializeFromJson(string json)
        {
            return JsonConvert.DeserializeObject(json);
        }

        /// <summary>
        /// 把json字符串反序列化成泛型T
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="json">json字符串</param>
        /// <returns>泛型T</returns>
        public static T DeserializeFromJson<T>(string json)
        {
            return JsonConvert.DeserializeObject<T>(json);
        }
    }
}

  Category/Index视图

展开@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<link href="~/Content/themes/default/easyui.css" rel="stylesheet" />
<link href="~/Content/themes/icon.css" rel="stylesheet" />

<table id="tt"></table>

@section scripts
{
    <script src="~/Scripts/jquery.easyui.min.js"></script>
    <script src="~/Scripts/easyui-lang-zh_CN.js"></script>
    <script type="text/javascript">
        $(function() {
            initData();
        });

        function initData(params) {
            $('#tt').datagrid({
                url: '@Url.Action("GetData","Category")',
                width: 600,
                height: 400,
                title: 'Category列表',
                iconCls: 'icon-save',
                fitColumns: true,
                rownumbers: true, //是否加行号
                pagination: true, //是否显式分页
                pageSize: 15, //页容量,必须和pageList对应起来,否则会报错
                pageNumber: 2, //默认显示第几页
                pageList: [15, 30, 45],//分页中下拉选项的数值
                columns: [[
                    //book.ItemId, book.ProductId, book.ListPrice, book.UnitCost, book.Status, book.Attr1                   
                    { field: 'ID', title: '编号'},
                    { field: 'Name', title: '类别名称'},
                    { field: 'Description', title: '描述', width: 600 }
                ]],
                queryParams: params, //搜索json对象
            });
        }
    </script>
}

这里的@section scripts对应/Shared/_Layout.cshtml中的@RenderSection("scripts", required: false)。

Category列表

  Master表有了,接下来就是Detail表。需要一个根据Category的ID来获取Product列表的服务类方法。

展开using System;
using System.Collections;
using System.Linq;
using DataGridInMVC2.Models;
using System.Collections.Generic;

namespace DataGridInMVC2.Helpers
{
    public class Service
    {

        //根据CategoryId获取Product集合
        public IEnumerable<Product> LoadProductsByCategory(int categoryId)
        {
            var products = InitializeProducts();
            var result = products.OrderBy( p => p.ProductID)
                                 .Where(p => p.CategoryID == categoryId);
            return result;
        }

        //初始化Product
        public IEnumerable<Product> InitializeProducts() 
        {
            var products = new List<Product>();
            var r = new Random();
            for (int i = 0; i < 35; i++)
            {
                products.Add(new Product()
                {
                    CategoryID = i + 1,
                    ProductID = r.Next(10000),
                    ProductName = "ProductName" + Convert.ToString(r.Next(10000)),
                    QuantityPerUnit = i + 10,
                    UnitPrice = (i + 5) * 10m,
                    UnitsInStock = (i + 2) * 10
                });
                products.Add(new Product()
                {
                    CategoryID = i + 1,
                    ProductID = r.Next(10000),
                    ProductName = "ProductName" + Convert.ToString(r.Next(10000)),
                    QuantityPerUnit = i + 10,
                    UnitPrice = (i + 5) * 10m,
                    UnitsInStock = (i + 2) * 10
                });
                products.Add(new Product()
                {
                    CategoryID = i + 1,
                    ProductID = r.Next(10000),
                    ProductName = "ProductName" + Convert.ToString(r.Next(10000)),
                    QuantityPerUnit = i + 10,
                    UnitPrice = (i + 5) * 10m,
                    UnitsInStock = (i + 2) * 10
                });
            }
            return products;
        }
    }
}

  ProductController

展开using System.Web;
using System.Web.Mvc;
using DataGridInMVC2.Helpers;

namespace DataGridInMVC2.Controllers
{
    public class ProductController : Controller
    {

        public ActionResult Index()
        {
            return View();
        }

        public ActionResult GetByCategory(int? categoryId = null)
        {
            if (!categoryId.HasValue)
            {
                return new EmptyResult();
            }

            var service = new Service();
            var products = service.LoadProductsByCategory((int)categoryId);
            return PartialView("_GetByCategory", products.ToList());
        }
    }
}

  _GetByCategory.cshtml部分视图

展开@model IEnumerable<DataGridInMVC2.Models.Product>

<style type="text/css">
    .dv-table td
    {
        border: 0;
        vertical-align: middle;
    }
</style>
<table  class="dv-table table table-striped">
    <tr style="background-color:#f5f5dc;">
        <th>
            @Html.DisplayNameFor(model => model.CategoryID)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ProductName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.QuantityPerUnit)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.UnitPrice)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.UnitsInStock)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.UnitOnOrder)
        </th>
     
    </tr>

@foreach (var item in Model) {
    <tr style="height:15px;line-height: 15px;">
        <td>
            @Html.DisplayFor(modelItem => item.CategoryID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.ProductName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.QuantityPerUnit)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.UnitPrice)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.UnitsInStock)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.UnitOnOrder)
        </td>
    </tr>
}

</table>

  Category/Index视图

展开@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<link href="~/Content/themes/default/easyui.css" rel="stylesheet" />
<link href="~/Content/themes/icon.css" rel="stylesheet" />

<table id="tt"></table>

@section scripts
{
    <script src="~/Scripts/jquery.easyui.min.js"></script>
    <script src="~/Scripts/datagrid-detailview.js"></script>
    <script src="~/Scripts/easyui-lang-zh_CN.js"></script>
    <script type="text/javascript">
        $(function() {
            initData();
        });

        function initData(params) {
            $('#tt').datagrid({
                url: '@Url.Action("GetData","Category")',
                width: 600,
                height: 400,
                title: 'Category列表',
                iconCls: 'icon-save',
                fitColumns: true,
                rownumbers: true, //是否加行号
                pagination: true, //是否显式分页
                pageSize: 15, //页容量,必须和pageList对应起来,否则会报错
                pageNumber: 2, //默认显示第几页
                pageList: [15, 30, 45],//分页中下拉选项的数值
                columns: [[
                    //book.ItemId, book.ProductId, book.ListPrice, book.UnitCost, book.Status, book.Attr1                   
                    { field: 'ID', title: '编号'},
                    { field: 'Name', title: '类别名称'},
                    { field: 'Description', title: '描述', width: 600 }
                ]],
                queryParams: params, //搜索json对象
                view: detailview,
                detailFormatter: function(index, row) {
                    return '<div id="ddv-' + index + '" style="padding:5px;"></div>';
                },
                onExpandRow: function(index, row) {
                    $('#ddv-' + index).panel({
                        border: false,
                        cache: false,
                        href: '@Url.Action("GetByCategory", "Product", new { categoryId = "_id_" })'
                            .replace("_id_", row.ID),
                        onLoad: function () {
                            $('#tt').datagrid('fixDetailRowHeight', index);
                        }
                    });
                    $('#tt').datagrid('fixDetailRowHeight', index);
                }
            });
        }
    </script>
}

使用了Easyui的panel插件显式Detail表内容。
使用了datagrid的一个扩展datagrid-detailview.js用来显式Detail表,如下:

展开var detailview = $.extend({}, $.fn.datagrid.defaults.view, {
    render: function (target, container, frozen) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        if (frozen) {
            if (!(opts.rownumbers || (opts.frozenColumns && opts.frozenColumns.length))) {
                return;
            }
        }

        var rows = state.data.rows;
        var fields = $(target).datagrid('getColumnFields', frozen);
        var table = [];
        table.push('<table class="datagrid-btable" cellspacing="0" cellpadding="0" border="0"><tbody>');
        for (var i = 0; i < rows.length; i++) {
            // get the class and style attributes for this row
            var css = opts.rowStyler ? opts.rowStyler.call(target, i, rows[i]) : '';
            var classValue = '';
            var styleValue = '';
            if (typeof css == 'string') {
                styleValue = css;
            } else if (css) {
                classValue = css['class'] || '';
                styleValue = css['style'] || '';
            }

            var cls = 'class="datagrid-row ' + (i % 2 && opts.striped ? 'datagrid-row-alt ' : ' ') + classValue + '"';
            var style = styleValue ? 'style="' + styleValue + '"' : '';
            var rowId = state.rowIdPrefix + '-' + (frozen ? 1 : 2) + '-' + i;
            table.push('<tr id="' + rowId + '" datagrid-row-index="' + i + '" ' + cls + ' ' + style + '>');
            table.push(this.renderRow.call(this, target, fields, frozen, i, rows[i]));
            table.push('</tr>');

            table.push('<tr style="display:none;">');
            if (frozen) {
                table.push('<td colspan=' + (fields.length + 2) + ' style="border-right:0">');
            } else {
                table.push('<td colspan=' + (fields.length) + '>');
            }
            table.push('<div class="datagrid-row-detail">');
            if (frozen) {
                table.push(' ');
            } else {
                table.push(opts.detailFormatter.call(target, i, rows[i]));
            }
            table.push('</div>');
            table.push('</td>');
            table.push('</tr>');

        }
        table.push('</tbody></table>');

        $(container).html(table.join(''));
    },

    renderRow: function (target, fields, frozen, rowIndex, rowData) {
        var opts = $.data(target, 'datagrid').options;

        var cc = [];
        if (frozen && opts.rownumbers) {
            var rownumber = rowIndex + 1;
            if (opts.pagination) {
                rownumber += (opts.pageNumber - 1) * opts.pageSize;
            }
            cc.push('<td class="datagrid-td-rownumber"><div class="datagrid-cell-rownumber">' + rownumber + '</div></td>');
        }
        for (var i = 0; i < fields.length; i++) {
            var field = fields[i];
            var col = $(target).datagrid('getColumnOption', field);
            if (col) {
                var value = rowData[field];     // the field value
                var css = col.styler ? (col.styler(value, rowData, rowIndex) || '') : '';
                var classValue = '';
                var styleValue = '';
                if (typeof css == 'string') {
                    styleValue = css;
                } else if (cc) {
                    classValue = css['class'] || '';
                    styleValue = css['style'] || '';
                }
                var cls = classValue ? 'class="' + classValue + '"' : '';
                var style = col.hidden ? 'style="display:none;' + styleValue + '"' : (styleValue ? 'style="' + styleValue + '"' : '');

                cc.push('<td field="' + field + '" ' + cls + ' ' + style + '>');

                if (col.checkbox) {
                    style = '';
                } else if (col.expander) {
                    style = "text-align:center;height:16px;";
                } else {
                    style = styleValue;
                    if (col.align) { style += ';text-align:' + col.align + ';' }
                    if (!opts.nowrap) {
                        style += ';white-space:normal;height:auto;';
                    } else if (opts.autoRowHeight) {
                        style += ';height:auto;';
                    }
                }

                cc.push('<div style="' + style + '" ');
                if (col.checkbox) {
                    cc.push('class="datagrid-cell-check ');
                } else {
                    cc.push('class="datagrid-cell ' + col.cellClass);
                }
                cc.push('">');

                if (col.checkbox) {
                    cc.push('<input type="checkbox" name="' + field + '" value="' + (value != undefined ? value : '') + '">');
                } else if (col.expander) {
                    //cc.push('<div style="text-align:center;width:16px;height:16px;">');
                    cc.push('<span class="datagrid-row-expander datagrid-row-expand" style="display:inline-block;width:16px;height:16px;cursor:pointer;" />');
                    //cc.push('</div>');
                } else if (col.formatter) {
                    cc.push(col.formatter(value, rowData, rowIndex));
                } else {
                    cc.push(value);
                }

                cc.push('</div>');
                cc.push('</td>');
            }
        }
        return cc.join('');
    },

    insertRow: function (target, index, row) {
        var opts = $.data(target, 'datagrid').options;
        var dc = $.data(target, 'datagrid').dc;
        var panel = $(target).datagrid('getPanel');
        var view1 = dc.view1;
        var view2 = dc.view2;

        var isAppend = false;
        var rowLength = $(target).datagrid('getRows').length;
        if (rowLength == 0) {
            $(target).datagrid('loadData', { total: 1, rows: [row] });
            return;
        }

        if (index == undefined || index == null || index >= rowLength) {
            index = rowLength;
            isAppend = true;
            this.canUpdateDetail = false;
        }

        $.fn.datagrid.defaults.view.insertRow.call(this, target, index, row);

        _insert(true);
        _insert(false);

        this.canUpdateDetail = true;

        function _insert(frozen) {
            var v = frozen ? view1 : view2;
            var tr = v.find('tr[datagrid-row-index=' + index + ']');

            if (isAppend) {
                var newDetail = tr.next().clone();
                tr.insertAfter(tr.next());
            } else {
                var newDetail = tr.next().next().clone();
            }
            newDetail.insertAfter(tr);
            newDetail.hide();
            if (!frozen) {
                newDetail.find('div.datagrid-row-detail').html(opts.detailFormatter.call(target, index, row));
            }
        }
    },

    deleteRow: function (target, index) {
        var opts = $.data(target, 'datagrid').options;
        var dc = $.data(target, 'datagrid').dc;
        var tr = opts.finder.getTr(target, index);
        tr.next().remove();
        $.fn.datagrid.defaults.view.deleteRow.call(this, target, index);
        dc.body2.triggerHandler('scroll');
    },

    updateRow: function (target, rowIndex, row) {
        var dc = $.data(target, 'datagrid').dc;
        var opts = $.data(target, 'datagrid').options;
        var cls = $(target).datagrid('getExpander', rowIndex).attr('class');
        $.fn.datagrid.defaults.view.updateRow.call(this, target, rowIndex, row);
        $(target).datagrid('getExpander', rowIndex).attr('class', cls);

        // update the detail content
        if (this.canUpdateDetail) {
            var row = $(target).datagrid('getRows')[rowIndex];
            var detail = $(target).datagrid('getRowDetail', rowIndex);
            detail.html(opts.detailFormatter.call(target, rowIndex, row));
        }
    },

    bindEvents: function (target) {
        var state = $.data(target, 'datagrid');
        var dc = state.dc;
        var opts = state.options;
        var body = dc.body1.add(dc.body2);
        var clickHandler = ($.data(body[0], 'events') || $._data(body[0], 'events')).click[0].handler;
        body.unbind('click').bind('click', function (e) {
            var tt = $(e.target);
            var tr = tt.closest('tr.datagrid-row');
            if (!tr.length) { return }
            if (tt.hasClass('datagrid-row-expander')) {
                var rowIndex = parseInt(tr.attr('datagrid-row-index'));
                if (tt.hasClass('datagrid-row-expand')) {
                    $(target).datagrid('expandRow', rowIndex);
                } else {
                    $(target).datagrid('collapseRow', rowIndex);
                }
                $(target).datagrid('fixRowHeight');

            } else {
                clickHandler(e);
            }
            e.stopPropagation();
        });
    },

    onBeforeRender: function (target) {
        var state = $.data(target, 'datagrid');
        var opts = state.options;
        var dc = state.dc;
        var t = $(target);
        var hasExpander = false;
        var fields = t.datagrid('getColumnFields', true).concat(t.datagrid('getColumnFields'));
        for (var i = 0; i < fields.length; i++) {
            var col = t.datagrid('getColumnOption', fields[i]);
            if (col.expander) {
                hasExpander = true;
                break;
            }
        }
        if (!hasExpander) {
            if (opts.frozenColumns && opts.frozenColumns.length) {
                opts.frozenColumns[0].splice(0, 0, { field: '_expander', expander: true, width: 24, resizable: false, fixed: true });
            } else {
                opts.frozenColumns = [[{ field: '_expander', expander: true, width: 24, resizable: false, fixed: true }]];
            }

            var t = dc.view1.children('div.datagrid-header').find('table');
            var td = $('<td rowspan="' + opts.frozenColumns.length + '"><div class="datagrid-header-expander" style="width:24px;"></div></td>');
            if ($('tr', t).length == 0) {
                td.wrap('<tr></tr>').parent().appendTo($('tbody', t));
            } else if (opts.rownumbers) {
                td.insertAfter(t.find('td:has(div.datagrid-header-rownumber)'));
            } else {
                td.prependTo(t.find('tr:first'));
            }
        }

        var that = this;
        setTimeout(function () {
            that.bindEvents(target);
        }, 0);
    },

    onAfterRender: function (target) {
        var that = this;
        var state = $.data(target, 'datagrid');
        var dc = state.dc;
        var opts = state.options;
        var panel = $(target).datagrid('getPanel');

        $.fn.datagrid.defaults.view.onAfterRender.call(this, target);

        if (!state.onResizeColumn) {
            state.onResizeColumn = opts.onResizeColumn;
        }
        if (!state.onResize) {
            state.onResize = opts.onResize;
        }
        function setBodyTableWidth() {
            var columnWidths = dc.view2.children('div.datagrid-header').find('table').width();
            dc.body2.children('table').width(columnWidths);
        }

        opts.onResizeColumn = function (field, width) {
            setBodyTableWidth();
            var rowCount = $(target).datagrid('getRows').length;
            for (var i = 0; i < rowCount; i++) {
                $(target).datagrid('fixDetailRowHeight', i);
            }

            // call the old event code
            state.onResizeColumn.call(target, field, width);
        };
        opts.onResize = function (width, height) {
            setBodyTableWidth();
            state.onResize.call(panel, width, height);
        };

        this.canUpdateDetail = true;     // define if to update the detail content when 'updateRow' method is called;

        dc.footer1.find('span.datagrid-row-expander').css('visibility', 'hidden');
        $(target).datagrid('resize');
    }
});

$.extend($.fn.datagrid.methods, {
    fixDetailRowHeight: function (jq, index) {
        return jq.each(function () {
            var opts = $.data(this, 'datagrid').options;
            if (!(opts.rownumbers || (opts.frozenColumns && opts.frozenColumns.length))) {
                return;
            }
            var dc = $.data(this, 'datagrid').dc;
            var tr1 = opts.finder.getTr(this, index, 'body', 1).next();
            var tr2 = opts.finder.getTr(this, index, 'body', 2).next();
            // fix the detail row height
            if (tr2.is(':visible')) {
                tr1.css('height', '');
                tr2.css('height', '');
                var height = Math.max(tr1.height(), tr2.height());
                tr1.css('height', height);
                tr2.css('height', height);
            }
            dc.body2.triggerHandler('scroll');
        });
    },
    getExpander: function (jq, index) {     // get row expander object
        var opts = $.data(jq[0], 'datagrid').options;
        return opts.finder.getTr(jq[0], index).find('span.datagrid-row-expander');
    },
    // get row detail container
    getRowDetail: function (jq, index) {
        var opts = $.data(jq[0], 'datagrid').options;
        var tr = opts.finder.getTr(jq[0], index, 'body', 2);
        return tr.next().find('div.datagrid-row-detail');
    },
    expandRow: function (jq, index) {
        return jq.each(function () {
            var opts = $(this).datagrid('options');
            var dc = $.data(this, 'datagrid').dc;
            var expander = $(this).datagrid('getExpander', index);
            if (expander.hasClass('datagrid-row-expand')) {
                expander.removeClass('datagrid-row-expand').addClass('datagrid-row-collapse');
                var tr1 = opts.finder.getTr(this, index, 'body', 1).next();
                var tr2 = opts.finder.getTr(this, index, 'body', 2).next();
                tr1.show();
                tr2.show();
                $(this).datagrid('fixDetailRowHeight', index);
                if (opts.onExpandRow) {
                    var row = $(this).datagrid('getRows')[index];
                    opts.onExpandRow.call(this, index, row);
                }
            }
        });
    },
    collapseRow: function (jq, index) {
        return jq.each(function () {
            var opts = $(this).datagrid('options');
            var dc = $.data(this, 'datagrid').dc;
            var expander = $(this).datagrid('getExpander', index);
            if (expander.hasClass('datagrid-row-collapse')) {
                expander.removeClass('datagrid-row-collapse').addClass('datagrid-row-expand');
                var tr1 = opts.finder.getTr(this, index, 'body', 1).next();
                var tr2 = opts.finder.getTr(this, index, 'body', 2).next();
                tr1.hide();
                tr2.hide();
                dc.body2.triggerHandler('scroll');
                if (opts.onCollapseRow) {
                    var row = $(this).datagrid('getRows')[index];
                    opts.onCollapseRow.call(this, index, row);
                }
            }
        });
    }
});

最终效果:
主次表

转载于:https://www.cnblogs.com/darrenji/p/3576258.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值