1商品类目选择功能实现
1.1 功能分析
样式绑定了selectItemCat,可以用工具栏上的搜索File Search,patten选择
*.js
类目选择js代码如下
// 初始化选择类目组件
initItemCat : function(data){
$(".selectItemCat").each(function(i,e){ //i e分别表示循环的下标与对象
var _ele = $(e); //DOM对象转换成jquery对象
if(data && data.cid){
_ele.after("<span style='margin-left:10px;'>"+data.cid+"</span>");
}else{
_ele.after("<span style='margin-left:10px;'></span>");
}
_ele.unbind('click').click(function(){
$("<div>").css({padding:"5px"}).html("<ul>") //新建div并转成jquery对象
.window({ //打开窗口
width:'500',
height:"450",
modal:true,
closed:true,
iconCls:'icon-save',
title:'选择类目',
onOpen : function(){
var _win = this;
$("ul",_win).tree({ //初始化tree
url:'/item/cat/list', //请求url,响应的数据自动处理
animate:true,
onClick : function(node){
if($(this).tree("isLeaf",node.target)){
// 填写到cid中
_ele.parent().find("[name=cid]").val(node.id);
_ele.next().text(node.text).attr("cid",node.id);
$(_win).window('close');
if(data && data.fun){
data.fun.call(this,node);
}
}
}
});
},
onClose : function(){
$(this).window("destroy");
}
}).window('open');
});
});
},
页面加载后$(function(){})
执行方法
//页面初始化完毕后执行此方法
$(function(){
//创建富文本编辑器
itemAddEditor = TAOTAO.createEditor("#itemAddForm [name=desc]");
//初始化类目选择和图片上传器
TAOTAO.init({fun:function(node){
//根据商品的分类id取商品 的规格模板,生成规格信息。第四天内容。
//TAOTAO.changeItemParam(node, "itemAddForm");
}});
});
初始化tree请求的url:/item/cat/list
参数:初始化tree时只需要把第一级节点展示,子节点异步加载。long id(父节点id)
返回值:json。数据格式如下
[{
"id": 1,
"text": "Node 1",
"state": "closed"
},{
"id": 2,
"text": "Node 2",
"state": "closed"
}]
state:如果节点下有子节点“closed”,如果没有子节点“open”
创建一个pojo来描述tree的节点信息,包含三个属性id、text、state。放到taotao-common工程中
public class EasyUITreeNode implements Serializable{
private long id;
private String text;
private String state;
......
查询的表:tb_item_cat
查询列:Id、name、isparent
查询条件:parentId
1.2 Dao层
tb_item_cat可以使用逆向工程生成的代码
1.3 Service层
参数:long parentId
业务逻辑:
1、根据parentId查询节点列表
2、转换成EasyUITreeNode列表。
3、返回。
返回值:List<EasyUITreeNode>
ItemCatServiceImpl添加方法getItemCatList实现业务逻辑
@Service
public class ItemCatServiceImpl implements ItemCatService {
@Autowired
private TbItemCatMapper itemCatMapper;
@Override
public List<EasyUITreeNode> getItemCatList(long parentId) {
//根据父节点id查询子节点列表
TbItemCatExample example = new TbItemCatExample();
//设置查询条件
Criteria criteria = example.createCriteria();
//设置parentid
criteria.andParentIdEqualTo(parentId);
//执行查询
List<TbItemCat> list = itemCatMapper.selectByExample(example);
//转换成EasyUITreeNode列表
List<EasyUITreeNode> resultList = new ArrayList<>();
for (TbItemCat tbItemCat : list) {
EasyUITreeNode node = new EasyUITreeNode();
node.setId(tbItemCat.getId());
node.setText(tbItemCat.getName());
//如果节点下有子节点“closed”,如果没有子节点“open”
node.setState(tbItemCat.getIsParent()?"closed":"open");
//添加到节点列表
resultList.add(node);
}
return resultList;
}
}
发布服务
<dubbo:service interface="com.taotao.service.ItemCatService" ref="itemCatServiceImpl" timeout="300000"/>
1.4 表现层
引用服务
<dubbo:reference interface="com.taotao.service.ItemCatService" id="itemCatService" />
创建Controller
初始化tree请求的url:/item/cat/list
参数:long id(父节点id)
返回值:json。数据格式 List<EasyUITreeNode>
@Controller
public class ItemCatController {
@Autowired
private ItemCatService itemCatService;
@RequestMapping("/item/cat/list")
@ResponseBody
public List<EasyUITreeNode> getItemCatList(@RequestParam(name="id", defaultValue="0")Long parentId) {
List<EasyUITreeNode> list = itemCatService.getItemCatList(parentId);
return list;
}
}
2 图片上传
2.1 图片服务器使用
服务端两个角色:
Tracker:管理集群,tracker也可以实现集群。每个tracker节点地位平等。收集Storage集群的状态。
Storage:实际保存文件。Storage分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。
2.2 文件上传下载
文件上传
客户端上传文件后存储服务器将文件ID返回给客户端,此文件ID用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
组名:文件上传后所在的storage组名称,在文件上传成功后有storage服务器返回,需要客户端自行保存。
虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path*对应。如果配置了store_path0则是M00,如果配置了store_path1则是M01,以此类推。
数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。
文件下载
2.3 图片上传测试
上传文件步骤:
1、加载配置文件,配置文件中的内容就是tracker服务的地址。配置文件内容:tracker_server=192.168.25.175:22122
2、创建一个TrackerClient对象。直接new一个。
3、使用TrackerClient对象创建连接,获得一个TrackerServer对象。
4、创建一个StorageServer的引用,值为null
5、创建一个StorageClient对象,需要两个参数TrackerServer对象、StorageServer的引用
6、使用StorageClient对象上传图片。
7、返回数组。包含组名和图片的路径。
使用工具类上传时,添加fastdfs_client项目
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.junit.Test;
import com.taotao.utils.FastDFSClient;
public class TestFastDFS {
@Test
public void uploadFile() throws Exception {
//1、向工程中添加jar包
//2、创建一个配置文件。配置tracker服务器地址
//3、加载配置文件
ClientGlobal.init("C:/Java/eclipse_workspace2/taotao-manager-web/src/main/resources/resource/client.conf");
//4、创建一个TrackerClient对象。
TrackerClient trackerClient = new TrackerClient();
//5、使用TrackerClient对象获得trackerserver对象。
TrackerServer trackerServer = trackerClient.getConnection();
//6、创建一个StorageServer的引用null就可以。
StorageServer storageServer = null;
//7、创建一个StorageClient对象。trackerserver、StorageServer两个参数。
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
//8、使用StorageClient对象上传文件。
String[] strings = storageClient.upload_file("C:/Users/C5228335/Desktop/test.jpg", "jpg", null);
for (String string : strings) {
System.out.println(string);
}
}
@Test
public void testFastDfsClient() throws Exception {
FastDFSClient fastDFSClient = new FastDFSClient("C:/Java/eclipse_workspace2/taotao-manager-web/src/main/resources/resource/client.conf");
String string = fastDFSClient.uploadFile("C:/Users/C5228335/Desktop/test.jpg");
System.out.println(string);
}
}
3 图片上传功能实现
3.1 js分析
// 编辑器参数
kingEditorParams : {
//指定上传文件参数名称
filePostName : "uploadFile",
//指定上传文件请求的url。
uploadJson : '/pic/upload',
//上传类型,分别为image、flash、media、file
dir : "image"
},
//图片上传功能
initPicUpload : function(data){
$(".picFileUpload").each(function(i,e){ //遍历所有对象
var _ele = $(e);
_ele.siblings("div.pics").remove();
_ele.after('\
<div class="pics">\
<ul></ul>\
</div>');
//有图片则回显图片
if(data && data.pics){
var imgs = data.pics.split(",");
for(var i in imgs){
if($.trim(imgs[i]).length > 0){
_ele.siblings(".pics").find("ul").append("<li><a href='"+imgs[i]+"' target='_blank'><img src='"+imgs[i]+"' width='80' height='50' /></a></li>");
}
}
}
//给“上传图片按钮”绑定click事件
$(e).click(function(){
var form = $(this).parentsUntil("form").parent("form");
//打开图片上传窗口,富文本编辑器插件,初始化指定上传路径,loadPlugin加载多图片上传插件
KindEditor.editor(TT.kingEditorParams).loadPlugin('multiimage',function(){
var editor = this;
editor.plugin.multiImageDialog({
clickFn : function(urlList) { //上传成功后回调
var imgArray = [];
KindEditor.each(urlList, function(i, data) {
imgArray.push(data.url);
form.find(".pics ul").append("<li><a href='"+data.url+"' target='_blank'><img src='"+data.url+"' width='80' height='50' /></a></li>");
});
form.find("[name=image]").val(imgArray.join(","));
editor.hideDialog();
}
});
});
});
});
},
3.2 功能实现
1 Web项目添加commons-io/fileupload 的jar包,在springmvc.xml中配置多媒体解析器
<!-- 多媒体解析器 -->
<!-- 配置文件上传解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设定默认编码 -->
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 设定文件上传的最大值5MB,5*1024*1024 -->
<property name="maxUploadSize" value="5242880"></property>
</bean>
2 创建PictureController,使用@ResponseBody注解返回java对象,将返回的map对象转成string类型
@Controller
public class PictureController {
@Value("${IMAGE_SERVER_URL}")
private String IMAGE_SERVER_URL;
@RequestMapping("/pic/upload")
@ResponseBody
public String picUpload(MultipartFile uploadFile) {
try {
//接收上传的文件
//取扩展名
String originalFilename = uploadFile.getOriginalFilename();
String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
//上传到图片服务器
FastDFSClient fastDFSClient = new FastDFSClient("classpath:resource/client.conf");
String url = fastDFSClient.uploadFile(uploadFile.getBytes(), extName);
url = IMAGE_SERVER_URL + url;
//响应上传图片的url
Map result = new HashMap<>();
result.put("error", 0);
result.put("url", url);
return JsonUtils.objectToJson(result);
} catch (Exception e) {
e.printStackTrace();
Map result = new HashMap<>();
result.put("error", 1);
result.put("message", "图片上传失败");
return JsonUtils.objectToJson(result);
}
}
}
4 富文本编辑器
常用的富文本编辑器
KindEditor:http://kindeditor.net/
UEditor(百度编辑器): http://ueditor.baidu.com/website/
CKEditor:http://ckeditor.com/
使用方法
在jsp中引入KindEditor的css和js代码。
<script type="text/javascript" charset="utf-8" src="/js/kindeditor-4.1.10/kindeditor-all-min.js"></script>
<script type="text/javascript" charset="utf-8" src="/js/kindeditor-4.1.10/lang/zh_CN.js"></script>
在表单中添加textarea控件
<td>商品描述:</td>
<td>
<textarea style="width:800px;height:300px;visibility:hidden;" name="desc"></textarea>
</td>
初始化富文本编辑器。使用官方提供的方法初始化
var itemAddEditor ;
//页面初始化完毕后执行此方法
$(function(){
//创建富文本编辑器
itemAddEditor = TAOTAO.createEditor("#itemAddForm [name=desc]");
//初始化类目选择和图片上传器
TAOTAO.init({fun:function(node){
//根据商品的分类id取商品 的规格模板,生成规格信息。第四天内容。
//TAOTAO.changeItemParam(node, "itemAddForm");
}});
});
createEditor : function(select){
return KindEditor.create(select, TT.kingEditorParams);
},
取富文本编辑器的内容。表单提交之前,把富文本编辑器的内容同步到textarea控件中。
//同步文本框中的商品描述
itemAddEditor.sync();
5 商品添加功能实现
5.1 功能分析
请求的url:/item/save
参数:表单的数据。可以使用pojo接收表单的数据,要求pojo的属性和input的name属性要一致。用TbItem对象接收表单数据,String接收描述。
返回值:Json数据。应该包含一个status的属性。可以使用TaotaoResult,放到taotao-common中。
业务逻辑:
1、生成商品id,使用IDUtils生成商品id
2、补全TbItem对象的属性
3、向商品表插入数据
4、创建一个TbItemDesc对象
5、补全TbItemDesc的属性
6、向商品描述表插入数据
7、TaotaoResult.ok()
5.2 DAO层
向tb_item, tb_item_desc表中插入数据,可以使用逆向工程。
创建taotaoResult类
import java.io.Serializable;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 淘淘商城自定义响应结构
*/
public class TaotaoResult implements Serializable{
// 定义jackson对象
private static final ObjectMapper MAPPER = new ObjectMapper();
// 响应业务状态
private Integer status;
// 响应消息
private String msg;
// 响应中的数据
private Object data;
public static TaotaoResult build(Integer status, String msg, Object data) {
return new TaotaoResult(status, msg, data);
}
public static TaotaoResult ok(Object data) {
return new TaotaoResult(data);
}
public static TaotaoResult ok() {
return new TaotaoResult(null);
}
public TaotaoResult() {
}
public static TaotaoResult build(Integer status, String msg) {
return new TaotaoResult(status, msg, null);
}
public TaotaoResult(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public TaotaoResult(Object data) {
this.status = 200;
this.msg = "OK";
this.data = data;
}
// set/get method for status/msg/data
/**
* 将json结果集转化为TaotaoResult对象
*
* @param jsonData json数据
* @param clazz TaotaoResult中的object类型
* @return
*/
public static TaotaoResult formatToPojo(String jsonData, Class<?> clazz) {
try {
if (clazz == null) {
return MAPPER.readValue(jsonData, TaotaoResult.class);
}
JsonNode jsonNode = MAPPER.readTree(jsonData);
JsonNode data = jsonNode.get("data");
Object obj = null;
if (clazz != null) {
if (data.isObject()) {
obj = MAPPER.readValue(data.traverse(), clazz);
} else if (data.isTextual()) {
obj = MAPPER.readValue(data.asText(), clazz);
}
}
return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
} catch (Exception e) {
return null;
}
}
/**
* 没有object对象的转化
*
* @param json
* @return
*/
public static TaotaoResult format(String json) {
try {
return MAPPER.readValue(json, TaotaoResult.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Object是集合转化
*
* @param jsonData json数据
* @param clazz 集合中的类型
* @return
*/
public static TaotaoResult formatToList(String jsonData, Class<?> clazz) {
try {
JsonNode jsonNode = MAPPER.readTree(jsonData);
JsonNode data = jsonNode.get("data");
Object obj = null;
if (data.isArray() && data.size() > 0) {
obj = MAPPER.readValue(data.traverse(),
MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
}
return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
} catch (Exception e) {
return null;
}
}
}
5.3 Service层
ItemServiceImpl类添加addItem方法,返回值TaotaoResult
@Override
public TaotaoResult addItem(TbItem item, String desc) {
//生成商品id
long itemId = IDUtils.genItemId();
//补全item的属性
item.setId(itemId);
//商品状态,1-正常,2-下架,3-删除
item.setStatus((byte) 1);
item.setCreated(new Date());
item.setUpdated(new Date());
//向商品表插入数据
itemMapper.insert(item);
//创建一个商品描述表对应的pojo
TbItemDesc itemDesc = new TbItemDesc();
//补全pojo的属性
itemDesc.setItemId(itemId);
itemDesc.setItemDesc(desc);
itemDesc.setUpdated(new Date());
itemDesc.setCreated(new Date());
//向商品描述表插入数据
itemDescMapper.insert(itemDesc);
//返回结果
return TaotaoResult.ok();
}
通过<dubbo:service interface="com.taotao.service.ItemService" ref="itemServiceImpl" timeout="300000"/>
发布服务
5.4 表现层
通过<dubbo:reference interface="com.taotao.service.ItemService" id="itemService" />
引用服务
请求的url:/item/save,ItemController中添加saveItem方法
@RequestMapping(value="/item/save", method=RequestMethod.POST)
@ResponseBody
public TaotaoResult addItem(TbItem item, String desc) {
TaotaoResult result = itemService.addItem(item, desc);
return result;
}
item-add.js中,提交表单,传递的数据被TBItem与String desc描述接收
//ajax的post方式提交表单
//$("#itemAddForm").serialize()将表单序列号为key-value形式的字符串
$.post("/item/save",$("#itemAddForm").serialize(), function(data){
if(data.status == 200){
$.messager.alert('提示','新增商品成功!');
}
});