1.FreeMarker的介绍
FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出,它不仅可以用作表现层的实现技术,而且还可以用于生成 XML,JSP 或 Java 等.本项目将有模板生成的静态页面放到nginx服务器(并发量大,静态資源)上
2.在pyg_parent父模块下新建pyg_page_interface模块和pyg_page_service模块
pyg_page_service模块的pom文件
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.fighting</groupId>
<artifactId>pyg_page_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fighting</groupId>
<artifactId>pyg_dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId
<version>2.2</version>
<configuration>
<!-- 指定端口 -->
<port>9005</port>
<!-- 请求路径 -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
pyg_page_service目录结构
将原始静态页面item.html(也就是想要当作模板生成其他商品详情的页面)拷贝到/WEB-INF/ftl目录下,并将后缀改为ftl格式的(FreeMarker模板格式的),接着可以把模板的头部和尾部进行拆分,并在item.ftl中做引入
item.ftl中:
然后模板准备工作完成后在ItemPageServiceImpl.java的中写生成静态页面的代码
@Service//别忘了把这个对象放到Dubbo的注册中心中
public class ItemPageServiceImpl implements ItemPageService {
@Value("${pagedir}")
private String pagedir;
@Autowired
private FreeMarkerConfig freeMarkerConfig;
@Autowired
private TbGoodsMapper tbGoodsMapper;
@Autowired
private TbGoodsDescMapper tbGoodsDescMapper;
@Autowired
private TbItemCatMapper tbItemCatMapper;
@Autowired
private TbItemMapper tbItemMapper;
@Override
public void createItemHtml(Long goodsId) {
try {
//通过FreeMarkerConfigurer直接获取Configuration
Configuration configuration = freeMarkerConfig.getConfiguration();
//再通过Configuration获取Template模板对象
Template template = configuration.getTemplate("item.ftl");
//需要声明FileWriter
FileWriter writer = new FileWriter(new File(pagedir + goodsId + ".html"));
//准备模板数据,根据goodsId获取goods和goodsDesc对象
TbGoods goods = tbGoodsMapper.selectByPrimaryKey(goodsId);
TbGoodsDesc goodsDesc = tbGoodsDescMapper.selectByPrimaryKey(goodsId);
//准备三级标题的数据
String category1 = tbItemCatMapper.selectByPrimaryKey(goods.getCategory1Id()).getName();
String category2 = tbItemCatMapper.selectByPrimaryKey(goods.getCategory2Id()).getName();
String category3 = tbItemCatMapper.selectByPrimaryKey(goods.getCategory3Id()).getName();
//准备goodsId对应的所有SKU商品的集合
TbItemExample example = new TbItemExample();
example.createCriteria().andGoodsIdEqualTo(goodsId);
//将所有SKU商品按照是否默认条件排序,把默认的商品排在集合的第一位
example.setOrderByClause("is_default DESC");
List<TbItem> itemList = tbItemMapper.selectByExample(example);
//封装了商品及商品详情到数据中
Map dataModel=new HashMap();
dataModel.put("goods",goods);
dataModel.put("goodsDesc",goodsDesc);
//封装了三级标题的数据
dataModel.put("category1",category1);
dataModel.put("category2",category2);
dataModel.put("category3",category3);
//封装goodsId对应的所有SKU商品的数据
dataModel.put("itemList",itemList);
//将模板生成页面
template.process(dataModel,writer);
//关闭writer(别忘了关,否则删除不掉生成的页面)
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.实现了商家对商品进行上架处理时才生成静态页面(利用FreeMarker技术)
所以在上架模块中在商品上架(商品状态改为"5")时才动态地生成静态页面
中:
@RequestMapping("/updateStatus")
public Result updateStatus(Long[] ids, String status) {
try {
goodsService.updateStatus(ids, status);
//如果商品上架或者是下架,就去把数据添加到solr中或者从solr中移除
if ("5".equals(status)) {
//就是上架,就要把新的SKU数据添加到solr中
itemSearchService.importItems(ids);
//如果商品上架成功,就根据FreeMarker的模板根据id创建指定的商品页面,一个id一个网页
//一个Good一个网页
for (Long id : ids) {
//生成静态页面
itemPageService.createItemHtml(id);
}
}
if ("6".equals(status)) {
//就是下架,将solr中对应的SKU数据删除
itemSearchService.removeItems(ids);
}
return new Result(true, "修改商品状态成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "修改商品状态失败");
}
}
这样商家点击商品上架之后,在E:\item目录下就会生成一个静态页面(别忘了在这个文件夹下放别的静态資源)
4.实现了使数据展示在静态页面上:修改模板
${}里名称必须和dataMode这个Map集合中的key对应,否则取不到,${}是FreeMarker中的语法,别和el表达式混淆
面包屑商品三级分类显示:
<li>
<a href="#">${category1}</a>
</li>
<li>
<a href="#">${category2}</a>
</li>
<li>
<a href="#">${category3}</a>
</li>
实现商品图片展示列表的展示:
item.ftl模块
<#--将图片列表itemImages字符串转成Json对象-->
<#assign images=goodsDesc.itemImages?eval>
<!--放大镜效果-->
<div class="zoom">
<!--默认第一个预览-->
<div id="preview" class="spec-preview">
<#if (images?size>0)>
<span class="jqzoom"><img jqimg="${images[0].url}" src="${images[0].url}"/></span>
</#if>
</div>
<!--下方的缩略图-->
<div class="spec-scroll">
<a class="prev"><</a>
<!--左右按钮-->
<div class="items">
<ul>
<#--循环images-->
<#list images as image>
<li><img src="${image.url}" bimg="${image.url}" onmousemove="preview(this)"/></li>
</#list>
</ul>
</div>
<a class="next">></a>
</div>
</div>
通过AngularJs完成购买数量的加减
在生成静态页面的文件夹下的js/controller目录下新建itemController.js文件
itemController.js中
app.controller('itemController', function ($scope) {
$scope.num = 1;
//对商品数量的操作
$scope.addNum = function (x) {
$scope.num += x;
if($scope.num<1){
$scope.num=1;
}
}
//记录用户选择的规格
$scope.specificationItems={};
//将用户点击添加规格事件
$scope.selectSpecification=function (key, value) {
$scope.specificationItems[key]=value;
//方法调用读取sku
searchSku();
}
//判断规格选项是否被用户选中,选中返回true,没选中返回false
$scope.isSelected=function (key,value) {
if($scope.specificationItems[key]==value){
return true;
}else {
return false;
}
}
//页面加载默认的SKU
$scope.sku={};//主要是为了显示标题和价格
$scope.loadSKU=function () {
//默认选择第一个SKU,也就是is_default为1的
$scope.sku=skuList[0];
//深克隆,为了让默认的SKU与页面上的$scope.specificationItems解绑,没有关联
$scope.specificationItems=JSON.parse(JSON.stringify($scope.sku.spec));
}
//循环所有skuList,如果有和当前页中用户选择的选项一样的,就赋值给sku
searchSku=function () {
for (var i = 0; i < skuList.length;i++){
if(matchObject(skuList[i].spec,$scope.specificationItems)){
$scope.sku=skuList[i];
return;
}
}
}
//匹配两个对象是否相等
matchObject=function (map1, map2) {
for (var k in map1){
if(map1[k]!=map2[k]){
return false;
}
}
//循环第二遍是防止map2的规格选项比map1的规格选项多的部分
for (var k in map2){
if(map1[k]!=map2[k]){
return false;
}
}
return true;
}
})
item.ftl模板中引入js文件
<#--模版商品页面引入js文件-->
<script type="text/javascript" src="plugins/angularjs/angular.min.js"></script>
<script type="text/javascript" src="js/base.js"></script>
<script type="text/javascript" src="js/controller/itemController.js"></script>
body标签
<body ng-app="pinyougou" ng-controller="itemController" ng-init="loadSKU()">
商品数量部分
<#--添加和减少商品数量-->
<input autocomplete="off" type="text" value="{{num}}" minnum="1" class="itxt"/>
<a ng-click="addNum(1)" href="javascript:void(0)" class="increment plus">+</a>
<a ng-click="addNum(-1)" href="javascript:void(0)" class="increment mins">-</a>
展示网站下方数据,如图:
item.ftl模板部分
head标签内
<#--将扩展属性列表customAttributeItems-->
<#assign customAttributeList=goodsDesc.customAttributeItems?eval>
数据填充部分
<div id="one" class="tab-pane active">
<ul class="goods-intro unstyled">
<#--显示扩展属性-->
<#list customAttributeList as attr>
<#if attr.value??>
<li>${attr.text}:${attr.value}</li>
</#if>
</#list>
</ul>
${goodsDesc.introduction}
</div>
<div id="two" class="tab-pane">
<p>${goodsDesc.packageList}</p>
</div>
<div id="three" class="tab-pane">
<p>${goodsDesc.saleService}</p>
</div>
注意:只修改模板不用重启tomcat服务,只需要对商品进行上架操作后刷新页面即可
显示商品的规格和选项:
item.ftl模板:
head标签内
<#--将所有的规格和规格选项specificationItems字符串转成Json对象-->
<#assign specificationList=goodsDesc.specificationItems?eval>
内容显示部分
<div id="specification" class="summary-wrap clearfix">
<#--循环规格和规格选项列表-->
<#list specificationList as spec>
<dl>
<dt>
<div class="fl title">
<i>${spec.attributeName}</i>
</div>
</dt>
<#list spec.attributeValue as specValue>
<#--给每一规格选项绑定isSelected和selectSpecification方法-->
<dd><a class="{{isSelected('${spec.attributeName}','${specValue}')?'selected':''}}"
ng-click="selectSpecification('${spec.attributeName}','${specValue}')">${specValue}
<span title="点击取消选择"> </span></a></dd>
</#list>
</dl>
</#list>
</div>
实现了点击不同的规格对应的标题和价格也发生变化,而且页面一加载就展示该商品对应的默认的SKU商品
item.ftl模板:
head标签内
<#--将itemList中的对象重新封装json数据给skuList集合,只要每个item的id,title,price,spec-->
<script type="text/javascript">
var skuList = [
<#list itemList as item>
{
"id": "${item.id?c}",
"title": "${item.title!''}",
"price": "${item.price?c}",
"spec": ${item.spec}
},
</#list>
]
</script>
主标题部分和价格部分
<div class="sku-name">
<#--freeMarker获取map中key对应的value值的表达式-->
<h4>{{sku.title}}</h4>
</div>
<div class="news"><span>${goods.caption}</span></div>
<div class="summary">
<div class="summary-wrap">
<div class="fl title">
<i>价 格</i>
</div>
<div class="fl price">
<i>¥</i>
<em>{{sku.price}}</em>
<span>降价通知</span>
</div>
内容显示部分
<div id="specification" class="summary-wrap clearfix">
<#--循环规格和规格选项列表-->
<#list specificationList as spec>
<dl>
<dt>
<div class="fl title">
<i>${spec.attributeName}</i>
</div>
</dt>
<#list spec.attributeValue as specValue>
<#--给每一规格选项绑定isSelected和selectSpecification方法-->
<dd><a class="{{isSelected('${spec.attributeName}','${specValue}')?'selected':''}}"
ng-click="selectSpecification('${spec.attributeName}','${specValue}')">${specValue}
<span title="点击取消选择"> </span></a></dd>
</#list>
</dl>
</#list>
</div>
这样点击每个规格选项就会动态的改变页面的内容