前面已经完成一个简单的商品中心服务提供商品列表接口,也用selenium捞了点数据,现在得折腾个展示页面,这个前端展示就直接html5+bootstrap+jquery来搞了,没有其他后端服务~

CV个导航栏

上bootstrap示例中cv一个nav下来改改

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
  <nav class="navbar navbar-expand-lg navbar-light bg-light rounded">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExample09" aria-controls="navbarsExample09" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" id="navbarsExample09">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled">Disabled</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
</ul>
<form class="form-inline my-2 my-md-0">
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
</form>
</div>
</nav>

随便用一个吧,就用grafana得图标整整

1
2
3
4
<a class="navbar-brand" href="#">
<img src="https://grafana.com/static/img/menu/grafana2.svg" width="" height="" alt=""
class="logo-img">
</a>

增加点效果,让logo图标选转起来,可以使用keyframes 来配置动画效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style>
.rounded-circle {
width: 30px;
height: 30px;
}

.rotate {
animation: rotation 3s infinite linear;
}

@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
</style>

最后nav中得图标img标签用上这个class即可

1
2
3
4
 <a class="navbar-brand" href="#">
<img src="https://grafana.com/static/img/menu/grafana2.svg" width="" height="" alt=""
class="logo-img rounded-circle rotate">
</a>

固定导航栏

如果要让导航栏固定在头部,可以给导航加个stick-top类属性

1
2
<nav class="navbar sticky-top navbar-expand-lg  navbar-light bg-light">
</nav>

搜索框调整

虽然搜索服务还没整,但是也先调整下样式,后面服务上了再细细搞

1
2
3
4
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-dark my-2 my-sm-0" type="submit">Search</button>
</form>

填充商品列表

创建个id为product-list的div,后面jq就根据这个id来定位更新元素

1
2
<div class="row" id="product-list">
</div>

用ajax获取后端数据,然后拼接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
    var products_center_addr = "http://products-center.xadocker.cn"
function renderProductList(page) {
$.getJSON(products_center_addr + '/products?per_page=24&page=' + page, function (data) {
var products = data.products;
totalPages = data.total_pages;

var html = '';
for (var i = 0; i < products.length; i++) {
var product = products[i];
html += '<div class="card col-2" >';
html += '<img class="card-img-top" src="' + product.img + '" alt="' + product.title + '" title="' + product.title + '">';
html += '<div class="card-body p-0">';
html += '<div class="row align-items-end">'
html += '<div class="col text-left">\n' +
' <span style="font-size: 15px; color: red;">¥' + product.price + '</span></div>';
html += '<h6 class="card-title" title="' + product.title + '">' + product.title + '</h6>';
html += '<div class="card-text" title="' + product.description + '"><span style="font-size: 15px; color: gray;">' + product.description + '</span></div>';


//html += '<div class="col text-right">\n' +
// ' <span style="font-size: 10px; color: gray;">库存:' + 10 + '</span></div>';
html += '</div>'
html += '</div></div>';
}
$('#product-list').html(html);
});
}

整个分页组件

在product-list的div下创个nav用来放置分页组件

1
2
3
4
<nav>
<ul class="pagination" id="pagination">
</ul>
</nav>

jq中处理分页信息方法

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
var currentPage = 1;
var totalPages = 1;
var viewPagesNum = 5;
function renderPagination() {
// 显示当前页码前后两个页码
var startPage = Math.max(1, currentPage - Math.floor(viewPagesNum / 2));
var endPage = Math.min(totalPages, currentPage + Math.floor(viewPagesNum / 2));
if (startPage == 1) {
endPage = viewPagesNum
}
if (endPage == totalPages) {
startPage = totalPages - viewPagesNum + 1
}


var html = '';

// 添加“上一页”按钮
html += '<li class="page-item' + (currentPage == 1 ? ' disabled' : '') + '"><a class="page-link" href="#" onclick="gotoPage(' + (currentPage - 1) + ')">上一页</a></li>';

// 添加页码按钮
for (var i = startPage; i <= endPage; i++) {
html += '<li class="page-item' + (i == currentPage ? ' active' : '') + '"><a class="page-link" href="#" onclick="gotoPage(' + i + ')">' + i + '</a></li>';
}

// 添加“下一页”按钮
html += '<li class="page-item' + (currentPage == totalPages ? ' disabled' : '') + '"><a class="page-link" href="#" onclick="gotoPage(' + (currentPage + 1) + ')">下一页</a></li>';
$('#pagination').html(html);
}

效果

效果图

最后效果如下,修修整整,浪费时间,没想到CV工程师不是CV这么简单。。。就先这样吧,反正博主只要能展示后端数据就好了

最后的代码

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
<!DOCTYPE html>
<html>
<head>
<title>DuckDuckRunning</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css">
<script src="js/jquery.min.js"></script>
<script src="js/popper.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<style>
body {
background-color: #f5f5f5;
}

.row {
margin: 0px;
}

.card {
margin: 0px;
border: none;
margin-top: 10px;
margin-bottom: 10px;
padding-top: 10px;
padding-left: 5px;
padding-right: 5px;
padding-bottom: 10px;
}

