当前位置:   article > 正文

Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)十六(商品排序,Thymeleaf快速入门,商品详情页的展示)_springboot商品排序

springboot商品排序

Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)十六(商品详情页的展示)

一、商品排序

1、完善页面信息

在这里插入图片描述
这是用来做排序的,默认按照综合排序。

点击新品,应该按照商品创建时间排序,点击价格应该按照价格排序。

因为我们没有统计销量和评价,这里咱们以新品价格为例,进行讲解,做法是相通的。

排序需要知道两个内容:

  • 排序的字段

  • 排序的方式

    因此,我们首先在search中记录这两个信息,因为created钩子函数会对search进行覆盖,因此我们在钩子函数中对这两个信息进行初始化即可:

在这里插入图片描述

            //对排序进行初始化,默认为“”,代表不排序
            search.sortBy = search.sortBy || "";
            //对排序方式进行初始化,转为布尔值
            search.descending = search.descending === "true" || false;
  • 1
  • 2
  • 3
  • 4

然后,在页面上给按钮绑定点击事件,修改sortBydescending的值:
在这里插入图片描述

					<li :class="{active:!search.sortBy}" @click="search.sortBy=''">
                            <a href="#">综合</a>
                        </li>
                        <li>
                            <a href="#">销量</a>
                        </li>
                        <li @click="search.sortBy='createTime'" :class="{active: search.sortBy==='createTime'}">
                            <a href="#">新品</a>
                        </li>
                        <li>
                            <a href="#">评价</a>
                        </li>
                        <li @click="search.sortBy='price'; search.descending = !search.descending"
                            :class="{active: search.sortBy==='price'}">
                            <a href="#">
                                价格
                                <v-icon v-show="search.descending">arrow_drop_down</v-icon>
                                <v-icon v-show="!search.descending">arrow_drop_up</v-icon>
                            </a>
                        </li>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2、实现后台逻辑

接下来,后台需要接收请求参数中的排序信息,然后在搜索中加入排序的逻辑。

(1)我们的请求参数对象SearchRequest中,需要进行扩展:

在这里插入图片描述

package com.leyou.search.pojo;

import java.util.List;
import java.util.Map;

public class SearchRequest {

    private String key;//搜索字段
    private Integer page;//当前页
    private static final Integer DEFAULT_SIZE = 20;//每页个数
    private static final Integer DEFAULT_PAGE = 1;//默认页

    private String sortBy;//排序字段
    private Boolean descending;//是否降序

    //过滤条件
    private Map<String,String> filter;

    public String getSortBy() {
        return sortBy;
    }

    public void setSortBy(String sortBy) {
        this.sortBy = sortBy;
    }

    public Boolean getDescending() {
        return descending;
    }

    public void setDescending(Boolean descending) {
        this.descending = descending;
    }

    public Map<String, String> getFilter() {
        return filter;
    }

    public void setFilter(Map<String, String> filter) {
        this.filter = filter;
    }

    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public Integer getPage() {
        if(page == null){
            return DEFAULT_PAGE;
        }
        //获取页码的时候进行校验,不能小于1
        return Math.max(DEFAULT_PAGE,page);// Math.max返回DEFAULT_PAGE,page之间的最大值
    }
    public void setPage(Integer page) {
        this.page = page;
    }
    public static Integer getSize() {
        return DEFAULT_SIZE;
    }

    @Override
    public String toString() {
        return "SearchRequest{" +
                "key='" + key + '\'' +
                ", page=" + page +
                ", filter=" + filter +
                '}';
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
(3)然后在搜索业务逻辑中,添加排序条件:

在这里插入图片描述

        //设置排序
        String sortBy = request.getSortBy();
        Boolean desc = request.getDescending();
        if(StringUtils.isNotBlank(sortBy)){
            //如果不为空,则进行排序
            queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(desc ? SortOrder.DESC : SortOrder.ASC));
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(4)运行测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、商品页面详情展示

当用户搜索到商品,肯定会点击查看,就会进入商品详情页,接下来我们完成商品详情页的展示

1、 Thymeleaf

官网文档地址
https://www.thymeleaf.org/documentation.html

在商品详情页中,我们会使用到Thymeleaf来渲染页面(替代传统的JSP)

Thymeleaf是适用于 Web 和独立环境的现代服务器端 Java 模板引擎。

Thymeleaf 的主要目标是为您的开发工作流程带来优雅的自然模板——HTML可以在浏览器中正确显示,也可以作为静态原型工作,从而加强开发团队的协作。

在线文档地址

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#what-kind-of-templates-can-thymeleaf-process

在这里插入图片描述

在这里插入图片描述

Spring官方支持的服务的渲染模板中,并不包含jsp。

而是Thymeleaf和Freemarker等,而Thymeleaf与SpringMVc的视图技术,

及SpringBoot的自动化配置集成非常完美,几乎没有任何成本,

你只用关注Thymeleaf的语法即可。

2、商品详情页服务(工程搭建)

商品详情浏览量比较大,并发高,我们会独立开启一个微服务,用来展示商品详情。

(1)创建工程模块

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(2)引入依赖

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou</artifactId>
        <groupId>com.leyou.parent</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.leyou.service</groupId>
    <artifactId>ly-page</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
         <dependency>
            <groupId>com.leyou.service</groupId>
            <artifactId>ly-item-interface</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

    </dependencies>


</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
(3)项目开始前先编写测试类学习一下
1)编写HelloController

在这里插入图片描述

2)在resources下创建固定文件夹名称的文件夹(templates)

在这里插入图片描述

3)在templates当中创建后缀为html的文件(hello.html会作为视图返回)

在这里插入图片描述
原理:访问当前的HelloController会自动找hello.html显示
在这里插入图片描述

4)在HelloController添加模型数据

在这里插入图片描述

package com.leyou.page.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping("hello")
    public String toHello(Model model){
        //将消息添加到模型当中
        model.addAttribute("msg","hello, thymeleaf!");
        return "hello";//普通字符串会被当成视图名称,综合前缀和后缀寻找视图
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
5)在hello.html当中获取对应的数据

