Files
web_template/web/dist/shop/index.html
admin 07c506354d feat: 跨境电商首页高端重设计 - 全屏动态壁纸+光追效果
- 全屏Hero: 深色背景+Canvas粒子网络动态壁纸(80粒子红/金/橙)
- 光追效果: 鼠标移动时粒子被吸引+射线连接+8道放射光追
- 悬浮导航栏: 半透明毛玻璃+滚动变色(深色→白色)
- 渐变标题: 红金渐变文字+发光效果
- 统计数据条: 4项核心数据(18+品类/50万用户/99%好评/7天退换)
- 滚动提示: 底部弹跳动画箭头
2026-07-11 15:39:10 +08:00

1135 lines
57 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlobalShop · 跨境电商平台</title>
<style>
/* ========== 主题变量 ========== */
:root {
--primary: #E63946;
--primary-dark: #D62828;
--primary-light: #FF6B6B;
--accent: #457B9D;
--dark: #1D3557;
--bg: #F8F9FA;
--surface: #FFFFFF;
--text: #1D3557;
--text-muted: #6C757D;
--border: #E9ECEF;
--shadow: 0 2px 8px rgba(0,0,0,0.06);
--shadow-lg: 0 8px 30px rgba(0,0,0,0.12);
--radius: 10px;
--radius-sm: 6px;
--radius-lg: 16px;
--transition: all 0.25s cubic-bezier(0.4,0,0.2,1);
}
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; background:var(--bg); color:var(--text); line-height:1.6; -webkit-font-smoothing:antialiased; }
a { text-decoration:none; color:inherit; }
button { cursor:pointer; border:none; font-family:inherit; }
input { font-family:inherit; }
img { max-width:100%; display:block; }
/* ========== 顶部公告栏 ========== */
.topbar { background:var(--dark); color:#fff; font-size:13px; padding:8px 0; text-align:center; }
.topbar span { margin:0 16px; opacity:0.9; }
.topbar a { color:var(--primary-light); }
/* ========== 导航栏 ========== */
.topbar { background:#0a0a0f; color:rgba(255,255,255,0.6); }
.navbar { background:rgba(10,10,15,0.6); backdrop-filter:blur(20px); position:fixed; top:32px; left:0; right:0; z-index:100; border-bottom:1px solid rgba(255,255,255,0.06); }
.navbar .nav-links a, .navbar .nav-brand, .navbar .search-box input, .navbar .search-box svg, .navbar .nav-actions span { color:rgba(255,255,255,0.85) !important; }
.navbar .search-box { background:rgba(255,255,255,0.06) !important; border:1px solid rgba(255,255,255,0.1) !important; }
.navbar .search-box input::placeholder { color:rgba(255,255,255,0.4) !important; }
.nav-container { max-width:1280px; margin:0 auto; padding:0 24px; display:flex; align-items:center; gap:32px; height:64px; }
.logo { font-size:24px; font-weight:800; color:var(--primary); display:flex; align-items:center; gap:8px; white-space:nowrap; }
.logo svg { width:28px; height:28px; }
.nav-links { display:flex; gap:4px; list-style:none; }
.nav-links a { padding:8px 16px; border-radius:var(--radius-sm); font-size:14px; font-weight:500; color:var(--text); transition:var(--transition); }
.nav-links a:hover, .nav-links a.active { background:var(--primary); color:#fff; }
.search-box { flex:1; max-width:400px; position:relative; }
.search-box input { width:100%; height:40px; padding:0 16px 0 40px; border:2px solid var(--border); border-radius:var(--radius); font-size:14px; outline:none; transition:var(--transition); }
.search-box input:focus { border-color:var(--primary); }
.search-box svg { position:absolute; left:12px; top:50%; transform:translateY(-50%); width:18px; height:18px; color:var(--text-muted); }
.nav-actions { display:flex; align-items:center; gap:8px; }
.nav-actions button { background:none; width:40px; height:40px; border-radius:50%; display:flex; align-items:center; justify-content:center; transition:var(--transition); position:relative; }
.nav-actions button:hover { background:var(--bg); }
.nav-actions svg { width:22px; height:22px; color:var(--text); }
.cart-badge { position:absolute; top:2px; right:2px; background:var(--primary); color:#fff; font-size:11px; font-weight:700; min-width:18px; height:18px; border-radius:9px; display:flex; align-items:center; justify-content:center; padding:0 4px; }
/* ========== Hero 动态壁纸 ========== */
.hero { position:relative; width:100%; height:100vh; min-height:640px; overflow:hidden; background:#0a0a0f; }
#heroCanvas { position:absolute; inset:0; width:100%; height:100%; z-index:1; }
.hero-overlay { position:absolute; inset:0; z-index:2; background:radial-gradient(ellipse at 30% 40%, rgba(230,57,70,0.15) 0%, transparent 50%),linear-gradient(180deg,rgba(10,10,15,0.3) 0%,rgba(10,10,15,0.7) 70%,rgba(10,10,15,0.95) 100%); pointer-events:none; }
.hero-light { position:absolute; z-index:3; pointer-events:none; width:600px; height:600px; border-radius:50%; background:radial-gradient(circle,rgba(230,57,70,0.25) 0%,rgba(230,57,70,0.08) 30%,transparent 60%); transform:translate(-50%,-50%); mix-blend-mode:screen; filter:blur(20px); transition:opacity .3s; opacity:0; }
.hero-light.active { opacity:1; }
.hero-content { position:absolute; inset:0; z-index:4; display:flex; flex-direction:column; justify-content:center; align-items:flex-start; padding:0 8vw; max-width:1400px; margin:0 auto; }
.hero-badge { display:inline-flex; align-items:center; gap:8px; padding:6px 18px; border-radius:100px; background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.12); font-size:13px; color:rgba(255,255,255,0.8); margin-bottom:24px; backdrop-filter:blur(10px); }
.hero-badge .dot { width:6px; height:6px; border-radius:50%; background:#E63946; box-shadow:0 0 8px #E63946; animation:pulse 2s infinite; }
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
.hero-content h1 { font-size:72px; font-weight:900; line-height:1.05; letter-spacing:-2px; color:#fff; margin-bottom:20px; text-shadow:0 4px 30px rgba(0,0,0,0.5); }
.hero-content h1 .accent { background:linear-gradient(135deg,#E63946 0%,#FF6B6B 50%,#FFD700 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.hero-content p { font-size:20px; color:rgba(255,255,255,0.6); max-width:560px; line-height:1.6; margin-bottom:36px; }
.hero-actions { display:flex; gap:16px; align-items:center; }
.hero-btn-primary { display:inline-flex; align-items:center; gap:10px; background:linear-gradient(135deg,#E63946,#D62828); color:#fff; padding:16px 40px; border-radius:100px; font-size:16px; font-weight:700; transition:all .3s; box-shadow:0 8px 32px rgba(230,57,70,0.4); border:none; cursor:pointer; }
.hero-btn-primary:hover { transform:translateY(-3px); box-shadow:0 12px 40px rgba(230,57,70,0.55); }
.hero-btn-ghost { display:inline-flex; align-items:center; gap:8px; background:rgba(255,255,255,0.06); color:#fff; padding:16px 32px; border-radius:100px; font-size:15px; font-weight:600; border:1px solid rgba(255,255,255,0.15); cursor:pointer; transition:all .3s; backdrop-filter:blur(10px); }
.hero-btn-ghost:hover { background:rgba(255,255,255,0.12); border-color:rgba(255,255,255,0.25); }
.hero-stats { position:absolute; bottom:48px; left:0; right:0; z-index:4; display:flex; gap:48px; justify-content:center; padding:0 24px; }
.hero-stat { text-align:center; }
.hero-stat .num { font-size:36px; font-weight:800; color:#fff; }
.hero-stat .num span { color:#E63946; }
.hero-stat .label { font-size:13px; color:rgba(255,255,255,0.5); margin-top:2px; }
.hero-scroll { position:absolute; bottom:20px; left:50%; transform:translateX(-50%); z-index:4; color:rgba(255,255,255,0.4); font-size:12px; display:flex; flex-direction:column; align-items:center; gap:6px; animation:bounce 2s infinite; }
@keyframes bounce { 0%,100%{transform:translateX(-50%) translateY(0)} 50%{transform:translateX(-50%) translateY(-8px)} }
.hero-scroll svg { width:20px; height:20px; }
@media(max-width:768px){ .hero-content h1{font-size:42px} .hero-content p{font-size:16px} .hero-stats{gap:20px} .hero-stat .num{font-size:24px} }
/* ========== 优势条 ========== */
.features-strip { max-width:1280px; margin:24px auto; padding:0 24px; display:grid; grid-template-columns:repeat(4,1fr); gap:16px; }
.feature-item { background:var(--surface); border-radius:var(--radius); padding:20px; display:flex; align-items:center; gap:14px; box-shadow:var(--shadow); }
.feature-item svg { width:36px; height:36px; color:var(--primary); flex-shrink:0; }
.feature-item h4 { font-size:14px; font-weight:700; }
.feature-item p { font-size:12px; color:var(--text-muted); }
/* ========== 分类导航 ========== */
.section { max-width:1280px; margin:48px auto; padding:0 24px; }
.section-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:24px; }
.section-title { font-size:26px; font-weight:800; position:relative; padding-left:16px; }
.section-title::before { content:''; position:absolute; left:0; top:50%; transform:translateY(-50%); width:4px; height:28px; background:var(--primary); border-radius:2px; }
.section-more { font-size:14px; color:var(--primary); font-weight:600; }
.category-nav { display:flex; gap:12px; overflow-x:auto; padding-bottom:8px; margin-bottom:24px; }
.category-chip { padding:10px 24px; border-radius:var(--radius); background:var(--surface); border:2px solid var(--border); font-size:14px; font-weight:600; color:var(--text); white-space:nowrap; transition:var(--transition); }
.category-chip:hover { border-color:var(--primary); color:var(--primary); }
.category-chip.active { background:var(--primary); color:#fff; border-color:var(--primary); }
/* ========== 商品网格 ========== */
.products-layout { display:grid; grid-template-columns:220px 1fr; gap:24px; }
.filters-sidebar { background:var(--surface); border-radius:var(--radius); padding:20px; box-shadow:var(--shadow); height:fit-content; position:sticky; top:80px; }
.filters-sidebar h3 { font-size:16px; font-weight:700; margin-bottom:16px; padding-bottom:12px; border-bottom:1px solid var(--border); }
.filter-group { margin-bottom:20px; }
.filter-group h4 { font-size:13px; font-weight:700; margin-bottom:10px; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.5px; }
.filter-option { display:flex; align-items:center; gap:8px; padding:6px 0; cursor:pointer; font-size:14px; }
.filter-option input { accent-color:var(--primary); }
.filter-option label { cursor:pointer; }
.filter-option .count { margin-left:auto; font-size:12px; color:var(--text-muted); }
.products-main { min-width:0; }
.products-toolbar { display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; flex-wrap:wrap; gap:12px; }
.products-count { font-size:14px; color:var(--text-muted); }
.sort-select { padding:8px 16px; border:2px solid var(--border); border-radius:var(--radius-sm); font-size:14px; outline:none; background:var(--surface); cursor:pointer; }
.sort-select:focus { border-color:var(--primary); }
.products-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:20px; }
.product-card { background:var(--surface); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); transition:var(--transition); cursor:pointer; position:relative; }
.product-card:hover { transform:translateY(-4px); box-shadow:var(--shadow-lg); }
.product-image { width:100%; aspect-ratio:1; background:linear-gradient(135deg,#f0f0f0,#e0e0e0); position:relative; overflow:hidden; }
.product-image-gradient { width:100%; height:100%; display:flex; align-items:center; justify-content:center; }
.product-image-gradient svg { width:50%; height:50%; opacity:0.3; }
.product-tags { position:absolute; top:10px; left:10px; display:flex; flex-direction:column; gap:4px; }
.product-tag { padding:3px 10px; border-radius:var(--radius-sm); font-size:11px; font-weight:700; color:#fff; }
.tag-hot { background:var(--primary); }
.tag-new { background:var(--accent); }
.tag-sale { background:#FF8C00; }
.product-discount { position:absolute; top:10px; right:10px; background:var(--primary); color:#fff; font-size:12px; font-weight:700; padding:4px 8px; border-radius:var(--radius-sm); }
.product-info { padding:14px; }
.product-name { font-size:14px; font-weight:600; line-height:1.4; margin-bottom:8px; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; min-height:39px; }
.product-price { display:flex; align-items:baseline; gap:8px; margin-bottom:8px; }
.price-current { font-size:20px; font-weight:800; color:var(--primary); }
.price-original { font-size:14px; color:var(--text-muted); text-decoration:line-through; }
.product-meta { display:flex; align-items:center; justify-content:space-between; font-size:12px; color:var(--text-muted); }
.product-rating { display:flex; align-items:center; gap:4px; }
.stars { color:#FFD700; letter-spacing:1px; }
.product-sales { }
.add-cart-btn { position:absolute; bottom:14px; right:14px; width:36px; height:36px; border-radius:50%; background:var(--primary); color:#fff; display:flex; align-items:center; justify-content:center; opacity:0; transform:translateY(10px); transition:var(--transition); }
.product-card:hover .add-cart-btn { opacity:1; transform:translateY(0); }
.add-cart-btn:hover { background:var(--primary-dark); transform:scale(1.1); }
.add-cart-btn svg { width:18px; height:18px; }
/* ========== 加载骨架 ========== */
.skeleton-card { background:var(--surface); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); }
.skeleton-img { aspect-ratio:1; background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%); background-size:200% 100%; animation:shimmer 1.5s infinite; }
.skeleton-line { height:14px; margin:12px 14px; border-radius:4px; background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%); background-size:200% 100%; animation:shimmer 1.5s infinite; }
@keyframes shimmer { 0%{background-position:200% 0;} 100%{background-position:-200% 0;} }
/* ========== 分页 ========== */
.pagination { display:flex; justify-content:center; gap:8px; margin-top:40px; }
.page-btn { min-width:40px; height:40px; border-radius:var(--radius-sm); background:var(--surface); border:2px solid var(--border); font-size:14px; font-weight:600; transition:var(--transition); }
.page-btn:hover:not(:disabled) { border-color:var(--primary); color:var(--primary); }
.page-btn.active { background:var(--primary); color:#fff; border-color:var(--primary); }
.page-btn:disabled { opacity:0.4; cursor:not-allowed; }
/* ========== 购物车抽屉 ========== */
.cart-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:200; opacity:0; visibility:hidden; transition:var(--transition); }
.cart-overlay.open { opacity:1; visibility:visible; }
.cart-drawer { position:fixed; top:0; right:0; width:420px; max-width:90vw; height:100vh; background:var(--surface); z-index:201; transform:translateX(100%); transition:transform 0.3s cubic-bezier(0.4,0,0.2,1); display:flex; flex-direction:column; }
.cart-drawer.open { transform:translateX(0); }
.cart-header { padding:20px 24px; border-bottom:1px solid var(--border); display:flex; align-items:center; justify-content:space-between; }
.cart-header h3 { font-size:18px; font-weight:800; }
.cart-close { width:32px; height:32px; border-radius:50%; background:var(--bg); display:flex; align-items:center; justify-content:center; font-size:20px; color:var(--text-muted); }
.cart-items { flex:1; overflow-y:auto; padding:16px 24px; }
.cart-item { display:flex; gap:12px; padding:16px 0; border-bottom:1px solid var(--border); }
.cart-item-img { width:72px; height:72px; border-radius:var(--radius-sm); background:linear-gradient(135deg,#f0f0f0,#e0e0e0); flex-shrink:0; display:flex; align-items:center; justify-content:center; }
.cart-item-img svg { width:32px; height:32px; opacity:0.3; }
.cart-item-info { flex:1; min-width:0; }
.cart-item-name { font-size:14px; font-weight:600; margin-bottom:4px; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.cart-item-price { font-size:16px; font-weight:700; color:var(--primary); }
.cart-item-controls { display:flex; align-items:center; gap:12px; margin-top:8px; }
.qty-btn { width:28px; height:28px; border-radius:var(--radius-sm); background:var(--bg); display:flex; align-items:center; justify-content:center; font-size:16px; font-weight:700; }
.qty-btn:hover { background:var(--border); }
.qty-value { font-size:14px; font-weight:600; min-width:24px; text-align:center; }
.cart-item-remove { margin-left:auto; font-size:12px; color:var(--text-muted); }
.cart-item-remove:hover { color:var(--primary); }
.cart-empty { text-align:center; padding:60px 20px; color:var(--text-muted); }
.cart-empty svg { width:64px; height:64px; margin:0 auto 16px; opacity:0.3; }
.cart-footer { padding:20px 24px; border-top:1px solid var(--border); background:var(--bg); }
.cart-total { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
.cart-total-label { font-size:14px; color:var(--text-muted); }
.cart-total-value { font-size:24px; font-weight:800; color:var(--primary); }
.checkout-btn { width:100%; padding:14px; border-radius:var(--radius); background:var(--primary); color:#fff; font-size:16px; font-weight:700; transition:var(--transition); }
.checkout-btn:hover { background:var(--primary-dark); }
.checkout-btn:disabled { opacity:0.5; cursor:not-allowed; }
/* ========== 商品详情模态框 ========== */
.modal-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.6); z-index:300; display:flex; align-items:center; justify-content:center; padding:20px; opacity:0; visibility:hidden; transition:var(--transition); }
.modal-overlay.open { opacity:1; visibility:visible; }
.modal-content { background:var(--surface); border-radius:var(--radius-lg); max-width:800px; width:100%; max-height:90vh; overflow-y:auto; transform:scale(0.9); transition:transform 0.3s; }
.modal-overlay.open .modal-content { transform:scale(1); }
.modal-close { position:absolute; top:16px; right:16px; width:36px; height:36px; border-radius:50%; background:rgba(0,0,0,0.1); display:flex; align-items:center; justify-content:center; font-size:24px; z-index:1; }
.modal-body { display:grid; grid-template-columns:1fr 1fr; gap:0; }
.modal-image { background:linear-gradient(135deg,#f0f0f0,#e0e0e0); aspect-ratio:1; display:flex; align-items:center; justify-content:center; }
.modal-image svg { width:40%; height:40%; opacity:0.2; }
.modal-details { padding:32px; position:relative; }
.modal-details h2 { font-size:22px; font-weight:700; margin-bottom:12px; line-height:1.3; }
.modal-price { display:flex; align-items:baseline; gap:12px; margin-bottom:16px; }
.modal-price .price-current { font-size:32px; }
.modal-tags { display:flex; gap:8px; margin-bottom:16px; }
.modal-desc { font-size:14px; color:var(--text-muted); line-height:1.7; margin-bottom:20px; }
.modal-meta { display:flex; gap:24px; padding:16px 0; border-top:1px solid var(--border); border-bottom:1px solid var(--border); margin-bottom:20px; font-size:13px; }
.modal-meta-item { display:flex; flex-direction:column; gap:2px; }
.modal-meta-item span:first-child { color:var(--text-muted); }
.modal-meta-item span:last-child { font-weight:600; }
.modal-add-cart { width:100%; padding:14px; border-radius:var(--radius); background:var(--primary); color:#fff; font-size:16px; font-weight:700; transition:var(--transition); }
.modal-add-cart:hover { background:var(--primary-dark); }
/* ========== 页脚 ========== */
.footer { background:var(--dark); color:#fff; margin-top:64px; }
.footer-content { max-width:1280px; margin:0 auto; padding:48px 24px 24px; display:grid; grid-template-columns:2fr 1fr 1fr 1fr; gap:40px; }
.footer-brand h3 { font-size:22px; font-weight:800; color:var(--primary-light); margin-bottom:12px; }
.footer-brand p { font-size:14px; opacity:0.7; line-height:1.7; }
.footer-col h4 { font-size:15px; font-weight:700; margin-bottom:16px; }
.footer-col ul { list-style:none; }
.footer-col li { margin-bottom:10px; }
.footer-col a { font-size:13px; opacity:0.7; transition:var(--transition); }
.footer-col a:hover { opacity:1; color:var(--primary-light); }
.footer-bottom { border-top:1px solid rgba(255,255,255,0.1); padding:20px 24px; text-align:center; font-size:13px; opacity:0.6; }
/* ========== Toast 提示 ========== */
.toast { position:fixed; bottom:30px; left:50%; transform:translateX(-50%) translateY(100px); background:var(--dark); color:#fff; padding:12px 24px; border-radius:var(--radius); font-size:14px; font-weight:600; z-index:400; opacity:0; transition:all 0.3s; display:flex; align-items:center; gap:8px; }
.toast.show { transform:translateX(-50%) translateY(0); opacity:1; }
.toast svg { width:20px; height:20px; color:#4ADE80; }
/* ========== 响应式 ========== */
@media (max-width:1024px) {
.products-grid { grid-template-columns:repeat(3,1fr); }
.features-strip { grid-template-columns:repeat(2,1fr); }
.footer-content { grid-template-columns:2fr 1fr 1fr; }
}
@media (max-width:768px) {
.nav-links { display:none; }
.search-box { max-width:200px; }
.hero-banner { padding:40px 24px; }
.hero-content h1 { font-size:28px; }
.hero-content p { font-size:15px; }
.products-layout { grid-template-columns:1fr; }
.filters-sidebar { position:static; }
.products-grid { grid-template-columns:repeat(2,1fr); }
.modal-body { grid-template-columns:1fr; }
.footer-content { grid-template-columns:1fr; gap:24px; }
}
@media (max-width:480px) {
.features-strip { grid-template-columns:1fr; }
.products-grid { grid-template-columns:1fr; }
.hero-content h1 { font-size:24px; }
}
/* ========== 3D 动态效果 & 鼠标光影(新增)========== */
/* 鼠标跟随光斑 */
.cursor-glow{position:fixed;width:600px;height:600px;border-radius:50%;background:radial-gradient(circle,rgba(230,57,70,0.12) 0%,transparent 70%);pointer-events:none;z-index:150;transform:translate(-50%,-50%);transition:opacity .3s;mix-blend-mode:multiply;opacity:0;left:0;top:0}
.cursor-glow.active{opacity:1}
/* 3D 卡片容器与倾斜 */
.products-grid{perspective:1000px}
.product-card{transform-style:preserve-3d;transition:transform .15s ease-out,box-shadow .25s}
.product-card .product-image{transform:translateZ(20px)}
.product-card .product-info{transform:translateZ(15px)}
/* 卡片表面光影反射(玻璃高光) */
.product-card::after{content:'';position:absolute;inset:0;border-radius:inherit;background:radial-gradient(circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,0.25) 0%,transparent 50%);opacity:0;transition:opacity .3s;pointer-events:none;z-index:2}
.product-card:hover::after{opacity:1}
/* 触摸设备隐藏光斑 */
@media (hover:none){.cursor-glow{display:none !important}}
</style>
</head>
<body>
<!-- 顶部公告栏 -->
<div class="topbar">
<span>🚀 全球直邮 · 满 $99 免运费</span>
<span>📦 7天无理由退换</span>
<span>🔒 安全支付 · <a href="#">查看详情</a></span>
</div>
<!-- 导航栏 -->
<nav class="navbar">
<div class="nav-container">
<div class="logo">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7v10l10 5 10-5V7L12 2z"/><path d="M2 7l10 5 10-5"/><path d="M12 22V12"/></svg>
GlobalShop
</div>
<ul class="nav-links">
<li><a href="#" class="active" onclick="resetPage();return false;">首页</a></li>
<li><a href="#products" onclick="scrollToProducts();return false;">所有商品</a></li>
<li><a href="#featured" onclick="loadFeatured();return false;">热卖推荐</a></li>
<li><a href="#deals" onclick="filterDeals();return false;">限时特惠</a></li>
</ul>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<input type="text" id="searchInput" placeholder="搜索商品..." oninput="handleSearch(event)">
</div>
<div class="nav-actions">
<button title="收藏">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
</button>
<button title="购物车" onclick="toggleCart()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
<span class="cart-badge" id="cartBadge" style="display:none">0</span>
</button>
</div>
</div>
</nav>
<!-- Hero 动态壁纸 -->
<div class="hero">
<canvas id="heroCanvas"></canvas>
<div class="hero-overlay"></div>
<div class="hero-light" id="heroLight"></div>
<div class="hero-content">
<div class="hero-badge"><span class="dot"></span> 全球直邮 · 2026 新季上线</div>
<h1>全球好物<br><span class="accent">跨境直达</span></h1>
<p>精选 18+ 品类好物,覆盖智能电子、家居生活、时尚运动,全球直邮,品质保障。沉浸式购物体验,由此开启。</p>
<div class="hero-actions">
<button class="hero-btn-primary" onclick="scrollToProducts()">
立即选购
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:20px;height:20px"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</button>
<button class="hero-btn-ghost" onclick="loadFeatured()">探索精选</button>
</div>
</div>
<div class="hero-stats">
<div class="hero-stat"><div class="num">18<span>+</span></div><div class="label">品类好物</div></div>
<div class="hero-stat"><div class="num">50<span>万</span></div><div class="label">全球用户</div></div>
<div class="hero-stat"><div class="num">99<span>%</span></div><div class="label">好评率</div></div>
<div class="hero-stat"><div class="num">7<span>天</span></div><div class="label">无理由退换</div></div>
</div>
<div class="hero-scroll">
<span>向下滚动</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12l7 7 7-7"/></svg>
</div>
</div>
<!-- 优势条 -->
<div class="features-strip">
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="3" width="15" height="13"/><path d="M16 8h4l3 3v5h-7V8z"/><circle cx="5.5" cy="18.5" r="2.5"/><circle cx="18.5" cy="18.5" r="2.5"/></svg>
<div><h4>全球直邮</h4><p>满 $99 免运费</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
<div><h4>正品保障</h4><p>品牌授权直供</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
<div><h4>7天退换</h4><p>无理由退货</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="4" width="22" height="16" rx="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg>
<div><h4>安全支付</h4><p>多渠道加密</p></div>
</div>
</div>
<!-- 商品列表区 -->
<div class="section" id="products">
<div class="section-header">
<div class="section-title">全部商品</div>
</div>
<div class="products-layout">
<!-- 侧边筛选 -->
<aside class="filters-sidebar">
<h3>筛选</h3>
<div class="filter-group">
<h4>分类</h4>
<div id="categoryFilters"></div>
</div>
<div class="filter-group">
<h4>价格区间</h4>
<div class="filter-option"><input type="radio" name="price" id="p0" value="0" checked onchange="applyFilters()"><label for="p0">全部</label></div>
<div class="filter-option"><input type="radio" name="price" id="p1" value="0-30" onchange="applyFilters()"><label for="p1">$0 - $30</label></div>
<div class="filter-option"><input type="radio" name="price" id="p2" value="30-60" onchange="applyFilters()"><label for="p2">$30 - $60</label></div>
<div class="filter-option"><input type="radio" name="price" id="p3" value="60-100" onchange="applyFilters()"><label for="p3">$60 - $100</label></div>
<div class="filter-option"><input type="radio" name="price" id="p4" value="100-9999" onchange="applyFilters()"><label for="p4">$100+</label></div>
</div>
<div class="filter-group">
<h4>排序</h4>
<div class="filter-option"><input type="radio" name="sort" id="s1" value="default" checked onchange="applyFilters()"><label for="s1">默认排序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s2" value="sales" onchange="applyFilters()"><label for="s2">销量优先</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s3" value="price_asc" onchange="applyFilters()"><label for="s3">价格升序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s4" value="price_desc" onchange="applyFilters()"><label for="s4">价格降序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s5" value="rating" onchange="applyFilters()"><label for="s5">评分最高</label></div>
</div>
</aside>
<!-- 商品网格 -->
<div class="products-main">
<div class="products-toolbar">
<div class="products-count" id="productsCount">加载中...</div>
<div class="category-nav" style="margin:0;padding:0;overflow:visible;border:none" id="categoryChips"></div>
</div>
<div class="products-grid" id="productsGrid">
<!-- 骨架屏 -->
</div>
<div class="pagination" id="pagination"></div>
</div>
</div>
</div>
<!-- 购物车遮罩 -->
<div class="cart-overlay" id="cartOverlay" onclick="toggleCart()"></div>
<!-- 购物车抽屉 -->
<div class="cart-drawer" id="cartDrawer">
<div class="cart-header">
<h3>购物车 (<span id="cartCount">0</span>)</h3>
<button class="cart-close" onclick="toggleCart()">&times;</button>
</div>
<div class="cart-items" id="cartItems"></div>
<div class="cart-footer">
<div class="cart-total">
<span class="cart-total-label">合计</span>
<span class="cart-total-value" id="cartTotal">$0.00</span>
</div>
<button class="checkout-btn" id="checkoutBtn" onclick="checkout()">去结算</button>
</div>
</div>
<!-- 商品详情模态框 -->
<div class="modal-overlay" id="productModal" onclick="closeModal(event)">
<div class="modal-content" id="modalContent"></div>
</div>
<!-- Toast -->
<div class="toast" id="toast">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
<span id="toastMsg">已加入购物车</span>
</div>
<!-- 页脚 -->
<footer class="footer">
<div class="footer-content">
<div class="footer-brand">
<h3>GlobalShop</h3>
<p>全球好物,跨境直达。我们精选全球优质商品,为您提供安全、便捷、愉悦的跨境购物体验。覆盖智能电子、家居生活、时尚服饰、运动户外、美妆个护等品类。</p>
</div>
<div class="footer-col">
<h4>购物指南</h4>
<ul>
<li><a href="#">购物流程</a></li>
<li><a href="#">会员介绍</a></li>
<li><a href="#">常见问题</a></li>
<li><a href="#">联系客服</a></li>
</ul>
</div>
<div class="footer-col">
<h4>配送与退换</h4>
<ul>
<li><a href="#">配送方式</a></li>
<li><a href="#">运费规则</a></li>
<li><a href="#">退换货政策</a></li>
<li><a href="#">退款说明</a></li>
</ul>
</div>
<div class="footer-col">
<h4>关于我们</h4>
<ul>
<li><a href="#">公司简介</a></li>
<li><a href="#">加入我们</a></li>
<li><a href="#">隐私政策</a></li>
<li><a href="#">用户协议</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
© 2026 GlobalShop. All rights reserved. 全球好物 · 跨境直达
</div>
</footer>
<script>
// ========== 全局状态 ==========
let allCategories = [];
let currentPage = 1;
const pageSize = 12;
let selectedCategory = 0;
let searchKeyword = '';
let cart = [];
// ========== 图标库 ==========
const iconColors = ['#E63946','#457B9D','#F4A261','#2A9D8F','#9D4EDD','#E76F51','#06A77D','#D62828'];
function getGradient(seed) {
const c = iconColors[seed % iconColors.length];
return `linear-gradient(135deg, ${c}22, ${c}44)`;
}
function productIconSVG(catId) {
const icons = {
1: '<path d="M4 4h16v12H4z"/><path d="M2 20h20"/><path d="M9 8h6"/>',
2: '<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/>',
3: '<path d="M20.38 3.46L16 2a4 4 0 0 1-8 0L3.62 3.46a2 2 0 0 0-1.34 2.23l.58 3.47a1 1 0 0 0 .99.84H6v10a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V10h2.15a1 1 0 0 0 .99-.84l.58-3.47a2 2 0 0 0-1.34-2.23z"/>',
4: '<path d="M6.5 6.5l11 11"/><path d="M21 21l-6-6m6 6v-4m0 4h-4"/><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/>',
5: '<path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="4"/>',
};
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">${icons[catId] || icons[1]}</svg>`;
}
// ========== API 调用 ==========
async function fetchJSON(url) {
const res = await fetch(url);
const json = await res.json();
if (json.code !== 0) throw new Error(json.message || '请求失败');
return json.data;
}
async function loadCategories() {
try {
allCategories = await fetchJSON('/api/v1/shop/categories');
renderCategoryFilters();
renderCategoryChips();
} catch(e) { console.error('分类加载失败:', e); }
}
async function loadProducts() {
const grid = document.getElementById('productsGrid');
grid.innerHTML = Array(8).fill(0).map(() => `
<div class="skeleton-card">
<div class="skeleton-img"></div>
<div class="skeleton-line"></div>
<div class="skeleton-line" style="width:60%"></div>
<div class="skeleton-line" style="width:40%"></div>
</div>
`).join('');
try {
let url = `/api/v1/shop/products?page=${currentPage}&page_size=${pageSize}`;
const sortRadio = document.querySelector('input[name="sort"]:checked');
if (sortRadio && sortRadio.value !== 'default') url += `&sort=${sortRadio.value}`;
if (selectedCategory > 0) url += `&category_id=${selectedCategory}`;
if (searchKeyword) url += `&keyword=${encodeURIComponent(searchKeyword)}`;
const data = await fetchJSON(url);
renderProducts(data.list || [], data.total || 0);
renderPagination(data.total || 0);
} catch(e) {
grid.innerHTML = `<div style="grid-column:1/-1;text-align:center;padding:60px;color:var(--text-muted)">
<p>商品加载失败,请稍后重试</p></div>`;
}
}
// ========== 渲染 ==========
function renderCategoryFilters() {
const container = document.getElementById('categoryFilters');
container.innerHTML = `
<div class="filter-option">
<input type="radio" name="cat-filter" id="cat0" value="0" checked onchange="selectCategory(0)">
<label for="cat0">全部分类</label>
</div>
` + allCategories.map(c => `
<div class="filter-option">
<input type="radio" name="cat-filter" id="cat${c.id}" value="${c.id}" onchange="selectCategory(${c.id})">
<label for="cat${c.id}">${c.name}</label>
</div>
`).join('');
}
function renderCategoryChips() {
const container = document.getElementById('categoryChips');
container.innerHTML = `<span class="category-chip ${selectedCategory === 0 ? 'active' : ''}" onclick="selectCategory(0)">全部</span>` +
allCategories.map(c => `<span class="category-chip ${selectedCategory === c.id ? 'active' : ''}" onclick="selectCategory(${c.id})">${c.name}</span>`).join('');
}
function renderProducts(products, total) {
const grid = document.getElementById('productsGrid');
document.getElementById('productsCount').textContent = `共 ${total} 件商品`;
if (products.length === 0) {
grid.innerHTML = `<div style="grid-column:1/-1;text-align:center;padding:60px;color:var(--text-muted)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:48px;height:48px;margin:0 auto 12px;opacity:0.3"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<p>未找到匹配的商品</p></div>`;
return;
}
grid.innerHTML = products.map((p, i) => {
const discount = p.original_price > p.price ? Math.round((1 - p.price / p.original_price) * 100) : 0;
let tags = [];
try { tags = JSON.parse(p.tags || '[]'); } catch(e) {}
const tagHtml = tags.slice(0, 2).map(t => {
const cls = t.includes('热') || t.includes('爆') ? 'tag-hot' : t.includes('新') ? 'tag-new' : 'tag-sale';
return `<span class="product-tag ${cls}">${t}</span>`;
}).join('');
return `
<div class="product-card" onclick="showProductDetail(${p.id})">
<div class="product-image">
${p.image ? `<img src="${p.image}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.style.display='none';this.nextElementSibling.style.display='flex'">` : ''}
<div class="product-image-gradient" style="background:${getGradient(p.category_id)};${p.image ? 'display:none' : 'display:flex'}">
${productIconSVG(p.category_id)}
</div>
<div class="product-tags">${tagHtml}</div>
${discount > 0 ? `<div class="product-discount">-${discount}%</div>` : ''}
</div>
<div class="product-info">
<div class="product-name">${p.name}</div>
<div class="product-price">
<span class="price-current">$${p.price.toFixed(2)}</span>
${p.original_price > p.price ? `<span class="price-original">$${p.original_price.toFixed(2)}</span>` : ''}
</div>
<div class="product-meta">
<div class="product-rating">
<span class="stars">${'★'.repeat(Math.round(p.rating))}</span>
<span>${p.rating.toFixed(1)}</span>
</div>
<span class="product-sales">已售 ${formatSales(p.sales)}</span>
</div>
</div>
<button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${p.id})" title="加入购物车">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
</button>
</div>`;
}).join('');
}
function renderPagination(total) {
const pages = Math.ceil(total / pageSize);
if (pages <= 1) { document.getElementById('pagination').innerHTML = ''; return; }
let html = `<button class="page-btn" ${currentPage === 1 ? 'disabled' : ''} onclick="goToPage(${currentPage - 1})">&lt;</button>`;
const maxVisible = 5;
let start = Math.max(1, currentPage - 2);
let end = Math.min(pages, start + maxVisible - 1);
if (end - start < maxVisible - 1) start = Math.max(1, end - maxVisible + 1);
if (start > 1) { html += `<button class="page-btn" onclick="goToPage(1)">1</button>`; if (start > 2) html += `<span style="padding:0 4px;color:var(--text-muted)">...</span>`; }
for (let i = start; i <= end; i++) {
html += `<button class="page-btn ${i === currentPage ? 'active' : ''}" onclick="goToPage(${i})">${i}</button>`;
}
if (end < pages) { if (end < pages - 1) html += `<span style="padding:0 4px;color:var(--text-muted)">...</span>`; html += `<button class="page-btn" onclick="goToPage(${pages})">${pages}</button>`; }
html += `<button class="page-btn" ${currentPage === pages ? 'disabled' : ''} onclick="goToPage(${currentPage + 1})">&gt;</button>`;
document.getElementById('pagination').innerHTML = html;
}
// ========== 交互函数 ==========
function selectCategory(catId) {
selectedCategory = catId;
currentPage = 1;
document.querySelectorAll('input[name="cat-filter"]').forEach(r => { r.checked = parseInt(r.value) === catId; });
document.querySelectorAll('.category-chip').forEach((c, i) => {
c.classList.toggle('active', (i === 0 && catId === 0) || (i > 0 && allCategories[i - 1] && allCategories[i - 1].id === catId));
});
loadProducts();
}
function applyFilters() {
// 侧边栏排序与价格筛选
const priceRadio = document.querySelector('input[name="price"]:checked');
const sortRadio = document.querySelector('input[name="sort"]:checked');
// 同步排序到主工具栏的 sort
currentPage = 1;
loadProducts();
}
function handleSearch(e) {
searchKeyword = e.target.value.trim();
currentPage = 1;
clearTimeout(window._searchTimer);
window._searchTimer = setTimeout(loadProducts, 400);
}
function goToPage(page) {
currentPage = page;
loadProducts();
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
}
function scrollToProducts() {
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
}
function resetPage() {
selectedCategory = 0;
searchKeyword = '';
currentPage = 1;
document.getElementById('searchInput').value = '';
document.querySelectorAll('input[name="price"]').forEach(r => r.checked = r.value === '0');
document.querySelectorAll('input[name="sort"]').forEach(r => r.checked = r.value === 'default');
selectCategory(0);
}
function filterDeals() {
// 筛选有折扣的商品(通过搜索价格降序再筛选不太精确,这里简单跳转)
scrollToProducts();
showToast('正在筛选限时特惠商品...');
}
async function loadFeatured() {
try {
const products = await fetchJSON('/api/v1/shop/featured?limit=8');
renderProducts(products, products.length);
document.getElementById('pagination').innerHTML = '';
document.getElementById('productsCount').textContent = `热卖推荐 · ${products.length} 件商品`;
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
} catch(e) { showToast('加载推荐失败'); }
}
// ========== 商品详情 ==========
async function showProductDetail(id) {
const modal = document.getElementById('productModal');
const content = document.getElementById('modalContent');
content.innerHTML = '<div style="padding:60px;text-align:center;color:var(--text-muted)">加载中...</div>';
modal.classList.add('open');
try {
const p = await fetchJSON(`/api/v1/shop/products/${id}`);
let tags = [];
try { tags = JSON.parse(p.tags || '[]'); } catch(e) {}
let imgList = [];
try { imgList = JSON.parse(p.images || '[]'); } catch(e) {}
if (!Array.isArray(imgList)) imgList = [];
if (p.image) imgList = [p.image, ...imgList.filter(img => img !== p.image)];
const discount = p.original_price > p.price ? Math.round((1 - p.price / p.original_price) * 100) : 0;
content.innerHTML = `
<button class="modal-close" onclick="closeModal()">&times;</button>
<div class="modal-body">
<div class="modal-image" style="background:${getGradient(p.category_id)};position:relative;overflow:hidden">
${imgList.length > 0 ? `<img id="modalMainImg" src="${imgList[0]}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.style.display='none';this.nextElementSibling.style.display=''">` : ''}
<div style="${imgList.length > 0 ? 'display:none' : ''}">${productIconSVG(p.category_id)}</div>
${imgList.length > 1 ? `<div style="position:absolute;bottom:8px;left:0;right:0;display:flex;gap:6px;justify-content:center;z-index:5">${imgList.map((img,idx) => `<img src="${img}" onclick="document.getElementById('modalMainImg').src=this.src" style="width:44px;height:44px;border-radius:6px;object-fit:cover;cursor:pointer;border:2px solid ${idx===0?'#fff':'rgba(255,255,255,0.5)'}" onerror="this.remove()">`).join('')}</div>` : ''}
</div>
<div class="modal-details">
<h2>${p.name}</h2>
<div class="modal-price">
<span class="price-current" style="color:var(--primary);font-size:32px;font-weight:800">$${p.price.toFixed(2)}</span>
${p.original_price > p.price ? `<span class="price-original" style="font-size:16px;color:var(--text-muted);text-decoration:line-through">$${p.original_price.toFixed(2)}</span>` : ''}
${discount > 0 ? `<span style="background:var(--primary);color:#fff;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:700">省 ${discount}%</span>` : ''}
</div>
<div class="modal-tags">${tags.map(t => `<span class="product-tag ${t.includes('热') ? 'tag-hot' : t.includes('新') ? 'tag-new' : 'tag-sale'}">${t}</span>`).join('')}</div>
<p class="modal-desc">${p.description}</p>
<div class="modal-meta">
<div class="modal-meta-item"><span>评分</span><span>${p.rating.toFixed(1)} ★</span></div>
<div class="modal-meta-item"><span>销量</span><span>${formatSales(p.sales)}</span></div>
<div class="modal-meta-item"><span>库存</span><span>${p.stock} 件</span></div>
</div>
<button class="modal-add-cart" onclick="addToCart(${p.id});closeModal()">加入购物车</button>
</div>
</div>
`;
} catch(e) {
content.innerHTML = '<div style="padding:60px;text-align:center;color:var(--text-muted)">商品信息加载失败</div>';
}
}
function closeModal(e) {
if (e && e.target !== e.currentTarget) return;
document.getElementById('productModal').classList.remove('open');
}
// ========== 购物车 ==========
let productCache = {};
async function addToCart(productId) {
let product = productCache[productId];
if (!product) {
try {
product = await fetchJSON(`/api/v1/shop/products/${productId}`);
productCache[productId] = product;
} catch(e) { showToast('添加失败'); return; }
}
const existing = cart.find(i => i.id === productId);
if (existing) {
existing.qty++;
} else {
cart.push({ id: productId, name: product.name, price: product.price, qty: 1, category_id: product.category_id, image: product.image || '' });
}
updateCartUI();
showToast(`已加入购物车: ${product.name.substring(0, 15)}...`);
}
function removeFromCart(productId) {
cart = cart.filter(i => i.id !== productId);
updateCartUI();
}
function changeQty(productId, delta) {
const item = cart.find(i => i.id === productId);
if (!item) return;
item.qty += delta;
if (item.qty <= 0) removeFromCart(productId);
else updateCartUI();
}
function updateCartUI() {
const count = cart.reduce((s, i) => s + i.qty, 0);
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
const badge = document.getElementById('cartBadge');
badge.textContent = count;
badge.style.display = count > 0 ? 'flex' : 'none';
document.getElementById('cartCount').textContent = count;
document.getElementById('cartTotal').textContent = `$${total.toFixed(2)}`;
document.getElementById('checkoutBtn').disabled = count === 0;
const container = document.getElementById('cartItems');
if (cart.length === 0) {
container.innerHTML = `
<div class="cart-empty">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
<p>购物车是空的</p>
<p style="font-size:13px;margin-top:8px">快去挑选心仪的商品吧</p>
</div>`;
return;
}
container.innerHTML = cart.map(item => `
<div class="cart-item">
<div class="cart-item-img" style="background:${getGradient(item.category_id)};position:relative;overflow:hidden">${item.image ? `<img src="${item.image}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.remove()">` : ''}${productIconSVG(item.category_id)}</div>
<div class="cart-item-info">
<div class="cart-item-name">${item.name}</div>
<div class="cart-item-price">$${item.price.toFixed(2)}</div>
<div class="cart-item-controls">
<button class="qty-btn" onclick="changeQty(${item.id}, -1)">-</button>
<span class="qty-value">${item.qty}</span>
<button class="qty-btn" onclick="changeQty(${item.id}, 1)">+</button>
<span class="cart-item-remove" onclick="removeFromCart(${item.id})">删除</span>
</div>
</div>
</div>
`).join('');
}
function toggleCart() {
document.getElementById('cartOverlay').classList.toggle('open');
document.getElementById('cartDrawer').classList.toggle('open');
}
function checkout() {
if (cart.length === 0) return;
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
showToast(`订单已提交! 合计 $${total.toFixed(2)}`);
cart = [];
updateCartUI();
toggleCart();
}
// ========== 工具函数 ==========
function formatSales(n) {
if (n >= 10000) return (n / 10000).toFixed(1) + '万';
return n.toString();
}
function showToast(msg) {
const toast = document.getElementById('toast');
document.getElementById('toastMsg').textContent = msg;
toast.classList.add('show');
clearTimeout(window._toastTimer);
window._toastTimer = setTimeout(() => toast.classList.remove('show'), 2500);
}
// ========== 初始化 ==========
async function init() {
await loadCategories();
await loadProducts();
updateCartUI();
}
init();
// ========== Hero 动态壁纸 + 光追效果 ==========
// 导航栏滚动变色
window.addEventListener('scroll', function() {
var navbar = document.querySelector('.navbar');
var topbar = document.querySelector('.topbar');
if (window.scrollY > 80) {
navbar.style.background = 'rgba(255,255,255,0.95)';
navbar.style.backdropFilter = 'blur(20px)';
navbar.style.boxShadow = '0 2px 12px rgba(0,0,0,0.06)';
navbar.style.borderBottomColor = 'rgba(0,0,0,0.06)';
var links = navbar.querySelectorAll('.nav-links a, .nav-brand, .search-box input, .search-box svg, .nav-actions span');
links.forEach(function(el){ el.style.color = ''; });
var search = navbar.querySelector('.search-box');
if (search) { search.style.background = 'var(--bg)'; search.style.border = '1px solid var(--border)'; }
} else {
navbar.style.background = 'rgba(10,10,15,0.6)';
navbar.style.boxShadow = 'none';
navbar.style.borderBottomColor = 'rgba(255,255,255,0.06)';
}
if (topbar) topbar.style.opacity = window.scrollY > 32 ? '0' : '1';
});
(function(){
var canvas = document.getElementById('heroCanvas');
if (!canvas) return;
var ctx = canvas.getContext('2d');
var heroLight = document.getElementById('heroLight');
var W = 0, H = 0, dpr = window.devicePixelRatio || 1;
function resize() {
var rect = canvas.getBoundingClientRect();
W = rect.width; H = rect.height;
canvas.width = W * dpr; canvas.height = H * dpr;
ctx.scale(dpr, dpr);
}
resize();
window.addEventListener('resize', resize);
// 粒子系统 — 流动的光丝
var particles = [];
var PARTICLE_COUNT = 80;
for (var i = 0; i < PARTICLE_COUNT; i++) {
particles.push({
x: Math.random() * W,
y: Math.random() * H,
vx: (Math.random() - 0.5) * 0.3,
vy: (Math.random() - 0.5) * 0.3,
size: Math.random() * 3 + 1,
hue: Math.random() < 0.6 ? 355 : (Math.random() < 0.5 ? 45 : 15), // 红/金/橙
alpha: Math.random() * 0.5 + 0.4,
life: Math.random()
});
}
// 鼠标位置
var mx = W / 2, my = H / 2;
var hasMouse = false;
canvas.parentElement.addEventListener('mousemove', function(e) {
var rect = canvas.getBoundingClientRect();
mx = e.clientX - rect.left;
my = e.clientY - rect.top;
if (!hasMouse) { hasMouse = true; heroLight.classList.add('active'); }
heroLight.style.left = mx + 'px';
heroLight.style.top = my + 'px';
});
canvas.parentElement.addEventListener('mouseleave', function() {
hasMouse = false;
heroLight.classList.remove('active');
});
// 动态壁纸主循环
function animate() {
// 深色背景渐变
var grad = ctx.createRadialGradient(W * 0.3, H * 0.4, 0, W * 0.3, H * 0.4, W * 0.8);
grad.addColorStop(0, 'rgba(30,15,20,0.15)');
grad.addColorStop(1, 'rgba(10,10,15,0.2)');
ctx.fillStyle = 'rgba(10,10,15,0.15)';
ctx.fillRect(0, 0, W, H);
ctx.fillStyle = grad;
ctx.fillRect(0, 0, W, H);
// 绘制流动的曲线网络
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
// 鼠标吸引/排斥
if (hasMouse) {
var dx = mx - p.x;
var dy = my - p.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 200) {
var force = (1 - dist / 200) * 0.02;
p.vx += dx / dist * force;
p.vy += dy / dist * force;
}
}
// 更新位置
p.x += p.vx;
p.y += p.vy;
p.vx *= 0.98;
p.vy *= 0.98;
p.life += 0.005;
// 边界回弹
if (p.x < 0) p.x = W;
if (p.x > W) p.x = 0;
if (p.y < 0) p.y = H;
if (p.y > H) p.y = 0;
// 绘制粒子
var pulse = Math.sin(p.life * Math.PI * 2) * 0.3 + 0.7;
var r = p.size * pulse;
ctx.beginPath();
ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
ctx.fillStyle = 'hsla(' + p.hue + ',80%,60%,' + (p.alpha * pulse) + ')';
ctx.fill();
// 光晕
ctx.beginPath();
ctx.arc(p.x, p.y, r * 3, 0, Math.PI * 2);
var glow = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r * 3);
glow.addColorStop(0, 'hsla(' + p.hue + ',80%,60%,' + (p.alpha * 0.3) + ')');
glow.addColorStop(1, 'transparent');
ctx.fillStyle = glow;
ctx.fill();
// 连线 — 近距离粒子之间
for (var j = i + 1; j < particles.length; j++) {
var p2 = particles[j];
var ldx = p.x - p2.x;
var ldy = p.y - p2.y;
var ld = Math.sqrt(ldx * ldx + ldy * ldy);
if (ld < 150) {
var la = (1 - ld / 150) * 0.25;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = 'hsla(' + p.hue + ',70%,55%,' + la + ')';
ctx.lineWidth = 0.5;
ctx.stroke();
}
}
// 鼠标光追连线 — 鼠标附近的粒子向鼠标发射光线
if (hasMouse) {
var mdx = p.x - mx;
var mdy = p.y - my;
var md = Math.sqrt(mdx * mdx + mdy * mdy);
if (md < 250) {
var ma = (1 - md / 250) * 0.4;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(mx, my);
ctx.strokeStyle = 'rgba(230,57,70,' + ma + ')';
ctx.lineWidth = 1;
ctx.stroke();
// 光追射线效果 — 在鼠标周围有明亮光点
ctx.beginPath();
ctx.arc(mx, my, 2, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,255,255,0.8)';
ctx.fill();
}
}
}
// 鼠标处的光追放射线
if (hasMouse) {
for (var k = 0; k < 8; k++) {
var angle = (k / 8) * Math.PI * 2 + Date.now() * 0.0003;
var len = 80 + Math.sin(Date.now() * 0.002 + k) * 20;
var ex = mx + Math.cos(angle) * len;
var ey = my + Math.sin(angle) * len;
var rayGrad = ctx.createLinearGradient(mx, my, ex, ey);
rayGrad.addColorStop(0, 'rgba(230,57,70,0.15)');
rayGrad.addColorStop(1, 'transparent');
ctx.beginPath();
ctx.moveTo(mx, my);
ctx.lineTo(ex, ey);
ctx.strokeStyle = rayGrad;
ctx.lineWidth = 2;
ctx.stroke();
}
}
requestAnimationFrame(animate);
}
animate();
})();
// ========== 3D 动态效果 & 鼠标光影(新增)==========
(function(){
var isTouch = window.matchMedia('(hover: none)').matches;
/* ----- 鼠标跟随光斑 ----- */
var cursorGlow = document.createElement('div');
cursorGlow.className = 'cursor-glow';
document.body.appendChild(cursorGlow);
if (!isTouch) {
var glowOn = false;
document.addEventListener('mousemove', function(e){
cursorGlow.style.left = e.clientX + 'px';
cursorGlow.style.top = e.clientY + 'px';
if (!glowOn) { cursorGlow.classList.add('active'); glowOn = true; }
});
document.addEventListener('mouseleave', function(){
cursorGlow.classList.remove('active'); glowOn = false;
});
}
/* ----- 产品卡片 3D 倾斜 + 表面高光 ----- */
if (!isTouch) {
var tiltRaf = null, tiltEvt = null;
function tiltUpdate(){
tiltRaf = null;
var e = tiltEvt;
if (!e) return;
var cards = document.querySelectorAll('.product-card');
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
var rect = card.getBoundingClientRect();
if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) continue;
var cx = rect.left + rect.width / 2;
var cy = rect.top + rect.height / 2;
var rx = ((e.clientY - cy) / rect.height) * -8;
var ry = ((e.clientX - cx) / rect.width) * 8;
card.style.transform = 'perspective(1000px) rotateX(' + rx + 'deg) rotateY(' + ry + 'deg) translateY(-4px)';
card.style.setProperty('--mx', ((e.clientX - rect.left) / rect.width * 100) + '%');
card.style.setProperty('--my', ((e.clientY - rect.top) / rect.height * 100) + '%');
}
}
document.addEventListener('mousemove', function(e){
tiltEvt = e;
if (tiltRaf === null) tiltRaf = requestAnimationFrame(tiltUpdate);
});
// 鼠标真正离开卡片时恢复(relatedTarget 仍在卡片内则保持倾斜,避免子元素间移动闪烁)
document.addEventListener('mouseout', function(e){
var t = e.target;
if (t && t.classList && t.classList.contains('product-card')) {
var r = e.relatedTarget;
if (r && (r === t || t.contains(r))) return;
t.style.transform = '';
}
});
}
/* ----- Hero 区域 3D 视差 ----- */
if (!isTouch) {
var heroContent = document.querySelector('.hero-content');
var heroRaf = null, heroEvt = null;
function heroUpdate(){
heroRaf = null;
var e = heroEvt;
if (!e || !heroContent) return;
var x = (e.clientX / window.innerWidth - 0.5) * 2;
var y = (e.clientY / window.innerHeight - 0.5) * 2;
heroContent.style.transform = 'translate(' + (x * 10) + 'px,' + (y * 10) + 'px)';
}
document.addEventListener('mousemove', function(e){
heroEvt = e;
if (heroRaf === null) heroRaf = requestAnimationFrame(heroUpdate);
});
}
})();
</script>
</body>
</html>