7.6 参考pdd,使用deepseek、gpt生成前端界面代码
This commit is contained in:
parent
3367c5ae73
commit
7ea47d62b2
327
docs/dev-ops/nginx/html/css/index.css
Normal file
327
docs/dev-ops/nginx/html/css/index.css
Normal file
@ -0,0 +1,327 @@
|
||||
/* index.css */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
/* 轮播图样式 */
|
||||
.swiper-container {
|
||||
width: 100%;
|
||||
height: 375px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.swiper-wrapper {
|
||||
display: flex;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.swiper-slide {
|
||||
flex: 0 0 100%;
|
||||
height: 375px;
|
||||
}
|
||||
.swiper-slide img {
|
||||
width:100%;
|
||||
height:100%;
|
||||
object-fit:contain; /* cover → contain 可看到整幅图 */
|
||||
background:#fff; /* 若比例不一致留白更自然 */
|
||||
}
|
||||
.swiper-pagination {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
.swiper-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.5);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.swiper-dot.active {
|
||||
background: #ff5000;
|
||||
width: 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
|
||||
/* 商品信息区域 */
|
||||
.group-left{
|
||||
margin-right:4px;
|
||||
color:#ff5000;
|
||||
font-weight:bold;
|
||||
}
|
||||
.product-info {
|
||||
background: #fff;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.price-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.current-price {
|
||||
color: #ff5000;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.current-price::before {
|
||||
content: "¥";
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
color: #999;
|
||||
font-size: 16px;
|
||||
text-decoration: line-through;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.original-price::before {
|
||||
content: "¥";
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.promo-tag {
|
||||
display: inline-block;
|
||||
background: linear-gradient(90deg, #ff2c2c, #ff6b22);
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.promo-info {
|
||||
color: #ff5000;
|
||||
font-size: 14px;
|
||||
margin: 8px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.promo-info i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* 拼单区域 - 修改了高度 */
|
||||
/* “仅剩 x 人成团” 前缀样式 */
|
||||
.left-num{
|
||||
color: #666;
|
||||
margin-right:4px;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.group-buying {
|
||||
background: #fff;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 3px;
|
||||
height: 16px;
|
||||
background: #ff5000;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.group-users {
|
||||
height: 120px; /* 修改为120px以容纳两条完整信息 */
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.user-list {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.user-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 0; /* 减小上下内边距 */
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.user-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 10px;
|
||||
color: #999;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-status {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
display: inline-block;
|
||||
background: #ff5000;
|
||||
color: white;
|
||||
padding: 1px 4px;
|
||||
border-radius: 2px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.buy-btn {
|
||||
background: linear-gradient(90deg, #ff2c2c, #ff6b22);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 15px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.action-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
height: 60px;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.action-btn i {
|
||||
font-size: 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.purchase-btn {
|
||||
flex: 2;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.btn-single {
|
||||
flex: 1;
|
||||
background: #ff9500;
|
||||
color: white;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
flex: 1;
|
||||
background: #ff5000;
|
||||
color: white;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ===== 支付弹窗 ===== */
|
||||
.pay-mask{
|
||||
position:fixed;inset:0;z-index:999;
|
||||
background:rgba(0,0,0,.45);
|
||||
display:none;align-items:center;justify-content:center;
|
||||
}
|
||||
|
||||
/* ① 适当放宽弹窗,保证二维码和按钮都有留白 */
|
||||
.pay-box{
|
||||
width:92%; /* → 手机端留 8% 边距 */
|
||||
max-width:420px; /* → PC 也能放下 260px 二维码 */
|
||||
background:#fff;
|
||||
border-radius:10px;
|
||||
padding:28px 24px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
/* ② 用 .qr-code 选中 <img>,限制尺寸并居中 */
|
||||
.qr-code{
|
||||
width:260px; /* 你想要的显示大小 */
|
||||
max-width:100%; /* 小屏自动缩小 */
|
||||
height:auto;
|
||||
display:block;
|
||||
margin:0 auto 32px; /* 居中 + 与按钮留间距 */
|
||||
border-radius:12px;
|
||||
object-fit:contain; /* 防止被拉伸/裁切 */
|
||||
}
|
||||
|
||||
/* 其余按钮样式保持不变 */
|
||||
.pay-btns{display:flex;gap:16px;justify-content:center;}
|
||||
.btn-primary,.btn-secondary{
|
||||
flex:1;padding:12px 0;border:none;border-radius:10px;
|
||||
font-size:16px;font-weight:600;cursor:pointer;
|
||||
}
|
||||
.btn-primary{background:#12a400;color:#fff;}
|
||||
.btn-secondary{background:#f0f0f0;}
|
91
docs/dev-ops/nginx/html/css/login.css
Normal file
91
docs/dev-ops/nginx/html/css/login.css
Normal file
@ -0,0 +1,91 @@
|
||||
/* 整体布局 */
|
||||
*{margin:0;padding:0;box-sizing:border-box;font-family:'PingFang SC','Helvetica Neue',Arial,sans-serif;}
|
||||
|
||||
body{
|
||||
height:100vh;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
background:#d5d1e8; /* 柔和紫色背景 */
|
||||
}
|
||||
|
||||
.container{
|
||||
width:90%;max-width:420px;
|
||||
}
|
||||
|
||||
.login-form{
|
||||
background:#fff;
|
||||
padding:36px 28px;
|
||||
border-radius:14px;
|
||||
box-shadow:0 4px 12px rgba(0,0,0,.08);
|
||||
}
|
||||
|
||||
.login-form h2{
|
||||
font-size:24px;
|
||||
font-weight:600;
|
||||
text-align:center;
|
||||
margin-bottom:32px;
|
||||
}
|
||||
|
||||
/* 输入框组 */
|
||||
.input-group{
|
||||
position:relative;
|
||||
margin-bottom:26px;
|
||||
}
|
||||
|
||||
.input-group input{
|
||||
width:100%;
|
||||
padding:14px 16px;
|
||||
font-size:16px;
|
||||
border:2px solid #aaa3;
|
||||
border-radius:10px;
|
||||
outline:none;
|
||||
transition:border .25s;
|
||||
}
|
||||
|
||||
.input-group input:focus{
|
||||
border-color:#1296ff;
|
||||
}
|
||||
|
||||
.input-group label{
|
||||
position:absolute;
|
||||
left:18px;
|
||||
top:50%;
|
||||
transform:translateY(-50%);
|
||||
color:#999;
|
||||
pointer-events:none;
|
||||
transition:all .25s;
|
||||
}
|
||||
|
||||
/* 上浮效果 */
|
||||
.input-group input:focus + label,
|
||||
.input-group input:not(:placeholder-shown) + label{
|
||||
top:0;
|
||||
transform:translateY(-50%) scale(.86);
|
||||
background:#fff;
|
||||
padding:0 4px;
|
||||
color:#1296ff;
|
||||
}
|
||||
|
||||
/* 登录按钮 */
|
||||
button[type="submit"]{
|
||||
width:100%;
|
||||
padding:14px 0;
|
||||
font-size:18px;
|
||||
font-weight:600;
|
||||
color:#fff;
|
||||
background:#1296ff;
|
||||
border:none;
|
||||
border-radius:10px;
|
||||
cursor:pointer;
|
||||
transition:opacity .25s;
|
||||
}
|
||||
button[type="submit"]:hover{opacity:.9;}
|
||||
|
||||
.error-message{
|
||||
margin-top:18px;
|
||||
font-size:14px;
|
||||
color:#e02424;
|
||||
text-align:center;
|
||||
display:none;
|
||||
}
|
BIN
docs/dev-ops/nginx/html/images/goods_info1.png
Normal file
BIN
docs/dev-ops/nginx/html/images/goods_info1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 347 KiB |
BIN
docs/dev-ops/nginx/html/images/goods_info2.png
Normal file
BIN
docs/dev-ops/nginx/html/images/goods_info2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 281 KiB |
BIN
docs/dev-ops/nginx/html/images/goods_info3.png
Normal file
BIN
docs/dev-ops/nginx/html/images/goods_info3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
docs/dev-ops/nginx/html/images/qrcode.png
Normal file
BIN
docs/dev-ops/nginx/html/images/qrcode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 455 KiB |
143
docs/dev-ops/nginx/html/index.html
Normal file
143
docs/dev-ops/nginx/html/index.html
Normal file
@ -0,0 +1,143 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>手写MyBatis:渐进式源码实践 - 拼多多</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="css/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 顶部轮播图 -->
|
||||
<div class="swiper-container">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide"><img src="images/goods_info2.png"></div>
|
||||
<div class="swiper-slide"><img src="images/goods_info3.png"></div>
|
||||
<div class="swiper-slide"><img src="images/goods_info1.png"></div>
|
||||
</div>
|
||||
<div class="swiper-pagination"></div>
|
||||
</div>
|
||||
|
||||
<!-- 商品信息区域 -->
|
||||
<div class="product-info">
|
||||
<div class="price-row">
|
||||
<div class="current-price">80</div>
|
||||
<div class="original-price">100</div>
|
||||
</div>
|
||||
<div class="title">手写MyBatis:渐进式源码实践(全彩)</div>
|
||||
<div>
|
||||
<span class="promo-tag">大促优惠</span>
|
||||
<span>直降¥60,76人再抢,参与马上抢到</span>
|
||||
</div>
|
||||
<div class="promo-info">
|
||||
<i class="fas fa-fire"></i>
|
||||
<!-- 新增:仅剩 x 人成团 -->
|
||||
<span class="group-left"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 拼单区域 - 修改了高度 -->
|
||||
<div class="group-buying">
|
||||
<div class="section-title">正在拼单</div>
|
||||
<div class="group-users">
|
||||
<div class="user-list" id="userList">
|
||||
<div class="user-item">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">宇哥</div>
|
||||
<div class="user-status">拼单即将结束 <span class="countdown">00:05:49</span></div>
|
||||
</div>
|
||||
<button class="buy-btn" data-price="80">参与拼团</button>
|
||||
</div>
|
||||
<div class="user-item">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">李二狗</div>
|
||||
<div class="user-status">拼单即将结束 <span class="countdown">00:05:49</span></div>
|
||||
</div>
|
||||
<button class="buy-btn" data-price="80">参与拼团</button>
|
||||
</div>
|
||||
<div class="user-item">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">张全蛋</div>
|
||||
<div class="user-status">拼单即将结束 <span class="countdown">00:02:30</span></div>
|
||||
</div>
|
||||
<button class="buy-btn" data-price="80">参与拼团</button>
|
||||
</div>
|
||||
<div class="user-item">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">王翠花</div>
|
||||
<div class="user-status">拼单即将结束 <span class="countdown">00:01:15</span></div>
|
||||
</div>
|
||||
<button class="buy-btn" data-price="80">参与拼团</button>
|
||||
</div>
|
||||
<!-- 添加更多用户项确保轮播效果 -->
|
||||
<div class="user-item">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">刘大壮</div>
|
||||
<div class="user-status">拼单即将结束 <span class="countdown">00:03:45</span></div>
|
||||
</div>
|
||||
<button class="buy-btn" data-price="80">参与拼团</button>
|
||||
</div>
|
||||
<div class="user-item">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">赵小敏</div>
|
||||
<div class="user-status" data-price="80">拼单即将结束 <span class="countdown">00:04:20</span></div>
|
||||
</div>
|
||||
<button class="buy-btn">参与拼团</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="action-bar">
|
||||
<div class="action-btn">
|
||||
<i class="fas fa-home"></i>
|
||||
<span>首页</span>
|
||||
</div>
|
||||
<div class="action-btn">
|
||||
<i class="fas fa-heart"></i>
|
||||
<span>收藏</span>
|
||||
</div>
|
||||
<div class="action-btn">
|
||||
<i class="fas fa-shopping-cart"></i>
|
||||
<span>购物车</span>
|
||||
</div>
|
||||
<div class="purchase-btn">
|
||||
<button class="btn-single" data-price="100">单独购买</button>
|
||||
<button class="btn-group" data-price="80">开团购买</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 支付弹窗(默认隐藏) -->
|
||||
<div id="paymentModal" class="pay-mask">
|
||||
<div class="pay-box">
|
||||
<h2 class="pay-title">请扫码支付</h2>
|
||||
<p class="pay-amount" id="paymentAmount"></p>
|
||||
<img src="images/qrcode.png" alt="支付二维码" class="qr-code">
|
||||
<div class="pay-btns">
|
||||
<button id="cancelPayment" class="btn-secondary">取消支付</button>
|
||||
<button id="completePayment" class="btn-primary">支付完成</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/index.js"></script>
|
||||
</body>
|
||||
</html>
|
198
docs/dev-ops/nginx/html/js/index.js
Normal file
198
docs/dev-ops/nginx/html/js/index.js
Normal file
@ -0,0 +1,198 @@
|
||||
// index.js (改进版)
|
||||
// 功能:
|
||||
// 1. 解决用户列表竖向轮播在无缝跳转时出现的卡顿/闪动问题
|
||||
// 2. 为每条拼单信息增加实时倒计时,秒级更新
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
/* ---------- 顶部横向轮播 ---------- */
|
||||
const wrapper = document.querySelector('.swiper-wrapper');
|
||||
const slides = [...wrapper.children];
|
||||
const pagination = document.querySelector('.swiper-pagination');
|
||||
const count = slides.length;
|
||||
|
||||
let current = 0; // 当前索引
|
||||
let startX = 0; // 手势起点
|
||||
let dragging = false; // 拖动状态
|
||||
let timer = null; // 自动轮播计时器
|
||||
|
||||
/* --- 1. 生成分页小圆点 --- */
|
||||
for(let i=0;i<count;i++){
|
||||
const dot = document.createElement('div');
|
||||
dot.className = 'swiper-dot' + (i===0?' active':'');
|
||||
dot.addEventListener('click',()=>goTo(i));
|
||||
pagination.appendChild(dot);
|
||||
}
|
||||
const dots = pagination.children;
|
||||
|
||||
/* --- 2. 切换核心 --- */
|
||||
function goTo(index){
|
||||
current = (index + count) % count; // 防越界
|
||||
wrapper.style.transition = 'transform .3s ease';
|
||||
wrapper.style.transform = `translateX(-${current*100}%)`;
|
||||
[...dots].forEach((d,i)=>d.classList.toggle('active',i===current));
|
||||
}
|
||||
|
||||
/* --- 3. 自动轮播 --- */
|
||||
function startAuto(){
|
||||
timer = setInterval(()=>goTo(current+1),3000);
|
||||
}
|
||||
function stopAuto(){
|
||||
clearInterval(timer);
|
||||
}
|
||||
startAuto();
|
||||
|
||||
/* --- 4. 手势/鼠标拖动 --- */
|
||||
const getX = e => e.touches ? e.touches[0].clientX : e.clientX;
|
||||
|
||||
wrapper.addEventListener('pointerdown',e=>{
|
||||
stopAuto();
|
||||
dragging = true;
|
||||
startX = getX(e);
|
||||
wrapper.style.transition = 'none'; // 跟手拖动
|
||||
});
|
||||
|
||||
wrapper.addEventListener('pointermove',e=>{
|
||||
if(!dragging) return;
|
||||
const diff = getX(e) - startX; // 像素位移
|
||||
wrapper.style.transform =
|
||||
`translateX(calc(${-current*100}% + ${diff}px))`;
|
||||
});
|
||||
|
||||
const endSwipe = e=>{
|
||||
if(!dragging) return;
|
||||
dragging = false;
|
||||
const diff = getX(e) - startX;
|
||||
const limit = wrapper.offsetWidth * 0.15; // 15% 宽度阈值
|
||||
if(diff > limit) goTo(current-1);
|
||||
else if(diff < -limit) goTo(current+1);
|
||||
else goTo(current); // 回弹
|
||||
startAuto();
|
||||
};
|
||||
wrapper.addEventListener('pointerup', endSwipe);
|
||||
wrapper.addEventListener('pointercancel', endSwipe);
|
||||
wrapper.addEventListener('pointerleave', endSwipe);
|
||||
|
||||
|
||||
/* --------- 动态生成“xx人在抢,参与可立即拼成” --------- */
|
||||
const leftNum = Math.floor(Math.random() * 101) + 100; // 100 ~ 200 之间的随机整数
|
||||
const leftSpan = document.querySelector('.group-left');
|
||||
if (leftSpan) leftSpan.textContent = `${leftNum}人在抢,参与可立即拼成`;
|
||||
|
||||
/* ---------- 拼单用户纵向轮播 ---------- */
|
||||
const userList = document.getElementById('userList');
|
||||
const userItems = userList.querySelectorAll('.user-item');
|
||||
const itemHeight = userItems[0].offsetHeight;
|
||||
let userIndex = 0;
|
||||
const originalCount = userItems.length;
|
||||
|
||||
// 克隆第一条放到末尾,实现无缝衔接
|
||||
userList.appendChild(userItems[0].cloneNode(true));
|
||||
|
||||
userList.addEventListener('transitionend', () => {
|
||||
if (userIndex >= originalCount) {
|
||||
// 闪电跳回首条,关闭过渡以避免闪屏
|
||||
userList.style.transition = 'none';
|
||||
userList.style.transform = 'translateY(0)';
|
||||
userIndex = 0;
|
||||
// 强制回流,保证下次 transition 生效
|
||||
void userList.offsetWidth;
|
||||
}
|
||||
});
|
||||
|
||||
function rotateUsers() {
|
||||
userIndex++;
|
||||
userList.style.transition = 'transform 0.5s ease';
|
||||
userList.style.transform = `translateY(${-userIndex * itemHeight}px)`;
|
||||
}
|
||||
|
||||
// 每 3 秒滚动一次
|
||||
setInterval(rotateUsers, 3000);
|
||||
// 页面加载后立即滚动一次,保证视觉一致
|
||||
rotateUsers();
|
||||
|
||||
/* ---------- 拼单倒计时 ---------- */
|
||||
const countdownEls = document.querySelectorAll('.countdown');
|
||||
// 预处理:把初始文本转为秒数
|
||||
const countdownData = Array.from(countdownEls).map((el) => ({
|
||||
el,
|
||||
remain: parseTime(el.textContent.trim()),
|
||||
}));
|
||||
|
||||
function parseTime(t) {
|
||||
const [h = '00', m = '00', s = '00'] = t.split(':');
|
||||
return Number(h) * 3600 + Number(m) * 60 + Number(s);
|
||||
}
|
||||
function formatTime(sec) {
|
||||
const h = String(Math.floor(sec / 3600)).padStart(2, '0');
|
||||
const m = String(Math.floor((sec % 3600) / 60)).padStart(2, '0');
|
||||
const s = String(sec % 60).padStart(2, '0');
|
||||
return `${h}:${m}:${s}`;
|
||||
}
|
||||
|
||||
function updateCountdown() {
|
||||
countdownData.forEach((c) => {
|
||||
if (c.remain > 0) {
|
||||
c.remain -= 1;
|
||||
c.el.textContent = formatTime(c.remain);
|
||||
} else {
|
||||
c.el.textContent = '00:00:00';
|
||||
// 可选:到点后给整条加灰色样式,并禁用按钮
|
||||
const item = c.el.closest('.user-item');
|
||||
item?.classList.add('expired');
|
||||
item?.querySelector('.buy-btn')?.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* --------- 给每条拼单状态前加 “仅剩 x 人成团” --------- */
|
||||
document.querySelectorAll('.user-status').forEach(statusEl=>{
|
||||
const x = Math.floor(Math.random()*3)+1; // 1 ~ 3 随机整数
|
||||
const span = document.createElement('span');
|
||||
span.className = 'left-num';
|
||||
span.textContent = `仅剩${x}人成团,`; // 注意带逗号或空格
|
||||
statusEl.prepend(span);
|
||||
});
|
||||
|
||||
/* ============= 支付 & 登录判断 ============= */
|
||||
const modal = document.getElementById('paymentModal');
|
||||
const paymentAmount = document.getElementById('paymentAmount');
|
||||
const cancelPayment = document.getElementById('cancelPayment');
|
||||
const completePayment= document.getElementById('completePayment');
|
||||
|
||||
/* 把 3 类按钮统一选出来 */
|
||||
[...document.querySelectorAll('.buy-btn, .btn-single, .btn-group')].forEach(btn=>{
|
||||
btn.addEventListener('click',()=>{
|
||||
/* 简单读取 cookie 判断是否登录 */
|
||||
if(!getCookie('username')){
|
||||
window.location.href='login.html'; // 跳转到登录页
|
||||
return;
|
||||
}
|
||||
/* 已登录:弹出支付弹窗 */
|
||||
const price = btn.dataset.price || '0';
|
||||
paymentAmount.textContent = `支付金额 ¥${price}`;
|
||||
modal.style.display='flex';
|
||||
});
|
||||
});
|
||||
|
||||
/* 取消/完成支付 */
|
||||
cancelPayment.addEventListener('click', ()=>modal.style.display='none');
|
||||
completePayment.addEventListener('click', ()=>{
|
||||
alert('支付成功!');
|
||||
modal.style.display='none';
|
||||
});
|
||||
|
||||
/* 读取 cookie 工具函数 */
|
||||
function getCookie(name){
|
||||
return document.cookie.split(';').map(c=>c.trim())
|
||||
.find(c=>c.startsWith(name+'='))?.split('=')[1] || null;
|
||||
}
|
||||
|
||||
/* 点击遮罩空白关闭弹窗 */
|
||||
modal.addEventListener('click', e=>{
|
||||
if(e.target===modal) modal.style.display='none';
|
||||
});
|
||||
|
||||
|
||||
// 每秒刷新一次倒计时
|
||||
setInterval(updateCountdown, 1000);
|
||||
});
|
28
docs/dev-ops/nginx/html/js/login.js
Normal file
28
docs/dev-ops/nginx/html/js/login.js
Normal file
@ -0,0 +1,28 @@
|
||||
document.addEventListener('DOMContentLoaded',()=>{
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
|
||||
loginForm.addEventListener('submit',e=>{
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('username').value.trim();
|
||||
const password = document.getElementById('password').value.trim();
|
||||
|
||||
if(!username || !password){
|
||||
errorMessage.textContent = '用户名和密码不能为空';
|
||||
errorMessage.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
/* 这里可替换为真实校验逻辑 —— 目前直接视为成功 */
|
||||
errorMessage.style.display = 'none';
|
||||
|
||||
/* 写入 cookie,1 天有效 */
|
||||
const expire = new Date();
|
||||
expire.setDate(expire.getDate() + 1);
|
||||
document.cookie = `username=${encodeURIComponent(username)}; expires=${expire.toUTCString()}; path=/`;
|
||||
|
||||
/* 登录后跳回首页(商品详情页) */
|
||||
window.location.href = 'index.html';
|
||||
});
|
||||
});
|
31
docs/dev-ops/nginx/html/login.html
Normal file
31
docs/dev-ops/nginx/html/login.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
|
||||
<title>欢迎登录 - 小傅哥拼团</title>
|
||||
<link rel="stylesheet" href="css/login.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<form id="loginForm" class="login-form">
|
||||
<h2>欢迎登录 - 小傅哥拼团</h2>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" id="username" required/>
|
||||
<label for="username">用户名</label>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="password" id="password" required/>
|
||||
<label for="password">密码</label>
|
||||
</div>
|
||||
|
||||
<button type="submit">登录</button>
|
||||
<p class="error-message" id="errorMessage"></p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="js/login.js"></script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user