增加、修改、删除功能 和之前 收派标准和取派员管理类似,留作课后练习
技术:文件上传 和 Excel解析 (已经 讲解POI如何读写Excel 文档)
/WEB-INF/pages/base/region.jsp 区域管理页面
在datagrid的toolbar 添加按钮
{
id : 'button-import',
text : '批量导入',
iconCls : 'icon-save'
}
文件上传基本要求 :
客户端 `form post`提交、 `enctype=multipart/form-data` 、`input file` 必须提供name属性
服务器 Apache commons-fileupload 、 COS、 JSPSmartUpload
Struts2 框架默认使用文件上传 jakarta 是 apache commons-fileupload
企业中制作文件上传,大多使用一键上传的效果 (页面中只提供一个按钮,点击按钮就可以完成文件上传 ) ,原理???
一键上传,往往和Ajax效果结合 ,页面不刷新
按钮点击 --- 触发隐藏form input file浏览文件动作 ---- 选定文件 触发onChange事件 --- 提交form表单,target指向隐藏iframe ---- 服务器完成文件上传,将结果更新隐藏iframe
使用 Jquery 插件 Ocupload
http://code.google.com/p/ocupload/ 下载 ocupload 1.1.2.js
将js插件导入项目
参照 Documentation - ocupload - One Click Upload - A jQuery Plugin - Google Project Hosting.mht 使用
在页面导入 一键上传js
<script type="text/javascript"
src="${pageContext.request.contextPath }/js/ocupload/jquery.ocupload-1.1.2.js">script>
对按钮添加一键上传效果
// 对批量导入添加一键上传效果
$('#button-import').upload({
name : 'upload' , //
action : '${pageContext.request.contextPath}/region_importXls.action', // 表单提交路径
onComplete : function(response){
$.messager.alert('信息','文件上传成功!','info');
}
});
编写RegionAction 接收上传文件 ,使用Struts2 内置FileUploadInterceptor 实现文件上传
public class RegionAction extends BaseAction implements ModelDriven<Region> {
}
public interface RegionService {
}
public class RegionServiceImpl extends BaseService implements RegionService {
}
将RegionDAO 注入 BaseService
将RegionService 注入BaseAction
导入区域数据,参考 region.xls、Mysql全国省市区以及邮编数据库
返回客户端 json
// 返回json
Map<String, Object> map = new HashMap<String, Object>();
map.put("result", "success");
map.put("msg", "区域导入完成");
ActionContext.getContext().put("map", map);
配置 struts.xml applicationContext.xml
<action name="region_*" class="regionAction" method="{1}">
<result name="importXlsSUCCESS" type="json">
<param name="root">mapparam>
result>
action>
使用log4j 记录导入失败日志
// 使用log4j 日志记录器,记录日志
private static final Logger LOG = Logger.getLogger(RegionAction.class);
记录日志
try {
regionService.saveRegion(region);
} catch (Exception e) {
// 导入region失败,记录日志
LOG.error("区域导入失败,编号:" + region.getId(), e);
}
将 region.jsp 的 datagrid的url 指向服务器Action
url : "${pageContext.request.contextPath}/region_pageQuery.action"
在RegionAction 添加 pageQuery方法,实现分页查询
Action 代码
Service接口继承PageQuery 接口
Service实现类调用 BaseService 抽取 pageQuery方法
配置返回结果
pageResponseBean
total,
rows\[\d+\]\.id,
rows\[\d+\]\.province,
rows\[\d+\]\.city,
rows\[\d+\]\.district,
rows\[\d+\]\.postcode,
rows\[\d+\]\.shortcode,
rows\[\d+\]\.citycode
列表查询完成后,在每次批量导入后,重写加载datagrid 数据
// 使datagrid 数据刷新
$('#grid').datagrid('reload');
重新发起请求,加载datagrid的url
工作类作用可以完成汉字到拼音的转换 —- 在搜索时使用
pinyin4j-2.5.0.jar 开发jar包
pinyin4j的使用手册.doc
需求:
汉字 转换 拼音首字母缩写,城市简码
汉字 转换 拼音全拼 , 城市编码
搜索坐标
<dependency>
<groupId>com.belerwebgroupId>
<artifactId>pinyin4jartifactId>
<version>2.5.0version>
dependency>
很多工作jar包在 中央仓库没有 ,手动将jar包放入本地仓库
参考: “安装jar包到仓库”
mvn install:install-file
-DgroupId=pinyin4j
-DartifactId=pinyin4j
-Dversion=2.5.0
-Dpackaging=jar
-Dfile=D:\itcast\bak\项目_20130711_javaee就业班\day4_项目第四天(取派员、区域、分区导入)\扩展资料\pinyin4j\pinyin4j-2.5.0.jar
使用命令之前,先将 maven安装目录/bin 配置path 环境变量 !!!
<dependency>
<groupId>pinyin4jgroupId>
<artifactId>pinyin4jartifactId>
<version>2.5.0version>
dependency>
进行物流管理时,不可能让取派员人员去负责一个自然区域 ,需要将自然区域按照 路段、小区进行划分,目的方便为区域指定对应取派人员
对管理分区进行维护,为定区管理,自动下单等后续操作提供基础数据支持。
打算引入一个复制功能按钮,用来增加信息基本相同的数据。
subarea.jsp 存在两个隐藏window窗口 , 添加form 和 查询form
在添加form 表单中,加载Region区域列表 (分区属于区域 )
class="easyui-combobox"
data-options="valueField:'id', textField:'info', url:'${pageContext.request.contextPath }/region_ajaxlist.action', required:true"/>
在RegionAction 提供 ajaxlist 方法,返回 区域列表,生成 下拉项
<result name="ajaxlistSUCCESS" type="json">
<param name="root">regionsparam>
<param name="includeProperties">
\[\d+\]\.id,
\[\d+\]\.info
param>
result>
Region 实体类,并没有getInfo方法,需要手动创建
// 自定义方法
public String getInfo() {
// 返回省市区信息
return province + "," + city + "," + district;
}
为区域列表添加 name属性
// 为添加分区 ,保存按钮添加点击 事件
$('#save').click(function(){
// 判断form 校验是否通过
if($('#subareaForm').form('validate')){
// 通过校验
$('#subareaForm').submit();
}else{
// 提示
$.messager.alert('警告','表单存在非法数据项,请重新输入!','warning');
}
});
编写服务器端 SubareaAction 完成分区管理
public class SubareaAction extends BaseAction implements ModelDriven<Subarea> {
}
public interface SubareaService {
}
public class SubareaServiceImpl extends BaseService implements SubareaService {
}
将DAO 注入BaseService
将Service 注入BaseAction
统一配置 struts.xml applicationContext.xml
<action name="subarea_*" class="subareaAction" method="{1}">
<result name="saveOrUpdateSUCCESS">/WEB-INF/pages/base/subarea.jspresult>
action>
将datagrid 控件url 指向服务器代码,查询所有分页数据
url : "${pageContext.request.contextPath}/subarea_pageQuery.action"
在SubareaAction 添加pageQuery 查询方法
pageResponseBean
total,
rows\[\d+\]\.id,
rows\[\d+\]\.addresskey,
rows\[\d+\]\.startnum,
rows\[\d+\]\.endnum,
rows\[\d+\]\.single,
rows\[\d+\]\.position,
rows\[\d+\]\.region\.province,
rows\[\d+\]\.region\.city,
rows\[\d+\]\.region\.district
问题一: 将条件查询参数,封装到model对象
分区查询 ,在subarea.jsp 提供隐藏窗口
在组合条件查询时,form表单中 不需要必须填写校验
修改 每个条件输入项的name属性
(name属性,参照目标Action的model对象,来编写)
问题二: 完成条件查询时,数据显示datagrid中,当需要分页时,分页完成带有查询条件的分页 ?
必须结合datagrid 完成条件查询 ,条件在datagrid中缓存
如果不使用datagrid
<a href= “xxx.action ?page=1&查询条件”>上一页a>
使用datagrid的load 方法,将查询条件缓存到datagrid的控件内部 !
执行load 方法时,需要json格式参数
问题三: 将form的查询条件,转换为json
借助于 jquery 提供 serializeArray 函数
最终需要格式 :
{
name : [‘Hello’,’World ’]
}
自定义jquery函数
$.fn.serializeJson=function(){
var serializeObj={};
var array=this.serializeArray();
var str=this.serialize();
$(array).each(function(){
if(serializeObj[this.name]){
if($.isArray(serializeObj[this.name])){
serializeObj[this.name].push(this.value);
}else{
serializeObj[this.name]=[serializeObj[this.name],this.value];
}
}else{
serializeObj[this.name]=this.value;
}
});
return serializeObj;
};
// 执行条件查询
$("#btn").click(function(){
// 将form的数据转换为 json
var params = $('#searchForm').serializeJson();
// 调用datagrid 执行查询,在查询是,缓存条件
$('#grid').datagrid('load',params);// 重新加载datagrid指定url
});
编写服务器代码 ,修改 SubareaAction 的pageQuery 方法 !!!
方案一:拼接SQL语句
String sql = “select * from subarea where 1=1” ; 默认语句
当存在一个条件, sql += “and key=value”
拼接一个条件
方案二:Hibernate QBC 面向对象,条件查询
单表可以直接添加属性,如果多表关联,必须创建别名进行表关联
// 针对QBC添加查询条件
if (subarea.getAddresskey() != null && subarea.getAddresskey().trim().length() > 0) {
// 添加关键字条件
detachedCriteria.add(Restrictions.like("addresskey", "%" + subarea.getAddresskey() + "%"));
}
if (subarea.getDecidedZone() != null && subarea.getDecidedZone().getId() != null && subarea.getDecidedZone().getId().trim().length() > 0) {
// 输入定区编号
detachedCriteria.add(Restrictions.eq("decidedZone", subarea.getDecidedZone()));// 比较对象,实际上比较定区id 属性
}
if (subarea.getRegion() != null) {
// 表关联,QBC解决方案 --- 别名
detachedCriteria.createAlias("region", "r");
if (subarea.getRegion().getProvince() != null && subarea.getRegion().getProvince().trim().length() > 0) {
detachedCriteria.add(Restrictions.like("r.province", "%" + subarea.getRegion().getProvince() + "%"));
}
if (subarea.getRegion().getCity() != null && subarea.getRegion().getCity().trim().length() > 0) {
detachedCriteria.add(Restrictions.like("r.city", "%" + subarea.getRegion().getCity() + "%"));
}
if (subarea.getRegion().getDistrict() != null && subarea.getRegion().getDistrict().trim().length() > 0) {
detachedCriteria.add(Restrictions.like("r.district", "%" + subarea.getRegion().getDistrict() + "%"));
}
}
问题:
DetachedCriteria.forClass(Subarea.class) ; // select subarea.* from…
detachedCriteria.setProjection(Projections.rowCount()); // select count(*) from …
detachedCriteria.setProjection(null); // select * from region, subarea
返回不是分区数据,而是区域和分区对象数组
修改BaseService 调换 查询rows和total的顺序, 不要使用 setProjection(null) ;
// 分页通用代码
public PageResponseBean pageQuery(PageRequestBean pageRequestBean, GenericDAO dao) {
PageResponseBean pageResponseBean = new PageResponseBean();
// 查询当前页显示数据
int firstResult = (pageRequestBean.getPage() - 1) * pageRequestBean.getRows(); // 从哪条开始
int maxResults = pageRequestBean.getRows(); // 返回记录数
List data = dao.pageQuery(pageRequestBean.getDetachedCriteria(), firstResult, maxResults);
pageResponseBean.setRows(data);
// 满足当前条件,记录总条数
long total = dao.findTotalCount(pageRequestBean.getDetachedCriteria());
pageResponseBean.setTotal(total);
return pageResponseBean;
}
隐患: DetachedCriteria 缓存firsetResult 和 maxResults
在查询 select count(*) from subarea 存在数组越界问题 ,count(*) 结果只有一条
List list = this.getHibernateTemplate().findByCriteria(detachedCriteria, 0, 1); // 解决越界问题
将查询当前页数据,进行Excel导出
(技术: Excel文件生成 和 文件下载 )
为了避免重复查询,先将查询数据缓存起来,Session 中 ,再通过页面点击导出按钮,生成Excel文件,提供下载 !
SubareaAction的pageQuery方法
// 将查询结果 保存到Session
ServletActionContext.getRequest().getSession().setAttribute("pageResponseBean", pageResponseBean);
function doExport(){ location.href="${pageContext.request.contextPath}/subarea_exportXls.action";
}
在SubareaAction 添加 exportXls 的方法
问题: 配置文件下载 (Struts2 框架提供 stream流结果集 )
一个流 默认 getInputStream
两个头信息 ContentType、ContentDisposition
inputName 默认值 “inputStream” ,需要在Action 提供 getInputStream 方法,用于返回文件流 ;
contentType 默认text/plain 文件形式,根据下载文件类型进行设置
contentDisposition 默认 inline ,在浏览器直接打开,弹出窗口 改为attachment
javaweb : ServletContext.getMimeType(文件名) 根据文件名 查询对应MIME类型
tomcat/conf/web.xml 存放很多MIME类型映射
在SubareaAction 提供 getInputStream 流方法
<result name="exportXlsSUCCESS" type="stream">
<param name="contentType">application/vnd.ms-excelparam>
<param name="contentDisposition">attachment;filename=subarea.xlsparam>
result>
将内存数据,生成Excel文件对象 中!
问题: 如何将内存Excel对象,转换输入流 ??
先将数据写入内存缓冲区中,在通过输入流读取进来 ByteArrayOutputStream
下载中文文件名乱码解决 ,通用方案,FireFox 使用Base64编码,对IE(其它)浏览器使用 URL编码
提供 FileUitls 工具类,可以根据不同浏览器,完成下载文件名的编码 !
也有人说 new String(filename.getBytes() , “ISO-8859-1” )
public static String encodeDownloadFilename(String filename, String agent)
throws IOException {
if (agent.contains("Firefox")) { // 火狐浏览器
filename = "=?UTF-8?B?"
+ new BASE64Encoder().encode(filename.getBytes("utf-8"))
+ "?=";
filename = filename.replaceAll("\r\n", "");
} else { // IE及其他浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+"," ");
}
return filename;
}
在struts.xml 配置从值栈中获取,已经编码后文件名
<result name="exportXlsSUCCESS" type="stream">
<param name="contentType">
application/vnd.ms-excelparam>
<param name="contentDisposition">
attachment;filename=${downloadFileName}
param>
result>
${downloadFileName}
从值栈获取,在Action中对文件名手动进行编码,将编码结果放入值栈 !
// 对文件名进行编码
String downloadFileName = "分区数据.xls";
// 获得用户使用浏览器类型
String agent = ServletActionContext.getRequest().getHeader("user-agent");
// 对下载文件名编码
downloadFileName = FileUtils.encodeDownloadFilename(downloadFileName, agent);
// 将结果放入值栈
ActionContext.getContext().put("downloadFileName", downloadFileName);