在这里会简述及展示,部分表单控件如何应用以及后端数据如何处理和应用。
框架使用Spring封装的一系列表单标签。
Spring MVC form声明
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
JSTL 1.1声明
- 文件位置:WEB-INF/tlds/fns.tld
涉及方法:
<!-- DictUtils -->
<function>
<description>获取字典标签</description>
<name>getDictLabel</name>
<function-class>com.thinkgem.jeesite.modules.sys.utils.DictUtils</function-class>
<function-signature>java.lang.String getDictLabel(java.lang.String, java.lang.String, java.lang.String)</function-signature>
<example>${fns:getDictLabel(value, type, defaultValue)}</example>
</function>
<function>
<description>获取字典标签(多个)</description>
<name>getDictLabels</name>
<function-class>com.thinkgem.jeesite.modules.sys.utils.DictUtils</function-class>
<function-signature>java.lang.String getDictLabels(java.lang.String, java.lang.String, java.lang.String)</function-signature>
<example>${fns:getDictLabels(values, type, defaultValue)}</example>
</function>
<function>
<description>获取字典值</description>
<name>getDictValue</name>
<function-class>com.thinkgem.jeesite.modules.sys.utils.DictUtils</function-class>
<function-signature>java.lang.String getDictValue(java.lang.String, java.lang.String, java.lang.String)</function-signature>
<example>${fns:getDictValue(label, type, defaultValue)}</example>
</function>
<function>
<description>获取字典对象列表</description>
<name>getDictList</name>
<function-class>com.thinkgem.jeesite.modules.sys.utils.DictUtils</function-class>
<function-signature>java.util.List getDictList(java.lang.String)</function-signature>
<example>${fns:getDictList(type)}</example>
</function>
<function>
<description>获取字典对象列表</description>
<name>getDictListJson</name>
<function-class>com.thinkgem.jeesite.modules.sys.utils.DictUtils</function-class>
<function-signature>java.lang.String getDictListJson(java.lang.String)</function-signature>
<example>${fns:getDictListJson(type)}</example>
</function>
DictUtils字典工具类
文件位置:com.thinkgem.jeesite.modules.sys.utils.DictUtils.java
/**
* Copyright © 2012-2016 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
*/
package com.thinkgem.jeesite.modules.sys.utils;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.thinkgem.jeesite.common.mapper.JsonMapper;
import com.thinkgem.jeesite.common.utils.CacheUtils;
import com.thinkgem.jeesite.common.utils.SpringContextHolder;
import com.thinkgem.jeesite.modules.sys.dao.DictDao;
import com.thinkgem.jeesite.modules.sys.entity.Dict;
/**
* 字典工具类
* @author ThinkGem
* @version 2013-5-29
*/
public class DictUtils {
private static DictDao dictDao = SpringContextHolder.getBean(DictDao.class);
public static final String CACHE_DICT_MAP = "dictMap";
public static String getDictLabel(String value, String type, String defaultValue){
if (StringUtils.isNotBlank(type) && StringUtils.isNotBlank(value)){
for (Dict dict : getDictList(type)){
if (type.equals(dict.getType()) && value.equals(dict.getValue())){
return dict.getLabel();
}
}
}
return defaultValue;
}
public static String getDictLabels(String values, String type, String defaultValue){
if (StringUtils.isNotBlank(type) && StringUtils.isNotBlank(values)){
List<String> valueList = Lists.newArrayList();
for (String value : StringUtils.split(values, ",")){
valueList.add(getDictLabel(value, type, defaultValue));
}
return StringUtils.join(valueList, ",");
}
return defaultValue;
}
public static String getDictValue(String label, String type, String defaultLabel){
if (StringUtils.isNotBlank(type) && StringUtils.isNotBlank(label)){
for (Dict dict : getDictList(type)){
if (type.equals(dict.getType()) && label.equals(dict.getLabel())){
return dict.getValue();
}
}
}
return defaultLabel;
}
public static List<Dict> getDictList(String type){
@SuppressWarnings("unchecked")
Map<String, List<Dict>> dictMap = (Map<String, List<Dict>>)CacheUtils.get(CACHE_DICT_MAP);
if (dictMap==null){
dictMap = Maps.newHashMap();
for (Dict dict : dictDao.findAllList(new Dict())){
List<Dict> dictList = dictMap.get(dict.getType());
if (dictList != null){
dictList.add(dict);
}else{
dictMap.put(dict.getType(), Lists.newArrayList(dict));
}
}
CacheUtils.put(CACHE_DICT_MAP, dictMap);
}
List<Dict> dictList = dictMap.get(type);
if (dictList == null){
dictList = Lists.newArrayList();
}
return dictList;
}
/**
* 返回字典列表(JSON)
* @param type
* @return
*/
public static String getDictListJson(String type){
return JsonMapper.toJsonString(getDictList(type));
}
}
Radio单选框、Select下拉框、CheckBox复选框
- 数据库存储:Varchar
- 后台数据:String
- 前端回显列表数据集合:List<Dict>
- 前端回显目标数据:Radio-String;Select-String;CheckBox-List<String>或List<Dict>;
- 前端提交:String
页面数据编辑和回显:
单选按钮(单选):
<form:radiobuttons path="check" items="${fns:getDictList('yes_no')}" itemLabel="label" itemValue="value" htmlEscape="false"/>
下拉框(单选):
<form:select path="check" class="input-xlarge ">
<form:option value="" label=""/>
<form:options items="${fns:getDictList('yes_no')}" itemLabel="label" itemValue="value" htmlEscape="false"/>
</form:select>
复选框(多选):
<form:checkboxes path="goodsTypeFormatDictList" items="${fns:getDictList('map_goods_format')}" itemLabel="label" itemValue="value" htmlEscape="false"/>
页面数据显示:
单选按钮和单选下拉框:
${fns:getDictLabel(对象.属性, 'yes_no', '')}
复选框:
${fns:getDictLabels(对象.属性, 'map_goods_format', '')}
控件属性说明
- path,属性名称或目标数据名称;
- items,源数据集合,封装源数据类型为List<Dict>或自定义数据类型;
- itemLabel,指定属性值为Dict对象的label属性或自定义;
- itemValue,指定数值为Dict对象的value属性或自定义;
CheckBox-多选说明及案例
Radio和Select比较简单,因为存储的都是单一值,在数据编辑方面并没有需要注意的地方,只需要稍微注意一下所封装的数据类型对应调整就可以了。
CheckBox在数据回显和提交时需要特别注意一下,因为封装的形式和提交的方式稍微会有所调整,在此我重新以自定义对象举例说明:
一共有3张表,TravelRout,旅游路线表;ScenicArea,景区信息表;TravelRouteScenicArea,旅游路线和景区信息关联表,多对多的关系;
- 需求解释:添加旅游路线时,可以关联多个景区,因此每一条旅游路线信息都会有景区信息的集合。
实体模型TravelRoute类中,需要ScenicArea属性标识,用于封装传递的多选数值:
private List<String> scenicAreaIdList = Lists.newArrayList(); //拥有的景区ID集合,用于数据回显
private List<ScenicArea> scenicAreaList = Lists.newArrayList(); //拥有的景区源数据集合
public List<ScenicArea> getScenicAreaList() {
return scenicAreaList;
}
public void setScenicAreaList(List<ScenicArea> scenicAreaList) {
this.scenicAreaList = scenicAreaList;
}
@JsonIgnore
public List<String> getScenicAreaIdList(){
List<String> scenicAreaIdList = Lists.newArrayList();
for(ScenicArea scenicArea : scenicAreaList){
scenicAreaIdList.add(scenicArea.getId());
}
return scenicAreaIdList;
}
public void setScenicAreaIdList(List<String> scenicAreaIdList){
scenicAreaList = Lists.newArrayList();
for(String scenicAreaId : scenicAreaIdList){
ScenicArea scenicArea = new ScenicArea();
scenicArea.setId(scenicAreaId);
scenicAreaList.add(scenicArea);
}
}
TravelRouteService类中,get方法需要获取scenicAreaList集合进行数据填充,该数据源是利用scenicAreaId查询TravelRouteScenicArea得到的集合,注意:
public TravelRoute get(String id) {
TravelRoute travelRoute = travelRouteDao.get(id);
List<ScenicArea> list = travelRouteDao.findTravelRouteScenicAreaByTR(id);
travelRoute.setScenicAreaList(list);
return travelRoute;
}
TravelRouteController类中,还需要针对前端全部景区数据列表做一个属性回显,allScenicAreas,即查询全部景区信息列表:
@RequiresPermissions("hnly:travelRoute:view")
@RequestMapping(value = "form")
public String form(TravelRoute travelRoute, Model model) {
model.addAttribute("travelRoute", travelRoute);
model.addAttribute("allScenicAreas", scenicAreaService.findList(null));
return "modules/hnly/travelRouteForm";
}
前端JSP:
<div class="control-group">
<label class="control-label">途径景区:</label>
<div class="controls">
<form:checkboxes path="scenicAreaIdList" items="${allScenicAreas}" itemLabel="name" itemValue="id" htmlEscape="false" class="required"/>
</div>
</div>
执行新增或更新操作时,TravelRouteService类中,需要做响应的修改,先删除联合表中的数据,再添加:
@Transactional(readOnly = false)
public void save(TravelRoute travelRoute) {
if (StringUtils.isBlank(travelRoute.getId())){
travelRouteDao.insert(travelRoute);
} else {
travelRouteDao.update(travelRoute);
}
travelRouteDao.deleteTravelRouteScenicArea(travelRoute);
travelRouteDao.insertTravelRouteScenicArea(travelRoute);
}
TravelRouteDao,在联合表中插入数据需要注意:
<!-- 删除推荐旅游路线和景区关联表数据 -->
<update id="deleteTravelRouteScenicArea">
DELETE FROM hnly_travel_route_scenic_area
WHERE hnly_travel_route_id = #{id}
</update>
<!-- 插入推荐旅游路线和景区关联表数据 -->
<insert id="insertTravelRouteScenicArea" useGeneratedKeys="true" keyProperty="id">
INSERT INTO hnly_travel_route_scenic_area(hnly_travel_route_id, hnly_scenic_area_id)
<foreach collection="scenicAreaList" item="scenicArea" separator=" union all ">
SELECT #{id}, #{scenicArea.id}
<if test="dbName != 'mssql'">
FROM dual
</if>
</foreach>
</insert>
<!-- 根据路线ID获取推荐旅游路线关联的景区数据 -->
<select id="findTravelRouteScenicAreaByTR" resultType="ScenicArea">
SELECT
a.hnly_scenic_area_id AS "id",
sa.name AS "name",
sa.addr AS "addr",
sa.lat AS "lat",
sa.lon AS "lon",
sa.hnly_panoramic_id AS "panoramic.id"
FROM hnly_travel_route_scenic_area a
LEFT JOIN hnly_scenic_area sa ON sa.id = a.hnly_scenic_area_id
WHERE a.hnly_travel_route_id = #{id}
</select>
图样图森破
Ckfinder-图片及文件上传控件
图片及文件上传均使用的CF控件,在页面编辑的同时就已经将图片和文件数据上传完了,最后表单提交的时候是一串上传地址,所以后台数据格式为String字符串类型即可。
<div class="control-group">
<label class="control-label">图片:</label>
<div class="controls">
<form:hidden id="pic" path="pic" htmlEscape="false" maxlength="255" class="input-xlarge"/>
<sys:ckfinder input="pic" type="images" uploadPath="/hnly/activity" maxHeight="100" maxWidth="100"/>
</div>
</div>
<div class="control-group">
<label class="control-label">文件:</label>
<div class="controls">
<form:hidden id="fil" path="fil" htmlEscape="false" maxlength="255" class="input-xlarge"/>
<sys:ckfinder input="fil" type="files" uploadPath="/hnly/activity" selectMultiple="true"/>
</div>
</div>
由两部分组成,form:hidden隐藏文本和sys:ckfinder控件,type分为两种images和files;uploadPath为上传路径;selectMultiple为是否为文件模式的是否允许多文件上传;maxHeight和maxWeight为图片模式使用的回显图片长宽;
WdatePicker-时间选择控件
如果系统中使用到时间数据格式是多样化的,如年月日/日分等等,建议后台和数据库的时间数据格式设置为文本,在页面中取消时间格式化就好了,原系统中如何设置请自行参考,这里我只贴修改后的。
<div class="control-group">
<label class="control-label">时间:</label>
<div class="controls">
<input name="publishTime" type="text" readonly="readonly" maxlength="20" class="input-medium Wdate "
value="${activity.publishTime}"
onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',isShowClear:false});"/>
</div>
</div>
value,为提交和回显属性,这里我已经将格式化方法去除,不再做格式化,且数据库和后台类型为字符串类型,如果使用原格式化方法处理则必须统一数据格式,不能使用字符串类型,去除后可使用。WdatePicker为控件调用方式;dataFmt,可设置多种时间显示方法;
Input-普通文本
<div class="control-group">
<label class="control-label">标题:</label>
<div class="controls">
<form:input path="title" htmlEscape="false" maxlength="255" class="input-xlarge "/>
</div>
</div>
Textarea-多行文本
<div class="control-group">
<label class="control-label">内容:</label>
<div class="controls">
<form:textarea path="content" htmlEscape="false" rows="4" class="input-xxlarge "/>
</div>
</div>