.card:hover {
z-index: 1;
border: 1px solid #c9c9c9; /* 设置边框为浅灰色 */
box-shadow: 0 0 1px 1px #f8f8f8;
}

.price-text {
font-size: 15px;
color: red;
margin-bottom: 0;

}

.stock-text {
margin-left: 5px;
margin-bottom: 0;
font-size: 10px;
color: grey;
}

.card-title, .card-text {
margin: 0;
padding-left: 10px;
margin-top: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}

.navbar-brand {
height: 100%;
display: flex;
align-items: center;
}

.rounded-circle {
width: 30px;
height: 30px;
}

.rotate {
animation: rotation 3s infinite linear;
}

@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}

.red-line {
height: 2px;
background-color: red;
}

.navbar {
border-bottom: 2px solid red;
}

.navbar-nav .nav-item.active .nav-link {
/*background-color: red;*/
color: red;

}

.navbar-nav li a {
font-weight: bold;
}
</style>
</head>
<body>


<div class="container">

<nav class="navbar sticky-top navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">
<img src="https://grafana.com/static/img/menu/grafana2.svg" width="" height="" alt=""
class="logo-img rounded-circle rotate">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">秒杀</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown"
aria-expanded="false">
类别
</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">电脑</a>
<a class="dropdown-item" href="#">手机</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">相机</a>
</div>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-dark my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
<div class="red-line"></div>
</nav>

<div class="row" id="product-list">
</div>
<nav>
<ul class="pagination" id="pagination">
</ul>
</nav>
</div>
<script>
var currentPage = 1;
var totalPages = 1;
var viewPagesNum = 5;
var products_center_addr = "http://products-center.xadocker.cn"

function renderProductList(page) {
$.getJSON(products_center_addr + '/products?per_page=24&page=' + page, function (data) {
var products = data.products;
totalPages = data.total_pages;

var html = '';
for (var i = 0; i < products.length; i++) {
var product = products[i];
html += '<div class="card col-2" >';
html += '<img class="card-img-top" src="' + product.img + '" alt="' + product.title + '" title="' + product.title + '">';
html += '<div class="card-body p-0">';
html += '<div class="row align-items-end">'
html += '<div class="col text-left">\n' +
' <span style="font-size: 15px; color: red;">¥' + product.price + '</span></div>';
html += '<h6 class="card-title" title="' + product.title + '">' + product.title + '</h6>';
html += '<div class="card-text" title="' + product.description + '"><span style="font-size: 15px; color: gray;">' + product.description + '</span></div>';


//html += '<div class="col text-right">\n' +
// ' <span style="font-size: 10px; color: gray;">库存:' + 10 + '</span></div>';
html += '</div>'
html += '</div></div>';
}
$('#product-list').html(html);
renderPagination();
});
}

function renderPagination() {
// 显示当前页码前后两个页码
var startPage = Math.max(1, currentPage - Math.floor(viewPagesNum / 2));
var endPage = Math.min(totalPages, currentPage + Math.floor(viewPagesNum / 2));
if (startPage == 1) {
endPage = viewPagesNum
}
if (endPage == totalPages) {
startPage = totalPages - viewPagesNum + 1
}


var html = '';

// 添加“上一页”按钮
html += '<li class="page-item' + (currentPage == 1 ? ' disabled' : '') + '"><a class="page-link" href="#" onclick="gotoPage(' + (currentPage - 1) + ')">上一页</a></li>';

// 添加页码按钮
for (var i = startPage; i <= endPage; i++) {
html += '<li class="page-item' + (i == currentPage ? ' active' : '') + '"><a class="page-link" href="#" onclick="gotoPage(' + i + ')">' + i + '</a></li>';
}

// 添加“下一页”按钮
html += '<li class="page-item' + (currentPage == totalPages ? ' disabled' : '') + '"><a class="page-link" href="#" onclick="gotoPage(' + (currentPage + 1) + ')">下一页</a></li>';
$('#pagination').html(html);
}

function gotoPage(page) {
if (page < 1 page > totalPages) {
return;
}
currentPage = page;
renderProductList(page);
}

$(document).ready(function () {
renderProductList(1);
});
</script>
</body>
</html>

部署文件

dockerfile

1
2
3
4
5
6
7
8
9
10
11
FROM openresty/openresty:1.21.4.1-buster

WORKDIR /app

COPY . .

COPY nginx.conf /etc/nginx/conf.d/default.conf

RUN rm -f nginx.conf Dockerfile
# 暴露端口
EXPOSE 80

nginx.conf

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
server {
listen 80;
server_name mall.xadocker.cn; # 修改为你的域名

# 设置网站根目录
root /app;

# 设置默认页面
index index.html;

# 静态资源缓存设置
location ~* \.(jscsspngjpgjpeggifico)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000";
access_log off;
}

# 处理HTML页面请求
location / {
try_files $uri $uri/ /index.html;
}

# 处理404错误
error_page 404 /404.html;
location = /404.html {
internal;
}
}