ofbiz学习——创建一个查询页面

来源:互联网 发布:淘宝店铺分类导航 编辑:程序博客网 时间:2024/06/06 04:46

创建一个查询页面,比如下面这个demo。


1. 分析界面的html源码。

右击查询按钮 ——》检查  弹出开发工具窗口,查看对应的html。


从上面的源码可以知道,该查找界面是通过component://common/widget/CommonScreens.xml#FindScreenDecorator这个装饰器界面实现的。

打开component://common/widget/CommonScreens.xml#FindScreenDecorator

    <screen name="FindScreenDecorator">        <section>            <widgets>                <section>                    <condition>                        <if-empty field="titleProperty"/>                    </condition>                    <widgets>                        <container style="page-title"><label text="${title}"></label></container>                    </widgets>                    <fail-widgets>                        <container style="page-title"><label text="${uiLabelMap[titleProperty]}"></label></container>                    </fail-widgets>                </section>                <decorator-section-include name="menu-bar"/>                <container style="clear"/>                <screenlet id="searchOptions" name="findScreenlet" collapsible="true" title="${uiLabelMap.CommonSearchOptions}">                    <container id="search-options">                        <decorator-section-include name="search-options" />                    </container>                </screenlet>                <screenlet padded="false">                    <label style="h3" text="${uiLabelMap.CommonSearchResults}"/>                    <container id="search-results">                        <decorator-section-include name="search-results"/>                    </container>                </screenlet>            </widgets>        </section>    </screen>

从该装饰器可以看到还需要实现的3个widgets:menu-bar,search-options,search-results。

2. 如何调用装饰器FindScreenDecorator的呢?

打开component://manufacturing/webapp/manufacturing/WEB-INF/controller.xml找到对uri请求/main的处理配置:

    <request-map uri="main">        <security https="true" auth="true"/>        <response name="success" type="view" value="main"/>    </request-map>    <view-map name="main" page="component://manufacturing/widget/manufacturing/JobshopScreens.xml#FindProductionRun" type="screen"/>
打开component://manufacturing/widget/manufacturing/JobshopScreens.xml#FindProductionRun

    <screen name="FindProductionRun">        <section>            <actions>                <set field="titleProperty" value="ManufacturingFindProductionRun"/>                <set field="headerItem" value="jobshop"/>                <set field="viewIndex" from-field="parameters.VIEW_INDEX" type="Integer"/>                <property-to-field resource="widget" property="widget.form.defaultViewSize" field="viewSizeDefaultValue"/>                <set field="viewSize" from-field="parameters.VIEW_SIZE" type="Integer" default-value="${viewSizeDefaultValue}"/>            </actions>            <widgets>                <decorator-screen name="CommonManufacturingDecorator" location="${parameters.commonManufacturingDecoratorLocation}">                    <decorator-section name="body">                        <section>                            <widgets>                                <decorator-screen name="FindScreenDecorator" location="component://common/widget/CommonScreens.xml">                                    <decorator-section name="menu-bar">                                        <container style="button-bar">                                            <link target="CreateProductionRun" text="${uiLabelMap.ManufacturingCreateProductionRun}" style="buttontext create"/>                                        </container>                                    </decorator-section>                                    <decorator-section name="search-options">                                        <include-form name="FindProductionRun" location="component://manufacturing/widget/manufacturing/ProductionRunForms.xml"/>                                    </decorator-section>                                    <decorator-section name="search-results">                                        <include-form name="ListFindProductionRun" location="component://manufacturing/widget/manufacturing/ProductionRunForms.xml"/>                                    </decorator-section>                                </decorator-screen>                            </widgets>                        </section>                    </decorator-section>                </decorator-screen>            </widgets>        </section>    </screen>

上面可以看到对装饰器FindScreenDecorator的初始化。


3. 进一步分析装饰器FindScreenDecorator的3个具体实现。

3.1 分析menu-bar

<decorator-section name="menu-bar"><container style="button-bar"><link target="CreateProductionRun" text="${uiLabelMap.ManufacturingCreateProductionRun}" style="buttontext create"/></container></decorator-section>

