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

创建一个查询页面,比如下面这个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控件的国际化是在哪里配置的。






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值