和jsp类似只不过${}是在属性值里面写的,这样做的好处就是在没有后台java的情况下前端页面更加规范,并且不会报错
在这里插入图片描述

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>hello</title>
</head>
<body>
    <h1>
        msg:<span th:text="${msg}"></span>
    </h1>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
6)编写启动类测试运行

在这里插入图片描述
修改包路径
在这里插入图片描述
在这里插入图片描述

package com.leyou;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LyPageApplication {

    public static void main(String[] args) {

        SpringApplication.run(LyPageApplication.class);

    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

刷新一下Maven项目
在这里插入图片描述
启动微服务
在这里插入图片描述
访问8080端口
在这里插入图片描述
访问成功
在这里插入图片描述

7)Thymeleaf变量
  • a、创建User实体类
    在这里插入图片描述
    在这里插入图片描述
package com.leyou.page.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    String name;
    int age;
    User friend;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • b、完善HelloController

在这里插入图片描述

package com.leyou.page.web;

import com.leyou.page.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping("hello")
    public String toHello(Model model){

        User user = new User();

        user.setAge(21);
        user.setName("Jack Chen");
        user.setFriend(new User("李小龙",30,null));

        //将消息添加到模型当中
        model.addAttribute("user",user);
        return "hello";//普通字符串会被当成视图名称,综合前缀和后缀寻找视图
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • c、在hello.html
    在这里插入图片描述
    在这里插入图片描述
   <h1>
        msg:<span th:text="${user.toString()}"></span>
    </h1>
  • 1
  • 2
  • 3

重新运行

在这里插入图片描述
刷新页面
页面效果
在这里插入图片描述

8)其他用法

在这里插入图片描述

    <span th:text="'<font color=\'red\'>hello</font>'"></span><br>
    <span th:text="'<font color=\'red\'>hello</font>'"></span>
  • 1
  • 2

刷新页面,发现只展示了文本
在这里插入图片描述
展示真正的标签

在这里插入图片描述

 <span th:utext="'<font color=\'red\'>hello</font>'"></span><br>
 <span th:utext="'<font color=\'red\'>hello</font>'"></span>
  • 1
  • 2

刷新页面
在这里插入图片描述

9)内置对象
  • dates
    在这里插入图片描述
    完善hello.html
    在这里插入图片描述
    刷新页面
    在这里插入图片描述
    格式化日期
    在这里插入图片描述
 <span th:text="${#dates.format(#dates.createNow(),'yyyy-MM-dd')}"></span>
  • 1

刷新页面
在这里插入图片描述

10)Thymeleaf自己的一些功能
  • 运算(加减乘除 判断 布尔运算 三元运算符)
    在这里插入图片描述
 <h1>
        <span th:text="100 * 200"></span>
    </h1>
  • 1
  • 2
  • 3

刷新页面

在这里插入图片描述

  • 循环
    修改HelloController
    在这里插入图片描述
package com.leyou.page.web;

import com.leyou.page.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.ArrayList;

@Controller
public class HelloController {

    @GetMapping("hello")
    public String toHello(Model model){

        User user1 =  new User("李1龙",30,null);
        User user2 =  new User("李2龙",22,null);
        User user3 =  new User("李3龙",20,null);
        User user4 =  new User("李4龙",31,null);
        User user5 =  new User("李5龙",35,null);

        ArrayList<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);
        users.add(user3);
        users.add(user4);
        users.add(user5);

        //将消息添加到模型当中
        model.addAttribute("users",users);
        model.addAttribute("user",user1);
        return "hello";//普通字符串会被当成视图名称,综合前缀和后缀寻找视图
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

完善hello.html
在这里插入图片描述


    <h1 th:each="user :  ${ users }">
        <span th:text="${ user.toString() }"></span>
    </h1>
  • 1
  • 2
  • 3
  • 4

刷新页面
在这里插入图片描述

11) JS模板(内联模板)

在这里插入图片描述

<script th:inline="javascript">
    const users = /*[[${users}]]*/[];
    const user = users[0];
    console.log(user);
    for (let u of users) {
        console.log(u);
    }

</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

浏览器控制台
在这里插入图片描述

3、创建item.html

在这里插入图片描述

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
	<meta charset="utf-8"/>
	<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE">
	<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/>
	<title>乐优商城--商品详情页</title>
	<link rel="icon" href="/img/assets/img/LOGO.ico">
	<link rel="stylesheet" type="text/css" href="/css/webbase.css"/>
	<link rel="stylesheet" type="text/css" href="/css/pages-item.css"/>
	<link rel="stylesheet" type="text/css" href="/css/pages-zoom.css"/>
	<link rel="stylesheet" type="text/css" href="/css/widget-cartPanelView.css"/>

	<style type="text/css">
		.goods-intro-list li {
			display: inline-block;
			width: 300px;
		}

		.Ptable {
			margin: 10px 0;
		}

		.Ptable-item {
			padding: 12px 0;
			line-height: 220%;
			color: #999;
			font-size: 12px;
			border-bottom: 1px solid #eee;
		}

		.Ptable-item h3 {
			width: 110px;
			text-align: right;
		}

		.Ptable-item h3, .package-list h3 {
			font-weight: 400;
			font-size: 12px;
			float: left;
		}

		h3 {
			display: block;
			font-size: 1.17em;
			-webkit-margin-before: 1em;
			-webkit-margin-after: 1em;
			-webkit-margin-start: 0px;
			-webkit-margin-end: 0px;
			font-weight: bold;
		}

		.Ptable-item dl {
			margin-left: 110px;
		}

		dl {
			display: block;
			-webkit-margin-before: 1em;
			-webkit-margin-after: 1em;
			-webkit-margin-start: 0px;
			-webkit-margin-end: 0px;
		}

		.Ptable-item dt {
			width: 160px;
			float: left;
			text-align: right;
			padding-right: 5px;
		}

		.Ptable-item dd {
			margin-left: 210px;
		}

		dd {
			display: block;
			-webkit-margin-start: 40px;
		}

		.package-list {
			padding: 12px 0;
			line-height: 220%;
			color: #999;
			font-size: 12px;
			margin-top: -1px;
		}

		.package-list h3 {
			width: 130px;
			text-align: right;
		}

		.package-list p {
			margin-left: 155px;
			padding-right: 50px;
		}
	</style>