菜单栏只包含一个按钮栏button-bar,而按钮栏包含一个链接(样式是按钮)link标签。点击后访问CreateProductionRun,按钮显示的文本查找ManufacturingUiLabels.xml的ManufacturingCreateProductionRun定义,如果语言选择中国,应该是 “新建一个生产运行”。
    <property key="ManufacturingCreateProductionRun">        <value xml:lang="de">Produktionsauftrag erstellen</value>        <value xml:lang="en">Create a Production Run</value>        <value xml:lang="es">Crear orden de producción</value>        <value xml:lang="fr">Création O.F.</value>        <value xml:lang="it">Nuovo ordine di produzione</value>        <value xml:lang="ja">生産実行を作成</value>        <value xml:lang="pt-BR">Criar execução de produção</value>        <value xml:lang="ro">Creare Comanda de Productie</value>        <value xml:lang="th">สร้างการผลิตสินค้า</value>        <value xml:lang="vi">Bắt đầu Chế tác lô hàng mới</value>        <value xml:lang="zh">新建一个生产运行</value>        <value xml:lang="zh-TW">新建一個生產運行</value>    </property>

3.2 分析search-options

该场景主要是包括一个表单。表单的定义见component://manufacturing/widget/manufacturing/ProductionRunForms.xml
    <form name="FindProductionRun" target="FindProductionRun" title="" type="single"        header-row-style="header-row" default-table-style="basic-table">        <field name="workEffortTypeId"><hidden value="PROD_ORDER_HEADER"/></field>        <field name="workEffortId" title="${uiLabelMap.ManufacturingProductionRunId}"><text-find/></field>        <field name="currentStatusId" title="${uiLabelMap.CommonStatus}">            <check all-checked="false">                <entity-options entity-name="StatusItem" key-field-name="statusId">                    <entity-constraint name="statusTypeId" value="PRODUCTION_RUN"/>                </entity-options>             </check>        </field>        <field name="productId" title="${uiLabelMap.ProductProductId}"><lookup target-form-name="LookupProduct"/></field>        <field name="workEffortName" title="${uiLabelMap.ManufacturingProductionRunName}"><text-find/></field>        <field name="estimatedStartDate" title="${uiLabelMap.ManufacturingStartDate}"><date-find default-value="${nowTimestamp}"/></field>        <field name="facilityId" title="${uiLabelMap.ProductFacilityId}">            <drop-down allow-empty="true">                <entity-options entity-name="Facility" key-field-name="facilityId" description="${facilityName} [${facilityId}]">                    <!--<entity-constraint name="facilityTypeId" value="WAREHOUSE"/>-->                </entity-options>             </drop-down>        </field>        <field name="submitButton" title="${uiLabelMap.CommonFind}"><submit/></field>    </form>

具体一个个字段分析:
<field name="workEffortTypeId"><hidden value="PROD_ORDER_HEADER"/></field>
该行代码表示解析一个隐藏字段,如:
<input type="hidden" name="workEffortTypeId" value="PROD_ORDER_HEADER" id="FindProductionRun_workEffortTypeId">

<field name="workEffortId" title="${uiLabelMap.ManufacturingProductionRunId}"><text-find/></field>
该行代码就是实现如下效果:



其中<text-find/>标签应该是对应上图中那个下拉框选择,表示该文本是 包含,等于,为空,忽略大小写 等查找方式。


<field name="currentStatusId" title="${uiLabelMap.CommonStatus}">            <check all-checked="false">                <entity-options entity-name="StatusItem" key-field-name="statusId">                    <entity-constraint name="statusTypeId" value="PRODUCTION_RUN"/>                </entity-options>             </check>        </field>

这个demo界面的状态复选框选项。对check和entity-option标签不是很了解。所以猜测一下:

