电商项目日总结(第十二天网页静态化技术FreeMarker)

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">&lt;</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">&gt;</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="点击取消选择">&nbsp;</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="点击取消选择">&nbsp;</span></a></dd>
                </#list>
            </dl>
        </#list>
</div>

这样点击每个规格选项就会动态的改变页面的内容

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值