API参考 - 筋斗云前端(桌面Web版)

最后更新:2019-03-15

@module WUI

1 对象管理功能

1.1 定义列表页和详情页

1.2 添加入口按钮

1.3 定义页面初始化函数

1.4 定义对话框的初始化函数

1.5 列表页中的常见需求

1.5.1 列表页中的列,以特定格式展现

1.5.2 排序与分页

1.5.3 列表导出Excel文件

1.5.4 datagrid增强项

1.6 详情页对话框的常见需求

1.6.1 通用查询

1.6.2 设计模式:关联选择框

1.6.3 picId字段显示图片

1.6.4 List字段显示为多个选项框

1.7 设计模式:展示层次对象

1.8 设计模式:页面间调用

2 对话框功能

2.1 定义对话框

2.2 显示对话框

2.3 页面传参数给对话框

2.4 示例:页面与对话框复用 (v5.1)

2.5 只读对话框

3 模块化开发

3.1 批量更新、批量删除

3.1 页面模板支持

4 参考文档说明

筋斗云前端框架-Web应用桌面版

此框架实现与筋斗云服务端接口的无缝整合。在界面上以jquery-easyui库为基础展示列表、Tab页等。
参考应用 web/store.html - 商户管理端应用。

1 对象管理功能

设计模式:列表页与详情页。

以订单对象Order为例:为订单对象增加“列表页”和“详情页”。

列表页应包含分页功能,默认只显示“未完成”订单。
点击列表中一项(一个订单),可显示详情页,即订单详情,并可进行查找、更新等功能。

1.1 定义列表页和详情页

@key #my-pages 包含所有页面、对话框定义的容器。

@key my-obj DOM属性,标识服务端对象

@key my-initfn DOM属性,标识页面或对话框的初始化函数,首次显示页面/对话框时调用。

