Files
web_template/web/dist/shop2/index.html
Geliebte a84129330e feat: 全站20个模板添加国际化(i18n)支持 - 简体中文/繁体中文/英文三语切换
- 所有18套前端模板 + 管理后台共20个HTML文件添加i18n引擎
- 前端JavaScript方案:I18N_LOCALES翻译字典 + data-i18n属性 + t()函数调用
- 语言切换器UI:下拉菜单选择,匹配各模板主题风格
- 语言持久化:localStorage存储 + URL参数覆盖 + 浏览器语言自动检测
- onLangChange回调:语言切换时动态重新渲染所有内容
- 支持语言:zh-CN(简体中文) / zh-TW(繁体中文) / en(English)
2026-09-09 16:58:40 +08:00

2372 lines
81 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);
}
/* ========== 语言切换器 ========== */
.lang-switcher { position:relative; margin-left:4px; }
.lang-btn {
display:flex; align-items:center; gap:6px; padding:8px 14px;
border-radius:var(--radius-sm); font-size:13px; font-weight:600;
color:var(--text-muted); cursor:pointer;
background:var(--surface); border:1px solid var(--border);
transition:var(--transition);
}
.lang-btn:hover { color:var(--text); background:var(--surface-2); border-color:rgba(100,210,255,0.3); }
.lang-btn svg { width:16px; height:16px; }
.lang-dropdown {
position:absolute; top:calc(100% + 8px); right:0; min-width:160px;
background:rgba(18,18,26,0.96); backdrop-filter:blur(20px);
-webkit-backdrop-filter:blur(20px);
border:1px solid var(--border-2); border-radius:var(--radius-sm);
box-shadow:0 12px 40px rgba(0,0,0,0.5); overflow:hidden;
opacity:0; transform:translateY(-8px); pointer-events:none;
transition:var(--transition); z-index:1001;
}
.lang-dropdown.open { opacity:1; transform:none; pointer-events:auto; }
.lang-option {
display:flex; align-items:center; gap:10px; padding:12px 16px;
font-size:14px; color:var(--text-muted); cursor:pointer;
transition:var(--transition);
}
.lang-option:hover { background:rgba(100,210,255,0.1); color:var(--text); }
.lang-option.active { color:var(--neon-cyan); font-weight:600; }
.lang-option .check { width:16px; height:16px; opacity:0; transition:opacity .2s; }
.lang-option.active .check { opacity:1; }
/* ========== 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; }
.lang-switcher { position:static; margin-left:0; }
.lang-dropdown { right:auto; left:0; }
}
@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;" data-i18n="nav_home">首页</a></li>
<li><a href="#products" onclick="scrollToProducts();return false;" data-i18n="nav_all_products">全部商品</a></li>
<li><a href="#products" onclick="loadFeatured();return false;" data-i18n="nav_featured">热门推荐</a></li>
</ul>
<div class="search-box">
<input type="text" id="searchInput" data-i18n-placeholder="search_placeholder" 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="lang-switcher" id="langSwitcher">
<button class="lang-btn" id="langBtn" onclick="toggleLangDropdown()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z"/></svg>
<span id="langLabel">中文</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:12px;height:12px"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<div class="lang-dropdown" id="langDropdown">
<div class="lang-option" data-lang="zh-CN" onclick="switchLang('zh-CN')">
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
<span>简体中文</span>
</div>
<div class="lang-option" data-lang="zh-TW" onclick="switchLang('zh-TW')">
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
<span>繁體中文</span>
</div>
<div class="lang-option" data-lang="en" onclick="switchLang('en')">
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
<span>English</span>
</div>
</div>
</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>
<span data-i18n="hero_tag">GLOBAL CROSS-BORDER · 全球直邮</span>
</div>
<h1 class="gradient-text">NEON<br>MART</h1>
<p class="hero-sub" data-i18n="hero_sub">点亮你的全球购物体验。精选智能电子、家居生活、时尚运动等品类好物,霓虹品质,跨境直达。</p>
<a href="#" class="hero-btn" onclick="scrollToProducts();return false;">
<span data-i18n="hero_cta">立即探索</span>
<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" data-i18n="hero_stat_categories">精选品类</div>
</div>
<div>
<div class="hero-stat-num">50K+</div>
<div class="hero-stat-label" data-i18n="hero_stat_users">全球用户</div>
</div>
<div>
<div class="hero-stat-num">99.8%</div>
<div class="hero-stat-label" data-i18n="hero_stat_rating">好评率</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 data-i18n="feature1_title">全球直邮</h4><p data-i18n="feature1_desc">满 $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 data-i18n="feature2_title">正品保障</h4><p data-i18n="feature2_desc">品牌授权直供</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 data-i18n="feature3_title">7天退换</h4><p data-i18n="feature3_desc">无理由退货</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 data-i18n="feature4_title">安全支付</h4><p data-i18n="feature4_desc">多渠道加密</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" data-i18n="section_title">霓虹好物</span>
</div>
<span class="section-sub" id="productsCount" data-i18n="loading">加载中...</span>
</div>
<!-- 分类标签栏 -->
<div class="category-nav" id="categoryChips"></div>
<!-- 工具栏 -->
<div class="products-toolbar">
<div class="products-count" id="productsCountInner" data-i18n="loading">加载中...</div>
<div class="sort-group">
<span class="sort-label" data-i18n="sort_label">排序</span>
<select class="sort-select" id="sortSelect" onchange="applySort()">
<option value="default" data-i18n="sort_default">默认排序</option>
<option value="sales" data-i18n="sort_sales">销量优先</option>
<option value="price_asc" data-i18n="sort_price_asc">价格升序</option>
<option value="price_desc" data-i18n="sort_price_desc">价格降序</option>
<option value="rating" data-i18n="sort_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 data-i18n="cart_title">购物车</span> <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" data-i18n="cart_total">合计</span>
<span class="cart-total-value" id="cartTotal">$0.00</span>
</div>
<button class="checkout-btn" id="checkoutBtn" onclick="checkout()" disabled data-i18n="checkout">去结算</button>
</div>
</div>
<!-- 商品详情模态框 -->
<div class="modal-overlay" id="productModal" onclick="closeModal(event)">
<div class="modal-content" id="modalContent"></div>
</div>
<!-- Toast -->
<div class="toast" id="toast">
<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" data-i18n="toast_added">已加入购物车</span>
</div>
<!-- 页脚 -->
<footer class="footer">
<div class="footer-content">
<div class="footer-brand">
<h3><span class="gradient-text">NEON MART</span></h3>
<p data-i18n="footer_desc">点亮你的全球购物体验。我们精选全球优质商品,为您提供安全、便捷、愉悦的跨境购物体验。覆盖智能电子、家居生活、时尚服饰、运动户外、美妆个护等品类。</p>
</div>
<div class="footer-col">
<h4 data-i18n="footer_guide">购物指南</h4>
<ul>
<li><a href="#" data-i18n="footer_guide_process">购物流程</a></li>
<li><a href="#" data-i18n="footer_guide_member">会员介绍</a></li>
<li><a href="#" data-i18n="footer_guide_faq">常见问题</a></li>
<li><a href="#" data-i18n="footer_guide_support">联系客服</a></li>
</ul>
</div>
<div class="footer-col">
<h4 data-i18n="footer_shipping">配送与退换</h4>
<ul>
<li><a href="#" data-i18n="footer_shipping_methods">配送方式</a></li>
<li><a href="#" data-i18n="footer_shipping_rules">运费规则</a></li>
<li><a href="#" data-i18n="footer_shipping_return">退换货政策</a></li>
<li><a href="#" data-i18n="footer_shipping_refund">退款说明</a></li>
</ul>
</div>
<div class="footer-col">
<h4 data-i18n="footer_about">关于我们</h4>
<ul>
<li><a href="#" data-i18n="footer_about_company">公司简介</a></li>
<li><a href="#" data-i18n="footer_about_join">加入我们</a></li>
<li><a href="#" data-i18n="footer_about_privacy">隐私政策</a></li>
<li><a href="#" data-i18n="footer_about_terms">用户协议</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
© 2026 <span class="neon">NEON MART</span> · All Rights Reserved · <span data-i18n="footer_suffix">全球好物 · 霓虹直达</span>
</div>
</footer>
<script>
// ========== i18n 国际化引擎 ==========
const I18N_LOCALES = {
'zh-CN': {
page_title: 'NEON MART · 暗黑霓虹跨境电商',
nav_home: '首页',
nav_all_products: '全部商品',
nav_featured: '热门推荐',
search_placeholder: '搜索霓虹好物...',
hero_tag: 'GLOBAL CROSS-BORDER · 全球直邮',
hero_sub: '点亮你的全球购物体验。精选智能电子、家居生活、时尚运动等品类好物,霓虹品质,跨境直达。',
hero_cta: '立即探索',
hero_stat_categories: '精选品类',
hero_stat_users: '全球用户',
hero_stat_rating: '好评率',
feature1_title: '全球直邮',
feature1_desc: '满 $99 免运费',
feature2_title: '正品保障',
feature2_desc: '品牌授权直供',
feature3_title: '7天退换',
feature3_desc: '无理由退货',
feature4_title: '安全支付',
feature4_desc: '多渠道加密',
section_title: '霓虹好物',
loading: '加载中...',
sort_label: '排序',
sort_default: '默认排序',
sort_sales: '销量优先',
sort_price_asc: '价格升序',
sort_price_desc: '价格降序',
sort_rating: '评分最高',
product_count: '共 {n} 件商品',
product_sales: '已售 {n}',
product_add_cart: '加入购物车',
category_all: '全部',
cart_title: '购物车',
cart_total: '合计',
checkout: '去结算',
cart_empty: '购物车是空的',
cart_empty_sub: '快去挑选心仪的霓虹好物吧',
cart_item_delete: '删除',
toast_added: '已加入购物车',
toast_added_item: '已加入购物车: {name}',
toast_removed: '已移除商品',
toast_add_failed: '添加失败,请重试',
toast_featured_loaded: '已加载热门推荐商品',
modal_loading: '加载中...',
modal_discount_save: '省 {n}%',
modal_label_rating: '评分',
modal_label_sales: '销量',
modal_label_stock: '库存 {n} 件',
modal_qty: '数量',
modal_add_cart: '加入购物车',
modal_load_error: '商品信息加载失败',
modal_close: '关闭',
products_load_error: '商品加载失败,请稍后重试',
products_reload: '重新加载',
products_empty: '未找到匹配的商品',
products_empty_hint: '试试其他关键词或分类',
categories_load_error: '分类加载失败',
featured_load_error: '推荐加载失败',
featured_retry: '重试',
featured_count: '热门推荐 · {n} 件商品',
checkout_success: '订单已提交! 共 {count} 件商品, 合计 ${total}',
footer_desc: '点亮你的全球购物体验。我们精选全球优质商品,为您提供安全、便捷、愉悦的跨境购物体验。覆盖智能电子、家居生活、时尚服饰、运动户外、美妆个护等品类。',
footer_guide: '购物指南',
footer_guide_process: '购物流程',
footer_guide_member: '会员介绍',
footer_guide_faq: '常见问题',
footer_guide_support: '联系客服',
footer_shipping: '配送与退换',
footer_shipping_methods: '配送方式',
footer_shipping_rules: '运费规则',
footer_shipping_return: '退换货政策',
footer_shipping_refund: '退款说明',
footer_about: '关于我们',
footer_about_company: '公司简介',
footer_about_join: '加入我们',
footer_about_privacy: '隐私政策',
footer_about_terms: '用户协议',
footer_suffix: '全球好物 · 霓虹直达'
},
'en': {
page_title: 'NEON MART · Neon Cross-Border Shop',
nav_home: 'Home',
nav_all_products: 'All Products',
nav_featured: 'Featured',
search_placeholder: 'Search neon goodies...',
hero_tag: 'GLOBAL CROSS-BORDER · Direct Delivery',
hero_sub: 'Light up your global shopping experience. Curated smart electronics, home living, fashion & sports, and more — neon quality, delivered worldwide.',
hero_cta: 'Explore Now',
hero_stat_categories: 'Categories',
hero_stat_users: 'Global Users',
hero_stat_rating: 'Satisfaction',
feature1_title: 'Global Shipping',
feature1_desc: 'Free over $99',
feature2_title: 'Authentic Guarantee',
feature2_desc: 'Brand authorized',
feature3_title: '7-Day Returns',
feature3_desc: 'No-questions-asked',
feature4_title: 'Secure Payment',
feature4_desc: 'Multi-channel encrypted',
section_title: 'Neon Picks',
loading: 'Loading...',
sort_label: 'Sort',
sort_default: 'Default',
sort_sales: 'Top Sales',
sort_price_asc: 'Price Low→High',
sort_price_desc: 'Price High→Low',
sort_rating: 'Top Rated',
product_count: '{n} products',
product_sales: 'Sold {n}',
product_add_cart: 'Add to Cart',
category_all: 'All',
cart_title: 'Cart',
cart_total: 'Total',
checkout: 'Checkout',
cart_empty: 'Your cart is empty',
cart_empty_sub: 'Go explore some neon goodies!',
cart_item_delete: 'Delete',
toast_added: 'Added to cart',
toast_added_item: 'Added: {name}',
toast_removed: 'Item removed',
toast_add_failed: 'Failed to add, please retry',
toast_featured_loaded: 'Featured products loaded',
modal_loading: 'Loading...',
modal_discount_save: 'Save {n}%',
modal_label_rating: 'Rating',
modal_label_sales: 'Sales',
modal_label_stock: '{n} in stock',
modal_qty: 'Qty',
modal_add_cart: 'Add to Cart',
modal_load_error: 'Failed to load product',
modal_close: 'Close',
products_load_error: 'Failed to load products, please try again',
products_reload: 'Reload',
products_empty: 'No matching products found',
products_empty_hint: 'Try other keywords or categories',
categories_load_error: 'Failed to load categories',
featured_load_error: 'Failed to load featured',
featured_retry: 'Retry',
featured_count: 'Featured · {n} products',
checkout_success: 'Order placed! {count} items, total ${total}',
footer_desc: 'Light up your global shopping experience. We curate quality products worldwide for a safe, convenient, and delightful cross-border shopping experience.',
footer_guide: 'Shopping Guide',
footer_guide_process: 'How to Buy',
footer_guide_member: 'Membership',
footer_guide_faq: 'FAQ',
footer_guide_support: 'Support',
footer_shipping: 'Shipping & Returns',
footer_shipping_methods: 'Delivery Options',
footer_shipping_rules: 'Shipping Policy',
footer_shipping_return: 'Return Policy',
footer_shipping_refund: 'Refund Info',
footer_about: 'About Us',
footer_about_company: 'Company Profile',
footer_about_join: 'Join Us',
footer_about_privacy: 'Privacy Policy',
footer_about_terms: 'Terms of Use',
footer_suffix: 'Global Goods · Neon Delivery'
},
'zh-TW': {
page_title: 'NEON MART · 暗黑霓虹跨境電商',
nav_home: '首頁',
nav_all_products: '全部商品',
nav_featured: '熱門推薦',
search_placeholder: '搜尋霓虹好物...',
hero_tag: 'GLOBAL CROSS-BORDER · 全球直郵',
hero_sub: '點亮你的全球購物體驗。精選智能電子、家居生活、時尚運動等品類好物,霓虹品質,跨境直達。',
hero_cta: '立即探索',
hero_stat_categories: '精選品類',
hero_stat_users: '全球用戶',
hero_stat_rating: '好評率',
feature1_title: '全球直郵',
feature1_desc: '滿 $99 免運費',
feature2_title: '正品保障',
feature2_desc: '品牌授權直供',
feature3_title: '7天退換',
feature3_desc: '無理由退貨',
feature4_title: '安全支付',
feature4_desc: '多渠道加密',
section_title: '霓虹好物',
loading: '載入中...',
sort_label: '排序',
sort_default: '預設排序',
sort_sales: '銷量優先',
sort_price_asc: '價格升序',
sort_price_desc: '價格降序',
sort_rating: '評分最高',
product_count: '共 {n} 件商品',
product_sales: '已售 {n}',
product_add_cart: '加入購物車',
category_all: '全部',
cart_title: '購物車',
cart_total: '合計',
checkout: '去結帳',
cart_empty: '購物車是空的',
cart_empty_sub: '快去挑選心儀的霓虹好物吧',
cart_item_delete: '刪除',
toast_added: '已加入購物車',
toast_added_item: '已加入購物車: {name}',
toast_removed: '已移除商品',
toast_add_failed: '新增失敗,請重試',
toast_featured_loaded: '已載入熱門推薦商品',
modal_loading: '載入中...',
modal_discount_save: '省 {n}%',
modal_label_rating: '評分',
modal_label_sales: '銷量',
modal_label_stock: '{n} 件庫存',
modal_qty: '數量',
modal_add_cart: '加入購物車',
modal_load_error: '商品資訊載入失敗',
modal_close: '關閉',
products_load_error: '商品載入失敗,請稍後重試',
products_reload: '重新載入',
products_empty: '未找到符合的商品',
products_empty_hint: '試試其他關鍵字或分類',
categories_load_error: '分類載入失敗',
featured_load_error: '推薦載入失敗',
featured_retry: '重試',
featured_count: '熱門推薦 · {n} 件商品',
checkout_success: '訂單已提交! 共 {count} 件商品, 合計 ${total}',
footer_desc: '點亮你的全球購物體驗。我們精選全球優質商品,為您提供安全、便捷、愉悅的跨境購物體驗。覆蓋智能電子、家居生活、時尚服飾、運動戶外、美妝個護等品類。',
footer_guide: '購物指南',
footer_guide_process: '購物流程',
footer_guide_member: '會員介紹',
footer_guide_faq: '常見問題',
footer_guide_support: '聯繫客服',
footer_shipping: '配送與退換',
footer_shipping_methods: '配送方式',
footer_shipping_rules: '運費規則',
footer_shipping_return: '退換貨政策',
footer_shipping_refund: '退款說明',
footer_about: '關於我們',
footer_about_company: '公司簡介',
footer_about_join: '加入我們',
footer_about_privacy: '隱私政策',
footer_about_terms: '用戶協議',
footer_suffix: '全球好物 · 霓虹直達'
}
};
const LANG_LABELS = {'zh-CN':'中文','en':'EN','zh-TW':'繁中'};
let currentLang = (function(){
try{
var u=new URLSearchParams(location.search).get('lang');
if(u&&I18N_LOCALES[u]) return u;
}catch(e){}
try{var s=localStorage.getItem('lang');if(s&&I18N_LOCALES[s]) return s;}catch(e){}
var b=navigator.language||navigator.userLanguage||'';
if(b.startsWith('zh-TW')||b.startsWith('zh-Hant')) return 'zh-TW';
if(b.startsWith('zh')) return 'zh-CN';
return 'en';
})();
function t(key){return (I18N_LOCALES[currentLang]&&I18N_LOCALES[currentLang][key])||(I18N_LOCALES['zh-CN'][key])||key;}
function applyI18n(){
document.querySelectorAll('[data-i18n]').forEach(function(el){
var key=el.getAttribute('data-i18n');
el.textContent=t(key);
});
document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el){
var key=el.getAttribute('data-i18n-placeholder');
el.placeholder=t(key);
});
document.documentElement.lang=currentLang==='zh-TW'?'zh-TW':currentLang==='zh-CN'?'zh-CN':'en';
document.title=t('page_title');
document.getElementById('langLabel').textContent=LANG_LABELS[currentLang]||currentLang;
document.querySelectorAll('.lang-option').forEach(function(opt){
opt.classList.toggle('active',opt.getAttribute('data-lang')===currentLang);
});
}
function switchLang(lang){
if(!I18N_LOCALES[lang]) return;
currentLang=lang;
try{localStorage.setItem('lang',lang);}catch(e){}
applyI18n();
if(typeof onLangChange==='function') onLangChange(lang);
document.getElementById('langDropdown').classList.remove('open');
}
function toggleLangDropdown(){
document.getElementById('langDropdown').classList.toggle('open');
}
document.addEventListener('click',function(e){
var sw=document.getElementById('langSwitcher');
if(sw&&!sw.contains(e.target)) document.getElementById('langDropdown').classList.remove('open');
});
// ========== 全局状态 ==========
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;
}
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('Network error ' + res.status);
const json = await res.json();
if (json.code !== 0) throw new Error(json.message || 'Request failed');
return json.data;
}
async function loadCategories() {
try {
allCategories = await fetchJSON('/api/v1/shop/categories');
renderCategoryChips();
} catch(e) {
console.error('Category load failed:', e);
document.getElementById('categoryChips').innerHTML = '<span style="color:var(--text-dim);font-size:13px">' + t('categories_load_error') + '</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('Product load failed:', 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>${t('products_load_error')}</p>
<button class="retry-btn" onclick="loadProducts()">${t('products_reload')}</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>
${t('category_all')}
</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>
${escapeHtml(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('爆') || tag.includes('Hot') || tag.includes('hot') || tag.includes('熱')) return 'tag-hot';
if (tag.includes('新') || tag.includes('New') || tag.includes('new')) return 'tag-new';
if (tag.includes('折') || tag.includes('惠') || tag.includes('特') || tag.includes('Sale') || tag.includes('sale') || tag.includes('OFF') || tag.includes('off')) return 'tag-sale';
return 'tag-default';
}
function formatSales(n) {
if (n >= 10000) return (n / 10000).toFixed(1) + (currentLang === 'en' ? '0K' : '万');
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 = t('product_count').replace('{n}', '<strong>' + total + '</strong>');
document.getElementById('productsCount').textContent = t('product_count').replace('{n}', total);
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>${t('products_empty')}</p>
<p style="font-size:13px;margin-top:6px;color:var(--text-dim)">${t('products_empty_hint')}</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">${t('product_sales').replace('{n}', formatSales(p.sales))}</span>
</div>
</div>
<button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${p.id})" title="${t('product_add_cart')}">
<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 = t('featured_count').replace('{n}', products.length);
document.getElementById('productsCountInner').innerHTML = t('featured_count').replace('{n}', '<strong>' + products.length + '</strong>');
scrollToProducts();
showToast(t('toast_featured_loaded'), 'success');
} catch(e) {
grid.innerHTML = `<div class="error-block"><p>${t('featured_load_error')}</p><button class="retry-btn" onclick="loadFeatured()">${t('featured_retry')}</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>' + t('modal_loading') + '</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">${t('modal_discount_save').replace('{n}', 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">${t('modal_label_rating')}</span><span class="value cyan">${p.rating.toFixed(1)} \u2605</span></div>
<div class="modal-meta-item"><span class="label">${t('modal_label_sales')}</span><span class="value">${formatSales(p.sales)}</span></div>
<div class="modal-meta-item"><span class="label">${t('modal_label_stock').replace('{n}', p.stock)}</span><span class="value">${p.stock}</span></div>
</div>
<div class="modal-qty-row">
<span class="modal-qty-label">${t('modal_qty')}</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>
${t('modal_add_cart')}
</button>
</div>
</div>
`;
} catch(e) {
content.innerHTML = `<div class="error-block"><p>${t('modal_load_error')}</p><button class="retry-btn" onclick="closeModal()">${t('modal_close')}</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(t('toast_add_failed'), '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(t('toast_added_item').replace('{name}', 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(t('toast_removed'), '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>${t('cart_empty')}</p>
<p class="sub">${t('cart_empty_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>
${t('cart_item_delete')}
</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(t('checkout_success').replace('{count}', count).replace('{total}', 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() {
applyI18n();
await loadCategories();
await loadProducts();
updateCartUI();
}
// ========== 语言切换回调:重新渲染动态内容 ==========
function onLangChange(lang) {
renderCategoryChips();
renderProducts([], 0);
loadProducts();
updateCartUI();
if (document.getElementById('productModal').classList.contains('open')) {
document.getElementById('productModal').classList.remove('open');
document.body.style.overflow = '';
}
}
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';
});
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 = '';
}
});
});
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>