Files
web_template/web/dist/shop4/index.html

866 lines
41 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>古树商城 · 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>