Files
web_template/web/dist/shop2/index.html
admin 8b00f14099 feat: 跨境电商3套模板添加3D动态光影效果
- shop: 红色经典风格 - 鼠标红色光斑+卡片8度3D倾斜+玻璃高光+Hero视差
- shop2: 暗黑霓虹风格 - 霓虹紫青光斑+卡片10度3D倾斜+霓虹高光+12个浮动粒子
- shop3: 北欧极简风格 - 柔和暖色光斑+卡片6度3D倾斜+柔和高光+入场动画
2026-07-11 15:31:22 +08:00

2020 lines
64 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>NEON MART · 暗黑霓虹跨境电商</title>
<style>
/* ========== 主题变量 ========== */
:root {
--bg: #0A0A0F;
--bg-2: #12121A;
--bg-3: #1A1A26;
--surface: rgba(255,255,255,0.03);
--surface-2: rgba(255,255,255,0.05);
--surface-3: rgba(255,255,255,0.08);
--neon-purple: #BF5AF2;
--neon-cyan: #64D2FF;
--neon-pink: #FF375F;
--neon-green: #30D158;
--neon-orange: #FF9F0A;
--text: #F5F5F7;
--text-muted: #8E8E93;
--text-dim: #636366;
--border: rgba(255,255,255,0.08);
--border-2: rgba(255,255,255,0.14);
--glow-purple: 0 0 24px rgba(191,90,242,0.45);
--glow-cyan: 0 0 24px rgba(100,210,255,0.45);
--glow-purple-lg: 0 0 40px rgba(191,90,242,0.35), 0 0 80px rgba(191,90,242,0.15);
--radius: 14px;
--radius-sm: 10px;
--radius-lg: 20px;
--transition: all 0.3s cubic-bezier(0.4,0,0.2,1);
--mono: "SF Mono","JetBrains Mono","Fira Code","Cascadia Code",Consolas,monospace;
}
* { margin:0; padding:0; box-sizing:border-box; }
html { scroll-behavior:smooth; }
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;
overflow-x:hidden;
min-height:100vh;
}
a { text-decoration:none; color:inherit; }
button { cursor:pointer; border:none; font-family:inherit; background:none; color:inherit; }
input { font-family:inherit; }
img { max-width:100%; display:block; }
/* 网格背景纹理 */
body::before {
content:'';
position:fixed;
inset:0;
background-image:
linear-gradient(rgba(191,90,242,0.025) 1px, transparent 1px),
linear-gradient(90deg, rgba(100,210,255,0.025) 1px, transparent 1px);
background-size: 50px 50px;
pointer-events:none;
z-index:0;
}
/* ========== 滚动条 ========== */
::-webkit-scrollbar { width:8px; height:8px; }
::-webkit-scrollbar-track { background:var(--bg-2); }
::-webkit-scrollbar-thumb { background:rgba(191,90,242,0.3); border-radius:4px; }
::-webkit-scrollbar-thumb:hover { background:rgba(191,90,242,0.5); }
/* ========== 渐变文字 ========== */
.gradient-text {
background:linear-gradient(135deg, var(--neon-cyan) 0%, var(--neon-purple) 100%);
-webkit-background-clip:text;
background-clip:text;
-webkit-text-fill-color:transparent;
}
.gradient-text-pink {
background:linear-gradient(135deg, var(--neon-purple) 0%, var(--neon-pink) 100%);
-webkit-background-clip:text;
background-clip:text;
-webkit-text-fill-color:transparent;
}
/* ========== 顶部导航栏 ========== */
.navbar {
position:sticky;
top:0;
z-index:100;
background:rgba(10,10,15,0.72);
backdrop-filter:blur(20px) saturate(180%);
-webkit-backdrop-filter:blur(20px) saturate(180%);
border-bottom:1px solid var(--border);
}
.nav-container {
max-width:1320px;
margin:0 auto;
padding:0 24px;
display:flex;
align-items:center;
gap:28px;
height:68px;
}
.logo {
font-size:22px;
font-weight:900;
letter-spacing:1px;
display:flex;
align-items:center;
gap:10px;
white-space:nowrap;
}
.logo-icon {
width:34px;
height:34px;
border-radius:9px;
background:linear-gradient(135deg, var(--neon-purple), var(--neon-cyan));
display:flex;
align-items:center;
justify-content:center;
box-shadow:var(--glow-purple);
position:relative;
overflow:hidden;
}
.logo-icon::after {
content:'';
position:absolute;
inset:1px;
border-radius:8px;
background:linear-gradient(135deg, var(--neon-purple), var(--neon-cyan));
}
.logo-icon svg { width:18px; height:18px; color:#0A0A0F; position:relative; z-index:1; }
.logo-text { background:linear-gradient(135deg, var(--neon-cyan), var(--neon-purple)); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }
.nav-links { display:flex; gap:6px; list-style:none; }
.nav-links a {
padding:8px 16px;
border-radius:var(--radius-sm);
font-size:14px;
font-weight:600;
color:var(--text-muted);
transition:var(--transition);
position:relative;
}
.nav-links a:hover { color:var(--text); background:var(--surface-2); }
.nav-links a.active { color:var(--neon-cyan); }
.nav-links a.active::after {
content:'';
position:absolute;
bottom:-2px;
left:50%;
transform:translateX(-50%);
width:20px;
height:2px;
background:var(--neon-cyan);
border-radius:1px;
box-shadow:var(--glow-cyan);
}
.search-box {
flex:1;
max-width:420px;
position:relative;
}
.search-box input {
width:100%;
height:42px;
padding:0 16px 0 44px;
border:1px solid var(--border);
border-radius:var(--radius);
font-size:14px;
outline:none;
transition:var(--transition);
background:var(--surface);
color:var(--text);
}
.search-box input::placeholder { color:var(--text-dim); }
.search-box input:focus {
border-color:var(--neon-purple);
box-shadow:0 0 0 3px rgba(191,90,242,0.15), var(--glow-purple);
background:var(--surface-2);
}
.search-box svg {
position:absolute;
left:14px;
top:50%;
transform:translateY(-50%);
width:18px;
height:18px;
color:var(--text-muted);
transition:var(--transition);
}
.search-box input:focus + svg { color:var(--neon-purple); }
.nav-actions { display:flex; align-items:center; gap:6px; }
.nav-btn {
width:42px;
height:42px;
border-radius:var(--radius-sm);
display:flex;
align-items:center;
justify-content:center;
transition:var(--transition);
position:relative;
color:var(--text-muted);
}
.nav-btn:hover { background:var(--surface-2); color:var(--neon-cyan); }
.nav-btn svg { width:22px; height:22px; }
.cart-badge {
position:absolute;
top:2px;
right:2px;
background:linear-gradient(135deg, var(--neon-pink), var(--neon-purple));
color:#fff;
font-size:11px;
font-weight:800;
min-width:19px;
height:19px;
border-radius:10px;
display:flex;
align-items:center;
justify-content:center;
padding:0 5px;
font-family:var(--mono);
box-shadow:0 0 10px rgba(255,55,95,0.6);
border:1.5px solid var(--bg);
}
/* ========== Hero Banner ========== */
.hero {
position:relative;
max-width:1320px;
margin:32px auto;
padding:0 24px;
z-index:1;
}
.hero-banner {
position:relative;
border-radius:var(--radius-lg);
padding:80px 56px;
overflow:hidden;
background:linear-gradient(135deg, #12121A 0%, #0A0A0F 100%);
border:1px solid var(--border);
min-height:340px;
display:flex;
align-items:center;
}
/* 霓虹光球 */
.orb {
position:absolute;
border-radius:50%;
filter:blur(80px);
opacity:0.55;
pointer-events:none;
}
.orb-1 {
width:480px; height:480px;
background:radial-gradient(circle, var(--neon-purple), transparent 70%);
top:-120px; right:-60px;
animation:float1 12s ease-in-out infinite;
}
.orb-2 {
width:380px; height:380px;
background:radial-gradient(circle, var(--neon-cyan), transparent 70%);
bottom:-100px; left:10%;
animation:float2 14s ease-in-out infinite;
}
.orb-3 {
width:260px; height:260px;
background:radial-gradient(circle, var(--neon-pink), transparent 70%);
top:30%; left:40%;
opacity:0.3;
animation:float3 10s ease-in-out infinite;
}
@keyframes float1 {
0%,100% { transform:translate(0,0) scale(1); }
50% { transform:translate(-40px,40px) scale(1.1); }
}
@keyframes float2 {
0%,100% { transform:translate(0,0) scale(1); }
50% { transform:translate(50px,-30px) scale(0.9); }
}
@keyframes float3 {
0%,100% { transform:translate(0,0) scale(1); }
50% { transform:translate(-30px,-40px) scale(1.15); }
}
.hero-content { position:relative; z-index:2; max-width:600px; }
.hero-tag {
display:inline-flex;
align-items:center;
gap:8px;
padding:6px 14px;
border-radius:20px;
background:rgba(191,90,242,0.12);
border:1px solid rgba(191,90,242,0.3);
font-size:12px;
font-weight:600;
color:var(--neon-purple);
margin-bottom:20px;
letter-spacing:1px;
}
.hero-tag-dot {
width:6px; height:6px; border-radius:50%;
background:var(--neon-purple);
box-shadow:0 0 8px var(--neon-purple);
animation:pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%,100% { opacity:1; }
50% { opacity:0.4; }
}
.hero-content h1 {
font-size:64px;
font-weight:900;
line-height:1.05;
letter-spacing:-1px;
margin-bottom:18px;
}
.hero-content .hero-sub {
font-size:17px;
color:var(--text-muted);
margin-bottom:32px;
line-height:1.7;
max-width:480px;
}
.hero-btn {
display:inline-flex;
align-items:center;
gap:10px;
padding:15px 36px;
border-radius:var(--radius);
font-size:15px;
font-weight:700;
letter-spacing:0.5px;
background:linear-gradient(135deg, var(--neon-purple), var(--neon-cyan));
color:#0A0A0F;
transition:var(--transition);
box-shadow:0 0 30px rgba(191,90,242,0.4), 0 0 60px rgba(100,210,255,0.2);
}
.hero-btn:hover {
transform:translateY(-2px);
box-shadow:0 0 40px rgba(191,90,242,0.6), 0 0 80px rgba(100,210,255,0.3);
}
.hero-btn:active { transform:translateY(0); }
.hero-btn svg { width:20px; height:20px; }
/* Hero 统计 */
.hero-stats {
display:flex;
gap:40px;
margin-top:40px;
}
.hero-stat-num {
font-size:28px;
font-weight:900;
font-family:var(--mono);
background:linear-gradient(135deg, var(--neon-cyan), var(--neon-purple));
-webkit-background-clip:text;
background-clip:text;
-webkit-text-fill-color:transparent;
}
.hero-stat-label { font-size:12px; color:var(--text-dim); text-transform:uppercase; letter-spacing:1px; }
/* ========== 优势条 ========== */
.features-strip {
max-width:1320px;
margin:32px auto;
padding:0 24px;
display:grid;
grid-template-columns:repeat(4,1fr);
gap:16px;
position:relative;
z-index:1;
}
.feature-item {
background:var(--surface);
backdrop-filter:blur(12px);
border:1px solid var(--border);
border-radius:var(--radius);
padding:20px;
display:flex;
align-items:center;
gap:14px;
transition:var(--transition);
}
.feature-item:hover {
border-color:rgba(100,210,255,0.3);
background:var(--surface-2);
transform:translateY(-2px);
}
.feature-icon {
width:42px; height:42px;
border-radius:11px;
display:flex;
align-items:center;
justify-content:center;
flex-shrink:0;
background:rgba(100,210,255,0.1);
border:1px solid rgba(100,210,255,0.2);
}
.feature-icon svg { width:20px; height:20px; color:var(--neon-cyan); }
.feature-item:nth-child(2) .feature-icon { background:rgba(191,90,242,0.1); border-color:rgba(191,90,242,0.2); }
.feature-item:nth-child(2) .feature-icon svg { color:var(--neon-purple); }
.feature-item:nth-child(3) .feature-icon { background:rgba(48,209,88,0.1); border-color:rgba(48,209,88,0.2); }
.feature-item:nth-child(3) .feature-icon svg { color:var(--neon-green); }
.feature-item:nth-child(4) .feature-icon { background:rgba(255,159,10,0.1); border-color:rgba(255,159,10,0.2); }
.feature-item:nth-child(4) .feature-icon svg { color:var(--neon-orange); }
.feature-item h4 { font-size:14px; font-weight:700; }
.feature-item p { font-size:12px; color:var(--text-muted); }
/* ========== 区块通用 ========== */
.section {
max-width:1320px;
margin:56px auto;
padding:0 24px;
position:relative;
z-index:1;
}
.section-header {
display:flex;
align-items:center;
justify-content:space-between;
margin-bottom:24px;
}
.section-title {
font-size:28px;
font-weight:900;
letter-spacing:-0.5px;
display:flex;
align-items:center;
gap:12px;
}
.section-title-bar {
width:4px; height:28px;
border-radius:2px;
background:linear-gradient(180deg, var(--neon-purple), var(--neon-cyan));
box-shadow:var(--glow-purple);
}
.section-sub { font-size:14px; color:var(--text-muted); font-weight:500; }
/* ========== 分类标签栏 ========== */
.category-nav {
display:flex;
gap:10px;
overflow-x:auto;
padding-bottom:8px;
margin-bottom:24px;
scrollbar-width:none;
}
.category-nav::-webkit-scrollbar { display:none; }
.category-chip {
padding:10px 22px;
border-radius:var(--radius);
background:var(--surface);
border:1px solid var(--border);
font-size:14px;
font-weight:600;
color:var(--text-muted);
white-space:nowrap;
transition:var(--transition);
cursor:pointer;
flex-shrink:0;
display:flex;
align-items:center;
gap:8px;
}
.category-chip:hover {
border-color:rgba(191,90,242,0.4);
color:var(--text);
background:var(--surface-2);
}
.category-chip.active {
background:rgba(191,90,242,0.12);
color:var(--neon-purple);
border-color:var(--neon-purple);
box-shadow:0 0 20px rgba(191,90,242,0.25), inset 0 0 12px rgba(191,90,242,0.08);
}
.category-chip-icon { width:16px; height:16px; opacity:0.8; }
/* ========== 商品布局 ========== */
.products-layout {
display:grid;
grid-template-columns:1fr;
gap:24px;
}
.products-toolbar {
display:flex;
align-items:center;
justify-content:space-between;
margin-bottom:20px;
flex-wrap:wrap;
gap:12px;
background:var(--surface);
backdrop-filter:blur(12px);
border:1px solid var(--border);
border-radius:var(--radius);
padding:14px 20px;
}
.products-count { font-size:14px; color:var(--text-muted); }
.products-count strong { color:var(--neon-cyan); font-family:var(--mono); }
.sort-group { display:flex; align-items:center; gap:10px; }
.sort-label { font-size:13px; color:var(--text-dim); font-weight:600; }
.sort-select {
padding:8px 16px;
border:1px solid var(--border);
border-radius:var(--radius-sm);
font-size:13px;
font-weight:600;
outline:none;
background:var(--bg-2);
color:var(--text);
cursor:pointer;
transition:var(--transition);
}
.sort-select:focus, .sort-select:hover { border-color:var(--neon-cyan); }
/* ========== 商品网格 ========== */
.products-grid {
display:grid;
grid-template-columns:repeat(4,1fr);
gap:20px;
}
.product-card {
background:var(--surface);
backdrop-filter:blur(12px);
border:1px solid var(--border);
border-radius:var(--radius);
overflow:hidden;
transition:var(--transition);
cursor:pointer;
position:relative;
display:flex;
flex-direction:column;
}
.product-card::before {
content:'';
position:absolute;
inset:0;
border-radius:var(--radius);
padding:1px;
background:linear-gradient(135deg, var(--neon-purple), var(--neon-cyan));
-webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite:xor;
mask-composite:exclude;
opacity:0;
transition:var(--transition);
pointer-events:none;
z-index:1;
}
.product-card:hover {
transform:translateY(-6px);
background:var(--surface-2);
box-shadow:0 12px 40px rgba(0,0,0,0.4), 0 0 30px rgba(191,90,242,0.2);
}
.product-card:hover::before { opacity:1; }
.product-image {
width:100%;
aspect-ratio:1;
position:relative;
overflow:hidden;
}
.product-image-gradient {
width:100%;
height:100%;
display:flex;
align-items:center;
justify-content:center;
position:relative;
}
.product-image-gradient::after {
content:'';
position:absolute;
inset:0;
background:linear-gradient(180deg, transparent 50%, rgba(0,0,0,0.4) 100%);
}
.product-image-gradient svg {
width:46%;
height:46%;
color:rgba(255,255,255,0.55);
filter:drop-shadow(0 0 12px rgba(255,255,255,0.2));
transition:var(--transition);
position:relative;
z-index:1;
}
.product-card:hover .product-image-gradient svg {
transform:scale(1.1);
color:rgba(255,255,255,0.8);
}
.product-tags {
position:absolute;
top:12px;
left:12px;
display:flex;
flex-direction:column;
gap:5px;
z-index:2;
}
.product-tag {
padding:4px 10px;
border-radius:6px;
font-size:11px;
font-weight:700;
color:#fff;
backdrop-filter:blur(8px);
letter-spacing:0.3px;
}
.tag-hot { background:rgba(255,55,95,0.85); box-shadow:0 0 12px rgba(255,55,95,0.4); }
.tag-new { background:rgba(100,210,255,0.85); box-shadow:0 0 12px rgba(100,210,255,0.4); color:#0A0A0F; }
.tag-sale { background:rgba(255,159,10,0.85); box-shadow:0 0 12px rgba(255,159,10,0.4); }
.tag-default { background:rgba(191,90,242,0.85); box-shadow:0 0 12px rgba(191,90,242,0.4); }
.product-discount {
position:absolute;
top:12px;
right:12px;
background:linear-gradient(135deg, var(--neon-pink), var(--neon-purple));
color:#fff;
font-size:12px;
font-weight:800;
padding:4px 10px;
border-radius:6px;
box-shadow:0 0 16px rgba(255,55,95,0.4);
font-family:var(--mono);
z-index:2;
}
.product-info { padding:16px; flex:1; display:flex; flex-direction:column; }
.product-name {
font-size:14px;
font-weight:600;
line-height:1.45;
margin-bottom:10px;
display:-webkit-box;
-webkit-line-clamp:2;
-webkit-box-orient:vertical;
overflow:hidden;
min-height:41px;
color:var(--text);
}
.product-price { display:flex; align-items:baseline; gap:8px; margin-bottom:10px; }
.price-current {
font-size:22px;
font-weight:900;
color:var(--neon-cyan);
font-family:var(--mono);
text-shadow:0 0 16px rgba(100,210,255,0.3);
}
.price-symbol { font-size:13px; color:var(--neon-cyan); opacity:0.8; font-family:var(--mono); }
.price-original {
font-size:13px;
color:var(--text-dim);
text-decoration:line-through;
font-family:var(--mono);
}
.product-meta {
display:flex;
align-items:center;
justify-content:space-between;
font-size:12px;
color:var(--text-muted);
margin-top:auto;
}
.product-rating { display:flex; align-items:center; gap:5px; }
.stars { color:var(--neon-orange); letter-spacing:1px; font-size:11px; }
.rating-num { font-family:var(--mono); color:var(--text-muted); }
.product-sales { font-family:var(--mono); }
.add-cart-btn {
position:absolute;
bottom:16px;
right:16px;
width:40px;
height:40px;
border-radius:11px;
background:linear-gradient(135deg, var(--neon-purple), var(--neon-cyan));
color:#0A0A0F;
display:flex;
align-items:center;
justify-content:center;
opacity:0;
transform:translateY(12px) scale(0.8);
transition:var(--transition);
box-shadow:0 4px 20px rgba(191,90,242,0.5);
z-index:2;
}
.product-card:hover .add-cart-btn { opacity:1; transform:translateY(0) scale(1); }
.add-cart-btn:hover { transform:scale(1.1); box-shadow:0 6px 28px rgba(191,90,242,0.7); }
.add-cart-btn svg { width:18px; height:18px; }
/* ========== 加载骨架 ========== */
.skeleton-card {
background:var(--surface);
border:1px solid var(--border);
border-radius:var(--radius);
overflow:hidden;
}
.skeleton-img {
aspect-ratio:1;
background:linear-gradient(90deg, var(--bg-2) 25%, var(--bg-3) 50%, var(--bg-2) 75%);
background-size:200% 100%;
animation:shimmer 1.8s infinite;
}
.skeleton-line {
height:14px;
margin:14px 16px;
border-radius:5px;
background:linear-gradient(90deg, var(--bg-2) 25%, var(--bg-3) 50%, var(--bg-2) 75%);
background-size:200% 100%;
animation:shimmer 1.8s infinite;
}
@keyframes shimmer {
0% { background-position:200% 0; }
100% { background-position:-200% 0; }
}
/* ========== 分页 ========== */
.pagination { display:flex; justify-content:center; gap:8px; margin-top:44px; }
.page-btn {
min-width:42px;
height:42px;
border-radius:var(--radius-sm);
background:var(--surface);
border:1px solid var(--border);
font-size:14px;
font-weight:700;
color:var(--text-muted);
transition:var(--transition);
font-family:var(--mono);
}
.page-btn:hover:not(:disabled) {
border-color:var(--neon-purple);
color:var(--neon-purple);
background:rgba(191,90,242,0.08);
}
.page-btn.active {
background:linear-gradient(135deg, var(--neon-purple), var(--neon-cyan));
color:#0A0A0F;
border-color:transparent;
box-shadow:0 0 20px rgba(191,90,242,0.4);
}
.page-btn:disabled { opacity:0.3; cursor:not-allowed; }
/* ========== 购物车抽屉 ========== */
.cart-overlay {
position:fixed;
inset:0;
background:rgba(0,0,0,0.6);
backdrop-filter:blur(4px);
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:440px;
max-width:92vw;
height:100vh;
background:var(--bg-2);
border-left:1px solid var(--border-2);
z-index:201;
transform:translateX(100%);
transition:transform 0.35s cubic-bezier(0.4,0,0.2,1);
display:flex;
flex-direction:column;
}
.cart-drawer.open { transform:translateX(0); }
.cart-header {
padding:22px 24px;
border-bottom:1px solid var(--border);
display:flex;
align-items:center;
justify-content:space-between;
}
.cart-header h3 {
font-size:18px;
font-weight:900;
display:flex;
align-items:center;
gap:10px;
}
.cart-header-count {
font-size:13px;
font-weight:700;
color:var(--neon-cyan);
background:rgba(100,210,255,0.12);
padding:3px 10px;
border-radius:20px;
font-family:var(--mono);
}
.cart-close {
width:34px; height:34px;
border-radius:9px;
background:var(--surface-2);
display:flex;
align-items:center;
justify-content:center;
color:var(--text-muted);
transition:var(--transition);
}
.cart-close:hover { background:var(--surface-3); color:var(--neon-pink); }
.cart-close svg { width:18px; height:18px; }
.cart-items { flex:1; overflow-y:auto; padding:16px 24px; }
.cart-item {
display:flex;
gap:14px;
padding:16px 0;
border-bottom:1px solid var(--border);
}
.cart-item-img {
width:76px; height:76px;
border-radius:var(--radius-sm);
flex-shrink:0;
display:flex;
align-items:center;
justify-content:center;
position:relative;
overflow:hidden;
}
.cart-item-img svg { width:32px; height:32px; color:rgba(255,255,255,0.6); }
.cart-item-info { flex:1; min-width:0; }
.cart-item-name {
font-size:14px;
font-weight:600;
margin-bottom:6px;
display:-webkit-box;
-webkit-line-clamp:2;
-webkit-box-orient:vertical;
overflow:hidden;
}
.cart-item-price {
font-size:17px;
font-weight:800;
color:var(--neon-cyan);
font-family:var(--mono);
}
.cart-item-controls {
display:flex;
align-items:center;
gap:10px;
margin-top:10px;
}
.qty-btn {
width:30px; height:30px;
border-radius:8px;
background:var(--surface-2);
border:1px solid var(--border);
display:flex;
align-items:center;
justify-content:center;
font-size:16px;
font-weight:700;
color:var(--text);
transition:var(--transition);
}
.qty-btn:hover { background:rgba(191,90,242,0.15); border-color:var(--neon-purple); color:var(--neon-purple); }
.qty-value {
font-size:14px;
font-weight:700;
min-width:28px;
text-align:center;
font-family:var(--mono);
}
.cart-item-remove {
margin-left:auto;
font-size:12px;
color:var(--text-dim);
transition:var(--transition);
display:flex;
align-items:center;
gap:4px;
}
.cart-item-remove:hover { color:var(--neon-pink); }
.cart-item-remove svg { width:14px; height:14px; }
.cart-empty {
text-align:center;
padding:80px 20px;
color:var(--text-muted);
}
.cart-empty svg { width:72px; height:72px; margin:0 auto 20px; opacity:0.2; }
.cart-empty p { font-size:15px; }
.cart-empty .sub { font-size:13px; margin-top:8px; color:var(--text-dim); }
.cart-footer {
padding:22px 24px;
border-top:1px solid var(--border);
background:var(--bg-3);
}
.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:28px;
font-weight:900;
font-family:var(--mono);
background:linear-gradient(135deg, var(--neon-cyan), var(--neon-purple));
-webkit-background-clip:text;
background-clip:text;
-webkit-text-fill-color:transparent;
}
.checkout-btn {
width:100%;
padding:15px;
border-radius:var(--radius);
background:linear-gradient(135deg, var(--neon-purple), var(--neon-cyan));
color:#0A0A0F;
font-size:16px;
font-weight:800;
letter-spacing:0.5px;
transition:var(--transition);
box-shadow:0 0 30px rgba(191,90,242,0.3);
}
.checkout-btn:hover:not(:disabled) {
box-shadow:0 0 40px rgba(191,90,242,0.5), 0 0 80px rgba(100,210,255,0.2);
transform:translateY(-1px);
}
.checkout-btn:disabled { opacity:0.4; cursor:not-allowed; }
/* ========== 商品详情模态框 ========== */
.modal-overlay {
position:fixed;
inset:0;
background:rgba(0,0,0,0.7);
backdrop-filter:blur(8px);
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(--bg-2);
border:1px solid var(--border-2);
border-radius:var(--radius-lg);
max-width:880px;
width:100%;
max-height:90vh;
overflow-y:auto;
transform:scale(0.92) translateY(20px);
transition:transform 0.35s cubic-bezier(0.4,0,0.2,1);
position:relative;
}
.modal-overlay.open .modal-content { transform:scale(1) translateY(0); }
.modal-close {
position:absolute;
top:16px;
right:16px;
width:38px; height:38px;
border-radius:50%;
background:rgba(0,0,0,0.4);
backdrop-filter:blur(8px);
display:flex;
align-items:center;
justify-content:center;
z-index:5;
transition:var(--transition);
color:#fff;
}
.modal-close:hover { background:rgba(255,55,95,0.3); }
.modal-close svg { width:20px; height:20px; }
.modal-body { display:grid; grid-template-columns:1fr 1fr; }
.modal-image {
aspect-ratio:1;
display:flex;
align-items:center;
justify-content:center;
position:relative;
overflow:hidden;
}
.modal-image svg {
width:42%;
height:42%;
color:rgba(255,255,255,0.6);
filter:drop-shadow(0 0 16px rgba(255,255,255,0.2));
}
.modal-image::after {
content:'';
position:absolute;
inset:0;
background:linear-gradient(135deg, transparent 60%, rgba(0,0,0,0.3) 100%);
}
.modal-details { padding:36px; position:relative; }
.modal-details h2 {
font-size:24px;
font-weight:800;
margin-bottom:14px;
line-height:1.3;
}
.modal-price { display:flex; align-items:baseline; gap:12px; margin-bottom:18px; flex-wrap:wrap; }
.modal-price .price-current { font-size:36px; }
.modal-price .price-symbol { font-size:18px; }
.modal-tags { display:flex; gap:8px; margin-bottom:18px; flex-wrap:wrap; }
.modal-desc {
font-size:14px;
color:var(--text-muted);
line-height:1.8;
margin-bottom:24px;
}
.modal-meta {
display:flex;
gap:28px;
padding:18px 0;
border-top:1px solid var(--border);
border-bottom:1px solid var(--border);
margin-bottom:24px;
}
.modal-meta-item { display:flex; flex-direction:column; gap:4px; }
.modal-meta-item .label { font-size:12px; color:var(--text-dim); text-transform:uppercase; letter-spacing:0.5px; }
.modal-meta-item .value { font-size:15px; font-weight:700; font-family:var(--mono); color:var(--text); }
.modal-meta-item .value.cyan { color:var(--neon-cyan); }
.modal-qty-row {
display:flex;
align-items:center;
gap:14px;
margin-bottom:24px;
}
.modal-qty-label { font-size:14px; color:var(--text-muted); font-weight:600; }
.modal-qty-control { display:flex; align-items:center; gap:0; border:1px solid var(--border); border-radius:var(--radius-sm); overflow:hidden; }
.modal-qty-control button {
width:38px; height:38px;
display:flex;
align-items:center;
justify-content:center;
font-size:18px;
font-weight:700;
color:var(--text);
transition:var(--transition);
}
.modal-qty-control button:hover { background:var(--surface-2); color:var(--neon-purple); }
.modal-qty-control .val {
width:48px;
text-align:center;
font-weight:700;
font-family:var(--mono);
border-left:1px solid var(--border);
border-right:1px solid var(--border);
height:38px;
line-height:38px;
}
.modal-add-cart {
width:100%;
padding:16px;
border-radius:var(--radius);
background:linear-gradient(135deg, var(--neon-purple), var(--neon-cyan));
color:#0A0A0F;
font-size:16px;
font-weight:800;
letter-spacing:0.5px;
transition:var(--transition);
box-shadow:0 0 30px rgba(191,90,242,0.3);
display:flex;
align-items:center;
justify-content:center;
gap:10px;
}
.modal-add-cart:hover { box-shadow:0 0 40px rgba(191,90,242,0.5), 0 0 80px rgba(100,210,255,0.2); transform:translateY(-1px); }
.modal-add-cart svg { width:20px; height:20px; }
/* ========== 页脚 ========== */
.footer {
background:var(--bg-2);
border-top:1px solid var(--border);
margin-top:80px;
position:relative;
z-index:1;
}
.footer-content {
max-width:1320px;
margin:0 auto;
padding:56px 24px 28px;
display:grid;
grid-template-columns:2fr 1fr 1fr 1fr;
gap:40px;
}
.footer-brand h3 {
font-size:24px;
font-weight:900;
margin-bottom:14px;
display:flex;
align-items:center;
gap:10px;
}
.footer-brand p { font-size:14px; color:var(--text-muted); line-height:1.8; max-width:380px; }
.footer-col h4 {
font-size:15px;
font-weight:700;
margin-bottom:18px;
color:var(--text);
}
.footer-col ul { list-style:none; }
.footer-col li { margin-bottom:12px; }
.footer-col a {
font-size:13px;
color:var(--text-muted);
transition:var(--transition);
}
.footer-col a:hover { color:var(--neon-cyan); text-shadow:0 0 8px rgba(100,210,255,0.4); }
.footer-bottom {
border-top:1px solid var(--border);
padding:22px 24px;
text-align:center;
font-size:13px;
color:var(--text-dim);
}
.footer-bottom .neon { color:var(--neon-purple); }
/* ========== Toast ========== */
.toast {
position:fixed;
bottom:32px;
left:50%;
transform:translateX(-50%) translateY(120px);
background:var(--bg-3);
border:1px solid var(--border-2);
backdrop-filter:blur(20px);
padding:14px 24px;
border-radius:var(--radius);
font-size:14px;
font-weight:600;
z-index:400;
opacity:0;
transition:all 0.35s cubic-bezier(0.4,0,0.2,1);
display:flex;
align-items:center;
gap:10px;
box-shadow:0 8px 32px rgba(0,0,0,0.4);
}
.toast.show { transform:translateX(-50%) translateY(0); opacity:1; }
.toast-icon {
width:24px; height:24px;
border-radius:50%;
display:flex;
align-items:center;
justify-content:center;
flex-shrink:0;
}
.toast-icon.success { background:rgba(48,209,88,0.15); }
.toast-icon.success svg { color:var(--neon-green); }
.toast-icon.error { background:rgba(255,55,95,0.15); }
.toast-icon.error svg { color:var(--neon-pink); }
.toast-icon svg { width:16px; height:16px; }
/* ========== 加载中 ========== */
.loading-center {
padding:80px 20px;
text-align:center;
color:var(--text-muted);
}
.loading-center .spinner {
width:40px; height:40px;
margin:0 auto 16px;
border:3px solid var(--surface-2);
border-top-color:var(--neon-purple);
border-radius:50%;
animation:spin 0.8s linear infinite;
}
@keyframes spin { to { transform:rotate(360deg); } }
.error-block {
grid-column:1/-1;
text-align:center;
padding:60px 20px;
color:var(--text-muted);
}
.error-block svg { width:56px; height:56px; margin:0 auto 16px; opacity:0.3; color:var(--neon-pink); }
.retry-btn {
margin-top:16px;
padding:10px 24px;
border-radius:var(--radius-sm);
background:var(--surface-2);
border:1px solid var(--neon-purple);
color:var(--neon-purple);
font-size:14px;
font-weight:600;
transition:var(--transition);
}
.retry-btn:hover { background:rgba(191,90,242,0.12); }
/* ========== 响应式 ========== */
@media (max-width:1100px) {
.products-grid { grid-template-columns:repeat(3,1fr); }
.features-strip { grid-template-columns:repeat(2,1fr); }
.footer-content { grid-template-columns:2fr 1fr 1fr; }
.hero-content h1 { font-size:52px; }
}
@media (max-width:768px) {
.nav-links { display:none; }
.search-box { max-width:220px; }
.nav-container { gap:16px; height:60px; }
.hero-banner { padding:48px 28px; min-height:auto; }
.hero-content h1 { font-size:38px; }
.hero-content .hero-sub { font-size:15px; }
.hero-stats { gap:24px; }
.hero-stat-num { font-size:22px; }
.products-grid { grid-template-columns:repeat(2,1fr); }
.modal-body { grid-template-columns:1fr; }
.modal-details { padding:24px; }
.footer-content { grid-template-columns:1fr; gap:28px; }
.section-title { font-size:22px; }
.products-toolbar { flex-direction:column; align-items:stretch; }
.sort-group { justify-content:space-between; }
}
@media (max-width:480px) {
.features-strip { grid-template-columns:1fr; }
.products-grid { grid-template-columns:1fr; }
.hero-content h1 { font-size:30px; }
.hero-stats { flex-wrap:wrap; gap:16px; }
}
/* ========== 3D 动态效果 & 鼠标光影(新增) ========== */
/* 鼠标霓虹光斑:固定跟随鼠标,紫青混合,screen 混合在暗色背景上发光 */
.cursor-glow {
position: fixed;
width: 500px;
height: 500px;
border-radius: 50%;
background: radial-gradient(circle, rgba(191,90,242,0.15) 0%, rgba(100,210,255,0.08) 40%, transparent 70%);
pointer-events: none;
z-index: 150;
transform: translate(-50%, -50%);
mix-blend-mode: screen;
filter: blur(20px);
left: 50vw;
top: 50vh;
}
/* 3D 卡片倾斜容器 */
.products-grid { perspective: 1200px; }
.product-card { transform-style: preserve-3d; transition: transform .15s ease-out, box-shadow .3s; }
.product-card:hover { box-shadow: 0 0 30px rgba(191,90,242,0.3), 0 8px 40px rgba(0,0,0,0.5); }
.product-card .product-image { transform: translateZ(25px); }
.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(191,90,242,0.2) 0%, transparent 50%);
opacity: 0;
transition: opacity .3s;
pointer-events: none;
z-index: 3;
}
.product-card:hover::after { opacity: 1; }
/* 浮动霓虹粒子背景 */
.neon-particle {
position: fixed;
width: 4px;
height: 4px;
border-radius: 50%;
pointer-events: none;
z-index: 1;
animation: float-particle 20s infinite ease-in-out;
}
@keyframes float-particle {
0%, 100% { transform: translate(0, 0); opacity: 0.3; }
50% { transform: translate(30px, -50px); opacity: 0.8; }
}
</style>
</head>
<body>
<!-- 顶部导航栏 -->
<nav class="navbar">
<div class="nav-container">
<div class="logo">
<div class="logo-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M13 2L3 14h7l-1 8 10-12h-7l1-8z"/></svg>
</div>
<span class="logo-text">NEON MART</span>
</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="#products" onclick="loadFeatured();return false;">热门推荐</a></li>
</ul>
<div class="search-box">
<input type="text" id="searchInput" placeholder="搜索霓虹好物..." oninput="handleSearch(event)">
<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>
</div>
<div class="nav-actions">
<button class="nav-btn" 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 Banner -->
<div class="hero">
<div class="hero-banner">
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
<div class="hero-content">
<div class="hero-tag">
<span class="hero-tag-dot"></span>
GLOBAL CROSS-BORDER · 全球直邮
</div>
<h1 class="gradient-text">NEON<br>MART</h1>
<p class="hero-sub">点亮你的全球购物体验。精选智能电子、家居生活、时尚运动等品类好物,霓虹品质,跨境直达。</p>
<a href="#" class="hero-btn" onclick="scrollToProducts();return false;">
立即探索
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
<div class="hero-stats">
<div>
<div class="hero-stat-num">18+</div>
<div class="hero-stat-label">精选品类</div>
</div>
<div>
<div class="hero-stat-num">50K+</div>
<div class="hero-stat-label">全球用户</div>
</div>
<div>
<div class="hero-stat-num">99.8%</div>
<div class="hero-stat-label">好评率</div>
</div>
</div>
</div>
</div>
</div>
<!-- 优势条 -->
<div class="features-strip">
<div class="feature-item">
<div class="feature-icon"><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>
<div><h4>全球直邮</h4><p>满 $99 免运费</p></div>
</div>
<div class="feature-item">
<div class="feature-icon"><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>
<div><h4>正品保障</h4><p>品牌授权直供</p></div>
</div>
<div class="feature-item">
<div class="feature-icon"><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>
<div><h4>7天退换</h4><p>无理由退货</p></div>
</div>
<div class="feature-item">
<div class="feature-icon"><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>
<div><h4>安全支付</h4><p>多渠道加密</p></div>
</div>
</div>
<!-- 商品列表区 -->
<div class="section" id="products">
<div class="section-header">
<div class="section-title">
<span class="section-title-bar"></span>
<span class="gradient-text-pink">霓虹好物</span>
</div>
<span class="section-sub" id="productsCount">加载中...</span>
</div>
<!-- 分类标签栏 -->
<div class="category-nav" id="categoryChips"></div>
<!-- 工具栏 -->
<div class="products-toolbar">
<div class="products-count" id="productsCountInner">加载中...</div>
<div class="sort-group">
<span class="sort-label">排序</span>
<select class="sort-select" id="sortSelect" onchange="applySort()">
<option value="default">默认排序</option>
<option value="sales">销量优先</option>
<option value="price_asc">价格升序</option>
<option value="price_desc">价格降序</option>
<option value="rating">评分最高</option>
</select>
</div>
</div>
<!-- 商品网格 -->
<div class="products-grid" id="productsGrid"></div>
<!-- 分页 -->
<div class="pagination" id="pagination"></div>
</div>
<!-- 购物车遮罩 -->
<div class="cart-overlay" id="cartOverlay" onclick="toggleCart()"></div>
<!-- 购物车抽屉 -->
<div class="cart-drawer" id="cartDrawer">
<div class="cart-header">
<h3>购物车 <span class="cart-header-count" id="cartCount">0</span></h3>
<button class="cart-close" onclick="toggleCart()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M18 6L6 18M6 6l12 12"/></svg>
</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()" disabled>去结算</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">
<div class="toast-icon success" id="toastIcon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
</div>
<span id="toastMsg">已加入购物车</span>
</div>
<!-- 页脚 -->
<footer class="footer">
<div class="footer-content">
<div class="footer-brand">
<h3><span class="gradient-text">NEON MART</span></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 <span class="neon">NEON MART</span> · All Rights Reserved · 全球好物 · 霓虹直达
</div>
</footer>
<script>
// ========== 全局状态 ==========
let allCategories = [];
let currentPage = 1;
const pageSize = 12;
let selectedCategory = 0;
let searchKeyword = '';
let currentSort = 'default';
let cart = [];
let productCache = {};
let modalQty = 1;
// ========== 分类渐变色 & 图标 ==========
const categoryGradients = {
1: 'linear-gradient(135deg, #BF5AF2 0%, #6B2FB5 100%)', // 智能电子 - 紫
2: 'linear-gradient(135deg, #64D2FF 0%, #2A7AB5 100%)', // 家居生活 - 青
3: 'linear-gradient(135deg, #FF375F 0%, #B51E3E 100%)', // 时尚服饰 - 粉
4: 'linear-gradient(135deg, #30D158 0%, #1A7A35 100%)', // 运动户外 - 绿
5: 'linear-gradient(135deg, #FF9F0A 0%, #B56B00 100%)', // 美妆个护 - 橙
};
const defaultGradient = 'linear-gradient(135deg, #BF5AF2 0%, #64D2FF 100%)';
function getGradient(catId) {
return categoryGradients[catId] || defaultGradient;
}
// 按分类选择不同 SVG 图标
function productIconSVG(catId) {
const icons = {
1: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="6" width="20" height="14" rx="3"/><path d="M6 6V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2"/><circle cx="8" cy="13" r="1.5"/><circle cx="16" cy="13" r="1.5"/></svg>',
2: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></svg>',
3: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><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"/></svg>',
4: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><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"/></svg>',
5: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="4"/><path d="M8 8l8 8M16 8l-8 8"/></svg>',
};
return icons[catId] || icons[1];
}
// 分类小图标
function categoryIconSVG(catId) {
return productIconSVG(catId);
}
// ========== API 调用 ==========
async function fetchJSON(url) {
const res = await fetch(url);
if (!res.ok) throw new Error('网络错误 ' + res.status);
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');
renderCategoryChips();
} catch(e) {
console.error('分类加载失败:', e);
document.getElementById('categoryChips').innerHTML = '<span style="color:var(--text-dim);font-size:13px">分类加载失败</span>';
}
}
function renderSkeletons() {
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:55%"></div>
<div class="skeleton-line" style="width:40%;margin-bottom:16px"></div>
</div>
`).join('');
}
async function loadProducts() {
const grid = document.getElementById('productsGrid');
renderSkeletons();
try {
let url = `/api/v1/shop/products?page=${currentPage}&page_size=${pageSize}`;
if (currentSort && currentSort !== 'default') url += `&sort=${currentSort}`;
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) {
console.error('商品加载失败:', e);
grid.innerHTML = `
<div class="error-block">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg>
<p>商品加载失败,请稍后重试</p>
<button class="retry-btn" onclick="loadProducts()">重新加载</button>
</div>`;
document.getElementById('pagination').innerHTML = '';
}
}
// ========== 渲染函数 ==========
function renderCategoryChips() {
const container = document.getElementById('categoryChips');
let html = `<span class="category-chip ${selectedCategory === 0 ? 'active' : ''}" onclick="selectCategory(0)">
<svg class="category-chip-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
全部
</span>`;
html += allCategories.map(c => `
<span class="category-chip ${selectedCategory === c.id ? 'active' : ''}" onclick="selectCategory(${c.id})">
<span class="category-chip-icon">${categoryIconSVG(c.id)}</span>
${c.name}
</span>
`).join('');
container.innerHTML = html;
}
function parseTags(tagsStr) {
try { return JSON.parse(tagsStr || '[]'); } catch(e) { return []; }
}
function tagClass(tag) {
if (tag.includes('热') || tag.includes('爆')) return 'tag-hot';
if (tag.includes('新')) return 'tag-new';
if (tag.includes('折') || tag.includes('惠') || tag.includes('特')) return 'tag-sale';
return 'tag-default';
}
function formatSales(n) {
if (n >= 10000) return (n / 10000).toFixed(1) + '万';
return n.toString();
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function renderProducts(products, total) {
const grid = document.getElementById('productsGrid');
const countText = `共 <strong>${total}</strong> 件商品`;
document.getElementById('productsCount').textContent = countText;
document.getElementById('productsCountInner').innerHTML = countText;
if (products.length === 0) {
grid.innerHTML = `
<div class="error-block">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<p>未找到匹配的商品</p>
<p style="font-size:13px;margin-top:6px;color:var(--text-dim)">试试其他关键词或分类</p>
</div>`;
return;
}
grid.innerHTML = products.map(p => {
const discount = (p.original_price && p.original_price > p.price) ? Math.round((1 - p.price / p.original_price) * 100) : 0;
const tags = parseTags(p.tags);
const tagHtml = tags.slice(0, 2).map(t => `<span class="product-tag ${tagClass(t)}">${escapeHtml(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">${escapeHtml(p.name)}</div>
<div class="product-price">
<span class="price-symbol">$</span>
<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">${'\u2605'.repeat(Math.round(p.rating))}</span>
<span class="rating-num">${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.5"><path d="M12 5v14M5 12h14"/></svg>
</button>
</div>`;
}).join('');
}
function renderPagination(total) {
const pages = Math.ceil(total / pageSize);
const container = document.getElementById('pagination');
if (pages <= 1) { container.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-dim)">...</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-dim)">...</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>`;
container.innerHTML = html;
}
// ========== 交互函数 ==========
function selectCategory(catId) {
selectedCategory = catId;
currentPage = 1;
renderCategoryChips();
loadProducts();
}
function applySort() {
currentSort = document.getElementById('sortSelect').value;
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', block:'start' });
}
function scrollToProducts() {
document.getElementById('products').scrollIntoView({ behavior:'smooth', block:'start' });
}
function resetPage() {
selectedCategory = 0;
searchKeyword = '';
currentPage = 1;
currentSort = 'default';
document.getElementById('searchInput').value = '';
document.getElementById('sortSelect').value = 'default';
selectCategory(0);
window.scrollTo({ top:0, behavior:'smooth' });
}
async function loadFeatured() {
const grid = document.getElementById('productsGrid');
renderSkeletons();
try {
const products = await fetchJSON('/api/v1/shop/featured?limit=8');
renderProducts(products, products.length);
document.getElementById('pagination').innerHTML = '';
document.getElementById('productsCount').innerHTML = '热门推荐 · <strong>' + products.length + '</strong> 件商品';
document.getElementById('productsCountInner').innerHTML = '热门推荐 · <strong>' + products.length + '</strong> 件商品';
scrollToProducts();
showToast('已加载热门推荐商品', 'success');
} catch(e) {
grid.innerHTML = `<div class="error-block"><p>推荐加载失败</p><button class="retry-btn" onclick="loadFeatured()">重试</button></div>`;
}
}
// ========== 商品详情 ==========
async function showProductDetail(id) {
const modal = document.getElementById('productModal');
const content = document.getElementById('modalContent');
modalQty = 1;
content.innerHTML = '<div class="loading-center"><div class="spinner"></div><p>加载中...</p></div>';
modal.classList.add('open');
document.body.style.overflow = 'hidden';
try {
const p = await fetchJSON(`/api/v1/shop/products/${id}`);
productCache[p.id] = p;
const tags = parseTags(p.tags);
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.original_price > p.price) ? Math.round((1 - p.price / p.original_price) * 100) : 0;
content.innerHTML = `
<button class="modal-close" onclick="closeModal()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
<div class="modal-body">
<div class="modal-image" style="background:${getGradient(p.category_id)}">
${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>` : ''}
${discount > 0 ? `<div class="product-discount">-${discount}%</div>` : ''}
</div>
<div class="modal-details">
<h2>${escapeHtml(p.name)}</h2>
<div class="modal-price">
<span class="price-symbol">$</span>
<span class="price-current">${p.price.toFixed(2)}</span>
${p.original_price > p.price ? `<span class="price-original">$${p.original_price.toFixed(2)}</span>` : ''}
${discount > 0 ? `<span class="product-tag tag-sale">省 ${discount}%</span>` : ''}
</div>
<div class="modal-tags">${tags.map(t => `<span class="product-tag ${tagClass(t)}">${escapeHtml(t)}</span>`).join('')}</div>
<p class="modal-desc">${escapeHtml(p.description)}</p>
<div class="modal-meta">
<div class="modal-meta-item"><span class="label">评分</span><span class="value cyan">${p.rating.toFixed(1)} \u2605</span></div>
<div class="modal-meta-item"><span class="label">销量</span><span class="value">${formatSales(p.sales)}</span></div>
<div class="modal-meta-item"><span class="label">库存</span><span class="value">${p.stock} 件</span></div>
</div>
<div class="modal-qty-row">
<span class="modal-qty-label">数量</span>
<div class="modal-qty-control">
<button onclick="changeModalQty(-1)">\u2212</button>
<span class="val" id="modalQtyVal">1</span>
<button onclick="changeModalQty(1)">+</button>
</div>
</div>
<button class="modal-add-cart" onclick="addToCartFromModal(${p.id})">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.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>
加入购物车
</button>
</div>
</div>
`;
} catch(e) {
content.innerHTML = `<div class="error-block"><p>商品信息加载失败</p><button class="retry-btn" onclick="closeModal()">关闭</button></div>`;
}
}
function changeModalQty(delta) {
modalQty = Math.max(1, modalQty + delta);
document.getElementById('modalQtyVal').textContent = modalQty;
}
function addToCartFromModal(productId) {
addToCart(productId, modalQty);
closeModal();
}
function closeModal(e) {
if (e && e.target !== e.currentTarget) return;
document.getElementById('productModal').classList.remove('open');
document.body.style.overflow = '';
}
// ========== 购物车 ==========
async function addToCart(productId, qty) {
qty = qty || 1;
let product = productCache[productId];
if (!product) {
try {
product = await fetchJSON(`/api/v1/shop/products/${productId}`);
productCache[productId] = product;
} catch(e) {
showToast('添加失败,请重试', 'error');
return;
}
}
const existing = cart.find(i => i.id === productId);
if (existing) {
existing.qty += qty;
} else {
cart.push({
id: productId,
name: product.name,
price: product.price,
qty: qty,
category_id: product.category_id,
image: product.image || ''
});
}
updateCartUI();
const shortName = product.name.length > 14 ? product.name.substring(0, 14) + '...' : product.name;
showToast('已加入购物车: ' + shortName, 'success');
// 购物车角标动画
const badge = document.getElementById('cartBadge');
badge.style.animation = 'none';
setTimeout(() => { badge.style.animation = 'pulse 0.4s ease'; }, 10);
}
function removeFromCart(productId) {
cart = cart.filter(i => i.id !== productId);
updateCartUI();
showToast('已移除商品', 'success');
}
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 class="sub">快去挑选心仪的霓虹好物吧</p>
</div>`;
return;
}
container.innerHTML = cart.map(item => `
<div class="cart-item">
<div class="cart-item-img" style="background:${getGradient(item.category_id)}">${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">${escapeHtml(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)">\u2212</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})">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
删除
</span>
</div>
</div>
</div>
`).join('');
}
function toggleCart() {
const open = document.getElementById('cartDrawer').classList.contains('open');
if (open) {
document.getElementById('cartOverlay').classList.remove('open');
document.getElementById('cartDrawer').classList.remove('open');
document.body.style.overflow = '';
} else {
document.getElementById('cartOverlay').classList.add('open');
document.getElementById('cartDrawer').classList.add('open');
document.body.style.overflow = 'hidden';
}
}
function checkout() {
if (cart.length === 0) return;
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
const count = cart.reduce((s, i) => s + i.qty, 0);
showToast('订单已提交! 共 ' + count + ' 件商品, 合计 $' + total.toFixed(2), 'success');
cart = [];
updateCartUI();
toggleCart();
}
// ========== Toast ==========
function showToast(msg, type) {
type = type || 'success';
const toast = document.getElementById('toast');
const icon = document.getElementById('toastIcon');
document.getElementById('toastMsg').textContent = msg;
icon.className = 'toast-icon ' + type;
if (type === 'error') {
icon.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg>';
} else {
icon.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>';
}
toast.classList.add('show');
clearTimeout(window._toastTimer);
window._toastTimer = setTimeout(() => toast.classList.remove('show'), 2800);
}
// ========== ESC 关闭 ==========
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeModal();
if (document.getElementById('cartDrawer').classList.contains('open')) toggleCart();
}
});
// ========== 初始化 ==========
async function init() {
await loadCategories();
await loadProducts();
updateCartUI();
}
init();
// ========== 3D 动态效果 & 鼠标光影(新增) ==========
// 鼠标霓虹光斑跟随
const cursorGlow = document.createElement('div');
cursorGlow.className = 'cursor-glow';
cursorGlow.style.left = '50vw';
cursorGlow.style.top = '50vh';
document.body.appendChild(cursorGlow);
document.addEventListener('mousemove', e => {
cursorGlow.style.left = e.clientX + 'px';
cursorGlow.style.top = e.clientY + 'px';
});
// 产品卡片 3D 倾斜 + 表面霓虹高光(鼠标在卡片上时倾斜,离开时恢复)
document.addEventListener('mousemove', e => {
document.querySelectorAll('.product-card').forEach(card => {
const rect = card.getBoundingClientRect();
if (e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom) {
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const rx = ((e.clientY - cy) / rect.height) * -10;
const ry = ((e.clientX - cx) / rect.width) * 10;
card.style.transform = `perspective(1200px) 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) + '%');
} else if (card.style.transform) {
card.style.transform = '';
}
});
});
// 创建霓虹浮动粒子(12 个,紫/青/粉/绿随机分布)
const particleColors = ['#BF5AF2', '#64D2FF', '#FF375F', '#30D158'];
for (let i = 0; i < 12; i++) {
const p = document.createElement('div');
p.className = 'neon-particle';
p.style.left = Math.random() * 100 + 'vw';
p.style.top = Math.random() * 100 + 'vh';
p.style.background = particleColors[i % 4];
p.style.boxShadow = '0 0 8px ' + particleColors[i % 4];
p.style.animationDuration = (15 + Math.random() * 15) + 's';
p.style.animationDelay = -Math.random() * 20 + 's';
document.body.appendChild(p);
}
</script>
</body>
</html>