
前面已经完成一个简单的商品中心服务提供商品列表接口,也用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>
|
增加个logo
随便用一个吧,就用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; } }
|