feat: 新增跨境电商D模板(shop4) - 古树动态壁纸+光追生命力+横向首页+商品表格+黑白模式切换

This commit is contained in:
admin
2026-07-11 16:00:20 +08:00
parent 07c506354d
commit be35490088
3 changed files with 881 additions and 3 deletions

865
web/dist/shop4/index.html vendored Normal file
View File

@@ -0,0 +1,865 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>古树商城 · TreeShop</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='20' fill='%231a1a2e'/><text x='50' y='68' font-size='50' fill='%2380d8' text-anchor='middle'>🌳</text></svg>">
<style>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
:root{
--bg:#0d1117;--bg2:#161b22;--surface:rgba(255,255,255,.04);--card:rgba(255,255,255,.06);
--border:rgba(255,255,255,.08);--text:#e6edf3;--text-muted:#7d8590;
--primary:#58d68d;--primary-glow:rgba(88,214,141,.25);--accent:#f0c674;
--radius:16px;--blur-amount:8px;
}
[data-theme="light"]{
--bg:#f5f3f0;--bg2:#ffffff;--surface:rgba(0,0,0,.03);--card:rgba(0,0,0,.04);
--border:rgba(0,0,0,.08);--text:#1a1a2e;--text-muted:#6b7280;
--primary:#2d7a4e;--primary-glow:rgba(45,122,78,.2);--accent:#b8860b;
}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Noto Sans SC,sans-serif;background:var(--bg);color:var(--text);overflow-x:hidden;transition:background .4s,color .4s}
a{color:inherit;text-decoration:none}
/* ========== 导航栏 ========== */
.navbar{position:fixed;top:0;left:0;right:0;z-index:200;display:flex;align-items:center;justify-content:space-between;padding:16px 32px;background:rgba(13,17,23,.6);backdrop-filter:blur(20px);border-bottom:1px solid var(--border);transition:all .4s}
[data-theme="light"] .navbar{background:rgba(255,255,255,.7)}
.nav-brand{font-size:22px;font-weight:800;display:flex;align-items:center;gap:8px}
.nav-brand .tree-icon{font-size:26px}
.nav-links{display:flex;gap:28px}
.nav-links a{font-size:14px;font-weight:500;color:var(--text-muted);transition:color .2s;cursor:pointer}
.nav-links a:hover{color:var(--text)}
.nav-actions{display:flex;align-items:center;gap:16px}
.cart-btn{position:relative;background:var(--surface);border:1px solid var(--border);border-radius:50%;width:40px;height:40px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:18px;color:var(--text)}
.cart-badge{position:absolute;top:-4px;right:-4px;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}
/* ========== 黑白模式切换 ========== */
.theme-toggle{position:relative;width:44px;height:44px;border-radius:50%;border:1px solid var(--border);background:var(--surface);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s;overflow:hidden}
.theme-toggle:hover{border-color:var(--primary);box-shadow:0 0 16px var(--primary-glow)}
.theme-toggle .sun-icon{position:absolute;font-size:22px;transition:all .4s cubic-bezier(.4,0,.2,1)}
[data-theme="dark"] .sun-icon{transform:translateY(0) rotate(0);opacity:1}
[data-theme="light"] .sun-icon{transform:translateY(-40px) rotate(180deg);opacity:0}
.theme-toggle .moon-icon{position:absolute;font-size:20px;transition:all .4s cubic-bezier(.4,0,.2,1)}
[data-theme="dark"] .moon-icon{transform:translateY(40px) rotate(180deg);opacity:0}
[data-theme="light"] .moon-icon{transform:translateY(0) rotate(0);opacity:1}
/* ========== Hero 横向动态 ========== */
.hero{position:relative;width:100%;height:100vh;min-height:640px;overflow:hidden;background:#0a0f0a}
#treeCanvas{position:absolute;inset:0;width:100%;height:100%;z-index:1}
.hero-overlay{position:absolute;inset:0;z-index:2;background:linear-gradient(180deg,rgba(10,15,10,.2) 0%,rgba(10,15,10,.5) 60%,var(--bg) 100%);pointer-events:none;transition:background .4s}
[data-theme="light"] .hero-overlay{background:linear-gradient(180deg,rgba(245,243,240,.1) 0%,rgba(245,243,240,.4) 60%,var(--bg) 100%)}
/* 横向滚动容器 */
.hero-scroll-wrap{position:absolute;inset:0;z-index:3;display:flex;align-items:center;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scrollbar-width:none}
.hero-scroll-wrap::-webkit-scrollbar{display:none}
.hero-panel{min-width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;scroll-snap-align:start;position:relative;padding:0 8vw}
.hero-panel-content{max-width:680px}
.hero-badge{display:inline-flex;align-items:center;gap:8px;padding:6px 18px;border-radius:100px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);font-size:13px;color:rgba(255,255,255,.7);margin-bottom:24px;backdrop-filter:blur(10px)}
.hero-badge .dot{width:6px;height:6px;border-radius:50%;background:var(--primary);box-shadow:0 0 8px var(--primary);animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
.hero-panel h1{font-size:64px;font-weight:900;line-height:1.08;letter-spacing:-2px;color:#fff;margin-bottom:20px;text-shadow:0 4px 30px rgba(0,0,0,.5)}
[data-theme="light"] .hero-panel h1{color:#1a1a2e;text-shadow:0 4px 30px rgba(0,0,0,.1)}
.hero-panel h1 .accent{background:linear-gradient(135deg,var(--primary) 0%,var(--accent) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.hero-panel p{font-size:18px;color:rgba(255,255,255,.55);line-height:1.7;margin-bottom:32px;max-width:520px}
[data-theme="light"] .hero-panel p{color:rgba(26,26,46,.6)}
.hero-panel-actions{display:flex;gap:14px;align-items:center}
.btn-primary{display:inline-flex;align-items:center;gap:8px;background:linear-gradient(135deg,var(--primary),#3da56a);color:#fff;padding:14px 36px;border-radius:100px;font-size:15px;font-weight:700;border:none;cursor:pointer;transition:all .3s;box-shadow:0 8px 28px var(--primary-glow)}
.btn-primary:hover{transform:translateY(-2px);box-shadow:0 12px 36px var(--primary-glow)}
.btn-ghost{display:inline-flex;align-items:center;gap:8px;background:rgba(255,255,255,.06);color:#fff;padding:14px 28px;border-radius:100px;font-size:14px;font-weight:600;border:1px solid rgba(255,255,255,.15);cursor:pointer;backdrop-filter:blur(10px);transition:all .3s}
[data-theme="light"] .btn-ghost{color:#1a1a2e;background:rgba(0,0,0,.04);border-color:rgba(0,0,0,.1)}
.btn-ghost:hover{background:rgba(255,255,255,.12)}
[data-theme="light"] .btn-ghost:hover{background:rgba(0,0,0,.08)}
/* 横向指示器 */
.hero-dots{position:absolute;bottom:40px;left:50%;transform:translateX(-50%);z-index:5;display:flex;gap:10px}
.hero-dot{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,.2);cursor:pointer;transition:all .3s}
.hero-dot.active{width:28px;border-radius:4px;background:var(--primary)}
.hero-arrow{position:absolute;bottom:20px;right:40px;z-index:5;color:rgba(255,255,255,.4);font-size:12px;display:flex;align-items:center;gap:6px;animation:bounce-h 2s infinite}
@keyframes bounce-h{0%,100%{transform:translateX(0)}50%{transform:translateX(8px)}}
/* 面板2/3 样式 */
.hero-features{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;margin-top:32px}
.hero-feature{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:var(--radius);padding:24px;backdrop-filter:blur(10px)}
[data-theme="light"] .hero-feature{background:rgba(0,0,0,.03);border-color:rgba(0,0,0,.06)}
.hero-feature .icon{font-size:32px;margin-bottom:12px}
.hero-feature h3{font-size:16px;font-weight:700;color:#fff;margin-bottom:6px}
[data-theme="light"] .hero-feature h3{color:#1a1a2e}
.hero-feature p{font-size:13px;color:rgba(255,255,255,.5);line-height:1.5}
[data-theme="light"] .hero-feature p{color:rgba(26,26,46,.55)}
/* ========== 商品表格区域 ========== */
.products-section{position:relative;min-height:100vh;padding:80px 0;z-index:10}
.products-bg{position:absolute;inset:0;z-index:0;overflow:hidden}
.products-bg canvas{width:100%;height:100%;display:block}
.products-bg::after{content:'';position:absolute;inset:0;background:var(--bg);opacity:.75}
.products-content{position:relative;z-index:1;max-width:1280px;margin:0 auto;padding:0 32px}
.section-header{text-align:center;margin-bottom:48px}
.section-header h2{font-size:36px;font-weight:800;letter-spacing:-1px;margin-bottom:12px}
.section-header h2 .accent{color:var(--primary)}
.section-header p{font-size:15px;color:var(--text-muted)}
/* 筛选条 */
.filter-bar{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:32px;justify-content:center}
.filter-chip{padding:8px 20px;border-radius:100px;background:var(--surface);border:1px solid var(--border);font-size:13px;font-weight:500;color:var(--text-muted);cursor:pointer;transition:all .2s}
.filter-chip:hover{border-color:var(--primary);color:var(--text)}
.filter-chip.active{background:var(--primary);color:#fff;border-color:var(--primary)}
.search-box{display:flex;align-items:center;gap:8px;background:var(--surface);border:1px solid var(--border);border-radius:100px;padding:8px 20px;min-width:240px}
.search-box input{background:none;border:none;outline:none;color:var(--text);font-size:14px;flex:1}
.search-box svg{width:16px;height:16px;color:var(--text-muted)}
/* 表格 */
.table-wrap{background:var(--bg2);border-radius:var(--radius);overflow:hidden;border:1px solid var(--border);box-shadow:0 8px 32px rgba(0,0,0,.15)}
.product-table{width:100%;border-collapse:collapse}
.product-table th{background:var(--surface);padding:16px 20px;text-align:left;font-size:13px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;border-bottom:1px solid var(--border);white-space:nowrap}
.product-table td{padding:14px 20px;border-bottom:1px solid var(--border);font-size:14px;vertical-align:middle}
.product-table tbody tr{transition:background .2s;cursor:pointer}
.product-table tbody tr:hover{background:var(--surface)}
.product-table tbody tr:last-child td{border-bottom:none}
.td-img{width:60px;height:60px;border-radius:10px;overflow:hidden;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.td-img img{width:100%;height:100%;object-fit:cover}
.td-img .placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:24px;background:var(--surface)}
.td-name{font-weight:600;color:var(--text)}
.td-price{font-weight:700;color:var(--primary);font-size:15px}
.td-category{color:var(--text-muted);font-size:13px}
.td-rating{display:flex;align-items:center;gap:4px}
.td-rating .star{color:var(--accent)}
.td-actions{display:flex;gap:8px}
.td-btn{padding:6px 16px;border-radius:8px;font-size:12px;font-weight:600;border:none;cursor:pointer;transition:all .2s}
.td-btn-primary{background:var(--primary);color:#fff}
.td-btn-primary:hover{opacity:.85}
.td-btn-ghost{background:var(--surface);color:var(--text);border:1px solid var(--border)}
.td-btn-ghost:hover{border-color:var(--primary)}
/* 分页 */
.pagination{display:flex;gap:8px;justify-content:center;align-items:center;margin-top:32px}
.page-btn{min-width:40px;height:40px;border-radius:10px;border:1px solid var(--border);background:var(--surface);color:var(--text-muted);font-size:14px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}
.page-btn:hover:not(:disabled){border-color:var(--primary);color:var(--text)}
.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary)}
.page-btn:disabled{opacity:.4;cursor:not-allowed}
/* ========== Footer ========== */
.footer{background:var(--bg2);border-top:1px solid var(--border);padding:48px 32px 32px}
.footer-content{max-width:1280px;margin:0 auto;display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:40px}
.footer-brand h3{font-size:20px;font-weight:800;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.footer-brand p{font-size:13px;color:var(--text-muted);line-height:1.6;max-width:320px}
.footer-col h4{font-size:14px;font-weight:700;margin-bottom:14px}
.footer-col a{display:block;font-size:13px;color:var(--text-muted);padding:4px 0;cursor:pointer;transition:color .2s}
.footer-col a:hover{color:var(--text)}
.footer-bottom{text-align:center;padding:24px 0 0;margin-top:32px;border-top:1px solid var(--border);font-size:12px;color:var(--text-muted)}
/* ========== Modal ========== */
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(8px);z-index:300;display:none;align-items:center;justify-content:center;padding:24px}
.modal-overlay.active{display:flex}
.modal{background:var(--bg2);border-radius:var(--radius);max-width:640px;width:100%;max-height:85vh;overflow-y:auto;border:1px solid var(--border)}
.modal-header{display:flex;align-items:center;justify-content:space-between;padding:24px;border-bottom:1px solid var(--border)}
.modal-header h3{font-size:20px;font-weight:700}
.modal-close{background:var(--surface);border:1px solid var(--border);border-radius:50%;width:36px;height:36px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:18px;color:var(--text)}
.modal-body{padding:24px}
.modal-body img{width:100%;border-radius:12px;margin-bottom:16px}
.modal-info{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
.modal-info-item{font-size:13px}
.modal-info-item .label{color:var(--text-muted);margin-bottom:2px}
.modal-info-item .value{font-weight:600}
.modal-desc{font-size:14px;line-height:1.6;color:var(--text-muted);margin-bottom:20px}
.modal-actions{display:flex;gap:12px}
/* Toast */
.toast{position:fixed;top:80px;left:50%;transform:translateX(-50%) translateY(-20px);background:var(--bg2);border:1px solid var(--primary);border-radius:12px;padding:12px 24px;font-size:14px;font-weight:600;z-index:400;opacity:0;transition:all .3s;pointer-events:none}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
/* Cart drawer */
.cart-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:280;display:none}
.cart-overlay.active{display:block}
.cart-drawer{position:fixed;top:0;right:0;width:400px;max-width:90vw;height:100vh;background:var(--bg2);border-left:1px solid var(--border);z-index:290;transform:translateX(100%);transition:transform .3s;display:flex;flex-direction:column}
.cart-drawer.active{transform:translateX(0)}
.cart-drawer-header{padding:20px 24px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.cart-drawer-header h3{font-size:18px;font-weight:700}
.cart-items{flex:1;overflow-y:auto;padding:16px 24px}
.cart-item{display:flex;gap:12px;padding:12px 0;border-bottom:1px solid var(--border)}
.cart-item-img{width:50px;height:50px;border-radius:8px;overflow:hidden;background:var(--surface);display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}
.cart-item-img img{width:100%;height:100%;object-fit:cover}
.cart-item-info{flex:1}
.cart-item-name{font-size:14px;font-weight:600;margin-bottom:2px}
.cart-item-price{font-size:13px;color:var(--primary);font-weight:700}
.cart-item-qty{display:flex;align-items:center;gap:8px;margin-top:4px}
.qty-btn{width:24px;height:24px;border-radius:6px;border:1px solid var(--border);background:var(--surface);cursor:pointer;color:var(--text);font-size:14px}
.cart-empty{text-align:center;padding:40px 0;color:var(--text-muted)}
.cart-drawer-footer{padding:20px 24px;border-top:1px solid var(--border)}
.cart-total{display:flex;justify-content:space-between;margin-bottom:16px;font-size:16px;font-weight:700}
.cart-total .price{color:var(--primary)}
.checkout-btn{width:100%;padding:14px;border-radius:12px;background:var(--primary);color:#fff;font-size:15px;font-weight:700;border:none;cursor:pointer;transition:opacity .2s}
.checkout-btn:hover{opacity:.85}
.checkout-btn:disabled{opacity:.4;cursor:not-allowed}
/* Responsive */
@media(max-width:768px){
.hero-panel h1{font-size:36px}
.hero-features{grid-template-columns:1fr}
.footer-content{grid-template-columns:1fr 1fr}
.nav-links{display:none}
.product-table{font-size:12px}
.product-table th,.product-table td{padding:10px 12px}
.td-img{width:40px;height:40px}
}
</style>
</head>
<body data-theme="dark">
<!-- 导航栏 -->
<nav class="navbar">
<div class="nav-brand"><span class="tree-icon">🌳</span> TreeShop</div>
<div class="nav-links">
<a onclick="scrollToProducts()">全部商品</a>
<a onclick="scrollToProducts()">分类</a>
<a onclick="showToast('关于我们')">关于</a>
<a onclick="showToast('联系方式')">联系</a>
</div>
<div class="nav-actions">
<button class="theme-toggle" onclick="toggleTheme()" title="切换黑白模式">
<span class="sun-icon">☀️</span>
<span class="moon-icon">🌙</span>
</button>
<button class="cart-btn" onclick="toggleCart()">
🛒 <span class="cart-badge" id="cartBadge" style="display:none">0</span>
</button>
</div>
</nav>
<!-- Hero 横向动态 -->
<div class="hero">
<canvas id="treeCanvas"></canvas>
<div class="hero-overlay"></div>
<div class="hero-scroll-wrap" id="heroScroll">
<!-- 面板1 -->
<div class="hero-panel">
<div class="hero-panel-content">
<div class="hero-badge"><span class="dot"></span> 古树商城 · 2026 新季</div>
<h1>扎根全球<br><span class="accent">枝连万物</span></h1>
<p>如古树般扎根品质,如枝叶般连接全球好物。精选 18+ 品类,从智能电子到家居生活,每一件商品都经过严选,为您带来自然纯粹的购物体验。</p>
<div class="hero-panel-actions">
<button class="btn-primary" onclick="scrollToProducts()">
探索商品
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:18px;height:18px"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</button>
<button class="btn-ghost" onclick="scrollToPanel(1)">了解更多 →</button>
</div>
</div>
</div>
<!-- 面板2 -->
<div class="hero-panel">
<div class="hero-panel-content">
<div class="hero-badge"><span class="dot"></span> 品质承诺</div>
<h1>四大根基<br><span class="accent">品质如树</span></h1>
<div class="hero-features">
<div class="hero-feature"><div class="icon">🌍</div><h3>全球直邮</h3><p>满 $99 免运费,覆盖全球 200+ 国家和地区</p></div>
<div class="hero-feature"><div class="icon">✅</div><h3>正品保障</h3><p>品牌授权直供,假一赔十承诺</p></div>
<div class="hero-feature"><div class="icon">🔄</div><h3>7天退换</h3><p>无理由退货,购物零风险</p></div>
<div class="hero-feature"><div class="icon">🔒</div><h3>安全支付</h3><p>多渠道加密,保障支付安全</p></div>
</div>
<div class="hero-panel-actions" style="margin-top:32px">
<button class="btn-primary" onclick="scrollToProducts()">浏览商品</button>
<button class="btn-ghost" onclick="scrollToPanel(0)">← 返回</button>
</div>
</div>
</div>
<!-- 面板3 -->
<div class="hero-panel">
<div class="hero-panel-content" style="text-align:center">
<div class="hero-badge" style="margin:0 auto 24px"><span class="dot"></span> 数据一览</div>
<h1 style="text-align:center">数字见证<br><span class="accent">用户信赖</span></h1>
<div class="hero-features" style="grid-template-columns:repeat(4,1fr);margin-top:40px">
<div class="hero-feature" style="text-align:center"><div class="icon">📦</div><h3 style="font-size:28px;color:var(--primary)">18+</h3><p>品类好物</p></div>
<div class="hero-feature" style="text-align:center"><div class="icon">👥</div><h3 style="font-size:28px;color:var(--primary)">50万</h3><p>全球用户</p></div>
<div class="hero-feature" style="text-align:center"><div class="icon">⭐</div><h3 style="font-size:28px;color:var(--primary)">99%</h3><p>好评率</p></div>
<div class="hero-feature" style="text-align:center"><div class="icon">🌿</div><h3 style="font-size:28px;color:var(--primary)">7天</h3><p>无理由退换</p></div>
</div>
<div class="hero-panel-actions" style="margin-top:32px;justify-content:center">
<button class="btn-primary" onclick="scrollToProducts()">立即选购</button>
</div>
</div>
</div>
</div>
<div class="hero-dots" id="heroDots">
<div class="hero-dot active" onclick="scrollToPanel(0)"></div>
<div class="hero-dot" onclick="scrollToPanel(1)"></div>
<div class="hero-dot" onclick="scrollToPanel(2)"></div>
</div>
<div class="hero-arrow">横向滚动 →</div>
</div>
<!-- 商品表格区域 -->
<div class="products-section" id="productsSection">
<div class="products-bg" id="productsBg"></div>
<div class="products-content">
<div class="section-header">
<h2>全部<span class="accent">商品</span></h2>
<p>精选好物,品质如树,扎根全球</p>
</div>
<div class="filter-bar">
<button class="filter-chip active" data-cat="0" onclick="filterCategory(0,this)">全部分类</button>
<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.3-4.3"/></svg>
<input type="text" id="searchInput" placeholder="搜索商品..." oninput="onSearch()">
</div>
</div>
<div class="table-wrap">
<table class="product-table">
<thead>
<tr>
<th>图片</th>
<th>商品名称</th>
<th>分类</th>
<th>价格</th>
<th>评分</th>
<th>销量</th>
<th>操作</th>
</tr>
</thead>
<tbody id="productTbody"></tbody>
</table>
</div>
<div class="pagination" id="pagination"></div>
</div>
</div>
<!-- Footer -->
<footer class="footer">
<div class="footer-content">
<div class="footer-brand">
<h3>🌳 TreeShop</h3>
<p>如古树般扎根品质,如枝叶般连接全球。精选好物,为您带来自然纯粹的跨境购物体验。</p>
</div>
<div class="footer-col">
<h4>购物指南</h4>
<a>购物流程</a><a>会员介绍</a><a>常见问题</a><a>联系客服</a>
</div>
<div class="footer-col">
<h4>配送退换</h4>
<a>配送方式</a><a>运费规则</a><a>退换货政策</a><a>退款说明</a>
</div>
<div class="footer-col">
<h4>关于我们</h4>
<a>公司简介</a><a>加入我们</a><a>隐私政策</a><a>用户协议</a>
</div>
</div>
<div class="footer-bottom">© 2026 TreeShop · 古树商城 · 多行业网站模板集合</div>
</footer>
<!-- Cart -->
<div class="cart-overlay" id="cartOverlay" onclick="toggleCart()"></div>
<div class="cart-drawer" id="cartDrawer">
<div class="cart-drawer-header">
<h3>购物车</h3>
<button class="modal-close" onclick="toggleCart()">×</button>
</div>
<div class="cart-items" id="cartItems"></div>
<div class="cart-drawer-footer">
<div class="cart-total"><span>合计</span><span class="price" id="cartTotal">$0</span></div>
<button class="checkout-btn" id="checkoutBtn" disabled onclick="showToast('结算功能开发中')">去结算</button>
</div>
</div>
<!-- Modal -->
<div class="modal-overlay" id="modalOverlay" onclick="if(event.target===this)closeModal()">
<div class="modal" id="modal"></div>
</div>
<!-- Toast -->
<div class="toast" id="toast"><span id="toastMsg"></span></div>
<script>
// ========== 全局状态 ==========
var currentPage = 1, pageSize = 10, totalPages = 1, currentCat = 0, searchKey = '';
var categories = [], cart = [];
// ========== 工具函数 ==========
async function fetchJSON(url) {
try {
var resp = await fetch(url);
return await resp.json();
} catch(e) { console.error(e); return null; }
}
function showToast(msg) {
var t = document.getElementById('toast');
document.getElementById('toastMsg').textContent = msg;
t.classList.add('show');
clearTimeout(window._tt);
window._tt = setTimeout(function(){ t.classList.remove('show'); }, 2500);
}
function getCatName(id) {
var c = categories.find(function(c){ return c.id == id; });
return c ? c.name : '未分类';
}
function fmtPrice(p) { return '$' + Number(p).toFixed(2); }
function starsHTML(r) {
var s = '';
for (var i = 0; i < 5; i++) s += i < Math.round(r) ? '<span class="star">★</span>' : '<span style="color:var(--text-muted)">★</span>';
return s;
}
function productImgHTML(p) {
if (p.image && (p.image.startsWith('/') || p.image.startsWith('http'))) {
return '<img src="' + p.image + '" onerror="this.parentElement.innerHTML=\'<div class=placeholder>📦</div>\'">';
}
return '<div class="placeholder">📦</div>';
}
// ========== 黑白模式切换 ==========
function toggleTheme() {
var body = document.body;
var current = body.getAttribute('data-theme');
body.setAttribute('data-theme', current === 'dark' ? 'light' : 'dark');
showToast(current === 'dark' ? '已切换至明亮模式' : '已切换至暗黑模式');
}
// ========== Hero 横向滚动 ==========
function scrollToPanel(idx) {
document.getElementById('heroScroll').scrollTo({ left: idx * window.innerWidth, behavior: 'smooth' });
}
document.getElementById('heroScroll').addEventListener('scroll', function() {
var idx = Math.round(this.scrollLeft / window.innerWidth);
document.querySelectorAll('.hero-dot').forEach(function(d, i) { d.classList.toggle('active', i === idx); });
});
function scrollToProducts() {
document.getElementById('productsSection').scrollIntoView({ behavior: 'smooth' });
}
// ========== 商品加载 ==========
async function loadCategories() {
var data = await fetchJSON('/api/v1/shop/categories');
if (data && data.code === 0 && data.data) {
categories = data.data.list || data.data || [];
var bar = document.querySelector('.filter-bar');
categories.forEach(function(c) {
var chip = document.createElement('button');
chip.className = 'filter-chip';
chip.setAttribute('data-cat', c.id);
chip.textContent = c.name;
chip.onclick = function() { filterCategory(c.id, chip); };
bar.insertBefore(chip, bar.querySelector('.search-box'));
});
}
}
async function loadProducts() {
var url = '/api/v1/shop/products?page=' + currentPage + '&page_size=' + pageSize;
if (currentCat > 0) url += '&category_id=' + currentCat;
if (searchKey) url += '&search=' + encodeURIComponent(searchKey);
var data = await fetchJSON(url);
if (!data || data.code !== 0) return;
var list = data.data.list || [];
totalPages = data.data.total_pages || 1;
renderTable(list);
renderPagination();
}
function renderTable(list) {
var tbody = document.getElementById('productTbody');
if (!list.length) {
tbody.innerHTML = '<tr><td colspan="7" style="text-align:center;padding:48px;color:var(--text-muted)">暂无商品</td></tr>';
return;
}
tbody.innerHTML = list.map(function(p) {
return '<tr onclick="showDetail(' + p.id + ')">' +
'<td><div class="td-img">' + productImgHTML(p) + '</div></td>' +
'<td class="td-name">' + (p.name || '') + '</td>' +
'<td class="td-category">' + getCatName(p.category_id) + '</td>' +
'<td class="td-price">' + fmtPrice(p.price) + '</td>' +
'<td><div class="td-rating">' + starsHTML(p.rating || 0) + ' <span style="font-size:12px;color:var(--text-muted)">' + (p.rating || 0).toFixed(1) + '</span></div></td>' +
'<td style="color:var(--text-muted)">' + (p.sales || 0) + '</td>' +
'<td><div class="td-actions" onclick="event.stopPropagation()">' +
'<button class="td-btn td-btn-primary" onclick="addToCart(' + p.id + ',\'' + (p.name || '').replace(/'/g, '') + '\',' + (p.price || 0) + ',\'' + (p.image || '') + '\')">加购</button>' +
'<button class="td-btn td-btn-ghost" onclick="showDetail(' + p.id + ')">详情</button>' +
'</div></td>' +
'</tr>';
}).join('');
}
function renderPagination() {
var pag = document.getElementById('pagination');
var html = '<button class="page-btn" ' + (currentPage <= 1 ? 'disabled' : '') + ' onclick="goPage(' + (currentPage - 1) + ')">‹</button>';
for (var i = 1; i <= totalPages; i++) {
html += '<button class="page-btn ' + (i === currentPage ? 'active' : '') + '" onclick="goPage(' + i + ')">' + i + '</button>';
}
html += '<button class="page-btn" ' + (currentPage >= totalPages ? 'disabled' : '') + ' onclick="goPage(' + (currentPage + 1) + ')">›</button>';
pag.innerHTML = html;
}
function goPage(p) { currentPage = p; loadProducts(); }
function filterCategory(cat, el) {
currentCat = cat; currentPage = 1;
document.querySelectorAll('.filter-chip').forEach(function(c){ c.classList.remove('active'); });
el.classList.add('active');
loadProducts();
}
function onSearch() {
clearTimeout(window._st);
window._st = setTimeout(function() {
searchKey = document.getElementById('searchInput').value;
currentPage = 1;
loadProducts();
}, 380);
}
async function showDetail(id) {
var data = await fetchJSON('/api/v1/shop/products/' + id);
if (!data || data.code !== 0) return;
var p = data.data;
var modal = document.getElementById('modal');
modal.innerHTML =
'<div class="modal-header"><h3>' + (p.name || '') + '</h3><button class="modal-close" onclick="closeModal()">×</button></div>' +
'<div class="modal-body">' +
(p.image ? '<img src="' + p.image + '" onerror="this.style.display=\'none\'">' : '') +
'<div class="modal-info">' +
'<div class="modal-info-item"><div class="label">分类</div><div class="value">' + getCatName(p.category_id) + '</div></div>' +
'<div class="modal-info-item"><div class="label">价格</div><div class="value" style="color:var(--primary);font-size:18px">' + fmtPrice(p.price) + '</div></div>' +
'<div class="modal-info-item"><div class="label">评分</div><div class="value">' + starsHTML(p.rating || 0) + ' ' + (p.rating || 0).toFixed(1) + '</div></div>' +
'<div class="modal-info-item"><div class="label">销量</div><div class="value">' + (p.sales || 0) + ' 件</div></div>' +
'</div>' +
'<div class="modal-desc">' + (p.description || '暂无描述') + '</div>' +
'<div class="modal-actions">' +
'<button class="btn-primary" style="flex:1" onclick="addToCart(' + p.id + ',\'' + (p.name || '').replace(/'/g, '') + '\',' + (p.price || 0) + ',\'' + (p.image || '') + '\');closeModal()">加入购物车</button>' +
'<button class="btn-ghost" onclick="closeModal()">关闭</button>' +
'</div>' +
'</div>';
document.getElementById('modalOverlay').classList.add('active');
}
function closeModal() { document.getElementById('modalOverlay').classList.remove('active'); }
// ========== 购物车 ==========
function addToCart(id, name, price, image) {
var existing = cart.find(function(c){ return c.id === id; });
if (existing) { existing.qty++; } else { cart.push({ id: id, name: name, price: price, image: image, qty: 1 }); }
updateCartUI();
showToast('已加入购物车: ' + name);
}
function updateCartUI() {
var badge = document.getElementById('cartBadge');
var totalQty = cart.reduce(function(s, c){ return s + c.qty; }, 0);
badge.textContent = totalQty;
badge.style.display = totalQty > 0 ? 'flex' : 'none';
var items = document.getElementById('cartItems');
if (!cart.length) {
items.innerHTML = '<div class="cart-empty">购物车是空的<br>快去挑选心仪的商品吧</div>';
} else {
items.innerHTML = cart.map(function(c) {
return '<div class="cart-item">' +
'<div class="cart-item-img">' + (c.image ? '<img src="' + c.image + '" onerror="this.style.display=\'none\'">' : '📦') + '</div>' +
'<div class="cart-item-info">' +
'<div class="cart-item-name">' + c.name + '</div>' +
'<div class="cart-item-price">' + fmtPrice(c.price) + '</div>' +
'<div class="cart-item-qty">' +
'<button class="qty-btn" onclick="changeQty(' + c.id + ',-1)">−</button>' +
'<span>' + c.qty + '</span>' +
'<button class="qty-btn" onclick="changeQty(' + c.id + ',1)">+</button>' +
'</div>' +
'</div>' +
'</div>';
}).join('');
}
var total = cart.reduce(function(s, c){ return s + c.price * c.qty; }, 0);
document.getElementById('cartTotal').textContent = fmtPrice(total);
document.getElementById('checkoutBtn').disabled = cart.length === 0;
}
function changeQty(id, delta) {
var item = cart.find(function(c){ return c.id === id; });
if (!item) return;
item.qty += delta;
if (item.qty <= 0) cart = cart.filter(function(c){ return c.id !== id; });
updateCartUI();
}
function toggleCart() {
document.getElementById('cartOverlay').classList.toggle('active');
document.getElementById('cartDrawer').classList.toggle('active');
}
// ========== 古树 Canvas 壁画 + 光追生命力 ==========
(function() {
var canvas = document.getElementById('treeCanvas');
var ctx = canvas.getContext('2d');
var W = 0, H = 0, dpr = window.devicePixelRatio || 1;
var mx = 0, my = 0, hasMouse = false;
function resize() {
var rect = canvas.getBoundingClientRect();
W = rect.width; H = rect.height;
canvas.width = W * dpr; canvas.height = H * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
mx = W / 2; my = H / 2;
buildTree();
}
// 构建树形结构
var branches = [];
var leaves = [];
var fireflies = []; // 萤火虫粒子(生命力)
function buildTree() {
branches = [];
leaves = [];
fireflies = [];
var trunkX = W * 0.5;
var trunkY = H * 0.95;
var trunkH = H * 0.35;
// 递归生成树枝
function branch(x, y, angle, length, depth, width) {
if (depth <= 0 || length < 8) {
// 叶子
for (var i = 0; i < 3; i++) {
leaves.push({
x: x + (Math.random() - 0.5) * 30,
y: y + (Math.random() - 0.5) * 30,
size: Math.random() * 4 + 2,
hue: 100 + Math.random() * 60,
alpha: 0.3 + Math.random() * 0.3,
sway: Math.random() * Math.PI * 2
});
}
return;
}
var ex = x + Math.cos(angle) * length;
var ey = y + Math.sin(angle) * length;
branches.push({ x1: x, y1: y, x2: ex, y2: ey, width: width, depth: depth });
// 萤火虫沿枝干分布
fireflies.push({
x: x, y: y, baseX: x, baseY: y,
tx: ex, ty: ey,
progress: Math.random(),
speed: 0.002 + Math.random() * 0.003,
size: 1.5 + Math.random() * 2,
hue: 120 + Math.random() * 60,
offset: Math.random() * Math.PI * 2
});
var nChildren = depth > 3 ? 2 : (Math.random() < 0.3 ? 3 : 2);
for (var i = 0; i < nChildren; i++) {
var spread = 0.35 + Math.random() * 0.2;
var newAngle = angle + (i / (nChildren - 1) - 0.5) * spread * 2 + (Math.random() - 0.5) * 0.15;
var newLen = length * (0.65 + Math.random() * 0.15);
branch(ex, ey, newAngle, newLen, depth - 1, width * 0.7);
}
}
// 主干
branch(trunkX, trunkY, -Math.PI / 2, trunkH, 7, 14);
// 树根
for (var r = 0; r < 5; r++) {
var rAngle = -Math.PI / 2 + (r - 2) * 0.3;
var rLen = trunkH * 0.25;
branches.push({ x1: trunkX, y1: trunkY, x2: trunkX + Math.cos(rAngle) * rLen, y2: trunkY + Math.sin(rAngle) * rLen * 0.3, width: 6, depth: 0, isRoot: true });
}
}
canvas.parentElement.addEventListener('mousemove', function(e) {
var rect = canvas.getBoundingClientRect();
mx = e.clientX - rect.left;
my = e.clientY - rect.top;
hasMouse = true;
});
canvas.parentElement.addEventListener('mouseleave', function() { hasMouse = false; });
function drawTree() {
// 背景渐变
var bg = ctx.createRadialGradient(W * 0.5, H * 0.7, 0, W * 0.5, H * 0.7, W * 0.8);
bg.addColorStop(0, 'rgba(15,25,15,0.12)');
bg.addColorStop(1, 'rgba(10,15,10,0.15)');
ctx.fillStyle = 'rgba(10,15,10,0.12)';
ctx.fillRect(0, 0, W, H);
ctx.fillStyle = bg;
ctx.fillRect(0, 0, W, H);
var time = Date.now() * 0.001;
// 绘制树枝
branches.forEach(function(b) {
ctx.beginPath();
ctx.moveTo(b.x1, b.y1);
ctx.lineTo(b.x2, b.y2);
var isDark = document.body.getAttribute('data-theme') === 'dark';
ctx.strokeStyle = b.isRoot ? (isDark ? 'rgba(80,60,40,0.6)' : 'rgba(100,80,50,0.4)') : (isDark ? 'rgba(100,80,60,0.7)' : 'rgba(120,100,70,0.5)');
ctx.lineWidth = b.width;
ctx.lineCap = 'round';
ctx.stroke();
});
// 绘制叶子
leaves.forEach(function(l) {
var sway = Math.sin(time + l.sway) * 3;
var x = l.x + sway;
var y = l.y + Math.cos(time * 0.7 + l.sway) * 2;
// 鼠标附近叶子发光
var dx = mx - x, dy = my - y;
var dist = Math.sqrt(dx * dx + dy * dy);
var glow = hasMouse && dist < 150 ? (1 - dist / 150) : 0;
ctx.beginPath();
ctx.arc(x, y, l.size, 0, Math.PI * 2);
ctx.fillStyle = 'hsla(' + l.hue + ',60%,' + (40 + glow * 30) + '%,' + (l.alpha + glow * 0.4) + ')';
ctx.fill();
if (glow > 0.3) {
ctx.beginPath();
ctx.arc(x, y, l.size * 3, 0, Math.PI * 2);
var lg = ctx.createRadialGradient(x, y, 0, x, y, l.size * 3);
lg.addColorStop(0, 'hsla(' + l.hue + ',80%,60%,' + (glow * 0.3) + ')');
lg.addColorStop(1, 'transparent');
ctx.fillStyle = lg;
ctx.fill();
}
});
// 绘制萤火虫(生命力流动)
fireflies.forEach(function(f) {
f.progress += f.speed;
if (f.progress > 1) f.progress = 0;
// 沿枝干流动
var px = f.baseX + (f.tx - f.baseX) * f.progress;
var py = f.baseY + (f.ty - f.baseY) * f.progress;
// 添加浮动
px += Math.sin(time * 2 + f.offset) * 4;
py += Math.cos(time * 1.5 + f.offset) * 3;
// 鼠标光追 — 鼠标附近的萤火虫被激活,变亮变大
var dx = mx - px, dy = my - py;
var dist = Math.sqrt(dx * dx + dy * dy);
var activate = hasMouse && dist < 200 ? (1 - dist / 200) : 0;
var brightness = 0.4 + activate * 0.6;
var size = f.size * (1 + activate * 1.5);
var hue = activate > 0.3 ? 60 + activate * 30 : f.hue; // 激活时变金黄色
// 萤火虫核心
ctx.beginPath();
ctx.arc(px, py, size, 0, Math.PI * 2);
ctx.fillStyle = 'hsla(' + hue + ',90%,' + (55 + activate * 25) + '%,' + brightness + ')';
ctx.fill();
// 光晕
ctx.beginPath();
ctx.arc(px, py, size * 4, 0, Math.PI * 2);
var gl = ctx.createRadialGradient(px, py, 0, px, py, size * 4);
gl.addColorStop(0, 'hsla(' + hue + ',90%,60%,' + (brightness * 0.3) + ')');
gl.addColorStop(1, 'transparent');
ctx.fillStyle = gl;
ctx.fill();
// 鼠标光追连线
if (activate > 0.2) {
ctx.beginPath();
ctx.moveTo(px, py);
ctx.lineTo(mx, my);
ctx.strokeStyle = 'hsla(' + hue + ',80%,60%,' + (activate * 0.25) + ')';
ctx.lineWidth = 0.8;
ctx.stroke();
}
});
// 鼠标位置光追效果 — 生命力的源头
if (hasMouse) {
// 鼠标处光点
ctx.beginPath();
ctx.arc(mx, my, 3, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(180,255,150,0.9)';
ctx.fill();
// 光晕
ctx.beginPath();
ctx.arc(mx, my, 40, 0, Math.PI * 2);
var mg = ctx.createRadialGradient(mx, my, 0, mx, my, 40);
mg.addColorStop(0, 'rgba(120,255,100,0.15)');
mg.addColorStop(1, 'transparent');
ctx.fillStyle = mg;
ctx.fill();
// 放射光线
for (var k = 0; k < 6; k++) {
var angle = (k / 6) * Math.PI * 2 + time * 0.3;
var len = 60 + Math.sin(time * 2 + k) * 15;
var ex = mx + Math.cos(angle) * len;
var ey = my + Math.sin(angle) * len;
var rg = ctx.createLinearGradient(mx, my, ex, ey);
rg.addColorStop(0, 'rgba(120,255,100,0.12)');
rg.addColorStop(1, 'transparent');
ctx.beginPath();
ctx.moveTo(mx, my);
ctx.lineTo(ex, ey);
ctx.strokeStyle = rg;
ctx.lineWidth = 1.5;
ctx.stroke();
}
}
requestAnimationFrame(drawTree);
}
resize();
window.addEventListener('resize', resize);
drawTree();
})();
// ========== 商品区域背景 — 模糊古树 ==========
(function() {
var bg = document.getElementById('productsBg');
var canvas = document.createElement('canvas');
bg.appendChild(canvas);
var ctx = canvas.getContext('2d');
var W = 0, H = 0;
function resize() {
W = bg.offsetWidth; H = bg.offsetHeight;
canvas.width = W; canvas.height = H;
draw();
}
function draw() {
ctx.clearRect(0, 0, W, H);
var trunkX = W * 0.5, trunkY = H * 0.98;
var trunkH = H * 0.4;
function branch(x, y, angle, length, depth, width) {
if (depth <= 0 || length < 6) return;
var ex = x + Math.cos(angle) * length;
var ey = y + Math.sin(angle) * length;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(ex, ey);
var isDark = document.body.getAttribute('data-theme') === 'dark';
ctx.strokeStyle = isDark ? 'rgba(90,70,50,0.3)' : 'rgba(100,85,60,0.2)';
ctx.lineWidth = width;
ctx.lineCap = 'round';
ctx.stroke();
if (depth <= 2) {
ctx.beginPath();
ctx.arc(ex, ey, 5, 0, Math.PI * 2);
ctx.fillStyle = isDark ? 'rgba(100,180,80,0.15)' : 'rgba(80,150,60,0.1)';
ctx.fill();
}
var n = 2;
for (var i = 0; i < n; i++) {
var a2 = angle + (i - 0.5) * 0.4 + (Math.random() - 0.5) * 0.1;
branch(ex, ey, a2, length * 0.7, depth - 1, width * 0.7);
}
}
branch(trunkX, trunkY, -Math.PI / 2, trunkH, 6, 12);
}
resize();
window.addEventListener('resize', resize);
// 主题切换时重绘
document.body.addEventListener('click', function() { setTimeout(resize, 100); }, true);
})();
// ========== 初始化 ==========
loadCategories();
loadProducts();
updateCartUI();
</script>
</body>
</html>