前端模板doT.js简介与安装
doT.js是什么?
是一个前端模板引擎。模板用于提取公共部分,变化的部分通过传入的数据,进行替换。
为什么使用前端模板引擎?
手动拼接字符串-复杂
手动拼接字符串页面响应速度比较慢,js模板加快页面响应速度
手动拼接字符串不安全
使用doT.js能够提高开发速度
为什么选择doT.js模板?
如何使用?
引入doT.js(doT.js不依赖于jQuery往往和jQuery结合,提高开发速度)
编写模板
调用模板
Demo.html
<!DOCTYPE html>
<head>
<!-- 1.引入doT.js -->
<scripttype="text/javascript"src="js/doT.min.js"></script>
<scripttype="text/javascript"src="js/jquery-1.8.2.min.js"></script>
</head>
<body>
<!-- content_01 -->
<ulid="content_01">
</ul>
<!-- content_02 -->
<table>
<thead>
<tr>
<td>编号</td>
<td>姓名</td>
</tr>
</thead>
<tbodyid="content_02"></tbody>
</table>
<!-- 2.编写模块 -->
<scripttype="template"id="temp_01">
{{ for(var i=0;i
{{ } }}
</script>
<scripttype="template"id="temp_02">
{{ for(var i=0;i
{{=it[i].id}}
{{=it[i].name}}
{{ } }}
</script>
<scripttype="text/javascript">
//模拟通过ajax从后台得到的数据
vardata=["1","2","3"];
// 获取模板
vartemp=doT.template($("#temp_01").text());
// 填充数据
$("#content_01").html(temp(data));
//模拟通过ajax从后台得到的数据
vardata2=[{"id":1,"name":"zhangsan"},{"id":2,"name":"lisi"},{"id":3,"name":"wangwu"}];
// 获取模板
vartemp2=doT.template($("#temp_02").text());
// 填充数据
$("#content_02").html(temp2(data2));
</script>
</body>
<html>
测试结果
商品列表-分页查询
Service服务层
GoodsCategoryService.java
/**
* 商品分类-查询所有商品分类
* @return
*/
List<GoodsCategory>selectCategoryList();
GoodsCategoryServiceImpl.java
/**
* 商品分类-查询所有商品分类
* @return
*/
@Override
publicList<GoodsCategory>selectCategoryList() {
returngoodsCategoryMapper.selectByExample(newGoodsCategoryExample());
}
BrandService.java
/**
* 查询所有商品品牌
* @return
*/
List<Brand>selectBrandList();
BrandServiceImpl.java
/**
* 查询所有商品品牌
* @return
*/
@Override
publicList<Brand>selectBrandList() {
returnbrandMapper.selectByExample(newBrandExample());
}
Controller控制层
GoodsController.java
/**
* 商品-列表-页面跳转
*
* @return
*/
@RequestMapping("list")
publicStringGoodsList(Modelmodel) {
//返回所有商品分类
List<GoodsCategory>gcList=goodsCategoryService.selectCategoryList();
model.addAttribute("gcList",gcList);
//返回所有商品品牌
List<Brand>brandList=brandService.selectBrandList();
model.addAttribute("brandList",brandList);
return"goods/goods-list";
}
ftl页面
goods-list.ftl
删除页面无用的代码
处理分类列表和品牌列表,修改form表单中的域名称与对象属性一一对应
<divclass="form-group">
<selectname="catId"id="catId"onchange="ajax_get_table(1);"class="form-control">
<optionvalue="">所有分类</option>
<#listgcListasgc>
<optionvalue="${gc.id}">${gc.name}</option>
</#list>
</select>
</div>
<divclass="form-group">
<selectname="brandId"id="brandId"onchange="ajax_get_table(1);"
class="form-control">
<optionvalue="">所有品牌</option>
<#listbrandListasbrand>
<optionvalue="${brand.id}">${brand.name}</option>
</#list>
</select>
</div>
添加每页显示*条,处理搜索词和筛选,修改form表单中的域名称与对象属性一一对应
<divclass="form-group">
<labelclass="control-label"for="input-order-id">每页显示</label>
<selectname="pageSize"id="pageSize"onchange="ajax_get_table(1);"
class="form-control">
<optionvalue="10">10</option>
<optionvalue="20">20</option>
<optionvalue="50">50</option>
<optionvalue="100">100</option>
</select>
<labelclass="control-label"for="input-order-id">条</label>
</div>
<divclass="form-group">
<labelclass="control-label"for="input-order-id">关键词</label>
<divclass="input-group">
<inputname="goodsName"id="goodsName"value=""placeholder="搜索词"class="form-control"type="text">
</div>
</div>
<!--排序规则-->
<inputname="orderby1"value="goods_id"type="hidden">
<inputname="orderby2"value="desc"type="hidden">
<buttontype="button"onclick="ajax_get_table(1)"
id="button-filter search-order"class="btn btn-primary"><iclass="fa fa-search"></i>
筛选
</button>
修改BaseResult.java添加成功返回分页对象的方法
//成功返回的对象-带分页对象
public static BaseResult success(PageInfo<?> pageInfo) {
BaseResult result = new BaseResult();
result.setCode(BaseResultEnum.SUCCESS.getCode());
result.setMessage(BaseResultEnum.SUCCESS.getMessage());
result.setPageInfo(pageInfo);
return result;
}
Service服务层
GoodsService.java
/**
* 商品-列表-分页查询
* @param goods
* @param pageNum
* @param pageSize
* @return
*/
BaseResult selectGoodsListByPage(Goods goods,Integer pageNum,Integer pageSize);
GoodsServiceImpl.java
/**
* 商品-列表-分页查询
* @param goods
* @param pageNum
* @param pageSize
* @return
*/
@Override
public BaseResult selectGoodsListByPage(Goods goods, Integer pageNum, Integer pageSize) {
//构建分页对象
PageHelper.startPage(pageNum,pageSize);
//创建查询对象
GoodsExample example = new GoodsExample();
GoodsExample.Criteria criteria = example.createCriteria();
//设置查询条件
//分类参数
if (null!=goods.getCatId()&&0!=goods.getCatId()){
criteria.andCatIdEqualTo(goods.getCatId());
}
//品牌参数
if (null!=goods.getBrandId()&&0!=goods.getBrandId()){
criteria.andBrandIdEqualTo(goods.getBrandId());
}
//关键词
if (!StringUtils.isEmpty(goods.getGoodsName())){
criteria.andGoodsNameLike("%"+goods.getGoodsName()+"%");
}
//查询
List<Goods> list = goodsMapper.selectByExample(example);
//将查询结果设置至分页对象
if (!CollectionUtils.isEmpty(list)){
PageInfo<Goods> pageInfo = new PageInfo<>(list);
return BaseResult.success(pageInfo);
}
return BaseResult.error();
}
Controller层
/**
* 商品-列表-分页查询
* @param goods
* @param pageNum
* @param pageSize
* @return
*/
@RequestMapping("listForPage")
@ResponseBody
public BaseResult selectGoodsListByPage(Goods goods,Integer pageNum,Integer pageSize){
return goodsService.selectGoodsListByPage(goods,pageNum,pageSize);
}
ftl页面
head.ftl引入doT.min.js文件(项目对应包下引入doT.min.js文件)
<!-- 引入doT.js -->
<script type="text/javascript" src="${ctx}/js/doT.min.js"></script>
在分类、品牌、每页显示、筛选上添加onchange="ajax_get_table(1);"
进入goods-list.ftl页面提交分页查询
$(document).ready(function () {
// ajax 加载商品列表
ajax_get_table(1);
});
//ajax抓取页面 page为当前第几页
function ajax_get_table(page) {
$.ajax({
url: "${ctx}/goods/listForPage",
type: "POST",
data: {
catId: $("#catId").val(),
brandId: $("#brandId").val(),
goodsName: $("#goodsName").val(),
pageNum: page,
pageSize: $("#pageSize").val()
},
dataType: "JSON",
success: function (result) {
if (200 == result.code) {
if (result.pageInfo.list.length > 0) {
//获取商品列表模板
var goodsTemp = doT.template($("#goodsTemplate").text());
//填充数据
$("#goodsContent").html(goodsTemp(result.pageInfo.list));
//获取分页模板
var pageTemp = doT.template($("#pageTemplate").text());
//填充数据
$("#pageContent").html(pageTemp(result.pageInfo));
} else {
layer.msg("该分类或品牌暂无商品信息!");
}
} else {
layer.msg("该分类或品牌暂无商品信息!");
}
},
error: function (result) {
console.log(result)
}
});
}
使用doT.js模板处理goods-list.ftl商品列表信息和分页
<!-- 编写商品模板 -->
<script type="template" id="goodsTemplate">
{{ for(var i = 0; i < it.length; i++){ }}
<tr>
<td class="text-center">
<input name="shipping_code[]" value="flat.flat" type="hidden">
</td>
<td class="text-right">{{=it[i].goodsId}}</td>
<td class="text-left">{{=it[i].goodsName}}</td>
<td class="text-left">{{=it[i].goodsSn}}</td>
<td class="text-left">{{=it[i].catId}}</td>
<td class="text-left">{{=it[i].shopPrice}}</td>
<td class="text-left">
<input onkeyup="this.value=this.value.replace(/[^\d.]/g,'')"
onpaste="this.value=this.value.replace(/[^\d.]/g,'')"
onchange="ajaxUpdateField(this);" name="store_count" size="4"
data-table="goods" data-id="143" value="{{=it[i].storeCount}}" type="text">
</td>
<td class="text-center">
<input onkeyup="this.value=this.value.replace(/[^\d]/g,'')"
onpaste="this.value=this.value.replace(/[^\d]/g,'')"
onchange="updateSort('goods','goods_id','143','sort',this)" size="4"
value="{{=it[i].sort}}" type="text">
</td>
<td class="text-center">
<a target="_blank" href="/index/Home/Goods/goodsInfo/id/143"
class="btn btn-info" title="查看详情"><i class="fa fa-eye"></i></a>
<a href="商品列表-添加新商品.html" class="btn btn-primary" title="编辑"><i
class="fa fa-pencil"></i></a>
<a href="javascript:void(0);" onclick="del('143')" class="btn btn-danger"
title="删除"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
{{ } }}
</script>
<!-- 编写分页模板 -->
<script type="template" id="pageTemplate">
{{ if(it.hasPreviousPage){ }}
<li class="paginate_button prev">
<a href="javascript:ajax_get_table('{{=it.prePage}}');">上一页</a>
</li>
{{ } }}
{{ for(var i = 1; i <= it.pages; i++){ }}
<li class="paginate_button
{{ if(i == it.pageNum){ }}
active
{{ } }}
">
<a href="javascript:ajax_get_table('{{=i}}');">{{=i}}</a>
</li>
{{ } }}
{{ if(it.hasNextPage){ }}
<li class="paginate_button next">
<a href="javascript:ajax_get_table('{{=it.nextPage}}');">下一页</a>
</li>
{{ } }}
</script>
goods-list.ftl最终修改部分结果如下
<div id="ajax_return">
<form method="post" enctype="multipart/form-data" target="_blank" id="form-order">
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<td style="width: 1px;" class="text-center">
</td>
<td class="text-right">
<a href="javascript:sort('goods_id');">ID</a>
</td>
<td class="text-left">
<a href="javascript:sort('goods_name');">商品名称</a>
</td>
<td class="text-left">
<a href="javascript:sort('goods_sn');">货号</a>
</td>
<td class="text-left">
<a href="javascript:sort('cat_id');">分类</a>
</td>
<td class="text-left">
<a href="javascript:sort('shop_price');">价格</a>
</td>
<td class="text-left">
<a href="javascript:void(0);">库存</a>
</td>
<td class="text-center">
<a href="javascript:sort('sort');">排序</a>
</td>
<td class="text-center" style="width:135px;">操作</td>
</tr>
</thead>
<tbody id="goodsContent">
</tbody>
</table>
</div>
<input name="__hash__"
value="00ea0d70ce1e0760a8bf5d90b5e30971_699560bd02bf6cad1be4e51b170eb190"
type="hidden"></form>
<div class="row">
<div class="col-sm-3 text-left"></div>
<div class="col-sm-9 text-right">
<div class="dataTables_paginate paging_simple_numbers">
<ul class="pagination" id="pageContent">
</ul>
</div>
</div>
</div>
</div>
商城项目中集成Redis实现缓存
shop-manager引入依赖
pom.xml
<!-- spring data redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- commons-pool2 对象池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
application.yml
# Redis配置
redis:
timeout: 10000ms # 连接超时时间
host: 192.168.10.100 # Redis服务器地址
port: 6379 # Redis服务器端口
database: 0 # 选择哪个库,默认0库
lettuce:
pool:
max-active: 1024 # 最大连接数,默认 8
max-wait: 10000ms # 最大连接阻塞等待时间,单位毫秒,默认 -1
max-idle: 200 # 最大空闲连接,默认 8
min-idle: 5 # 最小空闲连接,默认 0
# Redis Key
# 商品分类列表 Key
goods.category.list.key: goods:category:list:goodsCategoryList
RedisConfig.java
package com.xxxx.manager.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis配置类
*
* @author zhoubin
* @since 1.0.0
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
//为string类型key设置序列器
redisTemplate.setKeySerializer(new StringRedisSerializer());
//为string类型value设置序列器
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//为hash类型key设置序列器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//为hash类型value设置序列器
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
shop-common添加Json工具类
JsonUtil.java
package com.xxxx.common.util;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
/**
* @see(功能介绍) : Json转换工具类
* @version(版本号) : 1.0
* @author(创建人) : zhoubin
*/
public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
/**
* 将对象转换成json字符串
*
* @param obj
* @return
*/
public static String object2JsonStr(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
//打印异常信息
e.printStackTrace();
}
return null;
}
/**
* 将字符串转换为对象
*
* @param <T> 泛型
*/
public static <T> T jsonStr2Object(String jsonStr, Class<T> clazz) {
try {
return objectMapper.readValue(jsonStr.getBytes("UTF-8"), clazz);
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
* <p>Title: jsonToList</p>
* <p>Description: </p>
*
* @param jsonStr
* @param beanType
* @return
*/
public static <T> List<T> jsonToList(String jsonStr, Class<T> beanType) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = objectMapper.readValue(jsonStr, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Service层实现Redis缓存
商品列表查询
GoodsServiceImpl.java
/**
* 商品-列表-分页查询
*
* @param goods
* @param pageNum
* @param pageSize
* @return
*/
@Override
public BaseResult selectGoodsListByPage(Goods goods, Integer pageNum, Integer pageSize) {
//构建分页对象
PageHelper.startPage(pageNum, pageSize);
//创建查询对象
GoodsExample example = new GoodsExample();
GoodsExample.Criteria criteria = example.createCriteria();
//商品列表RedisKey
/*
分析:
此功能查询分为七种(所有条件都包含分页参数):
a. 无条件查询
b. 根据分类查询
c. 根据品牌查询
d. 根据关键词查询
e. 根据分类_品牌查询
f. 根据分类_关键词查询
g. 根据品牌_关键词查询
无条件查询key
goods:list:pageNum_:pageSize_:catId_:brandId_:goodsName_
条件查询key
goods:pageNum_:pageSize_:catId_123:brandId_:goodsName_(根据分类查询)
goods:pageNum_:pageSize_:catId_:brandId_123:goodsName_(根据品牌查询)
goods:pageNum_:pageSize_:catId_:brandId_:goodsName_华为(根据关键词查询)
goods:pageNum_:pageSize_:catId_123:brandId_123:goodsName_(根据分类_品牌查询)
goods:pageNum_:pageSize_:catId_123:brandId_:goodsName_华为(根据分类_关键词查询)
goods:pageNum_:pageSize_:catId_:brandId_123:goodsName_华为(根据品牌_关键词查询)
*/
String goodsKeyArr[] = new String[]{"goods:pageNum_" + pageNum + ":pageSize_" + pageSize + ":",
"catId_:",
"brandId_:",
"goodsName_:"};
//设置查询条件
//分类参数
if (null != goods.getCatId() && 0 != goods.getCatId()) {
criteria.andCatIdEqualTo(goods.getCatId());
goodsKeyArr[1] = "catId_" + goods.getCatId() + ":";
}
//品牌参数
if (null != goods.getBrandId() && 0 != goods.getBrandId()) {
criteria.andBrandIdEqualTo(goods.getBrandId());
goodsKeyArr[2] = "brandId" + goods.getBrandId() + ":";
}
//关键词
if (!StringUtils.isEmpty(goods.getGoodsName())) {
criteria.andGoodsNameLike("%" + goods.getGoodsName() + "%");
goodsKeyArr[3] = "goodsName_" + goods.getGoodsName() + ":";
}
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
//拼接Redis key
String goodsListKey = Arrays.stream(goodsKeyArr).collect(Collectors.joining());
//查询缓存,如果缓存中存在数据,直接返回
String pageInfoGoodsJson = valueOperations.get(goodsListKey);
if (!StringUtils.isEmpty(pageInfoGoodsJson)) {
return BaseResult.success(JsonUtil.jsonStr2Object(pageInfoGoodsJson, PageInfo.class));
}
//查询
List<Goods> list = goodsMapper.selectByExample(example);
//将查询结果设置至分页对象
if (!CollectionUtils.isEmpty(list)) {
PageInfo<Goods> pageInfo = new PageInfo<>(list);
//放入缓存
valueOperations.set(goodsListKey, JsonUtil.object2JsonStr(pageInfo));
return BaseResult.success(pageInfo);
} else {
//没有数据,将空数据缓存,设置失效时间60s
valueOperations.set(goodsListKey, JsonUtil.object2JsonStr(new PageInfo<>(new ArrayList<Goods>())), 60,
TimeUnit.SECONDS);
}
return BaseResult.error();
}
商品分类查询
GoodsCategoryServiceImpl.java
/**
* 商品分类-列表
*
* @return
*/
@Override
public List<GoodsCategoryVo> selectCategoryListForView() {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
//查询缓存,如果缓存中有数据,直接返回
String gcListJson = valueOperations.get(goodsCategoryListKey);
if (!StringUtils.isEmpty(gcListJson)){
return JsonUtil.jsonToList(gcListJson,GoodsCategoryVo.class);
}
//创建查询对象
GoodsCategoryExample example = new GoodsCategoryExample();
//查询所有的商品分类list
List<GoodsCategory> list = goodsCategoryMapper.selectByExample(example);
//将List<GoodsCategory>转成List<GoodsCategoryVo>
List<GoodsCategoryVo> gcvList = list.stream().map(e -> {
GoodsCategoryVo gcv = new GoodsCategoryVo();
BeanUtils.copyProperties(e, gcv);
return gcv;
}).collect(Collectors.toList());
/**
* 将List<GoodsCategoryVo>转成Map<parentId,List<GoodsCategoryVo>>
* 按parentId分组,key为parentId,value为parentId对应的List
*/
Map<Short, List<GoodsCategoryVo>> map =
gcvList.stream().collect(Collectors.groupingBy(GoodsCategoryVo::getParentId));
/**
* 循环,将childrenList赋值
*/
gcvList.forEach(e -> e.setChildrenList(map.get(e.getId())));
/**
* 拦截器,返回Level为1的List,也就是顶级分类
*/
List<GoodsCategoryVo> gcvList01 = gcvList.stream().filter(e -> 1 == e.getLevel()).collect(Collectors.toList());
//放入缓存
valueOperations.set(goodsCategoryListKey, JsonUtil.object2JsonStr(gcvList01));
return gcvList01;
}
测试Redis缓存
GoodsServiceTest.java
package com.xxxx.manager;
import com.xxxx.common.result.BaseResult;
import com.xxxx.common.util.JsonUtil;
import com.xxxx.manager.pojo.Goods;
import com.xxxx.manager.service.GoodsService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 商品测试
*
* @author zhoubin
* @since 1.0.0
*/
@SpringBootTest(classes = ManagerApplication.class)
public class GoodsServiceTest {
@Autowired
private GoodsService goodsService;
@Test
public void testGoodsRedis(){
Goods goods = new Goods();
goods.setGoodsName("华为");
BaseResult baseResult = goodsService.selectGoodsListByPage(goods, 1, 10);
System.out.println(JsonUtil.object2JsonStr(baseResult.getPageInfo()));
}
}
缓存更新
新增或者修改或者删除成功以后,删除Redis缓存的信息
/**
* 商品新增-保存
*
* @param goods
* @return
*/
@Override
public BaseResult saveGoods(Goods goods) {
BaseResult baseResult = BaseResult.error();
//如果goodsId不为空直接返回错误
if (null != goods.getGoodsId()) {
return baseResult;
}
//goodsName必填
if (StringUtils.isEmpty(goods.getGoodsName())) {
return baseResult;
}
// 写操作中,清除Redis缓存数据,再次查询时新的数据将会放入缓存
redisTemplate.delete(redisTemplate.keys("goods*"));
//如果有详情,先转义
if (!StringUtils.isEmpty(goods.getGoodsContent())) {
goods.setGoodsContent(HtmlUtils.htmlEscape(goods.getGoodsContent(), "UTF-8"));
}
int result = goodsMapper.insertSelective(goods);
//如果成功返回信息里加入插入商品的主键
if (result > 0) {
baseResult = BaseResult.success();
baseResult.setMessage(String.valueOf(goods.getGoodsId()));
}
return baseResult;
}
/**
* 商品分类-新增分类-保存
*
* @param goodsCategory
* @return
*/
@Override
public int categorySave(GoodsCategory goodsCategory) {
//写操作中,清除Redis中的数据,再次查询时将新数据放入缓存中
redisTemplate.delete(redisTemplate.keys("goods*"));
return goodsCategoryMapper.insertSelective(goodsCategory);
}