列表页使用逻辑页面定义如下(放在div#my-pages之下),它最终展示为一个tab页:

<div id="my-pages" style="display:none">
    ...
    <script type="text/html" id="tpl_pageOrder">
        <div class="pageOrder" title="订单管理" my-initfn="initPageOrder">
            <table id="tblOrder" style="width:auto;height:auto">
                <thead><tr>
                    <th data-options="field:'id', sortable:true, sorter:intSort">订单号</th>
                    <th data-options="field:'userPhone', sortable:true">用户联系方式</th>
                    <th data-options="field:'createTm', sortable:true">创建时间</th>
                    <th data-options="field:'status', jdEnumMap: OrderStatusMap, formatter:Formatter.orderStatus, styler:OrderColumns.statusStyler, sortable:true">状态</th>
                    <th data-options="field:'dscr', sortable:true">描述</th>
                    <th data-options="field:'cmt'">用户备注</th>
                </tr></thead>
            </table>
        </div>
    </script>
</div>

注意:

详情页展示为一个对话框,也将它也放在 div#my-pages 下。定义如下(此处为展示原理已简化):

<script type="text/html" id="tpl_dlgOrder">
    <div id="dlgOrder" my-obj="Ordr" my-initfn="initDlgOrder" title="用户订单" style="width:520px;height:500px;">  
        <form method="POST">
            订单号:<input name="id" disabled></td>
            订单状态:
                    <select name="status" style="width:150px">
                        <option value="">&nbsp;</option>
                        <option value="CR">未付款</option>
                        <option value="PA">待服务(已付款)</option>
                        <option value="ST">正在服务</option>
                        <option value="RE">已服务(待评价)</option>
                        <option value="RA">已评价</option>
                        <option value="CA">已取消</option>
                    </select>
        用户备注:<textarea name="cmt" rows=3 cols=30></textarea>
        </form>
    <div>
</script>

注意:

@see showObjDlg

@see showDlg

以上定义了订单对象的列表页和详情页,围绕对象"Order", 按规范,我们定义了以下名字:

1.2 添加入口按钮
<a href="#pageOrder" class="easyui-linkbutton" icon="icon-ok">订单管理</a><br/><br/>
1.3 定义页面初始化函数

打开页面后,页面的生存周期如下:

@key event-pagecreate,pageshow,pagedestroy 页面事件

@key wui-pageName 属性:页面名

@key .wui-page 页面类

订单列表页的初始化,需要将列表页(代码中jpage)、列表(代码中jtbl)与详情页(代码中jdlg)关联起来,实现对话增删改查各项功能。

function initPageOrder() 
{
    var jpage = $(this);
    var jtbl = jpage.find("#tblOrder");
    var jdlg = $("#dlgOrder");

    // 注意:此处定义显示哪些缺省操作按钮:
    // r-refresh/刷新, f-find/查找, s-set/更新。参考 WUI.dg_toolbar.
    // 如果不定义则所有操作按钮都展示。
    jtbl.jdata().toolbar = "rfs";

    // 当天订单
    var query1 = {cond: "createTm between '" + new Date().format("D") + "' and '" + new Date().addDay(1).format("D") + "'"};
    // var query1 = WUI.getQueryParam({createTm: new Date().format("D") + "~" + new Date().addDay(1).format("D")});
    // 显示待服务/正在服务订单
    var query2 = {cond: "status='CR' OR status='PA' OR status='ST'"};
    // var query2 = WUI.getQueryParam({status: "CR,PA,ST"});

    function getTodoOrders()
    {
        WUI.reload(jtbl, null, query2);
    }
    function getTodayOrders()
    {
        WUI.reload(jtbl, null, query1);
    }
    var btn1 = {text: "今天订单", iconCls:'icon-search', handler: getTodayOrders};
    var btn2 = {text: "所有未完成", iconCls:'icon-search', handler: getTodoOrders};

    var dgOpt = {
        // 设置查询接口
        url: WUI.makeUrl(["Ordr", "query"], {res:"*,createTm,userPhone"}),
        // 设置缺省查询条件
        queryParams: query1,
        // 设置工具栏上的按钮,默认有增删改查按钮,"export"表示"导出到Excel"的按钮,btn1, btn2是自定义按钮,"-"表示按钮分隔符。
        toolbar: WUI.dg_toolbar(jtbl, jdlg, "export", "-", btn1, btn2),
        // 双击一行,应展示详情页对话框
        onDblClickRow: WUI.dg_dblclick(jtbl, jdlg)
    };
    jtbl.datagrid(dgOpt);
}

@see showPage

@see dg_toolbar

@see dg_dblclick

@see makeUrl

1.4 定义对话框的初始化函数

@key example-dialog

默认对话框中由于设定了底层对象(my-obj)及属性关联(form中带name属性的组件,已关联对象属性),因而可自动显示和提交数据。

特别地,某些属性不宜直接展示,例如属性“人物头像”,服务器存储的是图片id(picId),而展示时应显示为图片而不是一个数字;
或者如“权限列表”属性,服务器存储的是逗号分隔的一组权限比如"emp,mgr",而展示时需要为每项显示一个勾选框。
这类需求就需要编码控制。

相关事件:

@see beforeshow show 对话框中form显示前后

对话框类名:

@see .wui-dialog

function initDlgOrder()
{
    var jdlg = $(this);
    jdlg.on("beforeshow", onBeforeShow)
        .on("show", onShow)
        .on("validate", onValidate)
        .on("retdata", onRetData);

    function onBeforeShow(ev, formMode, opt) {
        jdlg.find(".forFind").toggle(formMode == FormMode.forFind);
        jdlg.find(".notForFind").toggle(formMode != FormMode.forFind);
    }
    function onShow(ev, formMode, initData) {
        // data是列表页中一行对应的数据,框架自动根据此数据将对应属性填上值。
        // 如果界面上展示的字段无法与属性直接对应,可以在该事件回调中设置。
        // hiddenToCheckbox(jdlg.find("#divPerms"));
    }
    function onValidate(ev, formMode, initData, newData) {
        // 在form提交时,所有带name属性且不带disabled属性的对象值会被发往服务端。
        // 此事件回调可以设置一些界面上无法与属性直接对应的内容。
        // 额外要提交的数据可放在隐藏的input组件中,或(v5.1)这里直接设置到newData对象中。
        // checkboxToHidden(jdlg.find("#divPerms"));
    }
    function onRetData(ev, data, formMode) {
        var formMode = jdlg.jdata().mode;
        if (formMode == FormMode.forAdd) {
            alert('返回ID: ' + data);
        }
    }
}

在onBeforeShow中一般设置字段是否显示(show/hide/toggle)或只读(disabled),以及在forAdd/forFind模式时为opt.data设置初始值(forSet模式下opt.data已填上业务数据);
之后框架用opt.data数据填充相应字段,如需要补填或修改些字段(比如显示图片),可在onShow中处理,也可以直接在onBeforeShow中用setTimeout来指定,如:

function onBeforeShow(ev, formMode, opt) {
    // ... 根据formMode等参数控制某些字段显示隐藏、启用禁用等...
    var frm = jdlg.find("form")[0];
    var isFind = formMode == FormMode.forFind;
    frm.type.disabled = !isFind;
    // 这里可以对opt.data赋值,但不要直接为组件设置值,因为接下来组件值会被opt.data中的值覆盖。

    setTimeout(onShow);
    function onShow() {
        // 这里可根据opt.data直接为input等组件设置值。便于使用onBeforeShow中的变量
    }
}

@see checkboxToHidden (有示例)

@see hiddenToCheckbox

@see imgToHidden

@see hiddenToImg (有示例)

1.5 列表页中的常见需求

框架中,对象列表通过easyui-datagrid来展现。
注意:由于历史原因,我们没有使用datagrid中的编辑功能。

参考:http://www.jeasyui.net/plugins/183.html
教程:http://www.jeasyui.net/tutorial/148.html

1.5.1 列表页中的列,以特定格式展现

@key datagrid.formatter

@key datagrid.styler

示例一:显示名称及颜色

订单状态字段定义为:

status:: Enum. 订单状态。CR-新创建,RE-已服务,CA-已取消. 

在显示时,要求显示其中文名称,且根据状态不同,显示不同的背景颜色。

在table中设置formatter与styler选项:

<div class="pageOrder" title="订单管理" my-initfn="initPageOrder">
    <table id="tblOrder" style="width:auto;height:auto" title="订单列表">
        <thead><tr>
            <th data-options="field:'id', sortable:true, sorter:intSort">订单号</th>
            ...
            <th data-options="field:'status', jdEnumMap: OrderStatusMap, formatter:Formatter.orderStatus, styler:OrderColumns.statusStyler, sortable:true">状态</th>
        </tr></thead>
    </table>
</div>

formatter用于控制Cell中的HTML标签,styler用于控制Cell自己的CSS style.
在JS中定义:

var OrderStatusMap = {
    CR: "未付款", 
    RE: "已服务", 
    CA: "已取消"
};
var Formatter = {
    // 显示枚举值的描述,相当于`return map[value] || value`;
    orderStatus: WUI.formatter.enum(OrderStatusMap),
    // 显示链接,点击打开用户详情对话框
    userId: WUI.formatter.linkTo("userId", "#dlgUser")
};
Formatter = $.extend(WUI.formatter, Formatter);

var OrderColumns = {
    statusStyler: function (value, row) {
        var colors = {
            CR: "#000",
            RE: "#0f0",
            CA: "#ccc"
        };
        var color = colors[value];
        if (color)
            return "background-color: " + color;
    },
    ...
}

注意:

@see formatter 通用格式化函数

一些其它示例:

var Formatter = {
    // 显示数值
    number: function (value, row) {
        return parseFloat(value);
    },
    // 订单编号,显示为一个链接,点击就打开订单对话框该订单。
    orderId: function (value, row) {
        if (value) {
            return WUI.makeLinkTo("#dlgOrder", row.orderId, value);
        }
    },
    // 显示状态的同时,设置另一个本地字段,这种字段一般以"_"结尾,表示不是服务器传来的字段,例如
    // <th data-options="field:'hint_'">提醒事项</th>
    status: function (value, row) {
        if (value) {
            if (value == "PA") {
                row.hint_ = "请于2小时内联系";
            }
            return StatusMap[value] || value;
        }
    }
};

@see makeLinkTo 生成对象链接,以便点击时打开该对象的详情对话框。

1.5.2 排序与分页

@key datagrid.sortable

@key datagrid.sorter

使用sortable:true指定该列可排序(可点击列头排序),用sorter指定排序算法(缺省是字符串排序),例如:

<th data-options="field:'name', sortable:true">姓名</th>
<th data-options="field:'id', sortable:true, sorter:intSort">编号</th>
<th data-options="field:'score', sortable:true, sorter:numberSort">评分</th>

框架提供了intSort,numberSort这些函数用于整数排序或小数排序。也可以自定义函数。示例:

function intSort(a, b)
{
    return parseInt(a) - parseInt(b);
}

注意:

@see intSort numberSort

如果打开数据表就希望按某一列排序,可设置:

jtbl.datagrid({
    ...
    sortName: 'id',
    sortOrder: 'desc'
});

手工点击列标题栏排序,会自动修改这两个属性。
在添加数据时,如果当前sortOrder是倒序,则新数据显示在表格当前页的最前面,否则显示在最后。

框架对datagrid还做了以下缺省设置:

如果需要禁用分页,可以设置:

jtbl.datagrid({
    url: WUI.makeUrl("Ordr.query", {"pagesz": -1}), // -1表示取后端允许的最大数量
    pagination: false, // 禁用分页组件
    ...
});
1.5.3 列表导出Excel文件

(支持版本5.0)

除了默认地增删改查,还可为数据表添加标准的“导出Excel”操作,可自动按表格当前的显示字段、搜索条件、排序条件等,导出表格。
只需在dg_toolbar函数的参数中加上"export"(表示导出按钮),如:

jtbl.datagrid({
    url: WUI.makeUrl("User.query"),
    toolbar: WUI.dg_toolbar(jtbl, jdlg, "export"),
    onDblClickRow: WUI.dg_dblclick(jtbl, jdlg)
});

导出字段由jtbl对应的表格的表头定义,如下面表格定义:

<table id="tblOrder" style="width:auto;height:auto" title="订单列表">
    ...
    <th data-options="field:'id'">编号</th>
    <th data-options="field:'status'">状态</th>
    <th data-options="field:'hint_'">友情提示</th>
</table>

它生成的res参数为"id 编号, status 状态"。"hint_"字段以下划线结尾,它会被当作是本地虚拟字段,不会被导出。

table上的title属性可用于控制列表导出时的默认文件名,如本例导出文件名为"订单列表.xls"。
如果想导出表中没有显示的列,可以设置该列为隐藏,如:

    <th data-options="field:'userId', hidden:true">用户编号</th>

@key jdEnumMap datagrid中th选项, 在导出文件时,枚举变量可显示描述

对于枚举字段,可在th的data-options用formatter:WUI.formatter.enum(map)来显示描述,在导出Excel时,需要设置jdEnumMap:map属性来显示描述,如

    <th data-options="field:'status', jdEnumMap: OrderStatusMap, formatter: WUI.formatter.enum(OrderStatusMap)">状态</th>

OrderStatusMap在代码中定义如下

var OrderStatusMap = {
    CR: "未付款", 
    PA: "待服务"
}

它生成的res参数为"id 编号, status 状态=CR:未付款;PA:待服务"。筋斗云后端支持这种res定义方式将枚举值显示为描述。

@see dg_toolbar 指定列表上的操作按钮

@see getExportHandler 自定义导出Excel功能

@see getQueryParamFromTable 根据当前datagrid状态取query接口参数

HINT: 点“导出”时会直接下载文件,看不到请求和调用过程,如果需要调试导出功能,可在控制台中设置 window.open=$.get 即可在chrome中查看请求响应过程。

1.5.4 datagrid增强项

easyui-datagrid已适配筋斗云协议调用,底层将发起callSvr调用请求(参考dgLoader)。
此外,增加支持url_属性,以便初始化时不发起调用,直到调用"load"/"reload"方法时才发起调用:

jtbl.datagrid({
    url_: WUI.makeUrl("Item.query", {res:"id,name"}), // 如果用url则会立即用callSvr发起请求。
    ...
});
// ...
jtbl.datagrid("load", {cond: "itemId=" + itemId});
jtbl.datagrid("reload");

如果接口返回格式不符合,则可以使用loadData方法:

// 接口 Item.get() -> {item1=[{srcItemId, qty}]}
callSvr("Item.get", {res:"item1"}, function (data) {
    jtbl.datagrid("loadData", data.item1); // 是一个对象数组
});

datagrid默认加载数据要求格式为{total, rows},框架已对返回数据格式进行了默认处理,兼容筋斗云协议格式(参考dgLoadFilter)。

var rows = [ {id:1, name:"name1"}, {id:2, name:"name2"} ];
jtbl.datagrid("loadData", {tota:2, rows: rows});
// 还支持以下三种格式
jtbl.datagrid("loadData", rows);
jtbl.datagrid("loadData", {h: ["id","name"], d: [ [1, "name1"], [2, "name2"]}); // 筋斗云query接口默认返回格式。
jtbl.datagrid("loadData", {list: rows}); // 筋斗云query接口指定fmt=list参数时,返回这种格式
1.6 详情页对话框的常见需求
1.6.1 通用查询

在对话框中按快捷键"Ctrl-F"可进入查询模式。
详情页提供通用查询,如:

手机号: <input name="phone">  
注册时间: <input name="createTm">

可在手机号中输入"137*",在注册时间中输入">=2017-1-1 and <2018-1-1" (或用 "2017-1-1~2018-1-1"),这样生成的查询参数为:

{ cond: "phone like '137%' and (createTm>='2017-1-1' and createTm<'2018-1-1')" }

@see getQueryCond 查询条件支持

@see getQueryParam 生成查询条件

@key .wui-find-field 用于查找的字段样式

可设置该样式来标识哪些字段可以查找。一般设置为黄色。

@key .notForFind 指定非查询条件

不参与查询的字段,可以用notForFind类标识(为兼容,也支持属性notForFind),如:

登录密码: <input class="notForFind" type="password" name="pwd">
或者: <input notForFind type="password" name="pwd">

@key .wui-notCond 指定独立查询条件

如果查询时不想将条件放在cond参数中,可以设置wui-notCond类标识,如:

状态: <select name="status" class="my-combobox wui-notCond" data-options="jdEnumList:'0:可用;1:禁用'"></select>

如果不加wui-notCond类,生成的查询参数为:{cond: "status=0"};加上后,生成查询参数如:{status: 0}.

1.6.2 设计模式:关联选择框

示例:下拉框中显示员工列表 (Choose-from-list / 关联选择框)

@see jQuery.fn.mycombobox

1.6.3 picId字段显示图片

@see hiddenToImg (有示例)

@see imgToHidden

1.6.4 List字段显示为多个选项框

@see hiddenToCheckbox

@see checkboxToHidden  (有示例)

1.7 设计模式:展示层次对象

例如设计有商品表Item, 每个商品属于特定的商户:

@Item: id, storeId, name
storeId:: Integer. 商品所属商户编号。

也就是说,商户包含商品。要展现商品,可将它放在商户层次之下。
可以这样设计用户操作:在商户列表上增加一个按钮“查看商品”,点击后打开一个新的列表页,显示该商户的商品列表。

定义两个列表页:

<div class="pageStore" title="商户列表" my-initfn="initPageStore">
</div>

<div class="pageItem" title="商户商品" my-initfn="initPageItem">
</div>

为这两个列表页定义初始化函数:

// 商户列表页
function initPageStore()
{
    function showItemPage()
    {
        var row = jtbl.datagrid('getSelected');
        if(row == null){
            alert("您需要选择需要操作的行");
            return;
        }
        // !!! 调用showPage显示新页 !!!
        WUI.showPage("pageItem", "商户商品-" + row.name, [row.id]);
        // 要使每个商户都打开一个商品页面而不是共享一个页面,必须保证第二个参数(页面标题)根据商户不同而不一样。
        // 第三个参数是传给该页面初始化函数的参数列表,是一个数组。
    }
    var btn1 = {text: "查看商品", iconCls: "icon-search", handler: showItemPage};

    ...
    jtbl.datagrid({
        ...
        toolbar: WUI.dg_toolbar(jtbl, jdlg, btn1),
    });
}

// 商品列表页,注意有一个参数storeId, 并在查询时使用到它。
function initPageItem(storeId)
{
    jtbl.datagrid({
        // 用参数storeId过滤
        url: WUI.makeUrl("Item.query", {cond: "storeId=" + storeId}),
        ...
    });
}

注意:

调用WUI.showPage时,除了指定页面名,还指定了页面标题(第二参数)和页面初始化参数(第三参数, 一定是一个数组):

WUI.showPage("pageItem", "商户商品-" + row.name, [row.id]);

显然,第二个参数随着商户名称不同而不同,这保证了不同商户打开的商品页面不会共用。
在商品页面初始化时,第三参数将传递给初始化函数:

function initPageItem(storeId) // storeId=row.id

此外,在Item页对应的详情对话框上(dlgItem.html页面中),还应设置storeId字段是只读的,在添加、设置和查询时不可被修改。

<select name="storeId" class="my-combobox" data-options="ListOptions.Store()" readonly></select>

注意:select组件默认不支持readonly属性,框架定义了CSS:为select[readonly]设置pointer-events:none达到类似效果。

然后,在initDlgItem函数中(dlgItem.js文件),应设置在添加时自动填好该字段:

function onBeforeShow(ev, formMode, opt)
    if (formMode == FormMode.forAdd && objParam.storeId) {
        opt.data.storeId = objParam.storeId);
    }

@see showPage

1.8 设计模式:页面间调用

仍以上节数据结构为例,上节是在每个商品行上点“查看商品”,就打开一个新的该商户下的商品列表页,
现在我们换一种操作方法,改成只用一个商品列表页(默认打开时显示所有商户的商品,可以手工查找过滤),在商户页中点“查看商品”,就自动打开商品列表页并做条件过滤。

先在主页面逻辑中为商品页定义一个接口:(比如在store.js中)

var PageItem = {
    // param?: {storeId}
    show: function (param) {
        this.filterParam_ = param;
        WUI.showPage("pageItem");
    },
    filterParam_: null
};

在商户页中,点击“查看商品”按钮时做过滤:

function initPageStore()
{
    function showItemPage()
    {
        var row = jtbl.datagrid('getSelected');
        ...
        PageItem.show({storeId: row.id});
    }
    var btn1 = {text: "查看商品", iconCls: "icon-search", handler: showItemPage};

    ...
    jtbl.datagrid({
        ...
        toolbar: WUI.dg_toolbar(jtbl, jdlg, btn1),
    });
}

在商品页中,处理PageItem.filterParam_参数,实现过滤,我们在pageshow回调中处理它,同时把初始化datagrid也移到pageshow中:

function initPageItem()
{
    var isInit = true;
    jpage.on("pageshow", pageShow);

    function pageShow() {
        // 接口变量PageItem.filterParam_用后即焚
        var param = null;
        if (PageItem.filterParam_) {
            param = WUI.getQueryParam(PageItem.filterParam_);
            PageItem.filterParam_ = null;
        }
        // 保证表格初始化只调用一次
        if (isInit) {
            jtbl.datagrid({
                url: WUI.makeUrl("Item.query"),
                queryParams: param,
                toolbar: WUI.dg_toolbar(jtbl, jdlg, "export"),
                onDblClickRow: WUI.dg_dblclick(jtbl, jdlg),
                sortName: "id",
                sortOrder: "desc"
            });
            isInit = false;
        }
        else if (param) {
            WUI.reload(jtbl, null, param);
        }
    }
}

注意:

2 对话框功能

以群发短信功能为例。

假定服务端已有以下接口:

sendSms(phone, content)
phone:: 手机号
content:: 发送内容
2.1 定义对话框

注意:每个带name属性的组件对应接口中的参数。

<div id="dlgSendSms" title="群发短信" style="width:500px;height:300px;">  
    <form method="POST">
        手机号:<input name="phone" data-options="required:true">
        发送内容: <textarea rows=5 cols=30 name="content"></textarea>
    </form>
</div>
2.2 显示对话框

可以调用WUI.showDlg,写一个显示对话框的函数:

function showDlgSendSms()
{
    var jdlg = $("#dlgSendSms");
    WUI.showDlg(jdlg, {
        url: WUI.makeUrl("sendSms"),
        onOk: function (data) {
            WUI.closeDlg(jdlg);
            app_show('操作成功!');
        }
    });
}

在showDlg的选项url中指定了接口为"sendSms"。操作成功后,显示一个消息。

@see showDlg

@see app_show

除了直接调用该函数显示对话框外,还有一种更简单的通过a标签href属性指定打开对话框的做法,如:

<a href="?showDlgSendSms" class="easyui-linkbutton" icon="icon-ok">群发短信</a><br/><br/>

点击该按钮,即调用了showDlgSendSms函数打开对话框。

2.3 页面传参数给对话框

(v5.1)
可以通过showObjDlg(jdlg, mode, opt)中的opt参数,或jdlg.objParam来给对话框传参。
在对话框的beforeshow事件处理中,可通过opt.objParam拿到参数,如:

function initPageBizPartner() {
    var jdlg = $("#dlgSupplier");
    // 设置objParam参数供对话框使用。
    jdlg.objParam = {type: "C", obj: "Customer", title: "客户"}; // opt.title参数可直接设置对话框的标题。参考showObjDlg.
    jtbl.datagrid(toolbar: dg_toolbar(jtbl, jdlg, ...));
    // 点表格上的菜单或双击行时会调用 WUI.showObjDlg
}

function initDlgBizPartner() {
    // ...
    jdlg.on("beforeshow", onBeforeShow);

    function onBeforeShow(ev, formMode, opt) {
        // opt.objParam 中包含前面定义的type, obj, 以及id, mode等参数。
    }
}
2.4 示例:页面与对话框复用 (v5.1)

设计有客户(Customer)和供应商(Supplier)两个虚拟的逻辑对象,它们物理底层都是业务伙伴对象(BizPartner)。
现在只设计一个页面pageBizPartner和一个对话框dlgBizPartner。

菜单中两项:
默认pageBizPartner是供应商,如果要显示为"客户"页,需要明确调用showPage。

<a href="#pageBizPartner">供应商</a>
<a href="javascript:WUI.showPage('pageBizPartner', '客户', ['C']);">客户</a>

在initPageBizPartner函数中,为对话框传递参数objParam:

type = type || "S";
var obj = "type=="S"? "Supplier": "Customer";
jdlg.objParam = {type: type, obj: obj};
// ...

在对话框的beforeshow事件处理中,根据opt.objParam.type确定标题栏:

jdlg.on("beforeshow", function (ev, formMode, opt) {
    opt.title = opt.objParam.type == "C"? "客户": "供应商";
});
2.5 只读对话框

(v5.1)

@key .wui-readonly 只读对话框类名

设置是否为只读对话框只要加上该类:

jdlg.toggleClass("wui-readonly", isReadonly);

只读对话框不可输入(在style.css中设定pointer-events为none),点击确定按钮后直接关闭。

3 模块化开发

@key wui-script

@key options.pageFolder

允许将逻辑页、对话框的html片段和js片段放在单独的文件中。以前面章节示例中订单对象的列表页(是一个逻辑页)与详情页(是一个对话框)为例:

先在文件page/pageOrder.html中定义逻辑页

<div title="订单管理" wui-script="pageOrder.js" my-initfn="initPageOrder">
    <table id="tblOrder" style="width:auto;height:auto">
        ...
    </table>
</div>

注意:

在html文件的div中可以添加style样式标签:

<div>
    <style>
    table {
        background-color: #ddd;
    }
    </style>
    <table>...</table>
</div>

注意:其中定义的样式(比如这里的table)只应用于当前页面或对话框,因为框架会在加载它时自动限定样式作用范围。

在文件page/pageOrder.js中定义逻辑:

function initPageOrder() 
{
    var jpage = $(this);
    ...
}

这时,就可以用 WUI.showPage("#pageOrder")来显示逻辑页了。

注意:逻辑页的title字段不能和其它页中title重复,否则这两页无法同时显示,因为显示tab页时是按照title来标识逻辑页的。

动态加载页面时,先加载逻辑页html和js文件,再将逻辑页插入应用程序并做系统初始化(如增强mui组件或easyui组件等),然后调用页面的用户初始化函数。
若希望在系统初始化之前做一些操作,应放在用户初始化函数之外。
例如,初始化过程中的服务调用使用批处理:

functio initPageOrder() 
{
    ...
}
WUI.useBatchCall();

在文件page/dlgOrder.html中定义对话框UI:

<div wui-script="dlgOrder.js" my-obj="Ordr" my-initfn="initDlgOrder" title="用户订单" style="width:520px;height:500px;">  
    <form method="POST">
        ...
    </form>
<div>

注意:

在文件page/dlgOrder.js中定义js逻辑:

function initDlgOrder()
{
    var jdlg = $(this);
    ...
}

这时,就可以用 WUI.showObjDlg("#dlgOrder")来显示逻辑页了。

3.1 批量更新、批量删除

(v5.2)
列表页支持两种批量操作模式。

服务端应支持{obj}.setIf(cond){obj}.delIf(cond)接口。

3.1 页面模板支持

定义一个逻辑页面,可以在#my-pages下直接定义,也可以在单独的文件中定义,还可以在一个模板中定义,如:

<script type="text/html" id="tpl_pageOrder">
<div class="pageOrder" title="订单管理" my-initfn="initPageOrder">
...
</div>
</script>

模板用script标签定义,其id属性必须命名为tpl_{逻辑页面名}
这样就定义了逻辑页pageOrder,且在用到时才加载。与从外部文件加载类似,可以不设置class="pageOrder",框架会自动处理。

定义对话框也类似:

<script type="text/html" id="tpl_dlgOrder">
<div id="dlgOrder" my-obj="Ordr" my-initfn="initDlgOrder" title="用户订单" style="width:520px;height:500px;">  
...
</div>
</script>

定义了对话框dlgOrder,这个id属性也可以不设置。
模板用script标签定义,其id属性必须命名为tpl_{对话框名}

注意:

如果将script标签制作的页面模板内嵌在主页中,可能会造成加载时闪烁。
在chrome中,在easyui-layout之后定义任意script标签(哪怕是空内容),会导致加载首页时闪烁,标题栏是黑色尤其明显。
测试发现,将这些个script模板放在head标签中不会闪烁。

这个特性可用于未来WEB应用编译打包。

4 参考文档说明

以下参考文档介绍WUI模块提供的方法/函数(fn)、属性/变量(var)等,示例如下:

@fn showPage(pageName, title?, paramArr?)  一个函数。参数说明中问号表示参数可缺省。
@var options 一个属性。
@class batchCall(opt?={useTrans?=0}) 一个JS类。
@key example-dialog key表示一般关键字。前缀为"example-"用于示例讲解。
@key .wui-page 一个CSS类名"wui-page",关键字以"."开头。
@key #wui-pages 一个DOM对象,id为"wui-pages",关键字以"#"开头。

对于模块下的fn,var,class这些类别,如非特别说明,调用时应加WUI前缀,如

WUI.showPage("#pageOrder");
var opts = WUI.options;
var batch = new WUI.batchCall();
batch.commit();

以下函数可不加WUI前缀:

intSort
numberSort
callSvr
callSvrSync
app_alert
app_confirm
app_show

参考wui-name.js模块。

@fn assert(cond, dscr?)

@fn parseQuery(str)

解析url编码格式的查询字符串,返回对应的对象。

if (location.search) {
    var queryStr = location.search.substr(1); // "?id=100&name=abc&val=3.14"去掉"?"号
    var args = parseQuery(queryStr); // {id: 100, name: "abc", val: 3.14}
}

注意:

如果值为整数或小数,则会转成相应类型。如上例中 id为100,不是字符串"100".

@fn tobool(v)

将字符串转成boolean值。除"0", "1"外,还可以支持字符串 "on"/"off", "true"/"false"等。

@fn reloadSite()

重新加载当前页面,但不要#hash部分。

@fn Date.format(fmt?=L)

日期对象格式化字符串。

@param fmt 格式字符串。由以下组成:

yyyy - 四位年,如2008, 1999
yy - 两位年,如 08, 99
mm - 两位月,如 02, 12
dd - 两位日,如 01, 30
HH - 两位小时,如 00, 23
MM - 两位分钟,如 00, 59
SS - 两位秒,如 00, 59

支持这几种常用格式:
L - 标准日期时间,相当于 "yyyy-mm-dd HH:MM:SS"
D - 标准日期,相当于 "yyyy-mm-dd"
T - 标准时间,相当于 "HH:MM:SS"

示例:

var dt = new Date();
var dtStr1 = dt.format("D"); // "2009-10-20"
var dtStr2 = dt.format("yyyymmdd-HHMM"); // "20091020-2038"

@fn Date.addDay(n)

@fn Date.addHours(n)

@fn Date.addMin(n)

@fn Date.addMonth(n)

@fn parseTime(s)

将纯时间字符串生成一个日期对象。

var dt1 = parseTime("10:10:00");
var dt2 = parseTime("10:11");

@fn parseDate(dateStr)

将日期字符串转为日期时间格式。其效果相当于new Date(Date.parse(dateStr)),但兼容性更好(例如在safari中很多常见的日期格式无法解析)

示例:

var dt1 = parseDate("2012-01-01");
var dt2 = parseDate("2012/01/01 20:00:09");
var dt3 = parseDate("2012.1.1 20:00");

支持时区,时区格式可以是"+8", "+08", "+0800", "Z"这些,如

parseDate("2012-01-01T09:10:20.328+0800");
parseDate("2012-01-01T09:10:20Z");

@fn Date.add(sInterval, n)

为日期对象加几天/小时等。参数n为整数,可以为负数。

@param sInterval Enum. 间隔单位. d-天; m-月; y-年; h-小时; n-分; s-秒

示例:

var dt = new Date();
dt.add("d", 1); // 1天后
dt.add("m", 1); // 1个月后
dt.add("y", -1); // 1年前
dt.add("h", 3); // 3小时后
dt.add("n", 30); // 30分钟后
dt.add("s", 30); // 30秒后

@see Date.diff

@fn Date.diff(sInterval, dtEnd)

计算日期到另一日期间的间隔,单位由sInterval指定(具体值列表参见Date.add).

var dt = new Date();
...
var dt2 = new Date();
var days = dt.diff("d", dt2); // 相隔多少天

@see Date.add

@fn getTimeDiffDscr(tm, tm1)

从tm到tm1的时间差描述,如"2分钟前", "3天前"等。

tm和tm1可以为时间对象或时间字符串

@fn setCookie(name, value, days?=30)

设置cookie值。如果只是为了客户端长时间保存值,一般建议使用 setStorage.

@see getCookie

@see delCookie

@see setStorage

@fn getCookie(name)

取cookie值。

@see setCookie

@see delCookie

@fn delCookie(name)

删除一个cookie项。

@see getCookie

@see setCookie

@fn setStorage(name, value, useSession?=false)

使用localStorage存储(或使用sessionStorage存储, 如果useSession=true)。
value可以是简单类型,也可以为数组,对象等,后者将自动在序列化后存储。

如果设置了window.STORAGE_PREFIX, 则键值(name)会加上该前缀.

示例:

setStorage("id", "100");
var id = getStorage("id");
delStorage("id");

示例2:存储对象:

window.STORAGE_PREFIX = "jdcloud_"; // 一般在app.js中全局设置
var obj = {id:10, name:"Jason"};
setStorage("obj", obj);   // 实际存储键值为 "jdcloud_obj"
var obj2 = getStorage("obj");
alert(obj2.name);

@var STORAGE_PREFIX 本地存储的键值前缀

如果指定, 则调用setStorage/getStorage/delStorage时都将自动加此前缀, 避免不同项目的存储项冲突.

@see getStorage

@see delStorage

@fn getStorage(name, useSession?=false)

取storage中的一项。
默认使用localStorage存储,如果useSession=true,则使用sessionStorage存储。

如果浏览器不支持Storage,则使用cookie实现.

@see setStorage

@see delStorage

@fn delStorage(name)

删除storage中的一项。

@see getStorage

@see setStorage

@fn rs2Array(rs)

@param rs= {h=[header], d=[ @row ]} rs对象(RowSet)

@return arr=[ %obj ]

rs对象用于传递表格,包含表头与表内容。
函数用于将服务器发来的rs对象转成数组。

示例:

var rs = {
    h: ["id", "name"], 
    d: [ [100, "Tom"], [101, "Jane"] ] 
};
var arr = rs2Array(rs); 

// 结果为
arr = [
    {id: 100, name: "Tom"},
    {id: 101, name: "Jane"} 
];

@see rs2Hash

@see rs2MultiHash

@fn rs2Hash(rs, key)

@param rs= {h, d} rs对象(RowSet)

@return hash= {key => %obj}

示例:

var rs = {
    h: ["id", "name"], 
    d: [ [100, "Tom"], [101, "Jane"] ] 
};
var hash = rs2Hash(rs, "id"); 

// 结果为
hash = {
    100: {id: 100, name: "Tom"},
    101: {id: 101, name: "Jane"}
};

key可以为一个函数,返回实际key值,示例:

var hash = rs2Hash(rs, function (o) {
    return "USER-" + o.id;
}); 

// 结果为
hash = {
    "USER-100": {id: 100, name: "Tom"},
    "USER-101": {id: 101, name: "Jane"}
};

@see rs2Array

@fn rs2MultiHash(rs, key)

数据分组(group by).

@param rs= {h, d} rs对象(RowSet)

@return hash= {key => [ %obj ]}

示例:

var rs = {
    h: ["id", "name"], 
    d: [ [100, "Tom"], [101, "Jane"], [102, "Tom"] ] 
};
var hash = rs2MultiHash(rs, "name");  

// 结果为
hash = {
    "Tom": [{id: 100, name: "Tom"}, {id: 102, name: "Tom"}],
    "Jane": [{id: 101, name: "Jane"}]
};

key也可以是一个函数,返回实际的key值,示例,按生日年份分组:

var rs = {
    h: ["id", "name", "birthday"], 
    d: [ [100, "Tom", "1998-10-1"], [101, "Jane", "1999-1-10"], [102, "Tom", "1998-3-8"] ] 
};
// 按生日年份分组
var hash = rs2MultiHash(rs, function (o) {
    var m = o.birthday.match(/^\d+/);
    return m && m[0];
});

// 结果为
hash = {
    "1998": [{id: 100, name: "Tom", birthday: "1998-10-1"}, {id: 102, name: "Tom", birthday:"1998-3-8"}],
    "1999": [{id: 101, name: "Jane", birthday: "1999-1-10"}]
};

@see rs2Hash

@see rs2Array

@fn list2varr(ls, sep=':', sep2=',')

将字符串代表的压缩表("v1:v2:v3,...")转成对象数组。

e.g.

var users = "101:andy,102:beddy";
var varr = list2varr(users);
// varr = [["101", "andy"], ["102", "beddy"]];
var arr = rs2Array({h: ["id", "name"], d: varr});
// arr = [ {id: 101, name: "andy"}, {id: 102, name: "beddy"} ];

var cmts = "101\thello\n102\tgood";
var varr = list2varr(cmts, "\t", "\n");
// varr=[["101", "hello"], ["102", "good"]]

@fn objarr2list(objarr, fields, sep=':', sep2=',')

将对象数组转成字符串代表的压缩表("v1:v2:v3,...")。

示例:

var objarr = [
    {id:100, name:'name1', qty:2},
    {id:101, name:'name2', qty:3}
];
var list = objarr2list(objarr, ["id","qty"]);
// 返回"100:2,101:3"

var list2 = objarr2list(objarr, function (e, i) { return e.id + ":" + e.qty; });
// 结果同上

@fn intSort(a, b)

整数排序. 用于datagrid column sorter:

<th data-options="field:'id', sortable:true, sorter:intSort">编号</th>

@fn numberSort(a, b)

小数排序. 用于datagrid column sorter:

<th data-options="field:'score', sortable:true, sorter:numberSort">评分</th>

@fn getAncestor(o, fn)

取符合条件(fn)的对象,一般可使用$.closest替代

@fn appendParam(url, param)

示例:

var url = "http://xxx/api.php";
if (a)
    url = appendParam(url, "a=" + a);
if (b)
    url = appendParam(url, "b=" + b);

appendParam(url, $.param({a:1, b:3}));

支持url中带有"?"或"#",如

var url = "http://xxx/api.php?id=1#order";
appendParam(url, "pay=1"); // "http://xxx/api.php?id=1&pay=1#order";

@fn deleteParam(url, paramName)

示例:

var url = "http://xxx/api.php?a=1&b=3&c=2";
var url1 = deleteParam(url, "b"); // "http://xxx/api.php?a=1&c=2";

@fn isWeixin()

当前应用运行在微信中。

@fn isIOS()

当前应用运行在IOS平台,如iphone或ipad中。

@fn isAndroid()

当前应用运行在安卓平台。

@fn parseValue(str)

如果str符合整数或小数,则返回相应类型。

@fn applyTpl(tpl, data)

对模板做字符串替换

var tpl = "<li><p>{name}</p><p>{dscr}</p></li>";
var data = {name: 'richard', dscr: 'hello'};
var html = applyTpl(tpl, data);
// <li><p>richard</p><p>hello</p></li>

@fn delayDo(fn, delayCnt?=3)

设置延迟执行。当delayCnt=1时与setTimeout效果相同。
多次置于事件队列最后,一般3次后其它js均已执行完毕,为idle状态

@fn kvList2Str(kv, sep, sep2)

e.g.

var str = kvList2Str({"CR":"Created", "PA":"Paid"}, ';', ':');
// str="CR:Created;PA:Paid"

@fn parseKvList(kvListStr, sep, sep2) -> kvMap

解析key-value列表字符串,返回kvMap。
示例:

var map = parseKvList("CR:新创建;PA:已付款", ";", ":");
// map: {"CR": "新创建", "PA":"已付款"}

@fn jdModule(name?, fn?)

定义JS模块。这是一个全局函数。

定义一个模块:

jdModule("jdcloud.common", JdcloudCommon);
function JdcloudCommon() {
    var self = this;

    // 对外提供一个方法
    self.rs2Array = rs2Array;
    function rs2Array(rs)
    {
        return ...;
    }
}

获取模块对象:

var mCommon = jdModule("jdcloud.common");
var arr = mCommon.rs2Array(rs);

返回模块映射列表。

var moduleMap = jdModule(); // 返回 { "jdcloud.common": JdcloudCommon, ... }

@fn getFormData(jo)

取DOM对象中带name属性的子对象的内容, 放入一个JS对象中, 以便手工调用callSvr.

注意:

与setFormData配合使用时, 可以只返回变化的数据.

jf.submit(function () {
    var ac = jf.attr("action");
    callSvr(ac, fn, getFormData(jf));
});

如果在jo对象中存在有name属性的file组件(input[type=file][name]),或指定了属性enctype="multipart/form-data",则调用getFormData会返回FormData对象而非js对象,
再调用callSvr时,会以"multipart/form-data"格式提交数据。一般用于上传文件。
示例:

<div>
    课程文档
    <input name="pdf" type="file" accept="application/pdf">
</div>

或传统地:

<form method="POST" enctype='multipart/form-data'>
    课程文档
    <input name="pdf" type="file" accept="application/pdf">
</form>

@see setFormData

@fn formItems(jo, cb)

遍历jo下带name属性的有效控件,回调cb函数。

注意:

@param cb (name, val) this=ji=当前jquery对象

当cb返回false时可中断遍历。

@fn setFormData(jo, data?, opt?)

用于为带name属性的DOM对象设置内容为data[name].
要清空所有内容, 可以用 setFormData(jo), 相当于增强版的 form.reset().

注意:

@param opt {setOrigin?=false, setOnlyDefined?=false}

@param opt.setOrigin 为true时将data设置为数据源, 这样在getFormData时, 只会返回与数据源相比有变化的数据.

缺省会设置该DOM对象数据源为空.

@param opt.setOnlyDefined 设置为true时,只设置form中name在data中存在的项,其它项保持不变;而默认是其它项会清空。

对象关联的数据源, 可以通过 jo.data("origin") 来获取, 或通过 jo.data("origin", newOrigin) 来设置.

示例:

<div id="div1">
    <p>订单描述:<span name="dscr"></span></p>
    <p>状态为:<input type=text name="status"></p>
    <p>金额:<span name="amount"></span>元</p>
</div>

Javascript:

var data = {
    dscr: "筋斗云教程",
    status: "已付款",
    amount: "100"
};
var jo = $("#div1");
var data = setFormData(jo, data); 
$("[name=status]").html("已完成");
var changedData = getFormData(jo); // 返回 { dscr: "筋斗云教程", status: "已完成", amount: "100" }

var data = setFormData(jo, data, {setOrigin: true}); 
$("[name=status]").html("已完成");
var changedData = getFormData(jo); // 返回 { status: "已完成" }
$.extend(jo.data("origin_"), changedData); // 合并变化的部分到数据源.

@see getFormData

@fn loadScript(url, fnOK?, ajaxOpt?)

@param fnOK 加载成功后的回调函数

@param ajaxOpt 传递给$.ajax的额外选项。

默认未指定ajaxOpt时,简单地使用添加script标签机制异步加载。如果曾经加载过,可以重用cache。

如果指定ajaxOpt,且非跨域,则通过ajax去加载,可以支持同步调用。如果是跨域,仍通过script标签方式加载,注意加载完成后会自动删除script标签。

返回defered对象(与$.ajax类似),可以用 dfd.then() / dfd.fail() 异步处理。

常见用法:

如果要动态加载script,且使用后删除标签(里面定义的函数会仍然保留),建议直接使用$.getScript,它等同于:

loadScript("1.js", {cache: false});

@fn setDateBox(jo, defDateFn?)

设置日期框, 如果输入了非法日期, 自动以指定日期(如未指定, 用当前日期)填充.

setDateBox($("#txtComeDt"), function () { return genDefVal()[0]; });

@fn setTimeBox(jo, defTimeFn?)

设置时间框, 如果输入了非法时间, 自动以指定时间(如未指定, 用当前时间)填充.

setTimeBox($("#txtComeTime"), function () { return genDefVal()[1]; });

@fn waitFor(deferredObj)

用于简化异步编程. 可将不易读的回调方式改写为易读的顺序执行方式.

var dfd = $.getScript("http://...");
function onSubmit()
{
    dfd.then(function () {
        foo();
        bar();
    });
}

可改写为:

function onSubmit()
{
    if (waitFor(dfd)) return;
    foo();
    bar();
}

@fn rgb2hex(rgb)

将jquery取到的颜色转成16进制形式,如:"rgb(4, 190, 2)" -> "#04be02"

示例:

var color = rgb2hex( $(".mui-container").css("backgroundColor") );

@fn jQuery.fn.jdata(val?)

和使用$.data()差不多,更好用一些. 例:

$(o).jdata().hello = 100;
$(o).jdata({hello:100, world:200});

@fn compressImg(img, cb, opt)

通过限定图片大小来压缩图片,用于图片预览和上传。
不支持IE8及以下版本。

函数cb的回调参数: picData={b64src,blob,w,h,w0,h0,quality,name,mimeType,size0,size,b64size,info}

b64src为base64格式的Data URL, 如 "data:image/jpeg;base64,/9j/4AAQSk...", 用于给image或background-image赋值显示图片;

可以赋值给Image.src:

var img = new Image();
img.src = picData.b64src;

$("<div>").css("background-image", "url(" + picData.b64src + ")");

blob用于放到FormData中上传:

fd.append('file', picData.blob, picData.name);

其它picData属性:

[预览和上传示例]

HTML:

<form action="upfile.php">
    <div class="img-preview"></div>
    <input type="file" /><br/>
    <input type="submit" >
</form>

用picData.b64src来显示预览图,并将picData保存在img.picData_属性中,供后面上传用。

var jfrm = $("form");
var jpreview = jfrm.find(".img-preview");
var opt = {maxSize:1280};
jfrm.find("input[type=file]").change(function (ev) {
    $.each(this.files, function (i, fileObj) {
        compressImg(fileObj, function (picData) {
            $("<img>").attr("src", picData.b64src)
                .prop("picData_", picData)
                .appendTo(jpreview);
            //$("<div>").css("background-image", "url("+picData.b64src+")").appendTo(jpreview);
        }, opt);
    });
    this.value = "";
});

上传picData.blob到服务器

jfrm.submit(function (ev) {
    ev.preventDefault();

    var fd = new FormData();
    var idx = 1;
    jpreview.find("img").each(function () {
        // 名字要不一样,否则可能会覆盖
        fd.append('file' + idx, this.picData_.blob, this.picData_.name);
        ++idx;
    });

    $.ajax({
        url: jfrm.attr("action"),
        data: fd,
        processData: false,
        contentType: false,
        type: 'POST',
        // 允许跨域调用
        xhrFields: {
            withCredentials: true
        },
        success: cb
    });
    return false;
});

参考:JIC.js (https://github.com/brunobar79/J-I-C)

TODO: 用完后及时释放内存,如调用revokeObjectURL等。

@fn getDataOptions(jo, defVal?)

@key data-options

读取jo上的data-options属性,返回JS对象。例如:

<div data-options="a:1,b:'hello',c:true"></div>

上例可返回 {a:1, b:'hello', c:true}.

也支持各种表达式及函数调用,如:

<div data-options="getSomeOption()"></div>

@see getOptions

@fn triggerAsync(jo, ev, paramArr)

触发含有异步操作的事件,在异步事件完成后继续。兼容同步事件处理函数,或多个处理函数中既有同步又有异步。
返回Deferred对象,或false表示要求取消之后操作。

@param ev 事件名,或事件对象$.Event()

示例:以事件触发方式调用jo的异步方法submit:

var dfd = WUI.triggerAsync(jo, 'submit');
if (dfd === false)
    return;
dfd.then(doNext);

function doNext() { }

jQuery对象这样提供异步方法:triggerAsync会用事件对象ev创建一个dfds数组,将Deferred对象存入即可支持异步调用。

jo.on('submit', function (ev) {
    var dfd = $.ajax("upload", ...);
    if (ev.dfds)
        ev.dfds.push(dfd);
});

@fn evalAttr(jo, name)

返回一个属性做eval后的js值。

示例:读取一个对象值:

var opt = evalAttr(jo, "data-opt");

<div data-opt="{id:1, name:\"data1\"}"><div>

考虑兼容性,也支持忽略括号的写法,

<div data-opt="id:1, name:\"data1\""><div>

读取一个数组:

var arr = evalAttr(jo, "data-arr");

<div data-arr="['aa', 'bb']"><div>

读取一个函数名(或变量):

var fn = evalAttr(jo, "mui-initfn");

<div mui-initfn="initMyPage"><div>

@fn app_abort()

中止之后的调用, 直接返回.

@class DirectReturn

直接返回. 用法:

throw new DirectReturn();

可直接调用app_abort();

@fn setOnError()

一般框架自动设置onerror函数;如果onerror被其它库改写,应再次调用该函数。
allow throw("abort") as abort behavior.

@var m_enhanceFn

@fn enhanceWithin(jparent)

@fn getOptions(jo, defVal?)

第一次调用,根据jo上设置的data-options属性及指定的defVal初始化,或为{}
存到jo.prop("muiOptions")上。之后调用,直接返回该属性。

@see getDataOptions

@fn getQueryCond(kvList)

@var queryHint 查询用法提示

@param kvList {key=>value}, 键值对,值中支持操作符及通配符。也支持格式 [ [key, value] ], 这时允许key有重复。

根据kvList生成BPQ协议定义的{obj}.query的cond参数。

例如:

var kvList = {phone: "13712345678", id: ">100", addr: "上海*", picId: "null"};
WUI.getQueryCond(kvList);

有多项时,每项之间以"AND"相连,以上定义将返回如下内容:

"phone='13712345678' AND id>100 AND addr LIKE '上海*' AND picId IS NULL"

示例二:

var kvList = [ ["phone", "13712345678"], ["id", ">100"], ["addr", "上海*"], ["picId", "null"] ];
WUI.getQueryCond(kvList); // 结果同上。

设置值时,支持以下格式:

支持and/or查询,但不支持在其中使用括号:

以下表示的范围相同:

{k1:'1-5,7-10', k2:'1-10 and <>6'}

符号优先级依次为:"-"(类似and) ","(类似or) and or

在详情页对话框中,切换到查找模式,在任一输入框中均可支持以上格式。

@see getQueryParam

@see getQueryParamFromTable 获取datagrid的当前查询参数

@fn getQueryParam(kvList)

根据键值对生成BQP协议中{obj}.query接口需要的cond参数.
{cond: WUI.getQueryCond(kvList) }

示例:

WUI.getQueryParam({phone: '13712345678', id: '>100'})
返回
{cond: "phone='13712345678' AND id>100"}

@see getQueryCond

@see getQueryParamFromTable 获取datagrid的当前查询参数

@fn doSpecial(jo, filter, fn)

连续5次点击某处,执行隐藏动作。

例:
// 连续5次点击当前tab标题,重新加载页面
var self = WUI;
self.doSpecial(self.tabMain.find(".tabs-header"), ".tabs-selected", function () {
self.reloadPage();
self.reloadDialog(true);
});

@var lastError = ctx

出错时,取出错调用的上下文信息。

ctx: {ac, tm, tv, ret}

@var disableBatch ?= false

设置为true禁用batchCall, 仅用于内部测试。

@var m_curBatch

当前batchCall对象,用于内部调试。

@var mockData 模拟调用后端接口。

在后端接口尚无法调用时,可以配置MUI.mockData做为模拟接口返回数据。
调用callSvr时,会直接使用该数据,不会发起ajax请求。

mockData={ac => data/fn}

mockData中每项可以直接是数据,也可以是一个函数:fn(param, postParam)->data

例:模拟"User.get(id)"和"User.set()(key=value)"接口:

var user = {
    id: 1001,
    name: "孙悟空",
};
MUI.mockData = {
    // 方式1:直接指定返回数据
    "User.get": [0, user],

    // 方式2:通过函数返回模拟数据
    "User.set": function (param, postParam) {
        $.extend(user, postParam);
        return [0, "OK"];
    }
}

// 接口调用:
var user = callSvrSync("User.get");
callSvr("User.set", {id: user.id}, function () {
    alert("修改成功!");
}, {name: "大圣"});

实例详见文件 mockdata.js。

在mockData的函数中,可以用this变量来取ajax调用参数。
要取HTTP动词可以用this.type,值为GET/POST/PATCH/DELETE之一,从而可模拟RESTful API.

可以通过MUI.options.mockDelay设置模拟调用接口的网络延时。

@see options.mockDelay

模拟数据可直接返回[code, data]格式的JSON数组,框架会将其序列化成JSON字符串,以模拟实际场景。
如果要查看调用与返回数据日志,可在浏览器控制台中设置 MUI.options.logAction=true,在控制台中查看日志。

如果设置了MUI.callSvrExt,调用名(ac)中应包含扩展(ext)的名字,例:

MUI.callSvrExt['zhanda'] = {...};
callSvr(['token/get-token', 'zhanda'], ...);

要模拟该接口,应设置

MUI.mockData["zhanda:token/get-token"] = ...;

@see callSvrExt

也支持"default"扩展,如:

MUI.callSvrExt['default'] = {...};
callSvr(['token/get-token', 'default'], ...);
或
callSvr('token/get-token', ...);

要模拟该接口,可设置

MUI.mockData["token/get-token"] = ...;

@fn enterWaiting(ctx?)

@param ctx {ac, tm, tv?, tv2?, noLoadingImg?}

@fn leaveWaiting(ctx?)

@fn defDataProc(rv)

@param rv BQP协议原始数据,如 "[0, {id: 1}]",一般是字符串,也可以是JSON对象。

@return data 按接口定义返回的数据对象,如 {id: 1}. 如果返回==null,调用函数应直接返回,不回调应用层。

注意:服务端不应返回null, 否则客户回调无法执行; 习惯上返回false表示让回调处理错误。

@fn getBaseUrl()

取服务端接口URL对应的目录。可用于拼接其它服务端资源。
相当于dirname(MUI.options.serverUrl);

例如:

serverUrl为"../jdcloud/api.php" 或 "../jdcloud/",则MUI.baseUrl返回 "../jdcloud/"
serverUrl为"http://myserver/myapp/api.php" 或 "http://myserver/myapp/",则MUI.baseUrl返回 "http://myserver/myapp/"

@fn makeUrl(action, params?)

生成对后端调用的url.

var params = {id: 100};
var url = MUI.makeUrl("Ordr.set", params);

注意:函数返回的url是字符串包装对象,可能含有这些属性:{makeUrl=true, action?, params?}
这样可通过url.action得到原始的参数。

支持callSvr扩展,如:

var url = MUI.makeUrl('zhanda:login');

(deprecated) 为兼容旧代码,action可以是一个数组,在WUI环境下表示对象调用:

WUI.makeUrl(['Ordr', 'query']) 等价于 WUI.makeUrl('Ordr.query');

在MUI环境下表示callSvr扩展调用:

MUI.makeUrl(['login', 'zhanda']) 等价于 MUI.makeUrl('zhanda:login');

@see callSvrExt

@fn callSvr(ac, [params?], fn?, postParams?, userOptions?) -> deferredObject

1 调用监控

2 文件上传支持(FormData)

3 callSvr扩展

4 适配RESTful API

@param ac String. action, 交互接口名. 也可以是URL(比如由makeUrl生成)

@param params Object. URL参数(或称HTTP GET参数)

@param postParams Object. POST参数. 如果有该参数, 则自动使用HTTP POST请求(postParams作为POST内容), 否则使用HTTP GET请求.

@param fn Function(data). 回调函数, data参考该接口的返回值定义。

@param userOptions 用户自定义参数, 会合并到$.ajax调用的options参数中.可在回调函数中用"this.参数名"引用.

常用userOptions:

想为ajax选项设置缺省值,可以用callSvrExt中的beforeSend回调函数,也可以用$.ajaxSetup,
但要注意:ajax的dataFilter/beforeSend选项由于框架已用,最好不要覆盖。

@see callSvrExt[].beforeSend (opt) 为callSvr选项设置缺省值

@return deferred对象,与$.ajax相同。

例如,

var dfd = callSvr(ac, fn1);
dfd.then(fn2);

function fn1(data) {}
function fn2(data) {}

在接口调用成功后,会依次回调fn1, fn2.

@key callSvr.noex 调用接口时忽略出错,可由回调函数fn自己处理错误。

当后端返回错误时, 回调fn(false)(参数data=false). 可通过 MUI.lastError.ret 或 this.lastError 取到返回的原始数据。

示例:

callSvr("logout");
callSvr("logout", api_logout);
function api_logout(data) {}

callSvr("login", api_login);
function api_login(data) {}

callSvr("info/hotline.php", {q: '大众'}, api_hotline);
function api_hotline(data) {}

// 也可使用makeUrl生成的URL如:
callSvr(MUI.makeUrl("logout"), api_logout);
callSvr(MUI.makeUrl("logout", {a:1}), api_logout);

callSvr("User.get", function (data) {
    if (data === false) { // 仅当设置noex且服务端返回错误时可返回false
        // var originalData = MUI.lastError.ret; 或
        // var originalData = this.lastError;
        return;
    }
    foo(data);
}, null, {noex:1});

@see lastError 出错时的上下文信息

1 调用监控

框架会自动在ajaxOption中增加ctx_属性,它包含 {ac, tm, tv, tv2, ret} 这些信息。
当设置MUI.options.logAction=1时,将输出这些信息。

2 文件上传支持(FormData)

callSvr支持FormData对象,可用于上传文件等场景。示例如下:

@key example-upload

HTML:

file: <input id="file1" type="file" multiple>
<button type="button" id="btn1">upload</button>

JS:

jpage.find("#btn1").on('click', function () {
    var fd = new FormData();
    $.each(jpage.find('#file1')[0].files, function (i, e) {
        fd.append('file' + (i+1), e);
    });
    callSvr('upload', api_upload, fd);

    function api_upload(data) { ... }
});

3 callSvr扩展

@key callSvrExt

当调用第三方API时,也可以使用callSvr扩展来代替$.ajax调用以实现:

例:合作方接口使用HTTP协议,格式如(以生成token调用为例)

http://<Host IP Address>:<Host Port>/lcapi/token/get-token?user=用户名&password=密码

返回格式为:{code, msg, data}

成功返回:

{
    "code":"0",
    "msg":"success",
    "data":[ { "token":"xxxxxxxxxxxxxx" } ]
}

失败返回:

{
    "code":"4001",
    "msg":"invalid username or password",
    "data":[]
}

callSvr扩展示例:

MUI.callSvrExt['zhanda'] = {
    makeUrl: function(ac, param) {
        return 'http://hostname/lcapi/' + ac;
    },
    dataFilter: function (data) {
        if ($.isPlainObject(data) && data.code !== undefined) {
            if (data.code == 0)
                return data.data;
            if (this.noex)
                return false;
            app_alert("操作失败:" + data.msg, "e");
        }
        else {
            app_alert("服务器通讯协议异常!", "e"); // 格式不对
        }
    }
};

在调用时,ac参数传入一个数组:

callSvr(['token/get-token', 'zhanda'], {user: 'test', password: 'test123'}, function (data) {
    console.log(data);
});

@key callSvrExt[].makeUrl (ac, param)

根据调用名ac生成url, 注意无需将param放到url中。

注意:
对方接口应允许JS跨域调用,或调用方支持跨域调用。

@key callSvrExt[].dataFilter (data) = null/false/data

对调用返回数据进行通用处理。返回值决定是否调用callSvr的回调函数以及参数值。

callSvr(ac, callback);

当返回false时,应用层可以通过MUI.lastError.ret来获取服务端返回数据。

@see lastError 出错时的上下文信息

@key callSvrExt['default']

(支持版本: v3.1)
如果要修改callSvr缺省调用方法,可以改写 MUI.callSvrExt['default'].
例如,定义以下callSvr扩展:

MUI.callSvrExt['default'] = {
    makeUrl: function(ac) {
        return '../api.php/' + ac;
    },
    dataFilter: function (data) {
        var ctx = this.ctx_ || {};
        if (data && $.isArray(data) && data.length >= 2 && typeof data[0] == "number") {
            if (data[0] == 0)
                return data[1];

            if (this.noex)
            {
                return false;
            }

            if (data[0] == E_NOAUTH) {
                // 如果支持自动重登录
                //if (MUI.tryAutoLogin()) {
                //  $.ajax(this);
                //}
                // 不支持自动登录,则跳转登录页
                MUI.popPageStack(0);
                MUI.showLogin();
                return;
            }
            else if (data[0] == E_AUTHFAIL) {
                app_alert("验证失败,请检查输入是否正确!", "e");
                return;
            }
            else if (data[0] == E_ABORT) {
                console.log("!!! abort call");
                return;
            }
            logError();
            app_alert("操作失败:" + data[1], "e");
        }
        else {
            logError();
            app_alert("服务器通讯协议异常!", "e"); // 格式不对
        }

        function logError()
        {
            console.log("failed call");
            console.log(ctx);
        }
    }
};

这样,以下调用

callSvr(['login', 'default']);

可以简写为:

callSvr('login');

@key callSvrExt[].beforeSend (opt) 为callSvr或$.ajax选项设置缺省值

如果有ajax选项想设置,可以使用beforeSend回调,例如POST参数使用JSON格式:

MUI.callSvrExt['default'] = {
    beforeSend: function (opt) {
        // 示例:设置contentType
        if (opt.contentType == null) {
            opt.contentType = "application/json;charset=utf-8";
        }
        // 示例:添加HTTP头用于认证
        if (g_data.auth) {
            if (opt.headers == null)
                opt.headers = {};
            opt.headers["Authorization"] = "Basic " + g_data.auth;
        }
    }
}

如果要设置请求的HTTP headers,可以用opt.headers = {header1: "value1", header2: "value2"}.
更多选项参考jquery文档:jQuery.ajax的选项。

4 适配RESTful API

接口示例:更新订单

PATCH /orders/{ORDER_ID}

调用成功仅返回HTTP状态,无其它内容:"200 OK" 或 "204 No Content"
调用失败返回非2xx的HTTP状态及错误信息,无其它内容,如:"400 bad id"

为了处理HTTP错误码,应设置:

MUI.callSvrExt["default"] = {
    beforeSend: function (opt) {
        opt.handleHttpError = true;
    },
    dataFilter: function (data) {
        var ctx = this.ctx_;
        if (ctx && ctx.status) {
            if (this.noex)
                return false;
            app_alert(ctx.statusText, "e");
            return;
        }
        return data;
    }
}

如果接口在出错时,返回固定格式的错误对象如{code, message},可以这样处理:

MUI.callSvrExt["default"] = {
    beforeSend: function (opt) {
        opt.handleHttpError = true;
    },
    dataFilter: function (data) {
        var ctx = this.ctx_;
        if (ctx && ctx.status) {
            if (this.noex)
                return false;
            if (data && data.message) {
                app_alert(data.message, "e");
            }
            else {
                app_alert("操作失败: 服务器错误. status=" + ctx.status + "-" + ctx.statusText, "e");
            }
            return;
        }
        return data;
    }
}

调用接口时,HTTP谓词可以用callSvr的userOptions中给定,如:

callSvr("orders/" + orderId, fn, postParam, {type: "PATCH"});

这种方式简单,但因调用名ac是变化的,不易模拟接口。
如果要模拟接口,可以保持调用名ac不变,像这样调用:

callSvr("orders/{id}", {id: orderId}, fn, postParam, {type: "PATCH"});

于是可以这样做接口模拟:

MUI.mockData = {
    "orders/{id}": function (param, postParam) {
        var ret = "OK";
        // 获取资源
        if (this.type == "GET") {
            ret = orders[param.id];
        }
        // 更新资源
        else if (this.type == "PATCH") {
            $.extend(orders[param.id], postParam);
        }
        // 删除资源
        else if (this.type == "DELETE") {
            delete orders[param.id];
        }
        return [0, ret];
    }
};

不过这种写法需要适配,以生成正确的URL,示例:

MUI.callSvrExt["default"] = {
    makeUrl: function (ac, param) {
        ac = ac.replace(/\{(\w+)\}/g, function (m, m1) {
            var ret = param[m1];
            assert(ret != null, "缺少参数");
            delete param[m1];
            return ret;
        });
        return "./api.php/" + ac;
    }
}

@fn callSvrSync(ac, [params?], fn?, postParams?, userOptions?)

@return data 原型规定的返回数据

同步模式调用callSvr.

@see callSvr

@fn setupCallSvrViaForm($form, $iframe, url, fn, callOpt)

该方法已不建议使用。上传文件请用FormData。

@see example-upload callSvr

@param $iframe 一个隐藏的iframe组件.

@param callOpt 用户自定义参数. 参考callSvr的同名参数. e.g. {noex: 1}

一般对后端的调用都使用callSvr函数, 但像上传图片等操作不方便使用ajax调用, 因为要自行拼装multipart/form-data格式的请求数据.
这种情况下可以使用form的提交和一个隐藏的iframe来实现类似的调用.

先定义一个form, 在其中放置文件上传控件和一个隐藏的iframe. form的target属性设置为iframe的名字:

<form data-role="content" action="upload" method=post enctype="multipart/form-data" target="ifrUpload">
    <input type=file name="file[]" multiple accept="image/*">
    <input type=submit value="上传">
    <iframe id='ifrUpload' name='ifrUpload' style="display:none"></iframe>
</form>

然后就像调用callSvr函数一样调用setupCallSvrViaForm:

var url = MUI.makeUrl("upload", {genThumb: 1});
MUI.setupCallSvrViaForm($frm, $frm.find("iframe"), url, onUploadComplete);
function onUploadComplete(data) 
{
    alert("上传成功");
}

@class batchCall(opt?={useTrans?=0})

批量调用。将若干个调用打包成一个特殊的batch调用发给服务端。
注意:

示例:

var batch = new MUI.batchCall();
callSvr("Family.query", {res: "id,name"}, api_FamilyQuery);
callSvr("User.get", {res: "id,phone"}, api_UserGet);
batch.commit();

以上两条调用将一次发送到服务端。
在批处理中,默认每条调用是一个事务,如果想把批处理中所有调用放到一个事务中,可以用useTrans选项:

var batch = new MUI.batchCall({useTrans: 1});
callSvr("Attachment.add", api_AttAdd, {path: "path-1"});
callSvr("Attachment.add", api_AttAdd, {path: "path-2"});
batch.commit();

在一个事务中,所有调用要么成功要么都取消。
任何一个调用失败,会导致它后面所有调用取消执行,且所有已执行的调用会回滚。

参数中可以引用之前结果中的值,引用部分需要用"{}"括起来,且要在opt.ref参数中指定哪些参数使用了引用:

var batch = new MUI.batchCall({useTrans: 1});
callSvr("Attachment.add", api_AttAdd, {path: "path-1"}); // 假如返回 22
var opt = {ref: ["id"]};
callSvr("Attachment.get", {id: "{$1}"}, api_AttGet, null, opt); // {$1}=22, 假如返回 {id: 22, path: '/data/1.png'}
opt = {ref: ["cond"]};
callSvr("Attachment.query", {res: "count(*) cnt", cond: "path='{$-1.path}'"}, api_AttQuery, null, opt); // {$-1.path}计算出为 '/data/1.png'
batch.commit();

以下为引用格式示例:

{$-2} // 前2次的结果。
{$2[0]} // 取第2次结果(是个数组)的第0个值。
{$-1.path} // 取前一次结果的path属性
{$2 -1}  // 可以做简单的计算

如果值计算失败,则当作"null"填充。

@see useBatchCall

@see disableBatch

@see m_curBatch

@fn useBatchCall(opt?={useTrans?=0}, tv?=0)

之后的callSvr调用都加入批量操作。例:

MUI.useBatchCall();
callSvr("Family.query", {res: "id,name"}, api_FamilyQuery);
callSvr("User.get", {res: "id,phone"}, api_UserGet);

可指定多少毫秒以内的操作都使用批处理,如10ms内:

MUI.useBatchCall(null, 10);

如果MUI.disableBatch=true, 该函数不起作用。

@see batchCall

@see disableBatch

@fn reload(jtbl, url?, queryParams?)

@fn reloadTmp(jtbl, url?, queryParams?)

临时reload一下,完事后恢复原url

@fn reloadRow(jtbl, rowData?)

@param rowData must be the original data from table row

rowData如果未指定,则使用当前选择的行。

@fn showPage(pageName, title?, paramArr?)

@param pageName 由page上的class指定。

@param title ? 如果未指定,则使用page上的title属性.

@param paramArr ? 调用initfn时使用的参数,是一个数组。

新页面以title作为id。
注意:每个页面都是根据pages下相应pageName复制出来的,显示在一个新的tab页中。相同的title当作同一页面。
初始化函数由page上的my-initfn属性指定。

page定义示例:

<div id="my-pages" style="display:none">
    <div class="pageHome" title="首页" my-initfn="initPageHome"></div>
</div>

page调用示例:

WUI.showPage("pageHome");
WUI.showPage("pageHome", "首页");
WUI.showPage("pageHome", "首页2");

@key wui-pageFile

动态加载的逻辑页(或对话框)具有该属性,值为源文件名。

@fn closeDlg(jdlg)

@fn isSmallScreen

判断是否为手机小屏显示. 宽度低于640当作小屏处理.

@fn showDlg(jdlg, opt?)

@param jdlg 可以是jquery对象,也可以是selector字符串或DOM对象,比如 "#dlgOrder".

注意:当对话框动态从外部加载时,jdlg=$("#dlgOrder") 一开始会为空数组,这时也可以调用该函数,且调用后jdlg会被修改为实际加载的对话框对象。

@param opt ?={url, buttons, noCancel=false, okLabel="确定", cancelLabel="取消", modal=true, reset=true, validate=true, data, onOk, onSubmit, title}

对话框有两种编程模式,一是通过opt参数在启动对话框时设置属性及回调函数(如onOk),另一种是在dialog初始化函数中处理事件(如validate事件)实现逻辑,有利于独立模块化。

对于自动提交数据的对话框(设置了opt.url),提交数据过程中回调函数及事件执行顺序为:

事件validate; // 提交前,用于验证或设置提交数据。返回false或ev.preventDefault()可取消提交,中止以下代码执行。
opt.onSubmit(); // 提交前,验证或设置提交数据,返回false将阻止提交。
... 框架通过callSvr自动提交数据,如添加、更新对象等。
opt.onOk(jdlg, data); // 提交且服务端返回数据后。data是服务端返回数据。
事件retdata; // 与onOk类似。

对于手动提交数据的对话框(opt.url为空),执行顺序为:

事件validate; // 用于验证、设置提交数据、提交数据。
opt.onOk(jdlg); // 同上

注意:

调用此函数后,对话框将加上以下CSS Class:

@key .wui-dialog 标识WUI对话框的类名。

对象型对话框与formMode

函数showObjDlg()会调用本函数显示对话框,称为对象型对话框,用于对象增删改查,它将以下操作集中在一起。
打开窗口时,会设置窗口模式(formMode):

注意:

初始数据与对话框中带name属性的对象相关联,显示对话框时,带name属性的DOM对象将使用数据opt.data自动赋值(对话框show事件中可修改),在点“确定”按钮提交时将改动的数据发到服务端(validate事件中可修改),详见

@see setFormData getFormData

对话框事件

操作对话框时会发出以下事件供回调:

beforeshow - 对话框显示前。常用来处理对话框显示参数opt或初始数据opt.data.
show - 显示对话框后。常用来设置字段值或样式,隐藏字段、初始化子表datagrid或隐藏子表列等。
validate - 用于提交前验证、补齐数据等。返回false可取消提交。(v5.2) 支持其中有异步操作.
retdata - 服务端返回结果时触发。用来根据服务器返回数据继续处理,如再次调用接口。

注意:

@key event-beforeshow (ev, formMode, opt)

显示对话框前触发。

注意:每次调用showDlg()都会回调,可能这时对话框已经在显示。

@key event-show (ev, formMode, initData)

对话框显示后事件,用于设置DOM组件。
注意如果在beforeshow事件中设置DOM,对于带name属性的组件会在加载数据时值被覆盖回去,对它们在beforeshow中只能通过设置opt.data来指定缺省值。

@key event-validate (ev, formMode, initData, newData)

initData为初始数据,如果要验证或修改待提交数据,应直接检查form中相应DOM元素的值。如果需要增加待提交字段,可加到newData中去。示例:添加参数: newData.mystatus='CR';

(v5.2) validate事件支持返回Deferred对象支持异步操作.
示例: 在提交前先弹出对话框询问. 由于app_alert是异步对话框, 需要将一个Deferred对象放入ev.dfds数组, 告诉框架等待ev.dfds中的延迟对象都resolve后再继续执行.

jdlg.on("validate", onValidate);
function onValidate(ev, mode, oriData, newData) 
{
    var dfd = $.Deferred();
    app_alert("确认?", "q", function () {
        console.log("OK!");
        dfd.resolve();
    });
    ev.dfds.push(dfd.promise());
}

常用于在validate中异步调用接口(比如上传文件).

@key event-retdata (ev, data, formMode)

form提交后事件,用于处理返回数据

以下事件将废弃:

@key event-initdata (ev, initData, formMode) 加载数据前触发。可修改要加载的数据initData, 用于为字段设置缺省值。将废弃,改用beforeshow事件。

@key event-loaddata (ev, initData, formMode) form加载数据后,一般用于将服务端数据转为界面显示数据。将废弃,改用show事件。

@key event-savedata (ev, formMode, initData) 对于设置了opt.url的窗口,将向后台提交数据,提交前将触发该事件,用于验证或补足数据(修正某个)将界面数据转为提交数据. 返回false或调用ev.preventDefault()可阻止form提交。将废弃,改用validate事件。

@see example-dialog 在对话框中使用事件

reset控制

对话框上有name属性的组件在显示对话框时会自动清除(除非设置opt.reset=false或组件设置有noReset属性)。

@key .my-reset 标识在显示对话框时应清除

对于没有name属性(不与数据关联)的组件,可加该CSS类标识要求清除。
例如,想在forSet模式下添加显示内容, 而在forFind/forAdd模式下时清除内容这样的需求。

<div class="my-reset">...</div>

@key [noReset]

某些字段希望设置后一直保留,不要被清空,可以设置noReset属性,例如:

<input type="hidden" name="status" value="PA" noReset>

控制底层jquery-easyui对话框

示例:关闭对话框时回调事件:

var dialogOpt = {  
    onClose:function(){
        console.log("close");
    }  
};

jfrm.on("beforeshow",function(ev, formMode, opt) {
    opt.dialogOpt = dialogOpt;
})

@fn batchOp(obj, ac, jtbl, data/dataFn, onBatchDone?, forceFlag?)

@param ac "setIf"/"delIf"

@param data/dataFn 批量操作的参数。

可以是一个函数dataFn(batchCnt),参数batchCnt为当前批量操作的记录数。
该函数返回data或一个Deferred对象(该对象适时应调用dfd.resolve(data)做批量操作)。dataFn返回false表示不做后续处理。

批量操作支持两种方式:

  1. 基于多选: 按Ctrl/Shift在表上多选,然后点删除或更新,批量操作选中行;
  2. 基于条件: 按住Ctrl键点删除或更新,批量操作过滤条件下的所有行

函数返回false表示当前非批量处理模式,不予处理。

@param forceFlag 强制批量操作。

默认仅当多选或按住Ctrl键才认为是批量操作;
如果值为1,表示无须按Ctrl键,即如果有多选,就用多选;如果没有多选,则使用当前的过滤条件;
如果值为2,表示只基于选择项操作,即使只选了一项也对其操作。但如果没有选任何行,则使用过滤条件。

示例:批量更新附件到行记录上, 在onBatch中返回一个Deferred对象,并在获得数据后调用dfd.resolve(data)

var forceFlag = 1; // 如果没有多选,则按当前过滤条件全部更新。
WUI.batchOp("Task", "setIf", jtbl, onBatch, function () {
    WUI.closeDlg(jdlg);
}, forceFlag);

function onBatch(batchCnt)
{
    if (batchCnt == 0) {
        app_alert("没有记录更新。");
        return false;
    }
    var dfd = $.Deferred();
    app_alert("批量上传附件到" + batchCnt + "行记录?", "q", function () {
        var dfd1 = triggerAsync(jdlg.find(".wui-upload"), "submit"); // 异步上传文件,返回Deferred对象
        dfd1.then(function () {
            var data = WUI.getFormData(jfrm);
            dfd.resolve(data);
        });
    });
    return dfd.promise();
}

@see triggerAsync 异步事件调用

上面函数中处理异步调用链,不易理解,可以简单理解为:

if (confirm("确认操作?") == no)
    return;
jupload.submit();
return getFormData(jfrm);

@fn getTopDialog()

取处于最上层的对话框。如果没有,返回jo.size() == 0

@fn unloadPage(pageName?)

@param pageName 如未指定,表示当前页。

删除一个页面。一般用于开发过程,在修改外部逻辑页后,调用该函数删除页面。此后载入页面,可以看到更新的内容。

注意:对于内部逻辑页无意义。

@fn reloadPage()

重新加载当前页面。一般用于开发过程,在修改外部逻辑页后,调用该函数可刷新页面。

@fn unloadDialog(all?=false)

@alias reloadDialog

删除当前激活的对话框。一般用于开发过程,在修改外部对话框后,调用该函数清除以便此后再载入页面,可以看到更新的内容。

WUI.reloadDialog();
WUI.reloadDialog(true); // 重置所有外部加载的对话框(v5.1)

注意:

@fn showObjDlg(jdlg, mode, opt?={jtbl, id, obj})

@param jdlg 可以是jquery对象,也可以是selector字符串或DOM对象,比如 "#dlgOrder". 注意:当对话框保存为单独模块时,jdlg=$("#dlgOrder") 一开始会为空数组,这时也可以调用该函数,且调用后jdlg会被修改为实际加载的对话框对象。

@param opt.id String. mode=link时必设,set/del如缺省则从关联的opt.jtbl中取, add/find时不需要

@param opt.jtbl Datagrid. dialog/form关联的datagrid -- 如果dlg对应多个tbl, 必须每次打开都设置

@param opt.obj String. (v5.1) 对象对话框的对象名,如果未指定,则从my-obj属性获取。通过该参数可动态指定对象名。

@param opt.offline Boolean. (v5.1) 不与后台交互。

@param opt.title String. (v5.1) 指定对话框标题。

@key objParam 对象对话框的初始参数。

(v5.1)
此外,通过设置jdlg.objParam,具有和设置opt参数一样的功能,常在initPageXXX中使用,因为在page中不直接调用showObjDlg.
示例:

var jdlg = $("#dlgSupplier");
jdlg.objParam = {type: "C", obj: "Customer"};
showObjDlg(jdlg, FormMode.forSet, {id:101});
// 等价于 showObjDlg(jdlg, FormMode.forSet, {id:101, obj: "Customer", type: "C"});

在dialog的事件beforeshow(ev, formMode, opt)中,可以通过opt.objParam取出showObjDlg传入的所有参数opt。

@param opt.onCrud Function(). (v5.1) 对话框操作完成时回调。

一般用于点击表格上的增删改查工具按钮完成操作时插入逻辑。
在回调函数中this对象就是objParam,可通过this.mode获取操作类型。示例:

jdlg1.objParam = {
    offline: true,
    onCrud: function () {
        if (this.mode == FormMode.forDel) {
            // after delete row
        }
        // ... 重新计算金额
        // ... 刷新关联的表格行
        // opt.objParam.reloadRow();
    }
};
jtbl.datagrid({
    toolbar: WUI.dg_toolbar(jtbl, jdlg1), // 添加增删改查工具按钮,点击则调用showObjDlg,这时objParam生效。
    onDblClickRow: WUI.dg_dblclick(jtbl, jdlg1),
    ...
});

在dialog逻辑中使用objParam:

function initDlgXXX() {
    // ...
    jdlg.on("beforeshow", onBeforeShow);

    function onBeforeShow(ev, formMode, opt) {
        var objParam = opt.objParam; // {id, mode, jtbl?, offline?...}
    }
}

@param opt.reloadRow () 可用于刷新本对话框关联的表格行数据

事件参考:

@see showDlg

@fn dg_toolbar(jtbl, jdlg, button_lists...)

@param jdlg 可以是对话框的jquery对象,或selector如"#dlgOrder".

设置easyui-datagrid上toolbar上的按钮。缺省支持的按钮有r(refresh), f(find), a(add), s(set), d(del), 可通过以下设置方式修改:

// jtbl.jdata().toolbar 缺省值为 "rfasd"
jtbl.jdata().toolbar = "rfs"; // 没有a-添加,d-删除

如果要添加自定义按钮,可通过button_lists一一传递.
示例:添加两个自定义按钮查询“今天订单”和“所有未完成订单”。

function getTodayOrders()
{
    var queryParams = WUI.getQueryParam({comeTm: new Date().format("D")});
    WUI.reload(jtbl, null, queryParams);
}
// 显示待服务/正在服务订单
function getTodoOrders()
{
    var queryParams = {cond: "status=" + OrderStatus.Paid + " or status=" + OrderStatus.Started};
    WUI.reload(jtbl, null, queryParams);
}
var btn1 = {text: "今天订单", iconCls:'icon-search', handler: getTodayOrders};
var btn2 = {text: "所有未完成", iconCls:'icon-search', handler: getTodoOrders};

// 默认显示当天订单
var queryParams = WUI.getQueryParam({comeTm: new Date().format("D")});

var dgOpt = {
    url: WUI.makeUrl(["Ordr", "query"]),
    queryParams: queryParams,
    pageList: ...
    pageSize: ...
    // "-" 表示按钮之间加分隔符
    toolbar: WUI.dg_toolbar(jtbl, jdlg, btn1, "-", btn2),
    onDblClickRow: WUI.dg_dblclick(jtbl, jdlg)
};
jtbl.datagrid(dgOpt);

特别地,要添加导出数据到Excel文件的功能按钮,可以增加参数"export"作为按钮定义:

var dgOpt = {
    ...
    toolbar: WUI.dg_toolbar(jtbl, jdlg, "export", "-", btn1),
}

如果想自行定义导出行为参数,可以参考WUI.getExportHandler

@see getExportHandler 导出按钮设置

@fn dg_dblclick(jtbl, jdlg)

@param jdlg 可以是对话框的jquery对象,或selector如"#dlgOrder".

设置双击datagrid行的回调,功能是打开相应的dialog

@key a[href=#page]

@key a[href= ?fn]

页面中的a[href]字段会被框架特殊处理:

<a href="#pageHome">首页</a>
<a href="?logout">退出登录</a>

@fn getExportHandler(jtbl, ac?, param?={})

为数据表添加导出Excel菜单,如:

jtbl.datagrid({
    url: WUI.makeUrl("User.query"),
    toolbar: WUI.dg_toolbar(jtbl, jdlg, {text:'导出', iconCls:'icon-save', handler: WUI.getExportHandler(jtbl) }),
    onDblClickRow: WUI.dg_dblclick(jtbl, jdlg)
});

默认是导出数据表中直接来自于服务端的字段,并应用表上的查询条件及排序。
也可以通过设置param参数手工指定,如:

handler: WUI.getExportHandler(jtbl, "User.query", {res: "id 编号, name 姓名, createTm 注册时间", orderby: "createTm DESC"})

注意:由于分页机制影响,会设置参数{pagesz: -1}以便在一页中返回所有数据,而实际一页能导出的最大数据条数取决于后端设置(默认1000,参考后端文档 AccessControl::$maxPageSz)。

@see getQueryParamFromTable 获取datagrid的当前查询参数

@fn getQueryParamFromTable(jtbl, param?)

@alias getParamFromTable

根据数据表当前设置,获取查询参数。
可能会设置{cond, orderby, res}参数。

res参数从列设置中获取,如"id 编号,name 姓名", 特别地,如果列对应字段以"_"结尾,不会加入res参数。

(v5.2)
如果表上有多选行,则导出条件为cond="t0.id IN (id1, id2)"这种形式。

@see getExportHandler 导出Excel

@var formatter = {dt, number, pics, flag(yes?=是,no?=否), enum(enumMap), linkTo(field, dlgRef) }

常常应用定义Formatter变量来扩展WUI.formatter,如

var Formatter = {
    userId: WUI.formatter.linkTo("userId", "#dlgUser"),
    orderStatus: WUI.formatter.enum({CR: "新创建", CA: "已取消"})
};
Formatter = $.extend(WUI.formatter, Formatter);

可用值:

在datagrid中使用:

<th data-options="field:'createTm', sortable:true, formatter:Formatter.dt">创建时间</th>
<th data-options="field:'amount', sortable:true, sorter: numberSort, formatter:Formatter.number">金额</th>
<th data-options="field:'userName', sortable:true, formatter:Formatter.userId">用户</th>
<th data-options="field:'status', sortable:true, jdEnumMap: OrderStatusMap, formatter: Formatter.orderStatus">状态</th>
<th data-options="field:'done', sortable:true, formatter: Formatter.flag()">已处理</th>

@key .noData

CSS类, 可定义无数据提示的样式

@key .easyui-validatebox

为form中的组件加上该类,可以限制输入类型,如:

<input name="amount" class="easyui-validatebox" data-options="required:true,validType:'number'" >

validType还支持:

其它自定义规则(或改写上面规则),可通过下列方式扩展:

$.extend($.fn.validatebox.defaults.rules, {
    workday: {
        validator: function(value) {
            return value.match(/^[1-7,abc]+$/);
        },
        message: '格式例:"1,3a,5b"表示周一,周三上午,周五下午.'
    }
});

@key .wui-form-table

在wui-dialog上,对于form下直接放置的table,一般用于字段列表排列,框架对它添加类wui-form-table并自动对列设置百分比宽度,以自适应显示。

在非对话框上,也可手工添加此类来应用该功能。

@var isBusy

标识应用当前是否正在与服务端交互。一般用于自动化测试。

@var g_args

应用参数。

URL参数会自动加入该对象,例如URL为 http://{server}/{app}/index.html?orderId=10&dscr=上门洗车,则该对象有以下值:

g_args.orderId=10; // 注意:如果参数是个数值,则自动转为数值类型,不再是字符串。
g_args.dscr="上门洗车"; // 对字符串会自动进行URL解码。

此外,框架会自动加一些参数:

@var g_args._app ?="user" 应用名称,由 WUI.options.appName 指定。

@see parseQuery URL参数通过该函数获取。

@var g_data = {userInfo?}

应用全局共享数据。

在登录时,会自动设置userInfo属性为个人信息。所以可以通过 g_data.userInfo==null 来判断是否已登录。

@key g_data.userInfo

@var BASE_URL

TODO: remove

设置应用的基本路径, 应以"/"结尾.

@var options

{appName=user, title="客户端", onShowLogin, pageHome="pageHome", pageFolder="page"}

@fn app_alert(msg, [type?=i], [fn?], opt?={timeoutInterval?, defValue?, onCancel()?})

@param type 对话框类型: "i": info, 信息提示框; "e": error, 错误框; "w": warning, 警告框; "q"(与app_confirm一样): question, 确认框(会有"确定"和"取消"两个按钮); "p": prompt, 输入框

@param fn Function(text?) 回调函数,当点击确定按钮时调用。当type="p" (prompt)时参数text为用户输入的内容。

@param opt Object. 可选项。 timeoutInterval表示几秒后自动关闭对话框。defValue用于输入框(type=p)的缺省值.

使用jQuery easyui弹出提示对话框.

示例:

// 信息框,3s后自动点确定
app_alert("操作成功", function () {
    WUI.showPage("pageGenStat");
}, {timeoutInterval: 3000});

// 错误框
app_alert("操作失败", "e");

// 确认框(确定/取消)
app_alert("立即付款?", "q", function () {
    WUI.showPage("#pay");
});

// 输入框
app_alert("输入要查询的名字:", "p", function (text) {
    callSvr("Book.query", {cond: "name like '%" + text + "%'"});
});

@fn app_confirm(msg, fn?)

@param fn Function(isOk). 用户点击确定或取消后的回调。

使用jQuery easyui弹出确认对话框.

@fn app_show(msg)

使用jQuery easyui弹出对话框.

@fn app_progress(value, msg?)

@param value 0-100间数值.

显示进度条对话框. 达到100%后自动关闭.

注意:同一时刻只能显示一个进度条。

@fn makeLinkTo(dlg, id, text?=id, obj?)

生成一个链接的html代码,点击该链接可以打开指定对象的对话框。

示例:根据订单号,生成一个链接,点击链接打开订单详情对话框。

var orderId = 101;
var html = makeLinkTo("#dlgOrder", orderId, "订单" + orderId);

(v5.1)
示例:如果供应商(obj=Supplier)和客户(obj=Customer)共用一个对话框BizPartner,要显示一个id=101的客户,必须指定obj参数:

var html = makeLinkTo("#dlgBizPartner", 101, "客户-101", "Customer");

点击链接将调用

WUI.showObjDlg("#dlgBizPartner", FormMode.forSet, {id: 101, obj: "Customer"};

@fn tryAutoLogin(onHandleLogin, reuseCmd?)

@param onHandleLogin Function(data). 调用后台login()成功后的回调函数(里面使用this为ajax options); 可以直接使用WUI.handleLogin

@param reuseCmd String. 当session存在时替代后台login()操作的API, 如"User.get", "Employee.get"等, 它们在已登录时返回与login相兼容的数据. 因为login操作比较重, 使用它们可减轻服务器压力.

@return Boolean. true=登录成功; false=登录失败.

该函数一般在页面加载完成后调用,如

function main()
{
    $.extend(WUI.options, {
        appName: APP_NAME,
        title: APP_TITLE,
        onShowLogin: showDlgLogin
    });

    WUI.tryAutoLogin(WUI.handleLogin, "whoami");
}

$(main);

@fn handleLogin(data)

@param data 调用API "login"成功后的返回数据.

处理login相关的操作, 如设置g_data.userInfo, 保存自动登录的token等等.

@fn initClient()

@class Plugins

@fn Plugins.exists(pluginName)

@fn Plugins.list()

@fn setApp(opt)

@see options

TODO: remove. use $.extend instead.

@fn logout(dontReload?=0)

@param dontReload 如果非0, 则注销后不刷新页面.

注销当前登录, 成功后刷新页面(除非指定dontReload=1)

@fn tabClose(idx?)

关闭指定idx的标签页。如果未指定idx,则关闭当前标签页.

@fn getActivePage()

返回当前激活的逻辑页jpage,注意可能为空: jpage.size()==0。

@fn showLoading()

@fn hideLoading()

@var tabMain

标签页组件。为jquery-easyui的tabs插件,可以参考easyui文档调用相关命令进行操作,如关闭当前Tab:

var jtab = WUI.tabMain.tabs("getSelected");
var idx = WUI.tabMain.tabs("getTabIndex", jtab);
WUI.tabMain.tabs("close", idx);

注:要关闭当前Tab,可以直接用WUI.tabClose().

@module jquery-mycombobox

1 用url选项加载下拉列表

2 用jdEnumMap选项指定下拉列表

3 用loadFilter调整返回数据

4 级联列表支持

@fn jQuery.fn.mycombobox (force?=false)

@key .my-combobox 关联选择框

@var ListOptions 定义关联选择框的数据源

@param force ?=false 如果为true, 则调用时强制重新初始化。默认只初始化一次。

关联选择框组件。

用法:先定义select组件:

<select name="empId" class="my-combobox" data-options="valueField: 'id', ..."></select>

通过data-options可设置选项: { url, formatter(row), loadFilter(data), valueField, textField, jdEnumMap/jdEnumList }

初始化:

var jo = $(".my-combobox").mycombobox();

注意:使用WUI.showPage或WUI.showDlg显示的逻辑页或对话框中如果有my-combobox组件,会自动初始化,无须再调用上述代码。

操作:

特性:

注意:

@param opt {url, jdEnumMap/jdEnumList, formatter, textField, valueField, loadFilter, urlParams, isLoaded_, url_}

@param opt.url 动态加载使用的url,或一个返回URL的函数(这时会调用opt.url(opt.urlParams)得到实际URL,并保存在opt.url_中)

所以要取URL可以用

var opt = WUI.getOptions(jo);
url = opt.url_ || opt.url;

1 用url选项加载下拉列表

例如,想显示所有员工(Employee)的下拉列表,绑定员工编号字段(id),显示是员工姓名(name):

分派给 <select name="empId" class="my-combobox" data-options="url:WUI.makeUrl('Employee.query', {res:'id,name',pagesz:-1})"></select>

注意查询默认是有分页的(页大小一般为20条),用参数{pagesz:-1}使用服务器设置的最大的页大小(后端最大pagesz默认100,可使用maxPageSz参数调节)。
为了精确控制返回字段与显示格式,data-options可能更加复杂,习惯上定义一个ListOptions变量包含各种下拉框的数据获取方式,便于多个页面上共享,像这样:

<select name="empId" class="my-combobox" data-options="ListOptions.Emp()"></select>

var ListOptions = {
    // ListOptions.Emp()
    Emp: function () {
        var opts = {
            url: WUI.makeUrl('Employee.query', {
                res: 'id,name,uname',
                cond: 'storeId=' + g_data.userInfo.storeId,
                pagesz:-1
            }),
            formatter: function (row) { return row.name + '(' + row.uname + ')'; }
        };
        return opts;
    },
    ...
};

返回对象的前两个字段被当作值字段(valueField)和显示字段(textField),上例中分别是id和name字段。
如果返回对象只有一个字段,则valueField与textField相同,都是这个字段。
如果指定了formatter,则显示内容由它决定,textField此时无意义。

可以显式指定这两个字段,如:

var opts = {
    valueField: "id",
    textField: "name",
    url: ...
}

示例2:下拉框绑定User.city字段,可选项为该列已有的值:

<select name="city" class="my-combobox" data-options="ListOptions.City()"></select>

var ListOptions = {
    City: function () {
        var opts = {
            url: WUI.makeUrl('User.query', {
                res: 'city',
                cond: 'city IS NOT NULL'
                distinct: 1,
                pagesz:-1
            })
        };
        return opts;
    },
    ...
};

(v5.2) url还可以是一个函数。如果带一个参数,一般用于级联列表。参考级联列表支持节.

2 用jdEnumMap选项指定下拉列表

也支持通过key-value列表用jdEnumMap选项或jdEnumList选项来初始化下拉框,如:

订单状态: <select name="status" class="my-combobox" data-options="jdEnumMap:OrderStatusMap" style="width:150px"></select>
或者:
订单状态: <select name="status" class="my-combobox" data-options="jdEnumList:'CR:未付款;CA:已取消'" style="width:150px"></select>
或者:(key-value相同时, 只用';'间隔)
订单状态: <select name="status" class="my-combobox" data-options="jdEnumList:'未付款;已取消'" style="width:150px"></select>

其中OrderStatusMap定义如下:

var OrderStatusMap = {
    "CR": "未付款",
    "CA": "已取消"
};

3 用loadFilter调整返回数据

另一个例子:在返回列表后,可通过loadFilter修改列表,例如添加或删除项:

<select name="brandId" class="my-combobox" data-options="ListOptions.Brand()" ></select>

JS代码ListOptions.Brand:

var ListOptions = {
    ...
    // ListOptions.Brand()
    Brand: function () {
        var opts = {
            url:WUI.makeUrl('queryBrand', {res: "id,name", pagesz:-1}),
            loadFilter: function(data) {
                data.unshift({id:'0', name:'所有品牌'});
                return data;
            }
        };
        return opts;
    }
};

更简单地,这个需求还可以通过同时使用jdEnumMap和url来实现:

var ListOptions = {
    ...
    // ListOptions.Brand()
    Brand: function () {
        var opts = {
            url:WUI.makeUrl('queryBrand', {res: "id,name", pagesz:-1}),
            jdEnumMap: {0: '所有品牌'}
        };
        return opts;
    }
};

注意:jdEnumMap指定的固定选项会先出现。

4 级联列表支持

(v5.2)

缺陷类型(defectTypeId)与缺陷代码(defectId)二级关系:选一个缺陷类型,缺陷代码自动刷新为该类型下的代码。
在初始化时,如果字段有值,下拉框应分别正确显示。

在一级内容切换时,二级列表自动从后台查询获取。同时如果是已经获取过的,缓存可以生效不必反复获取。
双击仍支持刷新。

对话框上HTML如下:(defectId是用于提交的字段,所以用name属性;defectTypeId不用提交,所以用了id属性;后端接口最好两个值都返回)

<select id="defectTypeId" class="my-combobox" data-options="ListOptions.DefectType()" style="width:45%"></select>
<select name="defectId" class="my-combobox" data-options="ListOptions.Defect()" style="width:45%"></select>

其中,DefectType()与传统设置无区别,在Defect()函数中,应设置url为一个带参函数:

var ListOptions = {
    DefectType: function () {
        var opts = {
            valueField: "id",
            textField: "code",
            url: WUI.makeUrl('Defect.query', {
                res: 'id,code,name',
                cond: 'typeId is null',
                pagesz: -1
            }),
            formatter: function (row) { return row.code + "-" + row.name; }
        };
        return opts;
    },
    // ListOptions.Defect
    Defect: function () {
        var opts = {
            valueField: "id",
            textField: "code",
            url: function (typeId) {
                return WUI.makeUrl('Defect.query', {
                    res: 'id,code,name',
                    cond: "typeId=" + typeId,
                    pagesz: -1
                })
            },
            formatter: function (row) { return row.code + "-" + row.name; }
        };
        return opts;
    }
}

在对话框上设置关联动作,调用loadOptions方法:

$(frm.defectTypeId).on("change", function () {
    var typeId = $(this).val();
    if (typeId)
        $(frm.defectId).trigger("loadOptions", typeId);
});

对话框加载时,手工设置defectTypeId的值:

function onShow() {
    $(frm.defectTypeId).val(defectTypeId).trigger("change");
}
Generated by jdcloud-gendoc