</head>

<body>

<!-- 头部栏位 -->
<!--页面顶部,由js动态加载-->
<div id="itemApp">
	<div id="nav-bottom">
		<ly-top/>
	</div>
	<div class="py-container">
		<div id="item">
			<div class="crumb-wrap">
				<ul class="sui-breadcrumb">
					<li th:each="c : ${categories}">
						<a href="#" th:text="${c.name}">手机</a>
					</li>
					<li>
						<a href="#" th:text="${brand.name}">Apple</a>
					</li>
					<li class="active" th:text="${title}">Apple iPhone 6s</li>
				</ul>
			</div>
			<!--product-info-->
			<div class="product-info">
				<div class="fl preview-wrap">
					<!--放大镜效果-->
					<div class="zoom">
						<!--默认第一个预览-->
						<div id="preview" class="spec-preview">
							<span class="jqzoom">
								<img :jqimg="images[0]" :src="images[0]" width="400px" height="400px"/>
							</span>
						</div>
						<!--下方的缩略图-->
						<div class="spec-scroll">
							<a class="prev">&lt;</a>
							<!--左右按钮-->
							<div class="items">
								<ul>
									<li v-for="img in images">
										<img :src="img" :bimg="img" onmousemove="preview(this)"/>
									</li>
								</ul>
							</div>
							<a class="next">&gt;</a>
						</div>
					</div>
				</div>
				<div class="fr itemInfo-wrap">
					<div class="sku-name">
						<h4 v-text="sku.title"></h4>
					</div>
					<div class="news"><span th:utext="${subTitle}">Apple iPhone 6s 4GB 128GB 土豪金</span></div>
					<div class="summary">
						<div class="summary-wrap">
							<div class="fl title"><i>价  格</i></div>
							<div class="fl price">
								<i>¥</i><em v-text="ly.formatPrice(sku.price)"></em><span>降价通知</span>
							</div>
							<div class="fr remark"><i>累计评价</i><em>612188</em></div>
						</div>
						<div class="summary-wrap">
							<div class="fl title">
								<i>促  销</i>
							</div>
							<div class="fl fix-width">
								<i class="red-bg">加价购</i>
								<em class="t-gray">满999.00另加20.00元,或满1999.00另加30.00元,或满2999.00另加40.00元,即可在购物车换
									购热销商品</em>
							</div>
						</div>
					</div>
					<div class="support">
						<div class="summary-wrap">
							<div class="fl title">
								<i>支  持</i>
							</div>
							<div class="fl fix-width">
								<em class="t-gray">以旧换新,闲置手机回收 4G套餐超值抢 礼品购</em>
							</div>
						</div>
						<div class="summary-wrap">
							<div class="fl title">
								<i>配 送 至</i>
							</div>
							<div class="fl fix-width">
								<em class="t-gray">上海 <span v-text="sku.stock > 0 ? '有货' : '缺货'"></span></em>
							</div>
						</div>
					</div>
					<div class="clearfix choose">
						<div id="specification" class="summary-wrap clearfix">
							<dl v-for="(options,id) in specialSpec" :key="id">
								<dt>
									<div class="fl title">
										<i v-text="params[id]"></i>
									</div>
								</dt>
								<dd v-for="(o,i) in options" :key="i" @click="selectSku(id,i)">
									<a href="javascript:;" :class="{selected:i === indexes[id], locked:locked(id, i)}">
										{{o}}<span title="点击取消选择">&nbsp;</span>
									</a>
								</dd>
							</dl>
						</div>

						<div class="summary-wrap">
							<div class="fl title">
								<div class="control-group">
									<div class="controls">
										<input autocomplete="off" type="text" disabled v-model="num" minnum="1"
											   class="itxt"/>
										<a href="javascript:void(0)" class="increment plus" @click="increment">+</a>
										<a href="javascript:void(0)" class="increment mins" @click="decrement">-</a>
									</div>
								</div>
							</div>
							<div class="fl">
								<ul class="btn-choose unstyled">
									<li>
										<a href="#" @click.prevent="addCart" target="_blank"
										   class="sui-btn  btn-danger addshopcar">加入购物车</a>
									</li>
								</ul>
							</div>
						</div>
					</div>
				</div>
			</div>
			<!--product-detail-->
			<div class="clearfix product-detail">
				<div class="fl aside">
					<ul class="sui-nav nav-tabs tab-wraped">
						<li class="active">
							<a href="#index" data-toggle="tab">
								<span>相关分类</span>
							</a>
						</li>
						<li>
							<a href="#profile" data-toggle="tab">
								<span>推荐品牌</span>
							</a>
						</li>
					</ul>
					<div class="tab-content tab-wraped">
						<div id="index" class="tab-pane active">
							<ul class="part-list unstyled">
								<li>手机</li>
								<li>手机壳</li>
								<li>内存卡</li>
								<li>Iphone配件</li>
								<li>贴膜</li>
								<li>手机耳机</li>
								<li>移动电源</li>
								<li>平板电脑</li>
							</ul>
							<ul class="goods-list unstyled">
								<li>
									<div class="list-wrap">
										<div class="p-img">
											<img src="/img/_/part01.png"/>
										</div>
										<div class="attr">
											<em>Apple苹果iPhone 6s (A1699)</em>
										</div>
										<div class="price">
											<strong>
												<em>¥</em>
												<i>6088.00</i>
											</strong>
										</div>
										<div class="operate">
											<a href="javascript:void(0);" class="sui-btn btn-bordered">加入购物车</a>
										</div>
									</div>
								</li>
								<li>
									<div class="list-wrap">
										<div class="p-img">
											<img src="/img/_/part02.png"/>
										</div>
										<div class="attr">
											<em>Apple苹果iPhone 6s (A1699)</em>
										</div>
										<div class="price">
											<strong>
												<em>¥</em>
												<i>6088.00</i>
											</strong>
										</div>
										<div class="operate">
											<a href="javascript:void(0);" class="sui-btn btn-bordered">加入购物车</a>
										</div>
									</div>
								</li>
								<li>
									<div class="list-wrap">
										<div class="p-img">
											<img src="/img/_/part03.png"/>
										</div>
										<div class="attr">
											<em>Apple苹果iPhone 6s (A1699)</em>
										</div>
										<div class="price">
											<strong>
												<em>¥</em>
												<i>6088.00</i>
											</strong>
										</div>
										<div class="operate">
											<a href="javascript:void(0);" class="sui-btn btn-bordered">加入购物车</a>
										</div>
									</div>
									<div class="list-wrap">
										<div class="p-img">
											<img src="/img/_/part02.png"/>
										</div>
										<div class="attr">
											<em>Apple苹果iPhone 6s (A1699)</em>
										</div>
										<div class="price">
											<strong>
												<em>¥</em>
												<i>6088.00</i>
											</strong>
										</div>
										<div class="operate">
											<a href="javascript:void(0);" class="sui-btn btn-bordered">加入购物车</a>
										</div>
									</div>
									<div class="list-wrap">
										<div class="p-img">
											<img src="/img/_/part03.png"/>
										</div>
										<div class="attr">
											<em>Apple苹果iPhone 6s (A1699)</em>
										</div>
										<div class="price">
											<strong>
												<em>¥</em>
												<i>6088.00</i>
											</strong>
										</div>
										<div class="operate">
											<a href="javascript:void(0);" class="sui-btn btn-bordered">加入购物车</a>
										</div>
									</div>
								</li>
							</ul>
						</div>
						<div id="profile" class="tab-pane">
							<p>推荐品牌</p>
						</div>
					</div>
				</div>
				<div class="fr detail">
					<div class="clearfix fitting">
						<h4 class="kt">选择搭配</h4>
						<div class="good-suits">
							<div class="fl master">
								<div class="list-wrap">
									<div class="p-img">
										<img src="/img/_/l-m01.png"/>
									</div>
									<em>¥5299</em>
									<i>+</i>
								</div>
							</div>
							<div class="fl suits">
								<ul class="suit-list">
									<li class="">
										<div id="e">
											<img src="/img/_/dp01.png"/>
										</div>
										<i>Feless费勒斯VR</i>
										<label data-toggle="checkbox" class="checkbox-pretty">
											<input type="checkbox"><span>39</span>
										</label>
									</li>
									<li class="">
										<div id=""><img src="/img/_/dp02.png"/></div>
										<i>Feless费勒斯VR</i>
										<label data-toggle="checkbox" class="checkbox-pretty">
											<input type="checkbox"><span>50</span>
										</label>
									</li>
									<li class="">
										<div id=""><img src="/img/_/dp03.png"/></div>
										<i>Feless费勒斯VR</i>
										<label data-toggle="checkbox" class="checkbox-pretty">
											<input type="checkbox"><span>59</span>
										</label>
									</li>
									<li class="">
										<div id=""><img src="/img/_/dp04.png"/></div>
										<i>Feless费勒斯VR</i>
										<label data-toggle="checkbox" class="checkbox-pretty">
											<input type="checkbox"><span>99</span>
										</label>
									</li>
								</ul>
							</div>
							<div class="fr result">
								<div class="num">已选购0件商品</div>
								<div class="price-tit"><strong>套餐价</strong></div>
								<div class="price">¥5299</div>
								<button class="sui-btn  btn-danger addshopcar">加入购物车</button>
							</div>
						</div>
					</div>
					<div class="tab-main intro">
						<ul class="sui-nav nav-tabs tab-wraped">
							<li class="active">
								<a href="#one" data-toggle="tab">
									<span>商品介绍</span>
								</a>
							</li>
							<li>
								<a href="#two" data-toggle="tab">
									<span>规格与包装</span>
								</a>
							</li>
							<li>
								<a href="#three" data-toggle="tab">
									<span>售后保障</span>
								</a>
							</li>
							<li>
								<a href="#four" data-toggle="tab">
									<span>商品评价</span>
								</a>
							</li>
							<li>
								<a href="#five" data-toggle="tab">
									<span>手机社区</span>
								</a>
							</li>
						</ul>
						<div class="clearfix"></div>
						<div class="tab-content tab-wraped">
							<div id="one" class="tab-pane active">
								<ul class="goods-intro-list unstyled" style="list-style: none;">
									<li>分辨率:1920*1080(FHD)</li>
									<li>后置摄像头:1200万像素</li>
									<li>前置摄像头:500万像素</li>
									<li>核 数:其他</li>
									<li>频 率:以官网信息为准</li>
									<li>品牌: Apple</li>
									<li>商品名称:APPLEiPhone 6s Plus</li>
									<li>商品编号:1861098</li>
									<li>商品毛重:0.51kg</li>
									<li>商品产地:中国大陆</li>
									<li>热点:指纹识别,Apple Pay,金属机身,拍照神器</li>
									<li>系统:苹果(IOS)</li>
									<li>像素:1000-1600万</li>
									<li>机身内存:64GB</li>
								</ul>
								<!--商品详情-->
								<div class="intro-detail">
									<div th:utext="${detail.description}"></div>
								</div>
							</div>
							<div id="two" class="tab-pane">
								<div class="Ptable">
									<div class="Ptable-item" v-for="group in specGroups" :key="group.id">
										<h3 v-text="group.name"></h3>
										<dl>
            <span v-for="param in group.params" :key="param.id">
				<dt v-text="param.name"></dt><dd v-text="param.value + (param.unit || '')"></dd>
			</span>
										</dl>
									</div>
								</div>
								<div class="package-list">
									<h3>包装清单</h3>
									<p th:text="${detail.packingList}">手机X1、快速指南X1、华为SuperCharge 充电器X1、三包凭证X1、Type-C
										数字耳机X1、Type-C 数据线X1、取卡针X1、TPU保护壳X1、USB Type-C转3.5mm耳机转接线X1(备注:最终以实物为准)</p>
								</div>

							</div>
							<div id="three" class="tab-pane">
								<p th:text="${detail.afterService}">售后保障</p>
							</div>
							<div id="four" class="tab-pane">
								<p>商品评价</p>
							</div>
							<div id="five" class="tab-pane">
								<p>手机社区</p>
							</div>
						</div>
					</div>
				</div>
			</div>
			<!--like-->
			<div class="clearfix"></div>
			<div class="like">
				<h4 class="kt">猜你喜欢</h4>
				<div class="like-list">
					<ul class="yui3-g">
						<li class="yui3-u-1-6">
							<div class="list-wrap">
								<div class="p-img">
									<img src="/img/_/itemlike01.png"/>
								</div>
								<div class="attr">
									<em>DELL戴尔Ins 15MR-7528SS 15英寸 银色 笔记本</em>
								</div>
								<div class="price">
									<strong>
										<em>¥</em>
										<i>3699.00</i>
									</strong>
								</div>
								<div class="commit">
									<i class="command">已有6人评价</i>
								</div>
							</div>
						</li>
						<li class="yui3-u-1-6">
							<div class="list-wrap">
								<div class="p-img">
									<img src="/img/_/itemlike02.png"/>
								</div>
								<div class="attr">
									<em>Apple苹果iPhone 6s/6s Plus 16G 64G 128G</em>
								</div>
								<div class="price">
									<strong>
										<em>¥</em>
										<i>4388.00</i>
									</strong>
								</div>
								<div class="commit">
									<i class="command">已有700人评价</i>
								</div>
							</div>
						</li>
						<li class="yui3-u-1-6">
							<div class="list-wrap">
								<div class="p-img">
									<img src="/img/_/itemlike03.png"/>
								</div>
								<div class="attr">
									<em>DELL戴尔Ins 15MR-7528SS 15英寸 银色 笔记本</em>
								</div>
								<div class="price">
									<strong>
										<em>¥</em>
										<i>4088.00</i>
									</strong>
								</div>
								<div class="commit">
									<i class="command">已有700人评价</i>
								</div>
							</div>
						</li>
						<li class="yui3-u-1-6">
							<div class="list-wrap">
								<div class="p-img">
									<img src="/img/_/itemlike04.png"/>
								</div>
								<div class="attr">
									<em>DELL戴尔Ins 15MR-7528SS 15英寸 银色 笔记本</em>
								</div>
								<div class="price">
									<strong>
										<em>¥</em>
										<i>4088.00</i>
									</strong>
								</div>
								<div class="commit">
									<i class="command">已有700人评价</i>
								</div>
							</div>
						</li>
						<li class="yui3-u-1-6">
							<div class="list-wrap">
								<div class="p-img">
									<img src="/img/_/itemlike05.png"/>
								</div>
								<div class="attr">
									<em>DELL戴尔Ins 15MR-7528SS 15英寸 银色 笔记本</em>
								</div>
								<div class="price">
									<strong>
										<em>¥</em>
										<i>4088.00</i>
									</strong>
								</div>
								<div class="commit">
									<i class="command">已有700人评价</i>
								</div>
							</div>
						</li>
						<li class="yui3-u-1-6">
							<div class="list-wrap">
								<div class="p-img">
									<img src="/img/_/itemlike06.png"/>
								</div>
								<div class="attr">
									<em>DELL戴尔Ins 15MR-7528SS 15英寸 银色 笔记本</em>
								</div>
								<div class="price">
									<strong>
										<em>¥</em>
										<i>4088.00</i>
									</strong>
								</div>
								<div class="commit">
									<i class="command">已有700人评价</i>
								</div>
							</div>
						</li>
					</ul>
				</div>
			</div>
		</div>
	</div>

