今日目标:
(1)完成商品分类功能
(2)了解电商概念:SPU、SKU
(3)掌握富文本编辑器的使用
(4)掌握分布式文件服务器:FastDFS
(5)掌握 angular js 图片上传
目录
1、商品分类
1.1 商品分类列表展示
1.2 商品分类面包屑导航
1.3 新增分类
1.4 修改商品分类
2、商品录入[1]
2.1 商品基本信息的录入
2.2 商品介绍录入
2.3 商品图片上传
(1)后端:sellergoods-interface --> ItemCatService(服务接口层)
/**
* 根据上级ID 获取商品分类列表
*
* @param parentId
* @return java.util.List
*/
List findByParentId(Long parentId);
(2)后端:sellergoods-service --> ItemCatServiceImpl(服务实现)
@Override
public List findByParentId(Long parentId) {
// 封装查询条件
TbItemCatExample example = new TbItemCatExample();
example.createCriteria().andParentIdEqualTo(parentId);
// 执行查询
return itemCatMapper.selectByExample(example);
}
(3)后端:manager-web --> ItemCatController(控制层)
/**
* 根据上级ID,查询商品分类列表
*
* @param parentId 上级id
* @return java.util.List
*/
@RequestMapping("/findByParentId")
public List findByParentId(Long parentId) {
return itemCatService.findByParentId(parentId);
}
(4)前端:编写itemCatService.js,新增方法
//查询上级id的商品分类列表
this.findByParentId = function (parentId) {
return $http.get('../itemCat/findByParentId.do?parentId=' + parentId);
}
(5)前端:编写itemCatController.js,新增方法
// 根据上级ID 查询商品分类列表
$scope.findByParentId = function (parentId) {
itemCatService.findByParentId(parentId).success(
function (rtn) {
$scope.list = rtn;
}
);
}
(6)前端:修改页面,引入JS文件
(7)前端:引入angular js 指令,初始化执行查询列表方法
(8)前端:循环遍历列表,显示数据
(9)前端:为查询下一级按钮绑定单击事件
(10)效果:
(1)编写itemCatController.js
//当前菜单级别(默认为1):
// 顶级菜单:1 二级菜单:2 三级菜单:3
$scope.grade = 1;
$scope.setGrade = function (value) {
$scope.grade = value;
}
$scope.selectList = function (p_entity) {
if($scope.grade == 1) {
$scope.entity_1 = null;
$scope.entity_2 = null;
}
if($scope.grade == 2) {
$scope.entity_1 = p_entity;
$scope.entity_2 = null;
}
if($scope.grade == 3) {
$scope.entity_2 = p_entity;
}
$scope.findByParentId(p_entity.id);
}
(2)编写页面,给面包屑导航,绑定单击事件
(3)修改查询下级按钮的单击事件调用方法为:selectList
(4)效果:
(5)当菜单级别为三时,不显示查询下级,因为没有意义,已经没有下级菜单
注意:需要使用span,将被控制的元素包裹起来
(6)效果:
(1)前端:在itemCatController中定义一个变量,用于记录当前上级分类的ID
//记录当前上级分类ID,默认为0
$scope.parentId = 0;
(2)前端:在调用查询下级方法时,记录当前上级分类ID
(3)前端:新增时,设置其parentId的值为当前分类上级ID
(4)前端:页面显示当前上级分类
(5)类型模板下拉菜单
参考之前下拉菜单
(1)为修改按钮绑定单击事件
1.5 删除商品分类
(1)为复选框绑定单击事件
(2)为删除按钮绑定单击事件
(1)后端:编写组合实体类(Goods),用于存放前端传递的数据
package com.pinyougou.pojogroup;
import com.pinyougou.pojo.TbGoods;
import com.pinyougou.pojo.TbGoodsDesc;
import com.pinyougou.pojo.TbItem;
import java.io.Serializable;
import java.util.List;
/**
* 商品组合实体类
* Author xushuai
* Description
*/
public class Goods implements Serializable {
/** 商品基本信息 */
private TbGoods goods;
/** 商品扩展信息 */
private TbGoodsDesc goodsDesc;
/** SKU列表 */
private List itemList;
public TbGoods getGoods() {
return goods;
}
public void setGoods(TbGoods goods) {
this.goods = goods;
}
public TbGoodsDesc getGoodsDesc() {
return goodsDesc;
}
public void setGoodsDesc(TbGoodsDesc goodsDesc) {
this.goodsDesc = goodsDesc;
}
public List getItemList() {
return itemList;
}
public void setItemList(List itemList) {
this.itemList = itemList;
}
}
(2)后端:改造GoodsService的add方法(sellergoods-interface)
/**
* 增加
*/
void add(Goods goods);
(3)后端:改造GoodsServiceImpl中add方法的实现(sellergoods-service)
/**
* 增加
*/
@Override
public void add(Goods goods) {
// 补全商品基本信息
TbGoods tbGoods = goods.getGoods();
tbGoods.setAuditStatus(TbGoods.STATUS_CREATE);
// 保存商品基本信息
goodsMapper.insert(tbGoods);
// 补全商品扩展信息数据
TbGoodsDesc tbGoodsDesc = goods.getGoodsDesc();
tbGoodsDesc.setGoodsId(tbGoods.getId());
// 保存商品扩展信息
goodsDescMapper.insert(tbGoodsDesc);
}
注意:需要注入GoodsDescMapper
(4)后端:改造GoodsController中的add方法
/**
* 增加
* @param goods
* @return
*/
@RequestMapping("/add")
public Result add(@RequestBody Goods goods){
try {
//设置商家id
String sellerId = SecurityContextHolder.getContext().getAuthentication().getName();
goods.getGoods().setSellerId(sellerId);
//保存
goodsService.add(goods);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
(5)前端:改造goodsController.js中的save方法,将其修改为add方法
//增加商品
$scope.add = function () {
goodsService.add($scope.entity).success(
function (response) {
if (response.success) {
// 显示新增成功
alert("新增成功");
$scope.entity={};
} else {
alert(response.message);
}
}
);
}
(6)前端:为输入框绑定变量
(7)前端:为保存按钮绑定单击事件
(1)修改goodsController.js的add方法
(1)后端:引入FastDFS依赖,以及FastDFS的工具类(pinyougou-common)
package util;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
/**
* FastDFS 工具类
*/
public class FastDFSClient {
private TrackerClient trackerClient = null;
private TrackerServer trackerServer = null;
private StorageServer storageServer = null;
private StorageClient1 storageClient = null;
public FastDFSClient(String conf) throws Exception {
if (conf.contains("classpath:")) {
conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
}
ClientGlobal.init(conf);
trackerClient = new TrackerClient();
trackerServer = trackerClient.getConnection();
storageServer = null;
storageClient = new StorageClient1(trackerServer, storageServer);
}
/**
* 上传文件方法
* Title: uploadFile
* Description:
* @param fileName 文件全路径
* @param extName 文件扩展名,不包含(.)
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
String result = storageClient.upload_file1(fileName, extName, metas);
return result;
}
public String uploadFile(String fileName) throws Exception {
return uploadFile(fileName, null, null);
}
public String uploadFile(String fileName, String extName) throws Exception {
return uploadFile(fileName, extName, null);
}
/**
* 上传文件方法
* Title: uploadFile
* Description:
* @param fileContent 文件的内容,字节数组
* @param extName 文件扩展名
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception {
String result = storageClient.upload_file1(fileContent, extName, metas);
return result;
}
public String uploadFile(byte[] fileContent) throws Exception {
return uploadFile(fileContent, null, null);
}
public String uploadFile(byte[] fileContent, String extName) throws Exception {
return uploadFile(fileContent, extName, null);
}
}
(2)后端:引入FastDFS的配置文件,并配置访问图片时的URL前缀(shop-web)
(3)后端:配置 Spring MVC 多媒体解析器(shop-web),配置在springmvc.xml中
(4)后端:书写上传控制层(shop-web)
package com.pinyougou.shop.controller;
import entity.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import util.FastDFSClient;
/**
* 文件上传控制器
* Author xushuai
* Description
*/
@RestController
public class UploadController {
@Value("${FILE_SERVER_URL}")
private String FILE_SERVER_URL;
/**
* 文件上传
*
* @param file 上传的文件
* @return entity.Result
*/
@RequestMapping("/upload")
public Result upload(MultipartFile file) {
try {
// 创建FastDFS工具类对象
FastDFSClient dfsClient = new FastDFSClient("classpath:config/fdfs_client.conf");
// 获取扩展名
String originalFilename = file.getOriginalFilename();
String exName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
// 执行上传
String file_id = dfsClient.uploadFile(file.getBytes(), exName);
file_id = FILE_SERVER_URL + file_id;
return Result.success(file_id);
}catch (Exception e){
e.printStackTrace();
return Result.error("上传失败");
}
}
}
(5)前端:编写uploadService.js 文件
app.service('uploadService', function ($http) {
// 上传文件
this.uploadFile = function () {
// FormData对象,对数据进行二进制序列化
var formData = new FormData();
// 将文件转换为二进制数据进行传输
formData.append('file', file.files[0]);
/*
必须设置Content-Type为undefined
transformRequest:angular.identity 对表单进行序列化
*/
return $http({
url:'../upload.do',
method:'post',
data:formData,
headers:{'Content-Type':undefined},
transformRequest:angular.identity
});
}
});
(6)前端:编写goodsController.js,新增方法
// 上传文件
$scope.uploadFile = function () {
uploadService.uploadFile().success(
function (rtn) {
if(rtn.success) {
$scope.image_entity.url = rtn.message;
} else {
alert(rtn.message);
}
}
);
}
(7)为输入框绑定变量,并给上传按钮绑定单击事件
(8)效果:
(9)为显示图片表格绑定变量,循环展示图片信息
(10)编写goodsController.js,新增方法
// 初始化
$scope.entity={goodsDesc:{itemImages:[]}}
// image_entity到表格中
$scope.add_image_entity = function () {
// 将文件信息放入item
$scope.entity.goodsDesc.itemImages.push($scope.image_entity);
}
(11)每次单击新建按钮时,清空数据
(12)为保存按钮绑定单击事件
(13)效果:
(14)编写goodsController.js,新增方法
// 删除表格中展示的图片
$scope.remove_image_entity = function (index) {
$scope.entity.goodsDesc.itemImages.splice(index,1);
}
(15)为删除按钮,绑定单击事件