${uiLabelMap.CommonStatus}表示形式表单的左边的文本。
check标签表示一组复选框按钮。
entity-options标签表示一组复选框按钮里面的待选项。entity-name属性表示查询的实体名,key-field-name表示复选框的key值,entity-constraint表示约束限制。
即对应的查询sql:
SELECT * FROM Status_Item WHERE status_type_id='PRODUCTION_RUN' 
实际上Status_Item表有个排序字段sequence_id,但根据demo显示结果来看似乎并没有使用。下面是查询结果:
然后,复选框后面的文本,如:已取消,已关闭等是如何显示出来的呢?猜测是根据description字段在XXXUiLabels.xml文件中找到对应的值显示出来。
经过查找,在ManufacturingUiLabels.xml中并没有找到对应的定义。但是查找CommonUiLabels.xml文件发现有类似CommonCancelled、CommonClose等值的定义。
所以估计其逻辑是description值前面加上Common字符,然后去CommonUiLabels.xml找到对应的值显示。(测试修改了CommonCancelled的定义显示的文本,清除缓存和重启都没有生效,可能猜测不对,有空再深入研究具体实现源码)

下面这个控件是就牛了,实现了2个功能,一个是输入关键字后会自动提示下拉选项,另一个是可以点击右边的图标,在弹出窗口中选择选项。
<field name="productId" title="${uiLabelMap.ProductProductId}"><lookup target-form-name="LookupProduct"/></field>
具体重点应该是lookup标签的配置。访问controller.xml文件,找到LookupProduct的配置:
<view-map name="LookupProduct" page="component://product/widget/catalog/LookupScreens.xml#LookupProduct" type="screen"/>
打开文件component://product/widget/catalog/LookupScreens.xml#LookupProduct
    <screen name="LookupProduct">        <section>            <condition>                <if-service-permission service-name="catalogPermissionCheck" main-action="VIEW"/>            </condition>            <actions>                <property-map resource="ProductUiLabels" map-name="uiLabelMap" global="true"/>                <set field="title" value="${uiLabelMap.PageTitleLookupProduct}"/>                <set field="queryString" from-field="result.queryString"/>                <set field="entityName" value="Product"/>                <set field="searchFields" value="[productId, internalName, brandName]"/>            </actions>            <widgets>                <decorator-screen name="LookupDecorator" location="component://common/widget/CommonScreens.xml">                    <decorator-section name="search-options">                        <include-form name="LookupProduct" location="component://product/widget/catalog/FieldLookupForms.xml"/>                    </decorator-section>                    <decorator-section name="search-results">                        <include-form name="ListLookupProduct" location="component://product/widget/catalog/FieldLookupForms.xml"/>                    </decorator-section>                </decorator-screen>            </widgets>        </section>    </screen>

具体实现好像蛮复杂的,暂时不深入研究了,以后再专门研究lookup标签。

<field name="workEffortName" title="${uiLabelMap.ManufacturingProductionRunName}"><text-find/></field>
这个和前面的workEffortId类似,就不对说了。

<field name="estimatedStartDate" title="${uiLabelMap.ManufacturingStartDate}"><date-find default-value="${nowTimestamp}"/></field>
这个是日期的查询条件选择,date-find标签实现如下效果:


好像有点看不懂,这个日期到底是怎么判断的。

        <field name="facilityId" title="${uiLabelMap.ProductFacilityId}">            <drop-down allow-empty="true">                <entity-options entity-name="Facility" key-field-name="facilityId" description="${facilityName} [${facilityId}]">                    <!--<entity-constraint name="facilityTypeId" value="WAREHOUSE"/>-->                </entity-options>             </drop-down>        </field>

这个是下拉框控件。类似复选框控件,其对应的sql查询是:
SELECT * FROM Facility 
/* where facility_Type_Id ='WAREHOUSe' */
对应查询结果:


<field name="submitButton" title="${uiLabelMap.CommonFind}"><submit/></field>

这个是提交按钮。

3.3 分析search-results

