电商项目日总结(第七天)
商品新增之商品图片和规格的新增
1.完成了上传图片文件到FastDFS服务器上
准备工作:将老师给的带有FastDFS的Linux镜像安装到了虚拟机上
在pyg_parent父模块下建立一个子模块pyg_common(工具类模块),在这个模块中新建一个util目录,写一个文件上传的工具类到上面:
因为在商家页面添加商品时需要上传图片,所以在pyg_shop_web的pom文件中添加pyg_common的依赖,在springmvc.xml中配置多媒体解析器,并在resources目录下的config的目录下拷贝fdfs_client.conf配置文件,并在application.properties文件下添加相应内容
代码部分:
创建一个UploadController.java
@RestController
public class UploadController {
//获取文件服务器地址
@Value("${FILE_SERVER_URL}")
private String file_url;
@RequestMapping("/upload")
public Result upload(MultipartFile multipartFile) {
//1.取文件扩展名
String originalFilename = multipartFile.getOriginalFilename();
System.out.println("============================");
System.out.println("originalFilename:"+originalFilename);
String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
System.out.println("exName:"+extName);
//2.创建FastDFS客户端(用我们的工具类)
FastDFSClient fastDFSClient = null;
try {
fastDFSClient = new FastDFSClient("classpath:config/fdfs_client.conf");
//3.客户端上传图片后,将获取的路径拼接全路径后
String path = fastDFSClient.uploadFile(multipartFile.getBytes(), extName);
System.out.println("path:"+path);
String url = file_url + path;
//4.进行Result返回,success的时候返回图片路径给页面
return new Result(true,url);
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"文件上传失败");
}
}
}
创建一个uploadService.js文件
app.service('uploadService',function ($http) {
this.uploadFile = function() {
var formData = new FormData(); //上传文件的数据模型
formData.append("multipartFile", file.files[0]); //文件上传框的id必须是和append的第一个参数一致
return $http({
method : 'post',
url : "../upload.do",
data : formData,
headers : {'Content-Type' : undefined}, //上传文件必须是这个类型,默认text/plain
transformRequest : angular.identity //对整个表单进行二进制序列化
});
}
})
goodsController.js中,别忘了参数部分引入uploadService
$scope.entity_image = {};
//跨服务器上传图片
$scope.uploadFile = function () {
uploadService.uploadFile().success(
function (response) {
if (response.success) {
//数据库中的数据
//[{"color":"黄色","url":"http://192.168.25.133/group1/M00/00/00/wKgZhVrAnfuABpS9AABtBwqnSTg963.jpg"},
// {"color":"金黄色","url":"http://192.168.25.133/group1/M00/00/00/wKgZhVrFXJWAZ7DUAACQQzLtwW0606.jpg"}]
$scope.entity_image.url = response.message;
} else {
alert(response.message);
}
}
)
}
//将图片保存到entity.tbGoodsDesc这个对象的itemImages的属性中去,前提是把这个对象初始化(在上面)
$scope.addImage = function () {
$scope.entity.tbGoodsDesc.itemImages.push($scope.entity_image)
}
//将图片从entity.tbGoodsDesc这个对象的itemImages的属性中删除出去
$scope.deleImage = function ($index) {
$scope.entity.tbGoodsDesc.itemImages.splice($index, 1);
}
goods_edit.html页面部分,绑定上图goodsController.js中对应的方法
这样点击上传按钮,先去找tracker,然后tracker会告诉你哪个storage服务器空闲,可以存放文件,然后你再去访问对应的storage,并上传图片,文件就会上传到图片服务器上(fastdfs跨服务器上传文件成功后会返回一个id,当你在去访问这个图片的时候就拿着id去找,把id给tracker让他去找在storage里具体哪个服务器里)
2.实现了商品对应模板的所有规格和规格选项的展示
goodsController.js的监听模板id变化的$watch方法中调用typeTemplateService中的findSpecList方法
//根据模板id的变化去模板表里找对应的所有品牌(type_template表里所有的brandIds)
$scope.$watch('entity.tbGoods.typeTemplateId', function (newValue, oldValue) {
typeTemplateService.findOne(newValue).success(
function (response) {
$scope.brandList = JSON.parse(response.brandIds);
//根据模板id的变化去模板表里找对应的所有扩展属性(type_template表里所有的customAttributeItems)
$scope.entity.tbGoodsDesc.customAttributeItems = JSON.parse(response.customAttributeItems)
}
)
//根据模板id的变化去规格表里去找对应的所有规格和规格对应的所有选项
typeTemplateService.findSpecList(newValue).success(
function (response) {
//将返回来的所有规格和规格对应的所有选项构成的list集合
$scope.specList = response;
}
)
//别忘了初始化entity这个对象中的tbItemList这个集合属性(最后页面上就多显示了除了规格选项后面的部分,价格,库存,是否启用,是否默认这四个)
$scope.entity.tbItemList = [];
})
typeTemplateService.js中:
//查找模板中所有的规格findSpecList
this.findSpecList=function (id) {
return $http.get('../typeTemplate/findSpecList.do?id='+id);
}
TypeTemplateController.java(略)
TyepTemplateSerivceImpl.java中:
//查找模板id对应下的所有规格和规格对应的所有规格选项(因要要根据模板id查对应所有规格,所以,就在模板的
//实现接口下写逻辑
@Override
public List<Map> findSpecList(Long id) {
TbTypeTemplate tbTypeTemplate = typeTemplateMapper.selectByPrimaryKey(id);
//把每一个规格封装成了map类型,所有的规格构成了list集合
List<Map> maps = JSON.parseArray(tbTypeTemplate.getSpecIds(), Map.class);
//遍历每一个规格,并获得规格下面所有的规格选项
for (Map map : maps) {
TbSpecificationOptionExample example = new TbSpecificationOptionExample();
example.createCriteria().andSpecIdEqualTo(new Long((Integer)map.get("id")));//这里类型转换容易出现错误
List<TbSpecificationOption> options = specificationOptionMapper.selectByExample(example);
map.put("options",options);
}
return maps;
}
goods_edit.html页面:
<!--遍历每一个规格-->
<div ng-repeat="specification in specList">
<div class="col-md-2 title">{{specification.text}}</div>
<div class="col-md-10 data">
<!--遍历每一个规格选项-->
<span ng-repeat="option in specification.options">
<input ng-click="updateSpecAttribute($event,specification.text,option.optionName);createItemList()" type="checkbox" >{{option.optionName}}
</span>
</div>
</div>
3.实现了点击规格选项页面下方能动态的显示SKU商品的列表(全是前端部分的逻辑,不涉及后端)
goods_edit.html页面:给规格选项绑定点击事件
<!--遍历每一个规格-->
<div ng-repeat="specification in specList">
<div class="col-md-2 title">{{specification.text}}</div>
<div class="col-md-10 data">
<!--遍历每一个规格选项-->
<span ng-repeat="option in specification.options">
<input ng-click="updateSpecAttribute($event,specification.text,option.optionName);createItemList()" type="checkbox" >{{option.optionName}}
</span>
</div>
</div>
goodsController.js中:
//////////////////////////////以下两个方法实现点击规格选项能添加到$scope.entity.tbGoodsDesc.specificationItems这个集合中去/////////////////////
//提供一个方法,判断集合中是否存在key的方法
searchObjectByKey = function (list, key, value) {
//报错length is not defind 原因是找不到list对象,而list对象就是$scope.entity.tbGoodsDesc.specificationItems,因为没有给他初始化
for (var i = 0; i < list.length; i++) {
if (list[i][key] == value) {
return list[i]
}
}
return null;
}
//增加对当前规格集合增加内容的方法,需要判断是新增还是追加
$scope.updateSpecAttribute = function ($event, name, value) {
//先查询当前的集合中是否已经有当前的name了
var object = searchObjectByKey($scope.entity.tbGoodsDesc.specificationItems, 'attributeName', name)
if (object != null) {
//如果点击的规格选项被勾选,那么就进行追加
if ($event.target.checked) {
object.attributeValue.push(value);
} else {//如果点击为取消勾选
object.attributeValue.splice(object.attributeValue.indexOf(value), 1);//移出规格选项
//如果把该规格的规格选项都移出了,规格选项长度为0的化,那么将词条记录移除
if (object.attributeValue.length == 0) {
$scope.entity.tbGoodsDesc.specificationItems.splice(
$scope.entity.tbGoodsDesc.specificationItems.indexOf(object), 1)
}
}
} else {
$scope.entity.tbGoodsDesc.specificationItems.push({
"attributeName": name,
"attributeValue": [value]
});
}
}
//////////////////////////////以下两个方法实现sku列表的显示/////////////////////////////////////////////////////////////////////////////
//初始化Goods类中的List<TbItem>集合
$scope.entity.tbItemList = [{spec: {}, price: 0, num: 99999, status: '0', isDefault: '0'}];
//循环$scope.entity.goodsDesc.specificationItems的内容,attributeName和attributeValue
//再通过addColumn = function(list, columnName, conlumnValues)深克隆每一行 {"spec":{"网络":"移动3G"}的内容
$scope.createItemList = function () {
$scope.entity.tbItemList = [{spec: {}, price: 0, num: 99999, status: '0', isDefault: '0'}];// 初始
var items = $scope.entity.tbGoodsDesc.specificationItems;
for (var i = 0; i < items.length; i++) {
$scope.entity.tbItemList = addColumn($scope.entity.tbItemList,
items[i].attributeName, items[i].attributeValue);
}
}
// 添加列值:参数1是创建的itemList集合,参数2是规格名称,参数3是规格值的集合
addColumn = function (list, columnName, conlumnValues) {
var newList = [];// 新的集合
for (var i = 0; i < list.length; i++) {
var oldRow = list[i]; //获取出当前行的内容 {spec:{},price:'0.01',num:'99999',status:'0',isDefault:'0'}
for (var j = 0; j < conlumnValues.length; j++) {//循环attributeValue数组的内容
var newRow = JSON.parse(JSON.stringify(oldRow));// 深克隆,根据attributeValue的数量
newRow.spec[columnName] = conlumnValues[j];//{spec:{"网络制式":"移动4G"},price:'0.01',num:'99999',status:'0',isDefault:'0'}
newList.push(newRow);
}
}
return newList;
}
goods_edit.html页面:SKU商品列表的动态展示
<table class="table table-bordered table-striped table-hover dataTable">
<thead>
<tr>
<!--三层循环之第一层-->
<th ng-repeat="spec in entity.tbGoodsDesc.specificationItems" class="sorting">{{spec.attributeName}}</th>
<th class="sorting">价格</th>
<th class="sorting">库存</th>
<th class="sorting">是否启用</th>
<th class="sorting">是否默认</th>
</tr>
</thead>
<tbody>
<!--三层循环之第二层-->
<tr ng-repeat="item in entity.tbItemList">
<!--三层循环之第三层-->
<td ng-repeat="specification in entity.tbGoodsDesc.specificationItems">
{{item.spec[specification.attributeName]}}
</td>
<td>
<input ng-model="item.price" class="form-control" placeholder="价格">
</td>
<td>
<input ng-model="item.num" class="form-control" placeholder="库存数量">
</td>
<td>
<input ng-model="item.status" ng-true-value="1" ng-false-value="0" type="checkbox" >
</td>
<td>
<input ng-model="item.isDefault" ng-true-value="1" ng-false-value="0" type="checkbox" >
</td>
</tr>
</tbody>
</table>