文章目录
一、后台管理
采用左边栏多级菜单的形式,前端页面如下:
1、根据点击的 div 展示相应页面
根据点击不同的选项,需要在右侧页面中显示不同的内容:
<div class="content" style="width: 85%;height:100%;float:right">
<div style="background-color:black;display: none;height: 100%;color: #2b2b2b" onclick="cont(this)">我是A的内容</div>
<div style="background-color: blue;display: none;color: snow" onclick="cont(this)" >我是B的内容</div>
<div style="background-color: orange;display: none" onclick="cont(this)" >我是C的内容</div>
<div style="background-color: green;display: none" onclick="cont(this)" >我是D的内容</div>
</div>
<script type="text/javascript">
//点击菜单执行函数
function tab(param) {
var sp_an=$(param).attr('list');//获取被点击菜单的list属性值(0,1,2,3)
$('.content').children('div').eq(sp_an).click();//点击菜单后,对应的内容被点击,从而实现展示
//使用click()方法模拟点击事件,触发下面的cont函数
}
//这个函数的触发是通过点击菜单的时候触发的
function cont(param){
$(param).show();//被选中的内容显示
$(param).siblings().hide();//没有被选中的内容隐藏
}
</script>
2、解决 height: 100% 不起作用问题
注意到左边栏的高度并没有充满页面,需要调整一下:
只将此处的 height 改成 100%,是不会奏效的,因为 Web 浏览器在计算有效宽度时会考虑浏览器窗口的打开度。如果不给宽度设定任何缺省值,那浏览器会自动将页面内容平铺填满整个横向宽度。
但是高度的计算方式完全不一样。事实上,浏览器根本就不计算内容的高度,除非内容超出了视窗范围 (导致滚动条出现)。或者给整个页面设置一个绝对高度。否则,浏览器就会简单的让内容往下堆砌,页面的高度根本就无需考虑。
因为页面并没有缺省的高度值,所以,当让一个元素的高度设定为百分比高度时,无法根据获取父元素的高度,也就无法计算自己的高度。换句话说,父元素的高度只是一个缺省值:height: auto;。当要求浏览器根据这样一个缺省值来计算百分比高度时,只能得到undefined 的结果。也就是一个 null 值,浏览器不会对这个值有任何的反应。
也就是说,现在的问题在于,iframe 的父元素 body、body 的父元素 html 都没有设定固定高度,于是子元素的高度 height: 100% 也不会起作用。
如果想让一个元素的百分比高度 height: 100% ;起作用,需要给这个元素的所有父元素的高度设定一个有效值
:
这时再看效果:
还需要把右边的页面整成靠右,占 85% 的效果(因为左边占 15% 了),但是如果设置成 85% ,有一部分是重叠的,经过调整,83.5% 比较合适:
页面效果:
至此,页面布局和展示没问题了,后续需要设计管理的具体功能实现。
后端提供展示未入驻商家列表的方法。
先在 com.changgou.user.feign 包里提供 :
整成表格的形式:
接下来需要一些后端的逻辑实现,逻辑是相似的,在这里就不一一列举了。
比如,商家管理,展示未入驻商家列表,对应 http://localhost:18087/admin/unCenter 路径,是
在 user 微服务中
的,而要在 web-admin 工程中调用,就需要使用 feign,先提供个 feign:
@FeignClient(name = "user")
@RequestMapping("/admin")
public interface AdminFeign {
@GetMapping("/unCenter")
public Result unCenterList();
}
注意!!! 这里的
FeignClient 里的 name 属性值
不能随便写,它是用来指定 FeignClient 的名称,如果项目使用了 Ribbon,name 属性会作为微服务的名称,用于服务发现。也就是说,对应的路径是
在哪个微服务里,就写哪个微服务的名称
。 否则报错 :
com.netflix.client.ClientException: Load balancer does not have available server for client: admin at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
运行结果:
3、 th:onclick 引用的函数参数是 model值
这里主要要注意的是,从后端给 model 赋值后,在表格里获取 model 的值,并用 th:each 进行渲染时,按钮也需要遍历,而不是只写成 button:
<button th:onclick="permitCenter([[${unCenter.storeName}]]);" style="width: 50%;" >允许入驻</button>
permitCenter 方法中还有 ajax 代码:
<script type="text/javascript" src="/js/jquery.min.js"></script>
<script type="text/javascript">
function permitCenter(storeName){
$.ajax(
{
url: "http://localhost:18087/admin/permitCenter",
type: "GET",
dataType: "JSON",
contentType: "application/json;charset=UTF-8",
data: {storeName:storeName},
success: function (result) {
alert("商家入驻成功!");
},
error: function (result) {
console.log(result);
alert("商家入驻失败!");
},
cache: false
}
)
}
</script>
注意 thymeleaf 的 th:onclick 引用的函数参数的入参是 model 值时的写法。
如果传入多个参数,需要使用逗号隔开,并且,可以进行拼接,比如:
th:onclick="update([[${brands.name}]],[[${brands.id}]],'cell'+[[${brandState.index}]])"
二、商家管理
1、th:each=“obj,itemStat:${objList}”
同样的,修改商品品牌数据时,需要根据所在的 tr 获取修改的值,也是要循环遍历的,然后进行拼接:
注意到 thymeleaf 中使用
th:each
迭代循环对象集合时,还可以获取对象的状态,而且
th:id
是可以拼接的。
语法格式:
th:each="obj,itemStat:${objList}
itemStat 称作状态变量,属性有:
- index:当前迭代对象的index(从0开始计算)
- count: 当前迭代对象的index(从1开始计算)
- size:被迭代对象的大小
- current:当前迭代变量
- even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算)
- first:布尔值,当前循环是否是第一个
- last:布尔值,当前循环是否是最后一个
2、使用 min-height
当从后端查询出的数据很多,需要滚动条时,需要将 height 改为 min-height ,才能进行数据填充,否则只有一个不滚动页面的容量:
3、获取 input 的 text 值
<td th:text="${brands.image}" th:id="'cell'+${brandState.index}"></td>
function update(name,id,imageid){
var image=document.getElementById(imageid).innerText;
通过表格中的 td 的 id,获取 td 的值,需要用
innerText
。
4、ajax 中 async: false
注意,当 js 的方法里不仅有 ajax,还有其他要执行的代码时,需要在 ajax 中添加
async: false
,将请求设置为同步请求,这样才会按照代码的顺序执行。
5、让 input 填满 td
正确写法:
<td><input id="brandtrnameid" style="width: 90%"></input></td>
错误写法:
<td><input id="brandtrnameid" width="100%"></td>
三、商品详情页
点击商品后跳转至商品详情页的思路,是点击商品图片后,调用 http://localhost:18088/page/createHtml/1148477874278244352 方法,在 changgou-web-item\src\main\resources\static 路径下生成 Html。
同样地,需要解决跨域问题,否则在 GET 方法之前会使用 OPTIONS,而且它还会自己追加 Query String Parameters:
403 状态码是因为跨域,在调用的 createHtml 方法上加 @CrossOrigin 就好啦:
四、加入购物车功能
在 item.html 中,是创建了 Vue 实例,实现的根据 spec 切换 name、price,接下来需要把商品加入购物车,需要 skuid。
1、解决 Long 在 JS 中精度丢失问题
有一个问题,数据库 sku 表中 id 是 bigint 类型,Java 对象中 id 是 long 类型,先试图在网页上渲染 id 值,就会发现,long 类型在 JS 中,发生了精度丢失,比如 : 1148477874387296256 会显示成 1148477874387296300;1148477874399879168 会显示成 1148477874399879200。要解决这个问题,可以使用
@JsonSerialize
注解进行序列化处理。
这样,获取到的 skuid 的精度不会丢失。
(同理,在 search 服务中,用户在商品详情列表中,点击某个商品时,需要获取 spuid,进而生成对应的 html, Skuinfo 里 的 spuid 也是需要序列化处理的。
Long 类型在数据库、ES 索引库的存储中都是没问题的,但是涉及到 js,比如说传递参数、在页面弹窗显示,就会出现精度损失的问题,一定要引起注意!!!。
2、使用 Vue 实例
复习一下 Vue 实例:
<script th:inline="javascript">
var item = new Vue({
el: '#itemArray',
data: {
skuList : [[${skuList}]],
sku: {},
spec:{}
},
methods:{
......}
)
</script>
可以看到,创建了一个 vue 对象,并赋值给 el,绑定了 id 为 itemArray 的 div 标签,相当于 item 绑定了 id为 itemArray 的标签对象,可以使用此对象操作标签。然后在 data 中,创建了 key 为 skuList、sku 的数据对应的值。
在 div 标签中,使用
{
{}}
大括号,里面写 key 名称,就可以完成相应的渲染。而如果想获取 data 里面的数据的值(而不是渲染),比如当用户点击按钮时,获取此时 vue 实例中的 skuid,可以使用 v-bind 。
3、使用 v-bind 绑定数据
先导入 vue.js 的依赖,可以使用网址,也可以下到自己的项目中来。
https://cdn.jsdelivr.net/npm/vue/dist/vue.js
<script src="../static/js/vue.js"></script>
使用 v-bind 绑定数据,绑定 button 的 value 为 sku.id,直接引用 sku 对象即可,不需要什么符号。
然后试着在 div 外部获取 button 的值:
$(function () {
$('#cart').on('click', function () {
var skuid=$('#cart').val();
alert(skuid);
运行后成功获取到 skuid 值。