打开component://manufacturing/widget/manufacturing/ProductionRunForms.xml#ListFindProductionRun
    <form name="ListFindProductionRun" list-name="listIt" title="" type="list" paginate-target="FindProductionRun"        odd-row-style="alternate-row" default-table-style="basic-table hover-bar">        <actions>            <service service-name="performFind" result-map="result" result-map-list="listIt">                <field-map field-name="inputFields" from-field="requestParameters"/>                <field-map field-name="entityName" value="WorkEffortAndGoods"/>                <field-map field-name="viewIndex" from-field="viewIndex"/>                <field-map field-name="viewSize" from-field="viewSize"/>                <field-map field-name="orderBy" value="estimatedStartDate"/>            </service>        </actions>        <row-actions>            <entity-one entity-name="Product" value-field="product"/>            <entity-one entity-name="Uom" value-field="uom">                <field-map field-name="uomId" from-field="product.quantityUomId"/>            </entity-one>        </row-actions>        <field name="workEffortId" title=" " widget-style="buttontext">            <hyperlink description="${workEffortId}" target="ShowProductionRun" also-hidden="false">                <parameter param-name="productionRunId" from-field="workEffortId"/>            </hyperlink>        </field>        <field name="workEffortName" title="${uiLabelMap.ManufacturingProductionRunName}"><display/></field>        <field name="productId" title="${uiLabelMap.ProductProductId}"><display/></field>        <field name="estimatedQuantity" title="${uiLabelMap.ManufacturingQuantity}"><display/></field>        <field name="QuantityUom" title="${uiLabelMap.ProductQuantityUom}"><display description="${uom.abbreviation}"></display></field>        <field name="currentStatusId" title="${uiLabelMap.CommonStatus}">            <display-entity entity-name="StatusItem" key-field-name="statusId"/>        </field>        <field name="estimatedStartDate" title="${uiLabelMap.ManufacturingStartDate}"><display/></field>        <field name="description" title="${uiLabelMap.CommonDescription}"><display/></field>        <field name="facilityId" title="${uiLabelMap.ProductFacilityId}"><display/></field>    </form>

该查询的业务逻辑查看service的配置。service-name="performFind" 。找到performFind服务的定义。通过服务引擎工具查找如下信息:


打开文件org.apache.ofbiz.common.FindServices,路径:framework/common/servicedef/services.xml

    <service name="performFind" auth="false" engine="java" invoke="performFind" location="org.apache.ofbiz.common.FindServices">        <description>Generic service to return an entity iterator.  set filterByDate to Y to exclude expired records.           set noConditionFind to Y to find without conditions.  </description>        <attribute name="entityName" type="String" mode="IN" optional="false"/>        <attribute name="inputFields" type="java.util.Map" mode="IN" optional="false"/>        <attribute name="fieldList" type="java.util.List" mode="IN" optional="true"/>        <attribute name="orderBy" type="String" mode="IN" optional="true"/>        <attribute name="noConditionFind" type="String" mode="IN" optional="true"><!-- find with no condition (empty entityConditionList) only done when this is Y --></attribute>        <attribute name="distinct" type="String" mode="IN" optional="true"><!-- distinct find only done when this is Y --></attribute>        <attribute name="filterByDate" type="String" mode="IN" optional="true"/>        <attribute name="filterByDateValue" type="Timestamp" mode="IN" optional="true"/>        <attribute name="fromDateName" type="String" mode="IN" optional="true"/>        <attribute name="thruDateName" type="String" mode="IN" optional="true"/>        <attribute name="viewIndex" type="Integer" mode="IN" optional="true"/>        <attribute name="viewSize" type="Integer" mode="IN" optional="true"/>        <attribute name="listIt" type="org.apache.ofbiz.entity.util.EntityListIterator" mode="OUT" optional="true"/>        <attribute name="listSize" type="Integer" mode="OUT" optional="true"/>        <attribute name="queryString" type="String" mode="OUT" optional="true"/>        <attribute name="queryStringMap" type="java.util.Map" mode="OUT" optional="true"/>    </service>