</div>
<script src="/js/vue/vue.js"></script>
<script src="/js/axios.min.js"></script>
<script src="/js/common.js"></script>

<script th:inline="javascript">
	const specialSpec = JSON.parse(/*[[${detail.specialSpec}]]*/ "");
	const genericSpec = JSON.parse(/*[[${detail.genericSpec}]]*/ "");
	const skus = /*[[${skus}]]*/ [];
	const specs = /*[[${specs}]]*/ [];
	const params = {};
	specs.forEach(group => {
		group.params.forEach(param => {
			params[param.id] = param.name;
		})
	});
	// 初始化特有规格参数默认选中一个
	const indexes = {};
	const initIndex = skus[0].indexes.split("_");
	Object.keys(specialSpec).forEach((id, i) => {
		indexes[id] = parseInt(initIndex[i]);
	})
	const indexArr = skus.map(s => s.indexes);

</script>
<script>
	var itemVm = new Vue({
		el: "#itemApp",
		data: {
			ly,
			specialSpec,// 特有规格参数模板
			params,// 参数对象数组
			indexes,// 初始化被选中的参数
			num: 1,
		},
		methods: {
			decrement() {
				if (this.num > 1) {
					this.num--;
				}
			},
			increment() {
				this.num++;
			},
			addCart() {
				// 判断是否登录
				ly.http.get("/auth/verify").then(() => {
					// 已登录
					ly.http.post("/cart", {
						skuId: this.sku.id,
						title: this.sku.title,
						image: this.images[0],
						price: this.sku.price,
						num: this.num,
						ownSpec: JSON.stringify(this.ownSpec)
					}).then(() => {
						// 跳转到购物车列表页
						window.location.href = "http://www.leyou.com/cart.html";
					}).catch(() => {
						alert("添加购物车失败,请重试!");
					})
				}).catch(() => {
					// 获取以前的购物车
					const carts = ly.store.get("carts") || [];
					// 获取与当前商品id一致的购物车数据
					const cart = carts.find(c => c.skuId === this.sku.id);
					if (cart) {
						// 存在,修改数量
						cart.num += this.num;
					} else {
						// 不存在,新增
						carts.push({
							skuId: this.sku.id,
							title: this.sku.title,
							image: this.images[0],
							price: this.sku.price,
							num: this.num,
							ownSpec: JSON.stringify(this.ownSpec)
						})
					}
					// 未登录
					ly.store.set("carts", carts);
					// 跳转到购物车列表页
					window.location.href = "http://www.leyou.com/cart.html";
				})
			},
			locked(id, i) {
				// 如果只有一个可选项,永不锁定
				if(specialSpec[id].length === 1) return false;
				// 如果有其它项未选,不锁定
				let boo = true;
				Object.keys(this.indexes).forEach(key => {
					if (key !== id && this.indexes[key] == null) {
						boo = false;
						return;
					}
				});
				if (!boo) return false;
				// 如果当前项的组合不存在,锁定
				const {...o} = this.indexes;
				o[id] = i;
				const index = Object.values(o).join("_");
				return !indexArr.includes(index);
			},
			selectSku(id, i) {
				// 先判断当前选中的是否是锁定项
				const isLocked = this.locked(id, i);
				// 无论是否是锁定项,都允许修改
				this.indexes[id] = i;
				// 如果是锁定项,则需要调整其它项的选中状态
				if (isLocked) {
					Object.keys(this.indexes).forEach(key => {
						if (key !== id) {
							const remainSpec = specialSpec[key].filter((e, j) => !this.locked(key, j));
							this.indexes[key] = remainSpec.length === 1 ? specialSpec[key].findIndex(e => e === remainSpec[0]) : null;
						}
					})
				}

			}
		},
		computed: {
			sku() {
				if (Object.values(this.indexes).includes(null)) {
					return skus[0];
				}
				// 获取选中的规格参数的索引
				const index = Object.values(this.indexes).join("_");
				// 去skus集合寻找与index一致的sku
				return skus.find(s => s.indexes === index);
			},
			images() {
				return this.sku.images ? this.sku.images.split(",") : [];
			},
			specGroups() {
				// 获取特有规格参数值
				const ownSpec = JSON.parse(this.sku.ownSpec);
				specs.forEach(group => {
					group.params.forEach(param => {
						if (param.generic) {
							param.value = genericSpec[param.id];
						} else {
							param.value = ownSpec[param.id];
						}
					})
				})
				return specs;
			},
			ownSpec() {
				const ownSpec = JSON.parse(this.sku.ownSpec);
				const obj = {};
				Object.keys(ownSpec).forEach(id => {
					obj[this.params[id]] = ownSpec[id];
				})
				return obj;
			}
		},
		components: {
			lyTop: () => import('/js/pages/top.js')
		}
	});
