- 导航页(index.html)添加完整i18n引擎,支持zh-CN/zh-TW/en - 导航页添加语言切换按钮(EN/中/繁) - 所有20个前端模板默认语言从zh-CN改为en - 导航页SEO meta标签改为英文
1513 lines
74 KiB
HTML
1513 lines
74 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>GlobalShop · 跨境电商平台</title>
|
|
<style>
|
|
/* ========== 主题变量 ========== */
|
|
:root {
|
|
--primary: #E63946;
|
|
--primary-dark: #D62828;
|
|
--primary-light: #FF6B6B;
|
|
--accent: #457B9D;
|
|
--dark: #1D3557;
|
|
--bg: #F8F9FA;
|
|
--surface: #FFFFFF;
|
|
--text: #1D3557;
|
|
--text-muted: #6C757D;
|
|
--border: #E9ECEF;
|
|
--shadow: 0 2px 8px rgba(0,0,0,0.06);
|
|
--shadow-lg: 0 8px 30px rgba(0,0,0,0.12);
|
|
--radius: 10px;
|
|
--radius-sm: 6px;
|
|
--radius-lg: 16px;
|
|
--transition: all 0.25s cubic-bezier(0.4,0,0.2,1);
|
|
}
|
|
* { margin:0; padding:0; box-sizing:border-box; }
|
|
body { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; background:var(--bg); color:var(--text); line-height:1.6; -webkit-font-smoothing:antialiased; }
|
|
a { text-decoration:none; color:inherit; }
|
|
button { cursor:pointer; border:none; font-family:inherit; }
|
|
input { font-family:inherit; }
|
|
img { max-width:100%; display:block; }
|
|
|
|
/* ========== 顶部公告栏 ========== */
|
|
.topbar { background:var(--dark); color:#fff; font-size:13px; padding:8px 0; text-align:center; }
|
|
.topbar span { margin:0 16px; opacity:0.9; }
|
|
.topbar a { color:var(--primary-light); }
|
|
|
|
/* ========== 导航栏 ========== */
|
|
.topbar { background:#0a0a0f; color:rgba(255,255,255,0.6); }
|
|
.navbar { background:rgba(10,10,15,0.6); backdrop-filter:blur(20px); position:fixed; top:32px; left:0; right:0; z-index:100; border-bottom:1px solid rgba(255,255,255,0.06); }
|
|
.navbar .nav-links a, .navbar .nav-brand, .navbar .search-box input, .navbar .search-box svg, .navbar .nav-actions span { color:rgba(255,255,255,0.85) !important; }
|
|
.navbar .search-box { background:rgba(255,255,255,0.06) !important; border:1px solid rgba(255,255,255,0.1) !important; }
|
|
.navbar .search-box input::placeholder { color:rgba(255,255,255,0.4) !important; }
|
|
.nav-container { max-width:1280px; margin:0 auto; padding:0 24px; display:flex; align-items:center; gap:32px; height:64px; }
|
|
.logo { font-size:24px; font-weight:800; color:var(--primary); display:flex; align-items:center; gap:8px; white-space:nowrap; }
|
|
.logo svg { width:28px; height:28px; }
|
|
.nav-links { display:flex; gap:4px; list-style:none; }
|
|
.nav-links a { padding:8px 16px; border-radius:var(--radius-sm); font-size:14px; font-weight:500; color:var(--text); transition:var(--transition); }
|
|
.nav-links a:hover, .nav-links a.active { background:var(--primary); color:#fff; }
|
|
.search-box { flex:1; max-width:400px; position:relative; }
|
|
.search-box input { width:100%; height:40px; padding:0 16px 0 40px; border:2px solid var(--border); border-radius:var(--radius); font-size:14px; outline:none; transition:var(--transition); }
|
|
.search-box input:focus { border-color:var(--primary); }
|
|
.search-box svg { position:absolute; left:12px; top:50%; transform:translateY(-50%); width:18px; height:18px; color:var(--text-muted); }
|
|
.nav-actions { display:flex; align-items:center; gap:8px; }
|
|
.nav-actions button { background:none; width:40px; height:40px; border-radius:50%; display:flex; align-items:center; justify-content:center; transition:var(--transition); position:relative; }
|
|
.nav-actions button:hover { background:var(--bg); }
|
|
.nav-actions svg { width:22px; height:22px; color:var(--text); }
|
|
.cart-badge { position:absolute; top:2px; right:2px; background:var(--primary); color:#fff; font-size:11px; font-weight:700; min-width:18px; height:18px; border-radius:9px; display:flex; align-items:center; justify-content:center; padding:0 4px; }
|
|
|
|
/* ========== 语言切换器 ========== */
|
|
.lang-switcher{position:relative;margin-left:8px}
|
|
.lang-btn{
|
|
display:flex;align-items:center;gap:6px;padding:8px 14px;border-radius:10px;
|
|
font-size:13px;font-weight:600;color:rgba(255,255,255,0.85);cursor:pointer;
|
|
background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);
|
|
transition:all .25s cubic-bezier(.4,0,.2,1);
|
|
}
|
|
.lang-btn:hover{color:#fff;background:rgba(255,255,255,0.12);border-color:rgba(255,255,255,0.2)}
|
|
.lang-btn svg{width:16px;height:16px}
|
|
.lang-dropdown{
|
|
position:absolute;top:calc(100% + 8px);right:0;min-width:160px;
|
|
background:rgba(10,10,15,0.96);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
|
|
border:1px solid rgba(255,255,255,0.12);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:all .25s cubic-bezier(.4,0,.2,1);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:rgba(255,255,255,0.6);cursor:pointer;transition:all .2s;
|
|
}
|
|
.lang-option:hover{background:rgba(230,57,70,0.1);color:#fff}
|
|
.lang-option.active{color:var(--primary);font-weight:600}
|
|
.lang-option .check{width:16px;height:16px;opacity:0;transition:opacity .2s}
|
|
.lang-option.active .check{opacity:1}
|
|
|
|
/* ========== Hero 动态壁纸 ========== */
|
|
.hero { position:relative; width:100%; height:100vh; min-height:640px; overflow:hidden; background:#0a0a0f; }
|
|
#heroCanvas { position:absolute; inset:0; width:100%; height:100%; z-index:1; }
|
|
.hero-overlay { position:absolute; inset:0; z-index:2; background:radial-gradient(ellipse at 30% 40%, rgba(230,57,70,0.15) 0%, transparent 50%),linear-gradient(180deg,rgba(10,10,15,0.3) 0%,rgba(10,10,15,0.7) 70%,rgba(10,10,15,0.95) 100%); pointer-events:none; }
|
|
.hero-light { position:absolute; z-index:3; pointer-events:none; width:600px; height:600px; border-radius:50%; background:radial-gradient(circle,rgba(230,57,70,0.25) 0%,rgba(230,57,70,0.08) 30%,transparent 60%); transform:translate(-50%,-50%); mix-blend-mode:screen; filter:blur(20px); transition:opacity .3s; opacity:0; }
|
|
.hero-light.active { opacity:1; }
|
|
.hero-content { position:absolute; inset:0; z-index:4; display:flex; flex-direction:column; justify-content:center; align-items:flex-start; padding:0 8vw; max-width:1400px; margin:0 auto; }
|
|
.hero-badge { display:inline-flex; align-items:center; gap:8px; padding:6px 18px; border-radius:100px; background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.12); font-size:13px; color:rgba(255,255,255,0.8); margin-bottom:24px; backdrop-filter:blur(10px); }
|
|
.hero-badge .dot { width:6px; height:6px; border-radius:50%; background:#E63946; box-shadow:0 0 8px #E63946; animation:pulse 2s infinite; }
|
|
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
|
|
.hero-content h1 { font-size:72px; font-weight:900; line-height:1.05; letter-spacing:-2px; color:#fff; margin-bottom:20px; text-shadow:0 4px 30px rgba(0,0,0,0.5); }
|
|
.hero-content h1 .accent { background:linear-gradient(135deg,#E63946 0%,#FF6B6B 50%,#FFD700 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
|
|
.hero-content p { font-size:20px; color:rgba(255,255,255,0.6); max-width:560px; line-height:1.6; margin-bottom:36px; }
|
|
.hero-actions { display:flex; gap:16px; align-items:center; }
|
|
.hero-btn-primary { display:inline-flex; align-items:center; gap:10px; background:linear-gradient(135deg,#E63946,#D62828); color:#fff; padding:16px 40px; border-radius:100px; font-size:16px; font-weight:700; transition:all .3s; box-shadow:0 8px 32px rgba(230,57,70,0.4); border:none; cursor:pointer; }
|
|
.hero-btn-primary:hover { transform:translateY(-3px); box-shadow:0 12px 40px rgba(230,57,70,0.55); }
|
|
.hero-btn-ghost { display:inline-flex; align-items:center; gap:8px; background:rgba(255,255,255,0.06); color:#fff; padding:16px 32px; border-radius:100px; font-size:15px; font-weight:600; border:1px solid rgba(255,255,255,0.15); cursor:pointer; transition:all .3s; backdrop-filter:blur(10px); }
|
|
.hero-btn-ghost:hover { background:rgba(255,255,255,0.12); border-color:rgba(255,255,255,0.25); }
|
|
.hero-stats { position:absolute; bottom:48px; left:0; right:0; z-index:4; display:flex; gap:48px; justify-content:center; padding:0 24px; }
|
|
.hero-stat { text-align:center; }
|
|
.hero-stat .num { font-size:36px; font-weight:800; color:#fff; }
|
|
.hero-stat .num span { color:#E63946; }
|
|
.hero-stat .label { font-size:13px; color:rgba(255,255,255,0.5); margin-top:2px; }
|
|
.hero-scroll { position:absolute; bottom:20px; left:50%; transform:translateX(-50%); z-index:4; color:rgba(255,255,255,0.4); font-size:12px; display:flex; flex-direction:column; align-items:center; gap:6px; animation:bounce 2s infinite; }
|
|
@keyframes bounce { 0%,100%{transform:translateX(-50%) translateY(0)} 50%{transform:translateX(-50%) translateY(-8px)} }
|
|
.hero-scroll svg { width:20px; height:20px; }
|
|
@media(max-width:768px){ .hero-content h1{font-size:42px} .hero-content p{font-size:16px} .hero-stats{gap:20px} .hero-stat .num{font-size:24px} }
|
|
|
|
/* ========== 优势条 ========== */
|
|
.features-strip { max-width:1280px; margin:24px auto; padding:0 24px; display:grid; grid-template-columns:repeat(4,1fr); gap:16px; }
|
|
.feature-item { background:var(--surface); border-radius:var(--radius); padding:20px; display:flex; align-items:center; gap:14px; box-shadow:var(--shadow); }
|
|
.feature-item svg { width:36px; height:36px; color:var(--primary); flex-shrink:0; }
|
|
.feature-item h4 { font-size:14px; font-weight:700; }
|
|
.feature-item p { font-size:12px; color:var(--text-muted); }
|
|
|
|
/* ========== 分类导航 ========== */
|
|
.section { max-width:1280px; margin:48px auto; padding:0 24px; }
|
|
.section-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:24px; }
|
|
.section-title { font-size:26px; font-weight:800; position:relative; padding-left:16px; }
|
|
.section-title::before { content:''; position:absolute; left:0; top:50%; transform:translateY(-50%); width:4px; height:28px; background:var(--primary); border-radius:2px; }
|
|
.section-more { font-size:14px; color:var(--primary); font-weight:600; }
|
|
|
|
.category-nav { display:flex; gap:12px; overflow-x:auto; padding-bottom:8px; margin-bottom:24px; }
|
|
.category-chip { padding:10px 24px; border-radius:var(--radius); background:var(--surface); border:2px solid var(--border); font-size:14px; font-weight:600; color:var(--text); white-space:nowrap; transition:var(--transition); }
|
|
.category-chip:hover { border-color:var(--primary); color:var(--primary); }
|
|
.category-chip.active { background:var(--primary); color:#fff; border-color:var(--primary); }
|
|
|
|
/* ========== 商品网格 ========== */
|
|
.products-layout { display:grid; grid-template-columns:220px 1fr; gap:24px; }
|
|
.filters-sidebar { background:var(--surface); border-radius:var(--radius); padding:20px; box-shadow:var(--shadow); height:fit-content; position:sticky; top:80px; }
|
|
.filters-sidebar h3 { font-size:16px; font-weight:700; margin-bottom:16px; padding-bottom:12px; border-bottom:1px solid var(--border); }
|
|
.filter-group { margin-bottom:20px; }
|
|
.filter-group h4 { font-size:13px; font-weight:700; margin-bottom:10px; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.5px; }
|
|
.filter-option { display:flex; align-items:center; gap:8px; padding:6px 0; cursor:pointer; font-size:14px; }
|
|
.filter-option input { accent-color:var(--primary); }
|
|
.filter-option label { cursor:pointer; }
|
|
.filter-option .count { margin-left:auto; font-size:12px; color:var(--text-muted); }
|
|
|
|
.products-main { min-width:0; }
|
|
.products-toolbar { display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; flex-wrap:wrap; gap:12px; }
|
|
.products-count { font-size:14px; color:var(--text-muted); }
|
|
.sort-select { padding:8px 16px; border:2px solid var(--border); border-radius:var(--radius-sm); font-size:14px; outline:none; background:var(--surface); cursor:pointer; }
|
|
.sort-select:focus { border-color:var(--primary); }
|
|
|
|
.products-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:20px; }
|
|
.product-card { background:var(--surface); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); transition:var(--transition); cursor:pointer; position:relative; }
|
|
.product-card:hover { transform:translateY(-4px); box-shadow:var(--shadow-lg); }
|
|
.product-image { width:100%; aspect-ratio:1; background:linear-gradient(135deg,#f0f0f0,#e0e0e0); position:relative; overflow:hidden; }
|
|
.product-image-gradient { width:100%; height:100%; display:flex; align-items:center; justify-content:center; }
|
|
.product-image-gradient svg { width:50%; height:50%; opacity:0.3; }
|
|
.product-tags { position:absolute; top:10px; left:10px; display:flex; flex-direction:column; gap:4px; }
|
|
.product-tag { padding:3px 10px; border-radius:var(--radius-sm); font-size:11px; font-weight:700; color:#fff; }
|
|
.tag-hot { background:var(--primary); }
|
|
.tag-new { background:var(--accent); }
|
|
.tag-sale { background:#FF8C00; }
|
|
.product-discount { position:absolute; top:10px; right:10px; background:var(--primary); color:#fff; font-size:12px; font-weight:700; padding:4px 8px; border-radius:var(--radius-sm); }
|
|
.product-info { padding:14px; }
|
|
.product-name { font-size:14px; font-weight:600; line-height:1.4; margin-bottom:8px; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; min-height:39px; }
|
|
.product-price { display:flex; align-items:baseline; gap:8px; margin-bottom:8px; }
|
|
.price-current { font-size:20px; font-weight:800; color:var(--primary); }
|
|
.price-original { font-size:14px; color:var(--text-muted); text-decoration:line-through; }
|
|
.product-meta { display:flex; align-items:center; justify-content:space-between; font-size:12px; color:var(--text-muted); }
|
|
.product-rating { display:flex; align-items:center; gap:4px; }
|
|
.stars { color:#FFD700; letter-spacing:1px; }
|
|
.product-sales { }
|
|
.add-cart-btn { position:absolute; bottom:14px; right:14px; width:36px; height:36px; border-radius:50%; background:var(--primary); color:#fff; display:flex; align-items:center; justify-content:center; opacity:0; transform:translateY(10px); transition:var(--transition); }
|
|
.product-card:hover .add-cart-btn { opacity:1; transform:translateY(0); }
|
|
.add-cart-btn:hover { background:var(--primary-dark); transform:scale(1.1); }
|
|
.add-cart-btn svg { width:18px; height:18px; }
|
|
|
|
/* ========== 加载骨架 ========== */
|
|
.skeleton-card { background:var(--surface); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); }
|
|
.skeleton-img { aspect-ratio:1; background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%); background-size:200% 100%; animation:shimmer 1.5s infinite; }
|
|
.skeleton-line { height:14px; margin:12px 14px; border-radius:4px; background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%); background-size:200% 100%; animation:shimmer 1.5s infinite; }
|
|
@keyframes shimmer { 0%{background-position:200% 0;} 100%{background-position:-200% 0;} }
|
|
|
|
/* ========== 分页 ========== */
|
|
.pagination { display:flex; justify-content:center; gap:8px; margin-top:40px; }
|
|
.page-btn { min-width:40px; height:40px; border-radius:var(--radius-sm); background:var(--surface); border:2px solid var(--border); font-size:14px; font-weight:600; transition:var(--transition); }
|
|
.page-btn:hover:not(:disabled) { border-color:var(--primary); color:var(--primary); }
|
|
.page-btn.active { background:var(--primary); color:#fff; border-color:var(--primary); }
|
|
.page-btn:disabled { opacity:0.4; cursor:not-allowed; }
|
|
|
|
/* ========== 购物车抽屉 ========== */
|
|
.cart-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:200; opacity:0; visibility:hidden; transition:var(--transition); }
|
|
.cart-overlay.open { opacity:1; visibility:visible; }
|
|
.cart-drawer { position:fixed; top:0; right:0; width:420px; max-width:90vw; height:100vh; background:var(--surface); z-index:201; transform:translateX(100%); transition:transform 0.3s cubic-bezier(0.4,0,0.2,1); display:flex; flex-direction:column; }
|
|
.cart-drawer.open { transform:translateX(0); }
|
|
.cart-header { padding:20px 24px; border-bottom:1px solid var(--border); display:flex; align-items:center; justify-content:space-between; }
|
|
.cart-header h3 { font-size:18px; font-weight:800; }
|
|
.cart-close { width:32px; height:32px; border-radius:50%; background:var(--bg); display:flex; align-items:center; justify-content:center; font-size:20px; color:var(--text-muted); }
|
|
.cart-items { flex:1; overflow-y:auto; padding:16px 24px; }
|
|
.cart-item { display:flex; gap:12px; padding:16px 0; border-bottom:1px solid var(--border); }
|
|
.cart-item-img { width:72px; height:72px; border-radius:var(--radius-sm); background:linear-gradient(135deg,#f0f0f0,#e0e0e0); flex-shrink:0; display:flex; align-items:center; justify-content:center; }
|
|
.cart-item-img svg { width:32px; height:32px; opacity:0.3; }
|
|
.cart-item-info { flex:1; min-width:0; }
|
|
.cart-item-name { font-size:14px; font-weight:600; margin-bottom:4px; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
|
|
.cart-item-price { font-size:16px; font-weight:700; color:var(--primary); }
|
|
.cart-item-controls { display:flex; align-items:center; gap:12px; margin-top:8px; }
|
|
.qty-btn { width:28px; height:28px; border-radius:var(--radius-sm); background:var(--bg); display:flex; align-items:center; justify-content:center; font-size:16px; font-weight:700; }
|
|
.qty-btn:hover { background:var(--border); }
|
|
.qty-value { font-size:14px; font-weight:600; min-width:24px; text-align:center; }
|
|
.cart-item-remove { margin-left:auto; font-size:12px; color:var(--text-muted); }
|
|
.cart-item-remove:hover { color:var(--primary); }
|
|
.cart-empty { text-align:center; padding:60px 20px; color:var(--text-muted); }
|
|
.cart-empty svg { width:64px; height:64px; margin:0 auto 16px; opacity:0.3; }
|
|
.cart-footer { padding:20px 24px; border-top:1px solid var(--border); background:var(--bg); }
|
|
.cart-total { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
|
|
.cart-total-label { font-size:14px; color:var(--text-muted); }
|
|
.cart-total-value { font-size:24px; font-weight:800; color:var(--primary); }
|
|
.checkout-btn { width:100%; padding:14px; border-radius:var(--radius); background:var(--primary); color:#fff; font-size:16px; font-weight:700; transition:var(--transition); }
|
|
.checkout-btn:hover { background:var(--primary-dark); }
|
|
.checkout-btn:disabled { opacity:0.5; cursor:not-allowed; }
|
|
|
|
/* ========== 商品详情模态框 ========== */
|
|
.modal-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.6); z-index:300; display:flex; align-items:center; justify-content:center; padding:20px; opacity:0; visibility:hidden; transition:var(--transition); }
|
|
.modal-overlay.open { opacity:1; visibility:visible; }
|
|
.modal-content { background:var(--surface); border-radius:var(--radius-lg); max-width:800px; width:100%; max-height:90vh; overflow-y:auto; transform:scale(0.9); transition:transform 0.3s; }
|
|
.modal-overlay.open .modal-content { transform:scale(1); }
|
|
.modal-close { position:absolute; top:16px; right:16px; width:36px; height:36px; border-radius:50%; background:rgba(0,0,0,0.1); display:flex; align-items:center; justify-content:center; font-size:24px; z-index:1; }
|
|
.modal-body { display:grid; grid-template-columns:1fr 1fr; gap:0; }
|
|
.modal-image { background:linear-gradient(135deg,#f0f0f0,#e0e0e0); aspect-ratio:1; display:flex; align-items:center; justify-content:center; }
|
|
.modal-image svg { width:40%; height:40%; opacity:0.2; }
|
|
.modal-details { padding:32px; position:relative; }
|
|
.modal-details h2 { font-size:22px; font-weight:700; margin-bottom:12px; line-height:1.3; }
|
|
.modal-price { display:flex; align-items:baseline; gap:12px; margin-bottom:16px; }
|
|
.modal-price .price-current { font-size:32px; }
|
|
.modal-tags { display:flex; gap:8px; margin-bottom:16px; }
|
|
.modal-desc { font-size:14px; color:var(--text-muted); line-height:1.7; margin-bottom:20px; }
|
|
.modal-meta { display:flex; gap:24px; padding:16px 0; border-top:1px solid var(--border); border-bottom:1px solid var(--border); margin-bottom:20px; font-size:13px; }
|
|
.modal-meta-item { display:flex; flex-direction:column; gap:2px; }
|
|
.modal-meta-item span:first-child { color:var(--text-muted); }
|
|
.modal-meta-item span:last-child { font-weight:600; }
|
|
.modal-add-cart { width:100%; padding:14px; border-radius:var(--radius); background:var(--primary); color:#fff; font-size:16px; font-weight:700; transition:var(--transition); }
|
|
.modal-add-cart:hover { background:var(--primary-dark); }
|
|
|
|
/* ========== 页脚 ========== */
|
|
.footer { background:var(--dark); color:#fff; margin-top:64px; }
|
|
.footer-content { max-width:1280px; margin:0 auto; padding:48px 24px 24px; display:grid; grid-template-columns:2fr 1fr 1fr 1fr; gap:40px; }
|
|
.footer-brand h3 { font-size:22px; font-weight:800; color:var(--primary-light); margin-bottom:12px; }
|
|
.footer-brand p { font-size:14px; opacity:0.7; line-height:1.7; }
|
|
.footer-col h4 { font-size:15px; font-weight:700; margin-bottom:16px; }
|
|
.footer-col ul { list-style:none; }
|
|
.footer-col li { margin-bottom:10px; }
|
|
.footer-col a { font-size:13px; opacity:0.7; transition:var(--transition); }
|
|
.footer-col a:hover { opacity:1; color:var(--primary-light); }
|
|
.footer-bottom { border-top:1px solid rgba(255,255,255,0.1); padding:20px 24px; text-align:center; font-size:13px; opacity:0.6; }
|
|
|
|
/* ========== Toast 提示 ========== */
|
|
.toast { position:fixed; bottom:30px; left:50%; transform:translateX(-50%) translateY(100px); background:var(--dark); color:#fff; padding:12px 24px; border-radius:var(--radius); font-size:14px; font-weight:600; z-index:400; opacity:0; transition:all 0.3s; display:flex; align-items:center; gap:8px; }
|
|
.toast.show { transform:translateX(-50%) translateY(0); opacity:1; }
|
|
.toast svg { width:20px; height:20px; color:#4ADE80; }
|
|
|
|
/* ========== 响应式 ========== */
|
|
@media (max-width:1024px) {
|
|
.products-grid { grid-template-columns:repeat(3,1fr); }
|
|
.features-strip { grid-template-columns:repeat(2,1fr); }
|
|
.footer-content { grid-template-columns:2fr 1fr 1fr; }
|
|
}
|
|
@media (max-width:768px) {
|
|
.nav-links { display:none; }
|
|
.search-box { max-width:200px; }
|
|
.hero-banner { padding:40px 24px; }
|
|
.hero-content h1 { font-size:28px; }
|
|
.hero-content p { font-size:15px; }
|
|
.products-layout { grid-template-columns:1fr; }
|
|
.filters-sidebar { position:static; }
|
|
.products-grid { grid-template-columns:repeat(2,1fr); }
|
|
.modal-body { grid-template-columns:1fr; }
|
|
.footer-content { grid-template-columns:1fr; gap:24px; }
|
|
}
|
|
@media (max-width:480px) {
|
|
.features-strip { grid-template-columns:1fr; }
|
|
.products-grid { grid-template-columns:1fr; }
|
|
.hero-content h1 { font-size:24px; }
|
|
}
|
|
|
|
/* ========== 3D 动态效果 & 鼠标光影(新增)========== */
|
|
/* 鼠标跟随光斑 */
|
|
.cursor-glow{position:fixed;width:600px;height:600px;border-radius:50%;background:radial-gradient(circle,rgba(230,57,70,0.12) 0%,transparent 70%);pointer-events:none;z-index:150;transform:translate(-50%,-50%);transition:opacity .3s;mix-blend-mode:multiply;opacity:0;left:0;top:0}
|
|
.cursor-glow.active{opacity:1}
|
|
/* 3D 卡片容器与倾斜 */
|
|
.products-grid{perspective:1000px}
|
|
.product-card{transform-style:preserve-3d;transition:transform .15s ease-out,box-shadow .25s}
|
|
.product-card .product-image{transform:translateZ(20px)}
|
|
.product-card .product-info{transform:translateZ(15px)}
|
|
/* 卡片表面光影反射(玻璃高光) */
|
|
.product-card::after{content:'';position:absolute;inset:0;border-radius:inherit;background:radial-gradient(circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,0.25) 0%,transparent 50%);opacity:0;transition:opacity .3s;pointer-events:none;z-index:2}
|
|
.product-card:hover::after{opacity:1}
|
|
/* 触摸设备隐藏光斑 */
|
|
@media (hover:none){.cursor-glow{display:none !important}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- 顶部公告栏 -->
|
|
<div class="topbar">
|
|
<span data-i18n="topbar_shipping">🚀 全球直邮 · 满 $99 免运费</span>
|
|
<span data-i18n="topbar_return">📦 7天无理由退换</span>
|
|
<span data-i18n="topbar_payment">🔒 安全支付 · <a href="#" data-i18n="topbar_details">查看详情</a></span>
|
|
</div>
|
|
|
|
<!-- 导航栏 -->
|
|
<nav class="navbar">
|
|
<div class="nav-container">
|
|
<div class="logo">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7v10l10 5 10-5V7L12 2z"/><path d="M2 7l10 5 10-5"/><path d="M12 22V12"/></svg>
|
|
GlobalShop
|
|
</div>
|
|
<ul class="nav-links">
|
|
<li><a href="#" class="active" onclick="resetPage();return false;" data-i18n="nav_home">首页</a></li>
|
|
<li><a href="#products" onclick="scrollToProducts();return false;" data-i18n="nav_all">所有商品</a></li>
|
|
<li><a href="#featured" onclick="loadFeatured();return false;" data-i18n="nav_featured">热卖推荐</a></li>
|
|
<li><a href="#deals" onclick="filterDeals();return false;" data-i18n="nav_deals">限时特惠</a></li>
|
|
</ul>
|
|
<div class="search-box">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
|
<input type="text" id="searchInput" data-i18n-placeholder="search_placeholder" placeholder="搜索商品..." oninput="handleSearch(event)">
|
|
</div>
|
|
<div class="nav-actions">
|
|
<button title="收藏" data-i18n-title="btn_fav">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
|
|
</button>
|
|
<button data-i18n-title="btn_cart" 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 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"><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" style="width:12px;height:12px"><path d="M6 9l6 6 6-6"/></svg>
|
|
</button>
|
|
<div class="lang-dropdown" id="langDropdown">
|
|
<div class="lang-option" data-lang="zh-CN" onclick="switchLang('zh-CN')">
|
|
<span>🇨🇳</span><span>简体中文</span>
|
|
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
|
|
</div>
|
|
<div class="lang-option" data-lang="zh-TW" onclick="switchLang('zh-TW')">
|
|
<span>🇹🇼</span><span>繁體中文</span>
|
|
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
|
|
</div>
|
|
<div class="lang-option" data-lang="en" onclick="switchLang('en')">
|
|
<span>🌐</span><span>English</span>
|
|
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Hero 动态壁纸 -->
|
|
<div class="hero">
|
|
<canvas id="heroCanvas"></canvas>
|
|
<div class="hero-overlay"></div>
|
|
<div class="hero-light" id="heroLight"></div>
|
|
<div class="hero-content">
|
|
<div class="hero-badge"><span class="dot"></span> <span data-i18n="hero_badge">全球直邮 · 2026 新季上线</span></div>
|
|
<h1><span data-i18n="hero_title_line1">全球好物</span><br><span class="accent" data-i18n="hero_title_line2">跨境直达</span></h1>
|
|
<p data-i18n="hero_desc">精选 18+ 品类好物,覆盖智能电子、家居生活、时尚运动,全球直邮,品质保障。沉浸式购物体验,由此开启。</p>
|
|
<div class="hero-actions">
|
|
<button class="hero-btn-primary" onclick="scrollToProducts()">
|
|
<span data-i18n="hero_cta">立即选购</span>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:20px;height:20px"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
|
</button>
|
|
<button class="hero-btn-ghost" onclick="loadFeatured()" data-i18n="hero_explore">探索精选</button>
|
|
</div>
|
|
</div>
|
|
<div class="hero-stats">
|
|
<div class="hero-stat"><div class="num">18<span>+</span></div><div class="label" data-i18n="stat_categories">品类好物</div></div>
|
|
<div class="hero-stat"><div class="num">50<span data-i18n="stat_users_unit">万</span></div><div class="label" data-i18n="stat_users">全球用户</div></div>
|
|
<div class="hero-stat"><div class="num">99<span>%</span></div><div class="label" data-i18n="stat_rating">好评率</div></div>
|
|
<div class="hero-stat"><div class="num">7<span data-i18n="stat_return_unit">天</span></div><div class="label" data-i18n="stat_return">无理由退换</div></div>
|
|
</div>
|
|
<div class="hero-scroll">
|
|
<span data-i18n="scroll_hint">向下滚动</span>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12l7 7 7-7"/></svg>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 优势条 -->
|
|
<div class="features-strip">
|
|
<div class="feature-item">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="3" width="15" height="13"/><path d="M16 8h4l3 3v5h-7V8z"/><circle cx="5.5" cy="18.5" r="2.5"/><circle cx="18.5" cy="18.5" r="2.5"/></svg>
|
|
<div><h4 data-i18n="feat1_title">全球直邮</h4><p data-i18n="feat1_desc">满 $99 免运费</p></div>
|
|
</div>
|
|
<div class="feature-item">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
|
<div><h4 data-i18n="feat2_title">正品保障</h4><p data-i18n="feat2_desc">品牌授权直供</p></div>
|
|
</div>
|
|
<div class="feature-item">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
|
|
<div><h4 data-i18n="feat3_title">7天退换</h4><p data-i18n="feat3_desc">无理由退货</p></div>
|
|
</div>
|
|
<div class="feature-item">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="4" width="22" height="16" rx="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg>
|
|
<div><h4 data-i18n="feat4_title">安全支付</h4><p data-i18n="feat4_desc">多渠道加密</p></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 商品列表区 -->
|
|
<div class="section" id="products">
|
|
<div class="section-header">
|
|
<div class="section-title" data-i18n="section_all_products">全部商品</div>
|
|
</div>
|
|
|
|
<div class="products-layout">
|
|
<!-- 侧边筛选 -->
|
|
<aside class="filters-sidebar">
|
|
<h3 data-i18n="filter_title">筛选</h3>
|
|
<div class="filter-group">
|
|
<h4 data-i18n="filter_category">分类</h4>
|
|
<div id="categoryFilters"></div>
|
|
</div>
|
|
<div class="filter-group">
|
|
<h4 data-i18n="filter_price">价格区间</h4>
|
|
<div class="filter-option"><input type="radio" name="price" id="p0" value="0" checked onchange="applyFilters()"><label for="p0" data-i18n="price_all">全部</label></div>
|
|
<div class="filter-option"><input type="radio" name="price" id="p1" value="0-30" onchange="applyFilters()"><label for="p1">$0 - $30</label></div>
|
|
<div class="filter-option"><input type="radio" name="price" id="p2" value="30-60" onchange="applyFilters()"><label for="p2">$30 - $60</label></div>
|
|
<div class="filter-option"><input type="radio" name="price" id="p3" value="60-100" onchange="applyFilters()"><label for="p3">$60 - $100</label></div>
|
|
<div class="filter-option"><input type="radio" name="price" id="p4" value="100-9999" onchange="applyFilters()"><label for="p4">$100+</label></div>
|
|
</div>
|
|
<div class="filter-group">
|
|
<h4 data-i18n="filter_sort">排序</h4>
|
|
<div class="filter-option"><input type="radio" name="sort" id="s1" value="default" checked onchange="applyFilters()"><label for="s1" data-i18n="sort_default">默认排序</label></div>
|
|
<div class="filter-option"><input type="radio" name="sort" id="s2" value="sales" onchange="applyFilters()"><label for="s2" data-i18n="sort_sales">销量优先</label></div>
|
|
<div class="filter-option"><input type="radio" name="sort" id="s3" value="price_asc" onchange="applyFilters()"><label for="s3" data-i18n="sort_price_asc">价格升序</label></div>
|
|
<div class="filter-option"><input type="radio" name="sort" id="s4" value="price_desc" onchange="applyFilters()"><label for="s4" data-i18n="sort_price_desc">价格降序</label></div>
|
|
<div class="filter-option"><input type="radio" name="sort" id="s5" value="rating" onchange="applyFilters()"><label for="s5" data-i18n="sort_rating">评分最高</label></div>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- 商品网格 -->
|
|
<div class="products-main">
|
|
<div class="products-toolbar">
|
|
<div class="products-count" id="productsCount" data-i18n="loading">加载中...</div>
|
|
<div class="category-nav" style="margin:0;padding:0;overflow:visible;border:none" id="categoryChips"></div>
|
|
</div>
|
|
<div class="products-grid" id="productsGrid">
|
|
<!-- 骨架屏 -->
|
|
</div>
|
|
<div class="pagination" id="pagination"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 购物车遮罩 -->
|
|
<div class="cart-overlay" id="cartOverlay" onclick="toggleCart()"></div>
|
|
|
|
<!-- 购物车抽屉 -->
|
|
<div class="cart-drawer" id="cartDrawer">
|
|
<div class="cart-header">
|
|
<h3><span data-i18n="cart_title">购物车</span> (<span id="cartCount">0</span>)</h3>
|
|
<button class="cart-close" onclick="toggleCart()">×</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()" data-i18n="cart_checkout">去结算</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 商品详情模态框 -->
|
|
<div class="modal-overlay" id="productModal" onclick="closeModal(event)">
|
|
<div class="modal-content" id="modalContent"></div>
|
|
</div>
|
|
|
|
<!-- Toast -->
|
|
<div class="toast" id="toast">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
|
<span id="toastMsg" data-i18n="toast_added">已加入购物车</span>
|
|
</div>
|
|
|
|
<!-- 页脚 -->
|
|
<footer class="footer">
|
|
<div class="footer-content">
|
|
<div class="footer-brand">
|
|
<h3>GlobalShop</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_ship_methods">配送方式</a></li>
|
|
<li><a href="#" data-i18n="footer_ship_rules">运费规则</a></li>
|
|
<li><a href="#" data-i18n="footer_ship_return">退换货政策</a></li>
|
|
<li><a href="#" data-i18n="footer_ship_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 GlobalShop. All rights reserved. <span data-i18n="footer_suffix">全球好物 · 跨境直达</span>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
// ========== i18n 国际化引擎 ==========
|
|
const I18N_LOCALES = {
|
|
'zh-CN': {
|
|
topbar_shipping: '🚀 全球直邮 · 满 $99 免运费',
|
|
topbar_return: '📦 7天无理由退换',
|
|
topbar_payment: '🔒 安全支付 · ',
|
|
topbar_details: '查看详情',
|
|
nav_home: '首页',
|
|
nav_all: '所有商品',
|
|
nav_featured: '热卖推荐',
|
|
nav_deals: '限时特惠',
|
|
search_placeholder: '搜索商品...',
|
|
btn_fav: '收藏',
|
|
btn_cart: '购物车',
|
|
hero_badge: '全球直邮 · 2026 新季上线',
|
|
hero_title_line1: '全球好物',
|
|
hero_title_line2: '跨境直达',
|
|
hero_desc: '精选 18+ 品类好物,覆盖智能电子、家居生活、时尚运动,全球直邮,品质保障。沉浸式购物体验,由此开启。',
|
|
hero_cta: '立即选购',
|
|
hero_explore: '探索精选',
|
|
stat_categories: '品类好物',
|
|
stat_users: '全球用户',
|
|
stat_users_unit: '万',
|
|
stat_rating: '好评率',
|
|
stat_return: '无理由退换',
|
|
stat_return_unit: '天',
|
|
scroll_hint: '向下滚动',
|
|
feat1_title: '全球直邮',
|
|
feat1_desc: '满 $99 免运费',
|
|
feat2_title: '正品保障',
|
|
feat2_desc: '品牌授权直供',
|
|
feat3_title: '7天退换',
|
|
feat3_desc: '无理由退货',
|
|
feat4_title: '安全支付',
|
|
feat4_desc: '多渠道加密',
|
|
section_all_products: '全部商品',
|
|
filter_title: '筛选',
|
|
filter_category: '分类',
|
|
filter_price: '价格区间',
|
|
price_all: '全部',
|
|
filter_sort: '排序',
|
|
sort_default: '默认排序',
|
|
sort_sales: '销量优先',
|
|
sort_price_asc: '价格升序',
|
|
sort_price_desc: '价格降序',
|
|
sort_rating: '评分最高',
|
|
category_all: '全部分类',
|
|
category_all_chips: '全部',
|
|
loading: '加载中...',
|
|
cart_title: '购物车',
|
|
cart_total: '合计',
|
|
cart_checkout: '去结算',
|
|
cart_empty: '购物车是空的',
|
|
cart_empty_hint: '快去挑选心仪的商品吧',
|
|
cart_remove: '删除',
|
|
toast_added: '已加入购物车',
|
|
toast_adding: '正在加入...',
|
|
toast_add_fail: '添加失败',
|
|
toast_deals: '正在筛选限时特惠商品...',
|
|
toast_featured_fail: '加载推荐失败',
|
|
toast_order_submitted: '订单已提交! 合计 ',
|
|
error_load: '商品加载失败,请稍后重试',
|
|
error_no_match: '未找到匹配的商品',
|
|
error_detail_fail: '商品信息加载失败',
|
|
product_sold: '已售 ',
|
|
product_save: '省 ',
|
|
product_rating: '评分',
|
|
product_sales: '销量',
|
|
product_stock: '库存',
|
|
product_stock_unit: ' 件',
|
|
modal_add_cart: '加入购物车',
|
|
featured_prefix: '热卖推荐 · ',
|
|
footer_desc: '全球好物,跨境直达。我们精选全球优质商品,为您提供安全、便捷、愉悦的跨境购物体验。覆盖智能电子、家居生活、时尚服饰、运动户外、美妆个护等品类。',
|
|
footer_guide: '购物指南',
|
|
footer_guide_process: '购物流程',
|
|
footer_guide_member: '会员介绍',
|
|
footer_guide_faq: '常见问题',
|
|
footer_guide_support: '联系客服',
|
|
footer_shipping: '配送与退换',
|
|
footer_ship_methods: '配送方式',
|
|
footer_ship_rules: '运费规则',
|
|
footer_ship_return: '退换货政策',
|
|
footer_ship_refund: '退款说明',
|
|
footer_about: '关于我们',
|
|
footer_about_company: '公司简介',
|
|
footer_about_join: '加入我们',
|
|
footer_about_privacy: '隐私政策',
|
|
footer_about_terms: '用户协议',
|
|
footer_suffix: '全球好物 · 跨境直达'
|
|
},
|
|
'en': {
|
|
topbar_shipping: '🚀 Global Direct · Free Shipping on $99+',
|
|
topbar_return: '📦 7-Day Free Returns',
|
|
topbar_payment: '🔒 Secure Payment · ',
|
|
topbar_details: 'View Details',
|
|
nav_home: 'Home',
|
|
nav_all: 'All Products',
|
|
nav_featured: 'Top Picks',
|
|
nav_deals: 'Flash Deals',
|
|
search_placeholder: 'Search products...',
|
|
btn_fav: 'Favorites',
|
|
btn_cart: 'Cart',
|
|
hero_badge: 'Global Direct · 2026 New Season',
|
|
hero_title_line1: 'Global Finds',
|
|
hero_title_line2: 'Delivered Worldwide',
|
|
hero_desc: 'Explore 18+ categories covering smart electronics, home & living, fashion & sports. Global shipping with quality guarantee. Start your immersive shopping journey.',
|
|
hero_cta: 'Shop Now',
|
|
hero_explore: 'Explore Picks',
|
|
stat_categories: 'Categories',
|
|
stat_users: 'Global Users',
|
|
stat_users_unit: '',
|
|
stat_rating: 'Satisfaction',
|
|
stat_return: 'Free Returns',
|
|
stat_return_unit: 'Day',
|
|
scroll_hint: 'Scroll Down',
|
|
feat1_title: 'Global Shipping',
|
|
feat1_desc: 'Free on orders $99+',
|
|
feat2_title: 'Authentic Guarantee',
|
|
feat2_desc: 'Brand authorized',
|
|
feat3_title: '7-Day Returns',
|
|
feat3_desc: 'No questions asked',
|
|
feat4_title: 'Secure Payment',
|
|
feat4_desc: 'Multi-channel encryption',
|
|
section_all_products: 'All Products',
|
|
filter_title: 'Filters',
|
|
filter_category: 'Category',
|
|
filter_price: 'Price Range',
|
|
price_all: 'All',
|
|
filter_sort: 'Sort By',
|
|
sort_default: 'Default',
|
|
sort_sales: 'Best Selling',
|
|
sort_price_asc: 'Price: Low to High',
|
|
sort_price_desc: 'Price: High to Low',
|
|
sort_rating: 'Top Rated',
|
|
category_all: 'All Categories',
|
|
category_all_chips: 'All',
|
|
loading: 'Loading...',
|
|
cart_title: 'Cart',
|
|
cart_total: 'Total',
|
|
cart_checkout: 'Checkout',
|
|
cart_empty: 'Your cart is empty',
|
|
cart_empty_hint: 'Start adding some items!',
|
|
cart_remove: 'Remove',
|
|
toast_added: 'Added to cart',
|
|
toast_adding: 'Adding...',
|
|
toast_add_fail: 'Failed to add',
|
|
toast_deals: 'Filtering flash deals...',
|
|
toast_featured_fail: 'Failed to load recommendations',
|
|
toast_order_submitted: 'Order submitted! Total: ',
|
|
error_load: 'Failed to load products, please try again',
|
|
error_no_match: 'No matching products found',
|
|
error_detail_fail: 'Failed to load product details',
|
|
product_sold: 'Sold ',
|
|
product_save: 'Save ',
|
|
product_rating: 'Rating',
|
|
product_sales: 'Sales',
|
|
product_stock: 'Stock',
|
|
product_stock_unit: '',
|
|
modal_add_cart: 'Add to Cart',
|
|
featured_prefix: 'Top Picks · ',
|
|
footer_desc: 'Global finds, delivered worldwide. We curate quality products from around the world for a safe, convenient, and enjoyable cross-border shopping experience. Covering smart electronics, home & living, fashion, sports, beauty, and more.',
|
|
footer_guide: 'Shopping Guide',
|
|
footer_guide_process: 'How to Order',
|
|
footer_guide_member: 'Membership',
|
|
footer_guide_faq: 'FAQ',
|
|
footer_guide_support: 'Contact Support',
|
|
footer_shipping: 'Shipping & Returns',
|
|
footer_ship_methods: 'Shipping Methods',
|
|
footer_ship_rules: 'Shipping Policy',
|
|
footer_ship_return: 'Return Policy',
|
|
footer_ship_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 Service',
|
|
footer_suffix: 'Global Finds · Delivered Worldwide'
|
|
},
|
|
'zh-TW': {
|
|
topbar_shipping: '🚀 全球直郵 · 滿 $99 免運費',
|
|
topbar_return: '📦 7天無理由退換',
|
|
topbar_payment: '🔒 安全支付 · ',
|
|
topbar_details: '查看詳情',
|
|
nav_home: '首頁',
|
|
nav_all: '所有商品',
|
|
nav_featured: '熱賣推薦',
|
|
nav_deals: '限時特惠',
|
|
search_placeholder: '搜尋商品...',
|
|
btn_fav: '收藏',
|
|
btn_cart: '購物車',
|
|
hero_badge: '全球直郵 · 2026 新季上線',
|
|
hero_title_line1: '全球好物',
|
|
hero_title_line2: '跨境直達',
|
|
hero_desc: '精選 18+ 品類好物,涵蓋智慧電子、居家生活、時尚運動,全球直郵,品質保障。沉浸式購物體驗,由此開啟。',
|
|
hero_cta: '立即選購',
|
|
hero_explore: '探索精選',
|
|
stat_categories: '品類好物',
|
|
stat_users: '全球用戶',
|
|
stat_users_unit: '萬',
|
|
stat_rating: '好評率',
|
|
stat_return: '無理由退換',
|
|
stat_return_unit: '天',
|
|
scroll_hint: '向下捲動',
|
|
feat1_title: '全球直郵',
|
|
feat1_desc: '滿 $99 免運費',
|
|
feat2_title: '正品保障',
|
|
feat2_desc: '品牌授權直供',
|
|
feat3_title: '7天退換',
|
|
feat3_desc: '無理由退貨',
|
|
feat4_title: '安全支付',
|
|
feat4_desc: '多渠道加密',
|
|
section_all_products: '全部商品',
|
|
filter_title: '篩選',
|
|
filter_category: '分類',
|
|
filter_price: '價格區間',
|
|
price_all: '全部',
|
|
filter_sort: '排序',
|
|
sort_default: '預設排序',
|
|
sort_sales: '銷量優先',
|
|
sort_price_asc: '價格升序',
|
|
sort_price_desc: '價格降序',
|
|
sort_rating: '評分最高',
|
|
category_all: '全部分類',
|
|
category_all_chips: '全部',
|
|
loading: '載入中...',
|
|
cart_title: '購物車',
|
|
cart_total: '合計',
|
|
cart_checkout: '去結算',
|
|
cart_empty: '購物車是空的',
|
|
cart_empty_hint: '快去挑選心儀的商品吧',
|
|
cart_remove: '刪除',
|
|
toast_added: '已加入購物車',
|
|
toast_adding: '正在加入...',
|
|
toast_add_fail: '新增失敗',
|
|
toast_deals: '正在篩選限時特惠商品...',
|
|
toast_featured_fail: '載入推薦失敗',
|
|
toast_order_submitted: '訂單已提交! 合計 ',
|
|
error_load: '商品載入失敗,請稍後重試',
|
|
error_no_match: '未找到相符的商品',
|
|
error_detail_fail: '商品資訊載入失敗',
|
|
product_sold: '已售 ',
|
|
product_save: '省 ',
|
|
product_rating: '評分',
|
|
product_sales: '銷量',
|
|
product_stock: '庫存',
|
|
product_stock_unit: ' 件',
|
|
modal_add_cart: '加入購物車',
|
|
featured_prefix: '熱賣推薦 · ',
|
|
footer_desc: '全球好物,跨境直達。我們精選全球優質商品,為您提供安全、便捷、愉悅的跨境購物體驗。涵蓋智慧電子、居家生活、時尚服飾、運動戶外、美妝個護等品類。',
|
|
footer_guide: '購物指南',
|
|
footer_guide_process: '購物流程',
|
|
footer_guide_member: '會員註冊',
|
|
footer_guide_faq: '常見問題',
|
|
footer_guide_support: '聯絡客服',
|
|
footer_shipping: '配送與退換',
|
|
footer_ship_methods: '配送方式',
|
|
footer_ship_rules: '運費規則',
|
|
footer_ship_return: '退換貨政策',
|
|
footer_ship_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 'en';
|
|
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.querySelectorAll('[data-i18n-title]').forEach(function(el){
|
|
var key=el.getAttribute('data-i18n-title');
|
|
el.title=t(key);
|
|
});
|
|
document.documentElement.lang=currentLang==='zh-TW'?'zh-TW':currentLang==='zh-CN'?'zh-CN':'en';
|
|
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 cart = [];
|
|
|
|
// ========== 图标库 ==========
|
|
const iconColors = ['#E63946','#457B9D','#F4A261','#2A9D8F','#9D4EDD','#E76F51','#06A77D','#D62828'];
|
|
function getGradient(seed) {
|
|
const c = iconColors[seed % iconColors.length];
|
|
return `linear-gradient(135deg, ${c}22, ${c}44)`;
|
|
}
|
|
function productIconSVG(catId) {
|
|
const icons = {
|
|
1: '<path d="M4 4h16v12H4z"/><path d="M2 20h20"/><path d="M9 8h6"/>',
|
|
2: '<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/>',
|
|
3: '<path d="M20.38 3.46L16 2a4 4 0 0 1-8 0L3.62 3.46a2 2 0 0 0-1.34 2.23l.58 3.47a1 1 0 0 0 .99.84H6v10a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V10h2.15a1 1 0 0 0 .99-.84l.58-3.47a2 2 0 0 0-1.34-2.23z"/>',
|
|
4: '<path d="M6.5 6.5l11 11"/><path d="M21 21l-6-6m6 6v-4m0 4h-4"/><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/>',
|
|
5: '<path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="4"/>',
|
|
};
|
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">${icons[catId] || icons[1]}</svg>`;
|
|
}
|
|
|
|
// ========== API 调用 ==========
|
|
async function fetchJSON(url) {
|
|
const res = await fetch(url);
|
|
const json = await res.json();
|
|
if (json.code !== 0) throw new Error(json.message || t('error_load'));
|
|
return json.data;
|
|
}
|
|
|
|
async function loadCategories() {
|
|
try {
|
|
allCategories = await fetchJSON('/api/v1/shop/categories');
|
|
renderCategoryFilters();
|
|
renderCategoryChips();
|
|
} catch(e) { console.error('Categories load failed:', e); }
|
|
}
|
|
|
|
async function loadProducts() {
|
|
const grid = document.getElementById('productsGrid');
|
|
grid.innerHTML = Array(8).fill(0).map(() => `
|
|
<div class="skeleton-card">
|
|
<div class="skeleton-img"></div>
|
|
<div class="skeleton-line"></div>
|
|
<div class="skeleton-line" style="width:60%"></div>
|
|
<div class="skeleton-line" style="width:40%"></div>
|
|
</div>
|
|
`).join('');
|
|
|
|
try {
|
|
let url = `/api/v1/shop/products?page=${currentPage}&page_size=${pageSize}`;
|
|
const sortRadio = document.querySelector('input[name="sort"]:checked');
|
|
if (sortRadio && sortRadio.value !== 'default') url += `&sort=${sortRadio.value}`;
|
|
if (selectedCategory > 0) url += `&category_id=${selectedCategory}`;
|
|
if (searchKeyword) url += `&keyword=${encodeURIComponent(searchKeyword)}`;
|
|
|
|
const data = await fetchJSON(url);
|
|
renderProducts(data.list || [], data.total || 0);
|
|
renderPagination(data.total || 0);
|
|
} catch(e) {
|
|
grid.innerHTML = `<div style="grid-column:1/-1;text-align:center;padding:60px;color:var(--text-muted)">
|
|
<p>${t('error_load')}</p></div>`;
|
|
}
|
|
}
|
|
|
|
// ========== 渲染 ==========
|
|
function renderCategoryFilters() {
|
|
const container = document.getElementById('categoryFilters');
|
|
container.innerHTML = `
|
|
<div class="filter-option">
|
|
<input type="radio" name="cat-filter" id="cat0" value="0" checked onchange="selectCategory(0)">
|
|
<label for="cat0">${t('category_all')}</label>
|
|
</div>
|
|
` + allCategories.map(c => `
|
|
<div class="filter-option">
|
|
<input type="radio" name="cat-filter" id="cat${c.id}" value="${c.id}" onchange="selectCategory(${c.id})">
|
|
<label for="cat${c.id}">${c.name}</label>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function renderCategoryChips() {
|
|
const container = document.getElementById('categoryChips');
|
|
container.innerHTML = `<span class="category-chip ${selectedCategory === 0 ? 'active' : ''}" onclick="selectCategory(0)">${t('category_all_chips')}</span>` +
|
|
allCategories.map(c => `<span class="category-chip ${selectedCategory === c.id ? 'active' : ''}" onclick="selectCategory(${c.id})">${c.name}</span>`).join('');
|
|
}
|
|
|
|
function renderProducts(products, total) {
|
|
const grid = document.getElementById('productsGrid');
|
|
document.getElementById('productsCount').textContent = `${t('section_all_products')} ${total} 件商品`;
|
|
|
|
if (products.length === 0) {
|
|
grid.innerHTML = `<div style="grid-column:1/-1;text-align:center;padding:60px;color:var(--text-muted)">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:48px;height:48px;margin:0 auto 12px;opacity:0.3"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
|
<p>${t('error_no_match')}</p></div>`;
|
|
return;
|
|
}
|
|
|
|
grid.innerHTML = products.map((p, i) => {
|
|
const discount = p.original_price > p.price ? Math.round((1 - p.price / p.original_price) * 100) : 0;
|
|
let tags = [];
|
|
try { tags = JSON.parse(p.tags || '[]'); } catch(e) {}
|
|
const tagHtml = tags.slice(0, 2).map(t => {
|
|
const cls = t.includes('热') || t.includes('爆') || t.includes('Hot') ? 'tag-hot' : t.includes('新') || t.includes('New') ? 'tag-new' : 'tag-sale';
|
|
return `<span class="product-tag ${cls}">${t}</span>`;
|
|
}).join('');
|
|
|
|
return `
|
|
<div class="product-card" onclick="showProductDetail(${p.id})">
|
|
<div class="product-image">
|
|
${p.image ? `<img src="${p.image}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.style.display='none';this.nextElementSibling.style.display='flex'">` : ''}
|
|
<div class="product-image-gradient" style="background:${getGradient(p.category_id)};${p.image ? 'display:none' : 'display:flex'}">
|
|
${productIconSVG(p.category_id)}
|
|
</div>
|
|
<div class="product-tags">${tagHtml}</div>
|
|
${discount > 0 ? `<div class="product-discount">-${discount}%</div>` : ''}
|
|
</div>
|
|
<div class="product-info">
|
|
<div class="product-name">${p.name}</div>
|
|
<div class="product-price">
|
|
<span class="price-current">$${p.price.toFixed(2)}</span>
|
|
${p.original_price > p.price ? `<span class="price-original">$${p.original_price.toFixed(2)}</span>` : ''}
|
|
</div>
|
|
<div class="product-meta">
|
|
<div class="product-rating">
|
|
<span class="stars">${'★'.repeat(Math.round(p.rating))}</span>
|
|
<span>${p.rating.toFixed(1)}</span>
|
|
</div>
|
|
<span class="product-sales">${t('product_sold')}${formatSales(p.sales)}</span>
|
|
</div>
|
|
</div>
|
|
<button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${p.id})" title="${t('modal_add_cart')}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
|
|
</button>
|
|
</div>`;
|
|
}).join('');
|
|
}
|
|
|
|
function renderPagination(total) {
|
|
const pages = Math.ceil(total / pageSize);
|
|
if (pages <= 1) { document.getElementById('pagination').innerHTML = ''; return; }
|
|
|
|
let html = `<button class="page-btn" ${currentPage === 1 ? 'disabled' : ''} onclick="goToPage(${currentPage - 1})"><</button>`;
|
|
const maxVisible = 5;
|
|
let start = Math.max(1, currentPage - 2);
|
|
let end = Math.min(pages, start + maxVisible - 1);
|
|
if (end - start < maxVisible - 1) start = Math.max(1, end - maxVisible + 1);
|
|
|
|
if (start > 1) { html += `<button class="page-btn" onclick="goToPage(1)">1</button>`; if (start > 2) html += `<span style="padding:0 4px;color:var(--text-muted)">...</span>`; }
|
|
for (let i = start; i <= end; i++) {
|
|
html += `<button class="page-btn ${i === currentPage ? 'active' : ''}" onclick="goToPage(${i})">${i}</button>`;
|
|
}
|
|
if (end < pages) { if (end < pages - 1) html += `<span style="padding:0 4px;color:var(--text-muted)">...</span>`; html += `<button class="page-btn" onclick="goToPage(${pages})">${pages}</button>`; }
|
|
html += `<button class="page-btn" ${currentPage === pages ? 'disabled' : ''} onclick="goToPage(${currentPage + 1})">></button>`;
|
|
|
|
document.getElementById('pagination').innerHTML = html;
|
|
}
|
|
|
|
// ========== 交互函数 ==========
|
|
function selectCategory(catId) {
|
|
selectedCategory = catId;
|
|
currentPage = 1;
|
|
document.querySelectorAll('input[name="cat-filter"]').forEach(r => { r.checked = parseInt(r.value) === catId; });
|
|
document.querySelectorAll('.category-chip').forEach((c, i) => {
|
|
c.classList.toggle('active', (i === 0 && catId === 0) || (i > 0 && allCategories[i - 1] && allCategories[i - 1].id === catId));
|
|
});
|
|
loadProducts();
|
|
}
|
|
|
|
function applyFilters() {
|
|
currentPage = 1;
|
|
loadProducts();
|
|
}
|
|
|
|
function handleSearch(e) {
|
|
searchKeyword = e.target.value.trim();
|
|
currentPage = 1;
|
|
clearTimeout(window._searchTimer);
|
|
window._searchTimer = setTimeout(loadProducts, 400);
|
|
}
|
|
|
|
function goToPage(page) {
|
|
currentPage = page;
|
|
loadProducts();
|
|
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
|
|
function scrollToProducts() {
|
|
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
|
|
function resetPage() {
|
|
selectedCategory = 0;
|
|
searchKeyword = '';
|
|
currentPage = 1;
|
|
document.getElementById('searchInput').value = '';
|
|
document.querySelectorAll('input[name="price"]').forEach(r => r.checked = r.value === '0');
|
|
document.querySelectorAll('input[name="sort"]').forEach(r => r.checked = r.value === 'default');
|
|
selectCategory(0);
|
|
}
|
|
|
|
function filterDeals() {
|
|
scrollToProducts();
|
|
showToast(t('toast_deals'));
|
|
}
|
|
|
|
async function loadFeatured() {
|
|
try {
|
|
const products = await fetchJSON('/api/v1/shop/featured?limit=8');
|
|
renderProducts(products, products.length);
|
|
document.getElementById('pagination').innerHTML = '';
|
|
document.getElementById('productsCount').textContent = `${t('featured_prefix')}${products.length} 件商品`;
|
|
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
|
|
} catch(e) { showToast(t('toast_featured_fail')); }
|
|
}
|
|
|
|
// ========== 商品详情 ==========
|
|
async function showProductDetail(id) {
|
|
const modal = document.getElementById('productModal');
|
|
const content = document.getElementById('modalContent');
|
|
content.innerHTML = `<div style="padding:60px;text-align:center;color:var(--text-muted)">${t('loading')}</div>`;
|
|
modal.classList.add('open');
|
|
|
|
try {
|
|
const p = await fetchJSON(`/api/v1/shop/products/${id}`);
|
|
let tags = [];
|
|
try { tags = JSON.parse(p.tags || '[]'); } catch(e) {}
|
|
let imgList = [];
|
|
try { imgList = JSON.parse(p.images || '[]'); } catch(e) {}
|
|
if (!Array.isArray(imgList)) imgList = [];
|
|
if (p.image) imgList = [p.image, ...imgList.filter(img => img !== p.image)];
|
|
const discount = p.original_price > p.price ? Math.round((1 - p.price / p.original_price) * 100) : 0;
|
|
|
|
content.innerHTML = `
|
|
<button class="modal-close" onclick="closeModal()">×</button>
|
|
<div class="modal-body">
|
|
<div class="modal-image" style="background:${getGradient(p.category_id)};position:relative;overflow:hidden">
|
|
${imgList.length > 0 ? `<img id="modalMainImg" src="${imgList[0]}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.style.display='none';this.nextElementSibling.style.display=''">` : ''}
|
|
<div style="${imgList.length > 0 ? 'display:none' : ''}">${productIconSVG(p.category_id)}</div>
|
|
${imgList.length > 1 ? `<div style="position:absolute;bottom:8px;left:0;right:0;display:flex;gap:6px;justify-content:center;z-index:5">${imgList.map((img,idx) => `<img src="${img}" onclick="document.getElementById('modalMainImg').src=this.src" style="width:44px;height:44px;border-radius:6px;object-fit:cover;cursor:pointer;border:2px solid ${idx===0?'#fff':'rgba(255,255,255,0.5)'}" onerror="this.remove()">`).join('')}</div>` : ''}
|
|
</div>
|
|
<div class="modal-details">
|
|
<h2>${p.name}</h2>
|
|
<div class="modal-price">
|
|
<span class="price-current" style="color:var(--primary);font-size:32px;font-weight:800">$${p.price.toFixed(2)}</span>
|
|
${p.original_price > p.price ? `<span class="price-original" style="font-size:16px;color:var(--text-muted);text-decoration:line-through">$${p.original_price.toFixed(2)}</span>` : ''}
|
|
${discount > 0 ? `<span style="background:var(--primary);color:#fff;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:700">${t('product_save')}${discount}%</span>` : ''}
|
|
</div>
|
|
<div class="modal-tags">${tags.map(t => `<span class="product-tag ${t.includes('热') || t.includes('Hot') ? 'tag-hot' : t.includes('新') || t.includes('New') ? 'tag-new' : 'tag-sale'}">${t}</span>`).join('')}</div>
|
|
<p class="modal-desc">${p.description}</p>
|
|
<div class="modal-meta">
|
|
<div class="modal-meta-item"><span>${t('product_rating')}</span><span>${p.rating.toFixed(1)} ★</span></div>
|
|
<div class="modal-meta-item"><span>${t('product_sales')}</span><span>${formatSales(p.sales)}</span></div>
|
|
<div class="modal-meta-item"><span>${t('product_stock')}</span><span>${p.stock}${t('product_stock_unit')}</span></div>
|
|
</div>
|
|
<button class="modal-add-cart" onclick="addToCart(${p.id});closeModal()">${t('modal_add_cart')}</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
} catch(e) {
|
|
content.innerHTML = `<div style="padding:60px;text-align:center;color:var(--text-muted)">${t('error_detail_fail')}</div>`;
|
|
}
|
|
}
|
|
|
|
function closeModal(e) {
|
|
if (e && e.target !== e.currentTarget) return;
|
|
document.getElementById('productModal').classList.remove('open');
|
|
}
|
|
|
|
// ========== 购物车 ==========
|
|
let productCache = {};
|
|
|
|
async function addToCart(productId) {
|
|
let product = productCache[productId];
|
|
if (!product) {
|
|
try {
|
|
product = await fetchJSON(`/api/v1/shop/products/${productId}`);
|
|
productCache[productId] = product;
|
|
} catch(e) { showToast(t('toast_add_fail')); return; }
|
|
}
|
|
|
|
const existing = cart.find(i => i.id === productId);
|
|
if (existing) {
|
|
existing.qty++;
|
|
} else {
|
|
cart.push({ id: productId, name: product.name, price: product.price, qty: 1, category_id: product.category_id, image: product.image || '' });
|
|
}
|
|
updateCartUI();
|
|
showToast(`${t('toast_added')}: ${product.name.substring(0, 15)}...`);
|
|
}
|
|
|
|
function removeFromCart(productId) {
|
|
cart = cart.filter(i => i.id !== productId);
|
|
updateCartUI();
|
|
}
|
|
|
|
function changeQty(productId, delta) {
|
|
const item = cart.find(i => i.id === productId);
|
|
if (!item) return;
|
|
item.qty += delta;
|
|
if (item.qty <= 0) removeFromCart(productId);
|
|
else updateCartUI();
|
|
}
|
|
|
|
function updateCartUI() {
|
|
const count = cart.reduce((s, i) => s + i.qty, 0);
|
|
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
|
|
|
|
const badge = document.getElementById('cartBadge');
|
|
badge.textContent = count;
|
|
badge.style.display = count > 0 ? 'flex' : 'none';
|
|
document.getElementById('cartCount').textContent = count;
|
|
document.getElementById('cartTotal').textContent = `$${total.toFixed(2)}`;
|
|
document.getElementById('checkoutBtn').disabled = count === 0;
|
|
|
|
const container = document.getElementById('cartItems');
|
|
if (cart.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="cart-empty">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
|
|
<p>${t('cart_empty')}</p>
|
|
<p style="font-size:13px;margin-top:8px">${t('cart_empty_hint')}</p>
|
|
</div>`;
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = cart.map(item => `
|
|
<div class="cart-item">
|
|
<div class="cart-item-img" style="background:${getGradient(item.category_id)};position:relative;overflow:hidden">${item.image ? `<img src="${item.image}" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover" onerror="this.remove()">` : ''}${productIconSVG(item.category_id)}</div>
|
|
<div class="cart-item-info">
|
|
<div class="cart-item-name">${item.name}</div>
|
|
<div class="cart-item-price">$${item.price.toFixed(2)}</div>
|
|
<div class="cart-item-controls">
|
|
<button class="qty-btn" onclick="changeQty(${item.id}, -1)">-</button>
|
|
<span class="qty-value">${item.qty}</span>
|
|
<button class="qty-btn" onclick="changeQty(${item.id}, 1)">+</button>
|
|
<span class="cart-item-remove" onclick="removeFromCart(${item.id})">${t('cart_remove')}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function toggleCart() {
|
|
document.getElementById('cartOverlay').classList.toggle('open');
|
|
document.getElementById('cartDrawer').classList.toggle('open');
|
|
}
|
|
|
|
function checkout() {
|
|
if (cart.length === 0) return;
|
|
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
|
|
showToast(`${t('toast_order_submitted')}$${total.toFixed(2)}`);
|
|
cart = [];
|
|
updateCartUI();
|
|
toggleCart();
|
|
}
|
|
|
|
// ========== 工具函数 ==========
|
|
function formatSales(n) {
|
|
if (currentLang === 'en') {
|
|
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
|
if (n >= 1000) return (n / 1000).toFixed(1) + 'K';
|
|
return n.toString();
|
|
}
|
|
if (n >= 10000) return (n / 10000).toFixed(1) + '万';
|
|
return n.toString();
|
|
}
|
|
|
|
function showToast(msg) {
|
|
const toast = document.getElementById('toast');
|
|
document.getElementById('toastMsg').textContent = msg;
|
|
toast.classList.add('show');
|
|
clearTimeout(window._toastTimer);
|
|
window._toastTimer = setTimeout(() => toast.classList.remove('show'), 2500);
|
|
}
|
|
|
|
// ========== 语言切换回调:重新渲染动态内容 ==========
|
|
function onLangChange(){
|
|
applyI18n();
|
|
renderCategoryFilters();
|
|
renderCategoryChips();
|
|
loadProducts();
|
|
}
|
|
|
|
// ========== 初始化 ==========
|
|
async function init() {
|
|
applyI18n();
|
|
await loadCategories();
|
|
await loadProducts();
|
|
updateCartUI();
|
|
}
|
|
|
|
init();
|
|
|
|
// ========== Hero 动态壁纸 + 光追效果 ==========
|
|
// 导航栏滚动变色
|
|
window.addEventListener('scroll', function() {
|
|
var navbar = document.querySelector('.navbar');
|
|
var topbar = document.querySelector('.topbar');
|
|
if (window.scrollY > 80) {
|
|
navbar.style.background = 'rgba(255,255,255,0.95)';
|
|
navbar.style.backdropFilter = 'blur(20px)';
|
|
navbar.style.boxShadow = '0 2px 12px rgba(0,0,0,0.06)';
|
|
navbar.style.borderBottomColor = 'rgba(0,0,0,0.06)';
|
|
var links = navbar.querySelectorAll('.nav-links a, .nav-brand, .search-box input, .search-box svg, .nav-actions span');
|
|
links.forEach(function(el){ el.style.color = ''; });
|
|
var search = navbar.querySelector('.search-box');
|
|
if (search) { search.style.background = 'var(--bg)'; search.style.border = '1px solid var(--border)'; }
|
|
} else {
|
|
navbar.style.background = 'rgba(10,10,15,0.6)';
|
|
navbar.style.boxShadow = 'none';
|
|
navbar.style.borderBottomColor = 'rgba(255,255,255,0.06)';
|
|
}
|
|
if (topbar) topbar.style.opacity = window.scrollY > 32 ? '0' : '1';
|
|
});
|
|
|
|
(function(){
|
|
var canvas = document.getElementById('heroCanvas');
|
|
if (!canvas) return;
|
|
var ctx = canvas.getContext('2d');
|
|
var heroLight = document.getElementById('heroLight');
|
|
var W = 0, H = 0, dpr = window.devicePixelRatio || 1;
|
|
|
|
function resize() {
|
|
var rect = canvas.getBoundingClientRect();
|
|
W = rect.width; H = rect.height;
|
|
canvas.width = W * dpr; canvas.height = H * dpr;
|
|
ctx.scale(dpr, dpr);
|
|
}
|
|
resize();
|
|
window.addEventListener('resize', resize);
|
|
|
|
// 粒子系统 — 流动的光丝
|
|
var particles = [];
|
|
var PARTICLE_COUNT = 80;
|
|
for (var i = 0; i < PARTICLE_COUNT; i++) {
|
|
particles.push({
|
|
x: Math.random() * W,
|
|
y: Math.random() * H,
|
|
vx: (Math.random() - 0.5) * 0.3,
|
|
vy: (Math.random() - 0.5) * 0.3,
|
|
size: Math.random() * 3 + 1,
|
|
hue: Math.random() < 0.6 ? 355 : (Math.random() < 0.5 ? 45 : 15), // 红/金/橙
|
|
alpha: Math.random() * 0.5 + 0.4,
|
|
life: Math.random()
|
|
});
|
|
}
|
|
|
|
// 鼠标位置
|
|
var mx = W / 2, my = H / 2;
|
|
var hasMouse = false;
|
|
|
|
canvas.parentElement.addEventListener('mousemove', function(e) {
|
|
var rect = canvas.getBoundingClientRect();
|
|
mx = e.clientX - rect.left;
|
|
my = e.clientY - rect.top;
|
|
if (!hasMouse) { hasMouse = true; heroLight.classList.add('active'); }
|
|
heroLight.style.left = mx + 'px';
|
|
heroLight.style.top = my + 'px';
|
|
});
|
|
canvas.parentElement.addEventListener('mouseleave', function() {
|
|
hasMouse = false;
|
|
heroLight.classList.remove('active');
|
|
});
|
|
|
|
// 动态壁纸主循环
|
|
function animate() {
|
|
// 深色背景渐变
|
|
var grad = ctx.createRadialGradient(W * 0.3, H * 0.4, 0, W * 0.3, H * 0.4, W * 0.8);
|
|
grad.addColorStop(0, 'rgba(30,15,20,0.15)');
|
|
grad.addColorStop(1, 'rgba(10,10,15,0.2)');
|
|
ctx.fillStyle = 'rgba(10,10,15,0.15)';
|
|
ctx.fillRect(0, 0, W, H);
|
|
ctx.fillStyle = grad;
|
|
ctx.fillRect(0, 0, W, H);
|
|
|
|
// 绘制流动的曲线网络
|
|
for (var i = 0; i < particles.length; i++) {
|
|
var p = particles[i];
|
|
|
|
// 鼠标吸引/排斥
|
|
if (hasMouse) {
|
|
var dx = mx - p.x;
|
|
var dy = my - p.y;
|
|
var dist = Math.sqrt(dx * dx + dy * dy);
|
|
if (dist < 200) {
|
|
var force = (1 - dist / 200) * 0.02;
|
|
p.vx += dx / dist * force;
|
|
p.vy += dy / dist * force;
|
|
}
|
|
}
|
|
|
|
// 更新位置
|
|
p.x += p.vx;
|
|
p.y += p.vy;
|
|
p.vx *= 0.98;
|
|
p.vy *= 0.98;
|
|
p.life += 0.005;
|
|
|
|
// 边界回弹
|
|
if (p.x < 0) p.x = W;
|
|
if (p.x > W) p.x = 0;
|
|
if (p.y < 0) p.y = H;
|
|
if (p.y > H) p.y = 0;
|
|
|
|
// 绘制粒子
|
|
var pulse = Math.sin(p.life * Math.PI * 2) * 0.3 + 0.7;
|
|
var r = p.size * pulse;
|
|
ctx.beginPath();
|
|
ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
|
|
ctx.fillStyle = 'hsla(' + p.hue + ',80%,60%,' + (p.alpha * pulse) + ')';
|
|
ctx.fill();
|
|
|
|
// 光晕
|
|
ctx.beginPath();
|
|
ctx.arc(p.x, p.y, r * 3, 0, Math.PI * 2);
|
|
var glow = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r * 3);
|
|
glow.addColorStop(0, 'hsla(' + p.hue + ',80%,60%,' + (p.alpha * 0.3) + ')');
|
|
glow.addColorStop(1, 'transparent');
|
|
ctx.fillStyle = glow;
|
|
ctx.fill();
|
|
|
|
// 连线 — 近距离粒子之间
|
|
for (var j = i + 1; j < particles.length; j++) {
|
|
var p2 = particles[j];
|
|
var ldx = p.x - p2.x;
|
|
var ldy = p.y - p2.y;
|
|
var ld = Math.sqrt(ldx * ldx + ldy * ldy);
|
|
if (ld < 150) {
|
|
var la = (1 - ld / 150) * 0.25;
|
|
ctx.beginPath();
|
|
ctx.moveTo(p.x, p.y);
|
|
ctx.lineTo(p2.x, p2.y);
|
|
ctx.strokeStyle = 'hsla(' + p.hue + ',70%,55%,' + la + ')';
|
|
ctx.lineWidth = 0.5;
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
|
|
// 鼠标光追连线 — 鼠标附近的粒子向鼠标发射光线
|
|
if (hasMouse) {
|
|
var mdx = p.x - mx;
|
|
var mdy = p.y - my;
|
|
var md = Math.sqrt(mdx * mdx + mdy * mdy);
|
|
if (md < 250) {
|
|
var ma = (1 - md / 250) * 0.4;
|
|
ctx.beginPath();
|
|
ctx.moveTo(p.x, p.y);
|
|
ctx.lineTo(mx, my);
|
|
ctx.strokeStyle = 'rgba(230,57,70,' + ma + ')';
|
|
ctx.lineWidth = 1;
|
|
ctx.stroke();
|
|
|
|
// 光追射线效果 — 在鼠标周围有明亮光点
|
|
ctx.beginPath();
|
|
ctx.arc(mx, my, 2, 0, Math.PI * 2);
|
|
ctx.fillStyle = 'rgba(255,255,255,0.8)';
|
|
ctx.fill();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 鼠标处的光追放射线
|
|
if (hasMouse) {
|
|
for (var k = 0; k < 8; k++) {
|
|
var angle = (k / 8) * Math.PI * 2 + Date.now() * 0.0003;
|
|
var len = 80 + Math.sin(Date.now() * 0.002 + k) * 20;
|
|
var ex = mx + Math.cos(angle) * len;
|
|
var ey = my + Math.sin(angle) * len;
|
|
var rayGrad = ctx.createLinearGradient(mx, my, ex, ey);
|
|
rayGrad.addColorStop(0, 'rgba(230,57,70,0.15)');
|
|
rayGrad.addColorStop(1, 'transparent');
|
|
ctx.beginPath();
|
|
ctx.moveTo(mx, my);
|
|
ctx.lineTo(ex, ey);
|
|
ctx.strokeStyle = rayGrad;
|
|
ctx.lineWidth = 2;
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
|
|
requestAnimationFrame(animate);
|
|
}
|
|
animate();
|
|
})();
|
|
|
|
// ========== 3D 动态效果 & 鼠标光影(新增)==========
|
|
(function(){
|
|
var isTouch = window.matchMedia('(hover: none)').matches;
|
|
|
|
/* ----- 鼠标跟随光斑 ----- */
|
|
var cursorGlow = document.createElement('div');
|
|
cursorGlow.className = 'cursor-glow';
|
|
document.body.appendChild(cursorGlow);
|
|
if (!isTouch) {
|
|
var glowOn = false;
|
|
document.addEventListener('mousemove', function(e){
|
|
cursorGlow.style.left = e.clientX + 'px';
|
|
cursorGlow.style.top = e.clientY + 'px';
|
|
if (!glowOn) { cursorGlow.classList.add('active'); glowOn = true; }
|
|
});
|
|
document.addEventListener('mouseleave', function(){
|
|
cursorGlow.classList.remove('active'); glowOn = false;
|
|
});
|
|
}
|
|
|
|
/* ----- 产品卡片 3D 倾斜 + 表面高光 ----- */
|
|
if (!isTouch) {
|
|
var tiltRaf = null, tiltEvt = null;
|
|
function tiltUpdate(){
|
|
tiltRaf = null;
|
|
var e = tiltEvt;
|
|
if (!e) return;
|
|
var cards = document.querySelectorAll('.product-card');
|
|
for (var i = 0; i < cards.length; i++) {
|
|
var card = cards[i];
|
|
var rect = card.getBoundingClientRect();
|
|
if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) continue;
|
|
var cx = rect.left + rect.width / 2;
|
|
var cy = rect.top + rect.height / 2;
|
|
var rx = ((e.clientY - cy) / rect.height) * -8;
|
|
var ry = ((e.clientX - cx) / rect.width) * 8;
|
|
card.style.transform = 'perspective(1000px) rotateX(' + rx + 'deg) rotateY(' + ry + 'deg) translateY(-4px)';
|
|
card.style.setProperty('--mx', ((e.clientX - rect.left) / rect.width * 100) + '%');
|
|
card.style.setProperty('--my', ((e.clientY - rect.top) / rect.height * 100) + '%');
|
|
}
|
|
}
|
|
document.addEventListener('mousemove', function(e){
|
|
tiltEvt = e;
|
|
if (tiltRaf === null) tiltRaf = requestAnimationFrame(tiltUpdate);
|
|
});
|
|
document.addEventListener('mouseout', function(e){
|
|
var t = e.target;
|
|
if (t && t.classList && t.classList.contains('product-card')) {
|
|
var r = e.relatedTarget;
|
|
if (r && (r === t || t.contains(r))) return;
|
|
t.style.transform = '';
|
|
}
|
|
});
|
|
}
|
|
|
|
/* ----- Hero 区域 3D 视差 ----- */
|
|
if (!isTouch) {
|
|
var heroContent = document.querySelector('.hero-content');
|
|
var heroRaf = null, heroEvt = null;
|
|
function heroUpdate(){
|
|
heroRaf = null;
|
|
var e = heroEvt;
|
|
if (!e || !heroContent) return;
|
|
var x = (e.clientX / window.innerWidth - 0.5) * 2;
|
|
var y = (e.clientY / window.innerHeight - 0.5) * 2;
|
|
heroContent.style.transform = 'translate(' + (x * 10) + 'px,' + (y * 10) + 'px)';
|
|
}
|
|
document.addEventListener('mousemove', function(e){
|
|
heroEvt = e;
|
|
if (heroRaf === null) heroRaf = requestAnimationFrame(heroUpdate);
|
|
});
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|