打开org.apache.ofbiz.common.FindServices,查看performFind方法:

    /**     * performFind     *     * This is a generic method that expects entity data affixed with special suffixes     * to indicate their purpose in formulating an SQL query statement.     */    public static Map<String, Object> performFind(DispatchContext dctx, Map<String, ?> context) {        String entityName = (String) context.get("entityName");        String orderBy = (String) context.get("orderBy");        Map<String, ?> inputFields = checkMap(context.get("inputFields"), String.class, Object.class); // Input        String noConditionFind = (String) context.get("noConditionFind");        String distinct = (String) context.get("distinct");        List<String> fieldList =  UtilGenerics.<String>checkList(context.get("fieldList"));        GenericValue userLogin = (GenericValue) context.get("userLogin");        Locale locale = (Locale) context.get("locale");        Delegator delegator = dctx.getDelegator();        if (UtilValidate.isEmpty(noConditionFind)) {            // try finding in inputFields Map            noConditionFind = (String) inputFields.get("noConditionFind");        }        if (UtilValidate.isEmpty(noConditionFind)) {            // Use configured default            noConditionFind = EntityUtilProperties.getPropertyValue("widget", "widget.defaultNoConditionFind", delegator);        }        String filterByDate = (String) context.get("filterByDate");        if (UtilValidate.isEmpty(filterByDate)) {            // try finding in inputFields Map            filterByDate = (String) inputFields.get("filterByDate");        }        Timestamp filterByDateValue = (Timestamp) context.get("filterByDateValue");        String fromDateName = (String) context.get("fromDateName");        if (UtilValidate.isEmpty(fromDateName)) {            // try finding in inputFields Map            fromDateName = (String) inputFields.get("fromDateName");        }        String thruDateName = (String) context.get("thruDateName");        if (UtilValidate.isEmpty(thruDateName)) {            // try finding in inputFields Map            thruDateName = (String) inputFields.get("thruDateName");        }        Integer viewSize = (Integer) context.get("viewSize");        Integer viewIndex = (Integer) context.get("viewIndex");        Integer maxRows = null;        if (viewSize != null && viewIndex != null) {            maxRows = viewSize * (viewIndex + 1);        }        LocalDispatcher dispatcher = dctx.getDispatcher();        Map<String, Object> prepareResult = null;        try {            prepareResult = dispatcher.runSync("prepareFind", UtilMisc.toMap("entityName", entityName, "orderBy", orderBy,                                               "inputFields", inputFields, "filterByDate", filterByDate, "noConditionFind", noConditionFind,                                               "filterByDateValue", filterByDateValue, "userLogin", userLogin, "fromDateName", fromDateName, "thruDateName", thruDateName,                                               "locale", context.get("locale"), "timeZone", context.get("timeZone")));        } catch (GenericServiceException gse) {            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "CommonFindErrorPreparingConditions", UtilMisc.toMap("errorString", gse.getMessage()), locale));        }        EntityConditionList<EntityCondition> exprList = UtilGenerics.cast(prepareResult.get("entityConditionList"));        List<String> orderByList = checkList(prepareResult.get("orderByList"), String.class);        Map<String, Object> executeResult = null;        try {            executeResult = dispatcher.runSync("executeFind", UtilMisc.toMap("entityName", entityName, "orderByList", orderByList,                                                                             "fieldList", fieldList, "entityConditionList", exprList,                                                                             "noConditionFind", noConditionFind, "distinct", distinct,                                                                             "locale", context.get("locale"), "timeZone", context.get("timeZone"),                                                                             "maxRows", maxRows));        } catch (GenericServiceException gse) {            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "CommonFindErrorRetrieveIterator", UtilMisc.toMap("errorString", gse.getMessage()), locale));        }        if (executeResult.get("listIt") == null) {            if (Debug.verboseOn()) Debug.logVerbose("No list iterator found for query string + [" + prepareResult.get("queryString") + "]", module);        }        Map<String, Object> results = ServiceUtil.returnSuccess();        results.put("listIt", executeResult.get("listIt"));        results.put("listSize", executeResult.get("listSize"));        results.put("queryString", prepareResult.get("queryString"));        results.put("queryStringMap", prepareResult.get("queryStringMap"));        return results;    }

这里涉及到了服务的定义与实现,不是本章关注的重点。本章主要关注的重点是

<form name="ListFindProductionRun" list-name="listIt" title="" type="list" paginate-target="FindProductionRun"
        odd-row-style="alternate-row" default-table-style="basic-table hover-bar">

    <form name="FindProductionRun" target="FindProductionRun" title="" type="single"
        header-row-style="header-row" default-table-style="basic-table">

2个表单通过paginate-target属性关联起来。


总结:

查询页面的大体框架布局有了初步了解。介绍了常用的表单控件的使用。

还有待深入研究的lookup控件和date-find控件。以及check控件的国际化是在哪里配置的。






原创粉丝点击