创建一个查询页面,比如下面这个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}表示形式表单的左边的文本。
SELECT * FROM Status_Item WHERE status_type_id='PRODUCTION_RUN'
实际上Status_Item表有个排序字段sequence_id,但根据demo显示结果来看似乎并没有使用。下面是查询结果:
下面这个控件是就牛了,实现了2个功能,一个是输入关键字后会自动提示下拉选项,另一个是可以点击右边的图标,在弹出窗口中选择选项。
<field name="productId" title="${uiLabelMap.ProductProductId}"><lookup target-form-name="LookupProduct"/></field>
具体重点应该是lookup标签的配置。访问controller.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="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查询是:
/* where facility_Type_Id ='WAREHOUSe' */
<field name="submitButton" title="${uiLabelMap.CommonFind}"><submit/></field>
这个是提交按钮。
3.3 分析search-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">
<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控件的国际化是在哪里配置的。