</script>

<script type="text/javascript" src="/js/plugins/jquery/jquery.min.js"></script>
<script type="text/javascript">
	$(function () {
		$("#service").hover(function () {
			$(".service").show();
		}, function () {
			$(".service").hide();
		});
		$("#shopcar").hover(function () {
			$("#shopcarlist").show();
		}, function () {
			$("#shopcarlist").hide();
		});

	})
</script>
<script type="text/javascript" src="/js/model/cartModel.js"></script>
<script type="text/javascript" src="/js/plugins/jquery.easing/jquery.easing.min.js"></script>
<script type="text/javascript" src="/js/plugins/sui/sui.min.js"></script>
<script type="text/javascript" src="/js/plugins/jquery.jqzoom/jquery.jqzoom.js"></script>
<script type="text/javascript" src="/js/plugins/jquery.jqzoom/zoom.js"></script>
<script type="text/javascript" src="index/index.js"></script>
</body>

</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
  • 638
  • 639
  • 640
  • 641
  • 642
  • 643
  • 644
  • 645
  • 646
  • 647
  • 648
  • 649
  • 650
  • 651
  • 652
  • 653
  • 654
  • 655
  • 656
  • 657
  • 658
  • 659
  • 660
  • 661
  • 662
  • 663
  • 664
  • 665
  • 666
  • 667
  • 668
  • 669
  • 670
  • 671
  • 672
  • 673
  • 674
  • 675
  • 676
  • 677
  • 678
  • 679
  • 680
  • 681
  • 682
  • 683
  • 684
  • 685
  • 686
  • 687
  • 688
  • 689
  • 690
  • 691
  • 692
  • 693
  • 694
  • 695
  • 696
  • 697
  • 698
  • 699
  • 700
  • 701
  • 702
  • 703
  • 704
  • 705
  • 706
  • 707
  • 708
  • 709
  • 710
  • 711
  • 712
  • 713
  • 714
  • 715
  • 716
  • 717
  • 718
  • 719
  • 720
  • 721
  • 722
  • 723
  • 724
  • 725
  • 726
  • 727
  • 728
  • 729
  • 730
  • 731
  • 732
  • 733
  • 734
  • 735
  • 736
  • 737
  • 738
  • 739
  • 740
  • 741
  • 742
  • 743
  • 744
  • 745
  • 746
  • 747
  • 748
  • 749
  • 750
  • 751
  • 752
  • 753
  • 754
  • 755
  • 756
  • 757
  • 758
  • 759
  • 760
  • 761
  • 762
  • 763
  • 764
  • 765
  • 766
  • 767
  • 768
  • 769
  • 770
  • 771
  • 772
  • 773
  • 774
  • 775
  • 776
  • 777
  • 778
  • 779
  • 780
  • 781
  • 782
  • 783
  • 784
  • 785
  • 786
  • 787
  • 788
  • 789
  • 790
  • 791
  • 792
  • 793
  • 794
  • 795
  • 796
  • 797
  • 798
  • 799
  • 800
  • 801
  • 802
  • 803
  • 804
  • 805
  • 806
  • 807
  • 808

4、删除刚刚创建的一些测试内容

在这里插入图片描述

4、在resources下创建配置文件application.yml

在这里插入图片描述
在这里插入图片描述

server:
  port: 8086
spring:
  application:
    name: page-service
  thymeleaf:
    cache: false  #设置缓存关闭
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
    lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

5、nginx反向代理

在这里插入图片描述

server {
        listen       80;
        server_name  www.leyou.com;

        proxy_set_header X-Forwarded-Host $host;
		proxy_set_header X-Forwarded-Server $host;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;


        location /item {
            proxy_pass http://192.168.58.2:8086; #主机地址 下的8086端口
            proxy_connect_timeout 600;
			proxy_read_timeout 600;
        }
		location / {
            proxy_pass http://192.168.58.2:9002; #主机地址 下的9001端口
            proxy_connect_timeout 600;
			proxy_read_timeout 600;
        }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

重新加载nginx
在这里插入图片描述

6、完善启动类

在这里插入图片描述

package com.leyou;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LyPageApplication {

    public static void main(String[] args) {

        SpringApplication.run(LyPageApplication.class);

    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

6、完善search.html页面实现对应点击商品查看详情功能

在这里插入图片描述

<a :href="'/item/'+goods.id+'.html'" target="_blank">
                                    <img :src="goods.selectedSku.images" height="200"/>
                                </a>
  • 1
  • 2
  • 3

7、创建PageController

在这里插入图片描述
在这里插入图片描述

package com.leyou.page.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Controller
public class PageController {

    @GetMapping("item/{id}.html")
    public String toItemPage(@PathVariable("id") Long spuId, Model model){
        //准备模型数据
        //返回视图
        return "/item";
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

重新运行
在这里插入图片描述
点击实现页面跳转
在这里插入图片描述
在这里插入图片描述

8、封装模型数据

首先我们一起来分析一下,在这个页面中需要哪些数据

我们已知的条件是传递来的spu的id,我们需要根据spu的id查询到下面的数据:

  • spu信息
  • spu的详情
  • spu下的所有sku
  • 品牌
  • 商品三级分类
  • 商品规格参数、规格参数组
(1)商品微服务提供接口
1)查询spu,根据页面id查询对应的sup的信息
a、完善GoodsApi

在这里插入图片描述

   /*
    根据spu的id查询spu
     */
    @GetMapping("/spu/{id}")
    Spu querySpuById(@PathVariable("id") Long id);
  • 1
  • 2
  • 3
  • 4
  • 5
b、完善GoodsController

在这里插入图片描述

  /*
    根据spu的id查询spu
     */
    @GetMapping("/spu/{id}")
    public ResponseEntity<Spu> querySpuById(@PathVariable("id") Long id){
        return ResponseEntity.ok(goodsService.querySpuById(id));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
c、完善goodsService

在这里插入图片描述
在这里插入图片描述


    public Spu querySpuById(Long id) {
        Spu spu = spuMapper.selectByPrimaryKey(id);
        if(spu == null){
            throw  new LyException(ExceptionEnum.GOODS_NOT_FOND);
        }
        //查询sku
        List<Sku> skus = querySkuBySpuId(spu.getId());

        //查询详情detail
        SpuDetail spuDetail = queryDetailById(spu.getId());

        spu.setSkus(skus);
        spu.setSpuDetail(spuDetail);
        return spu;
    }
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
2)查询规格参数组

在这里插入图片描述

a、拓展SpecGroup

在这里插入图片描述

package com.leyou.item.pojo;

import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;

import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.List;

@Table(name = "tb_spec_group")
@Data
public class SpecGroup {

    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private Long cid;
    private String name;

    @Transient
    private List<SpecParam> params;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
b、完善SpecificationApi

在这里插入图片描述

    @GetMapping("spec/group")
    List<SpecGroup> queryGroupByCid(@RequestParam("cid") Long cid);
  • 1
  • 2
c、完善SpecificationController,这里的方法名可以与api当中的方法名不一致,因为调用是通过路径去调用的

在这里插入图片描述

  /*
    根据父类查询规格组以及组内参数
     */
    @GetMapping("group")
    public ResponseEntity<List<SpecGroup>> queryListByCid(@RequestParam("cid") Long cid){

        return ResponseEntity.ok(specificationService.queryListByCid(cid));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
d、完善specificationService

在这里插入图片描述
在这里插入图片描述


    public List<SpecGroup> queryListByCid(Long cid) {
        //查询规格参数组
        List<SpecGroup> specGroups = queryGroupByCid(cid);
        //查询当前分类下的参数
        List<SpecParam> specParams = queryParamList(null, cid, null);
        /*
        下拉代码的目的是将specParams集合放入specGroups当中,一一对应
        如果通过双层for循环性能太低
        在specParams集合当中的SpecParam对应拥有SpecGroup的id
        所以将SpecParam对应拥有SpecGroup的id作为键而 List<SpecParam>做为值组合为map集合
        遍历specGroups集合获取其specGroup的id到map当中查询到对应的specParams集合,然后放入到specGroup.setParams当中
         */
        //先把规格参数变为map,map的key是规格组id,map的值是组下的所有参数
        Map<Long,List<SpecParam>> map = new HashMap<>();
        for (SpecParam param : specParams) {
            if(!map.containsKey(param.getGroupId())){//判断map当中是否已经存在param.getGroupId(),如果存在
                //这一组id在map当中不存在
                //新增一个list
                map.put(param.getGroupId(),new ArrayList<>());
            }
            map.get(param.getGroupId()).add(param);//将集合添加到map当中
        }
        //填充param到Group当中
        for (SpecGroup specGroup : specGroups) {
            specGroup.setParams(map.get(specGroup.getId()));
        }

        return specGroups;

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
(2)实现页面数据的封装
1)在ly-page创建对应的包结构,复制之前在ly-search创建的client

在这里插入图片描述
在这里插入图片描述

2)分析页面所需要的数据

在这里插入图片描述
在这里插入图片描述

3)创建对应的Service

在这里插入图片描述
在这里插入图片描述

package com.leyou.page.service;

import org.springframework.stereotype.Service;

@Service
public class PageService {
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
4)将上述对应的数据在添加到PageController的Model当中
a、完善PageController

在这里插入图片描述

package com.leyou.page.web;

import com.leyou.page.service.PageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@Controller
@RequestMapping("item")
public class PageController {

    @Autowired
    private PageService pageService;

    @GetMapping("{id}.html")
    public String toItemPage(@PathVariable("id") Long spuId, Model model){
        //查询数据
        Map<String,Object> attributes = pageService.loadModel(spuId);

        //准备模型数据
        model.addAllAttributes(attributes);
        //返回视图
        return "item";
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
b、完善service

在这里插入图片描述
在这里插入图片描述

package com.leyou.page.service;

import com.leyou.item.pojo.*;
import com.leyou.page.client.BrandClient;
import com.leyou.page.client.CategoryClient;
import com.leyou.page.client.GoodsClient;
import com.leyou.page.client.SpecificationClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class PageService {

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private SpecificationClient specificationClient;


    public Map<String, Object> loadModel(Long spuId) {

        Map<String, Object> model = new HashMap<>();
        //查询spu
        Spu spu = goodsClient.querySpuById(spuId);
        //查询skus
        List<Sku> skus = spu.getSkus();
        //查询spuDetail
        SpuDetail spuDetail = spu.getSpuDetail();
        //查询brand
        Brand brand = brandClient.queryBrandById(spu.getBrandId());
        //查询商品的分类
        List<Category> categories = categoryClient.queryCategoryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
        //查询规格参数
        List<SpecGroup> groups = specificationClient.queryGroupByCid(spu.getCid3());

        // 查询特殊的规格参数
        List<SpecParam> params = specificationClient.queryParamList(null, spu.getCid3(), false);
        Map<Long, String> paramMap = new HashMap<>();
        params.forEach(param -> {
            paramMap.put(param.getId(), param.getName());
        });

        model.put("spu",spu);
        model.put("skus",skus);
        model.put("spuDetail",spuDetail);
        model.put("detail",spuDetail);
        model.put("brand",brand);
        model.put("categories",categories);
        model.put("groups",groups);
        model.put("specs",groups);
        model.put("paramMap",paramMap);
        return model;

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、页面静态化

1、简介

(1)问题分析

现在,我们的页面是通过Thymeleaf模板引擎渲染后返回到客户端。在后台需要大量的数据查询,而后渲染得到HTML页面。会对数据库造成压力,并且请求的响应时间过长,并发能力不高。

大家能想到什么办法来解决这个问题?

首先我们能想到的就是缓存技术,比如之前学习过的Redis。不过Redis适合数据规模比较小的情况。假如数据量比较大,例如我们的商品详情页。每个页面如果10kb,100万商品,就是10GB空间,对内存占用比较大。此时就给缓存系统带来极大压力,如果缓存崩溃,接下来倒霉的就是数据库了。

所以缓存并不是万能的,某些场景需要其它技术来解决,比如静态化。

(2)什么是静态化

静态化是指把动态生成的HTML页面变为静态内容保存,以后用户的请求到来,直接访问静态页面,不再经过服务的渲染。

而静态的HTML页面可以部署在nginx中,从而大大提高并发能力,减小tomcat压力。

(3)如何实现静态化

目前,静态化页面都是通过模板引擎来生成,而后保存到nginx服务器来部署。常用的模板引擎比如:

  • Freemarker
  • Velocity
  • Thymeleaf

我们之前就使用的Thymeleaf,来渲染html返回给用户。Thymeleaf除了可以把渲染结果写入Response,也可以写到本地文件,从而实现静态化。

2、Thymeleaf实现静态化

(1)概念

先说下Thymeleaf中的几个概念:

  • Context:运行上下文
  • TemplateResolver:模板解析器
  • TemplateEngine:模板引擎

Context

上下文: 用来保存模型数据,当模板引擎渲染时,可以从Context上下文中获取数据用于渲染。

当与SpringBoot结合使用时,我们放入Model的数据就会被处理到Context,作为模板渲染的数据使用。

TemplateResolver

模板解析器:用来读取模板相关的配置,例如:模板存放的位置信息,模板文件名称,模板文件的类型等等。

当与SpringBoot结合时,TemplateResolver已经由其创建完成,并且各种配置也都有默认值,比如模板存放位置,其默认值就是:templates。比如模板文件类型,其默认值就是html。

TemplateEngine

模板引擎:用来解析模板的引擎,需要使用到上下文、模板解析器。分别从两者中获取模板中需要的数据,模板文件。然后利用内置的语法规则解析,从而输出解析后的文件。来看下模板引擎进行处理的函数:

templateEngine.process("模板名", context, writer);
  • 1

三个参数:

  • 模板名称
  • 上下文:里面包含模型数据
  • writer:输出目的地的流

在输出时,我们可以指定输出的目的地,如果目的地是Response的流,那就是网络响应。如果目的地是本地文件,那就实现静态化了。

而在SpringBoot中已经自动配置了模板引擎,因此我们不需要关心这个。现在我们做静态化,就是把输出的目的地改成本地文件即可!

(2)具体实现
1)生成HTML文件

在这里插入图片描述


    public void createHtml(Long spuId) {
        //上下文
        Context context = new Context();
        context.setVariables(loadModel(spuId));
        //输出流(渲染输出)
        File dest = new File("D:/IDEAWORK/leyou/fileupload", spuId + ".html");
        try(PrintWriter writer = new PrintWriter(dest, "UTF-8")){
            //三个参数(- 模板名称 - 上下文:里面包含模型数据 - writer:输出目的地的流)
            templateEngine.process("item", context, writer);
        }catch (Exception e){
            log.error("[静态页服务]  生成静态页异常0",e);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
2)编写测试类
a、引入测试的依赖

在这里插入图片描述

 		 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4
b、编写测试类和测试方法

在这里插入图片描述

package com.leyou.page.service;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class PageServiceTest {

    @Autowired
    private PageService pageService;

    @Test
    public void createHtmlTest() {
        pageService.createHtml(141L);
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述
对应路径生成了HTML文件
在这里插入图片描述
打开可以看到内部代码
在这里插入图片描述

c、放入nginx当中在nginx的安装目录下当中的HTML目录下创建item目录

在nginx
使用mkdir item
在这里插入图片描述

d、将生成的HTML放入到对应的路径下

在这里插入图片描述

e、修改nginx的配置文件nginx.conf

设置先找本地的HTML文件如果找不到然后在找对应端口号的文件
在这里插入图片描述

		location /item {
			# 先找本地
			root html;
			if (!-f $request_filename) { #请求的文件不存在,就反向代理
				proxy_pass http://192.168.58.2:8086;
				break;
			}
		}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

抽象加载nginx -s reload

f、测试

将page服务停掉
在这里插入图片描述
访问:http://www.leyou.com/item/141.html

依旧访问成功
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/456086
推荐阅读
相关标签
  

闽ICP备14008679号