feat(site): 官网全新重建(App 真相源风格)+ 四档套餐 + 在线购买流

- tokens.css/site.css 逐值对齐原型设计系统,icons 雪碧图,零外链;首页/下载/帮助/法务全站重皮肤,真实截图
- 注册与登录在官网内完成(写 shared_preferences_web 兼容 localStorage,不再跳 App)
- 价格区四档:免费/标准 299/高级 599/定制;登录后标准/高级按钮变「购买 / 续费」
- 新增 /checkout/(套餐+月付年付切换)与 /license/result/(支付回跳轮询到账)
- 登录页支持 ?next= 站内回跳;二维码措辞改「扫码看详情可分享」
- 下载页构建期真实下载链接+运行时 release 刷新;旧 features 页/死 CSS/dist 产物清理

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
wangjia
2026-07-03 20:40:21 +08:00
parent 18925d7d15
commit d256e7ac85
24 changed files with 1313 additions and 1977 deletions
-2
View File
@@ -2,8 +2,6 @@ module.exports = function(cfg) {
// 静态资源直接复制
cfg.addPassthroughCopy("assets");
// scan.html 面向消费者,独立维护,直接复制不经模板
cfg.addPassthroughCopy("scan.html");
// features/ 已改为 .njk 模板,不再需要 passthrough
return {
-1
View File
@@ -1,4 +1,3 @@
scan.html
content/
features/approval.html
features/inventory.html
+3 -3
View File
@@ -3,9 +3,9 @@ const path = require('path');
const md = require('markdown-it')({ html: false, breaks: false, linkify: false });
const TOC_GROUPS = [
{ group: '快速开始', nums: [1, 2, 3] },
{ group: '业务操作', nums: [4, 5, 6, 7, 8, 9] },
{ group: '基础设置', nums: [10, 11, 12] },
{ group: '快速开始', nums: [1, 2] },
{ group: '业务操作', nums: [3, 4, 5, 6, 7, 8] },
{ group: '设置与答疑', nums: [9, 10] },
];
module.exports = function() {
+6 -7
View File
@@ -6,17 +6,16 @@
<meta name="color-scheme" content="light" />
<title>{% if title %}{{ title }} · {% endif %}{{ site.name }}</title>
{% if description %}<meta name="description" content="{{ description }}" />{% endif %}
<link rel="stylesheet" href="/assets/color.css" />
<link rel="stylesheet" href="/assets/style.css" />
{# 真相源:tokens.css = App 原型 tokens.css 主题A 拷贝;site.css 镜像 atoms.css。
全站零外链(无 Google Fonts / unpkg):字体走系统栈(与 App 真机一致),图标走内联雪碧图。 #}
<link rel="stylesheet" href="/assets/tokens.css" />
<link rel="stylesheet" href="/assets/site.css" />
{% if pageExtraCss %}<link rel="stylesheet" href="{{ pageExtraCss }}" />{% endif %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<script src="https://unpkg.com/lucide@{{ site.lucideVersion }}/dist/umd/lucide.min.js"></script>
<link rel="icon" href="/assets/logo-mark.svg" type="image/svg+xml" />
{% if pageStyle %}<style>{{ pageStyle | safe }}</style>{% endif %}
</head>
<body>
{% include "icons.njk" %}
{% include "nav.njk" %}
{{ content | safe }}
+37 -45
View File
@@ -1,49 +1,41 @@
<footer>
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<img src="/assets/logo-full.svg" alt="岩美" />
<p>为酒行与酒店设计的库存、审核、财务一体化管理平台。</p>
<div class="footer-contact">
{% if site.support.wechat %}<div>客服微信:{{ site.support.wechat }}</div>{% endif %}
<div><a href="mailto:{{ site.support.email }}">{{ site.support.email }}</a></div>
{% if site.support.phone %}<div>{{ site.support.phone }}</div>{% endif %}
</div>
</div>
<div class="footer-col">
<h5>产品</h5>
<ul>
<li><a href="/#modules">核心模块</a></li>
<li><a href="/#platforms">多端覆盖</a></li>
<li><a href="/#reports">数据洞察</a></li>
<li><a href="/#pricing">价格方案</a></li>
<li><a href="/download/">版本更新</a></li>
</ul>
</div>
<div class="footer-col">
<h5>资源</h5>
<ul>
<li><a href="/docs/">使用手册</a></li>
<li><a href="/download/">下载客户端</a></li>
</ul>
</div>
<div class="footer-col">
<h5>客户支持</h5>
<ul>
<li><a href="/#faq">常见问题</a></li>
<li><a href="mailto:{{ site.support.email }}">联系我们</a></li>
</ul>
</div>
<div class="footer-col">
<h5>公司</h5>
<ul>
<li><a href="/register/">注册门店</a></li>
</ul>
</div>
<footer class="footer">
<div class="footer-grid">
<div>
<div class="fbrand"><img src="/assets/logo-mark.svg" alt="岩美" />岩美酒库</div>
<p class="fdesc">为酒行与酒店设计的库存、审核、财务一体化管理平台。每一瓶酒一个编号,每一笔交易可追溯。</p>
<p class="fdesc" style="margin-top:10px">
{% if site.support.wechat %}客服微信:{{ site.support.wechat }}<br/>{% endif %}
<a href="mailto:{{ site.support.email }}">{{ site.support.email }}</a>
</p>
</div>
<div class="footer-bottom">
<div>{{ site.copyright }}</div>
{% if site.icp %}<div>{{ site.icp }}</div>{% endif %}
<div>
<h4>产品</h4>
<ul>
<li><a href="/#modules">核心模块</a></li>
<li><a href="/#workflow">审核驱动业务流</a></li>
<li><a href="/#pricing">价格方案</a></li>
<li><a href="/download/">下载客户端</a></li>
</ul>
</div>
<div>
<h4>支持</h4>
<ul>
<li><a href="/docs/">使用手册</a></li>
<li><a href="/#faq">常见问题</a></li>
<li><a href="/system-requirements/">系统要求</a></li>
<li><a href="/register/">注册门店</a></li>
</ul>
</div>
<div>
<h4>法务</h4>
<ul>
<li><a href="/privacy/">隐私政策</a></li>
<li><a href="/terms/">服务条款</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<div>{{ site.copyright }}</div>
{% if site.icp %}<div><a href="https://beian.miit.gov.cn" rel="noopener" target="_blank">{{ site.icp }}</a></div>{% endif %}
</div>
</footer>
File diff suppressed because one or more lines are too long
+18 -21
View File
@@ -1,34 +1,31 @@
<nav class="topnav">
<div class="topnav-inner">
<a href="/" class="topnav-brand">
<img src="/assets/logo-full.svg" alt="岩美" />
<nav class="nav">
<div class="nav-in">
<a class="nav-logo" href="/">
<img src="/assets/logo-mark.svg" alt="{{ site.name }}" />
岩美酒库
</a>
<div class="topnav-links">
<div class="nav-links">
<a href="/#modules">产品</a>
<a href="/features/inventory.html">库存</a>
<a href="/features/approval.html">审核流</a>
<a href="/#workflow">审核流</a>
<a href="/#pricing">价格</a>
<a href="/download/">下载</a>
<a href="/docs/">文档</a>
<a href="/#faq">支持</a>
<a href="/docs/">帮助</a>
</div>
<div class="topnav-cta" id="nav-cta">
<a href="/register/" class="btn btn-primary btn-sm">注册门店</a>
<div class="nav-cta" id="nav-cta">
<a class="btn ghost" href="/login/">登录</a>
<a class="btn primary" href="/register/">注册门店</a>
</div>
<button class="nav-hamburger" id="nav-hamburger" aria-label="菜单" aria-expanded="false">
<span></span><span></span><span></span>
<button class="nav-burger" id="nav-burger" aria-label="菜单" aria-expanded="false">
<svg class="ic" style="width:22px;height:22px"><use href="#i-menu"/></svg>
</button>
</div>
<div class="nav-mobile-menu" id="nav-mobile-menu">
<div class="nav-mobile" id="nav-mobile-menu">
<a href="/#modules">产品</a>
<a href="/features/inventory.html">库存管理</a>
<a href="/features/approval.html">审核流</a>
<a href="/#workflow">审核流</a>
<a href="/#pricing">价格</a>
<a href="/download/">下载</a>
<a href="/docs/">文档</a>
<a href="/#faq">支持</a>
<div class="nav-mobile-cta" id="nav-mobile-cta">
<a href="/register/" class="btn btn-primary">注册门店</a>
</div>
<a href="/docs/">帮助</a>
<a href="/register/">注册门店</a>
<a href="/login/">登录</a>
</div>
</nav>
+31 -123
View File
@@ -1,134 +1,42 @@
(function() {
var KEYS = ['access_token','refresh_token','username','real_name','shop_no','shop_id','role'];
/* 官网导航交互:汉堡菜单 + 登录态感知(Web 版 App 与官网同域时,
localStorage['flutter.*'] 有登录信息 → CTA 变「进入系统」)。零依赖。 */
(function () {
function getAuthUser() {
var token = localStorage.getItem('flutter.access_token');
var token = localStorage.getItem('flutter.access_token');
var username = localStorage.getItem('flutter.username');
if (!token || !username) return null;
return {
username: username,
realName: localStorage.getItem('flutter.real_name') || '',
role: localStorage.getItem('flutter.role') || '',
shopNo: localStorage.getItem('flutter.shop_no') || '',
shopNo: localStorage.getItem('flutter.shop_no') || '',
};
}
function doLogout() {
KEYS.forEach(function(k) { localStorage.removeItem('flutter.' + k); });
location.reload();
}
function toggleUserMenu() {
var el = document.getElementById('userDropdown');
if (el) el.classList.toggle('open');
}
function toggleMobileMenu() {
var menu = document.getElementById('nav-mobile-menu');
var btn = document.getElementById('nav-hamburger');
if (!menu) return;
var isOpen = menu.classList.toggle('open');
if (btn) btn.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
}
// 点击外部关闭 dropdown 和 mobile menu
document.addEventListener('click', function(e) {
var userMenu = document.getElementById('userDropdown');
if (userMenu) {
var navUser = userMenu.closest('.nav-user');
if (navUser && !navUser.contains(e.target)) userMenu.classList.remove('open');
}
var mobileMenu = document.getElementById('nav-mobile-menu');
var hamburger = document.getElementById('nav-hamburger');
if (mobileMenu && hamburger) {
var topnav = mobileMenu.closest('nav');
if (topnav && !topnav.contains(e.target)) {
mobileMenu.classList.remove('open');
hamburger.setAttribute('aria-expanded', 'false');
}
}
});
function renderNav() {
var cta = document.getElementById('nav-cta');
var mobileCta = document.getElementById('nav-mobile-cta');
if (!cta) return;
var user = getAuthUser();
if (user) {
var display = user.realName || user.username;
var initial = display.charAt(0).toUpperCase();
var roleMap = { admin: '管理员', user: '成员', readonly: '只读' };
var roleLabel = roleMap[user.role] || user.role;
cta.innerHTML =
'<div class="nav-user">'
+ '<button class="nav-user-btn" onclick="toggleUserMenu()">'
+ '<div class="nav-avatar">' + initial + '</div>'
+ '<span>' + display + '</span>'
+ '<i data-lucide="chevron-down" class="icon"></i>'
+ '</button>'
+ '<div class="nav-dropdown" id="userDropdown">'
+ '<div style="padding:10px 14px 6px;border-bottom:1px solid var(--border-subtle);margin-bottom:4px">'
+ '<div style="font-size:13px;font-weight:600;color:var(--brand-900)">' + display + '</div>'
+ '<div style="font-size:11px;color:var(--fg-muted);margin-top:2px">'
+ (user.shopNo ? user.shopNo + ' · ' : '') + roleLabel
+ '</div>'
+ '</div>'
+ '<a href="/app/"><i data-lucide="layout-dashboard" class="icon"></i>进入管理端</a>'
+ '<div class="nav-dropdown-divider"></div>'
+ '<button class="logout" onclick="doLogout()"><i data-lucide="log-out" class="icon"></i>退出登录</button>'
+ '</div></div>';
if (mobileCta) {
mobileCta.innerHTML =
'<a href="/app/" class="btn btn-ghost">进入管理端</a>'
+ '<button class="btn btn-ghost" onclick="doLogout()">退出登录</button>';
}
} else {
cta.innerHTML =
'<a href="/app/" class="btn btn-ghost">登录</a>'
+ '<a href="/app/" class="btn btn-primary">免费试用</a>';
if (mobileCta) {
mobileCta.innerHTML =
'<a href="/app/" class="btn btn-ghost">登录</a>'
+ '<a href="/register/" class="btn btn-primary">注册门店</a>';
}
}
if (window.lucide) lucide.createIcons();
}
// 暴露给 onclick 调用
window.toggleUserMenu = toggleUserMenu;
window.toggleMobileMenu = toggleMobileMenu;
window.doLogout = doLogout;
document.addEventListener('DOMContentLoaded', function() {
renderNav();
if (window.lucide) lucide.createIcons();
var hamburger = document.getElementById('nav-hamburger');
if (hamburger) hamburger.addEventListener('click', toggleMobileMenu);
// 点击 mobile menu 链接后自动收起
var mobileMenu = document.getElementById('nav-mobile-menu');
if (mobileMenu) {
mobileMenu.querySelectorAll('a').forEach(function(a) {
a.addEventListener('click', function() {
mobileMenu.classList.remove('open');
if (hamburger) hamburger.setAttribute('aria-expanded', 'false');
});
});
}
// 页内锚点平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(function(a) {
a.addEventListener('click', function(e) {
var id = a.getAttribute('href');
if (!id || id.length < 2) return;
var target = document.querySelector(id);
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
// 汉堡菜单
var burger = document.getElementById('nav-burger');
var mobile = document.getElementById('nav-mobile-menu');
if (burger && mobile) {
burger.addEventListener('click', function (e) {
e.stopPropagation();
var open = mobile.classList.toggle('open');
burger.setAttribute('aria-expanded', open ? 'true' : 'false');
});
});
document.addEventListener('click', function (e) {
if (!mobile.contains(e.target) && e.target !== burger) {
mobile.classList.remove('open');
}
});
}
// 登录态:CTA 换成「进入系统」
var user = getAuthUser();
if (user) {
var cta = document.getElementById('nav-cta');
if (cta) {
cta.innerHTML =
'<a class="btn primary" href="/app/">进入系统 · ' +
(user.realName || user.username).replace(/[<>&"]/g, '') +
'</a>';
}
}
})();
Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

+298
View File
@@ -0,0 +1,298 @@
/* ============================================================
岩美酒库官网 · 组件与版式层
原子样式一对一镜像 .superpowers/prototype/atoms.css.btn/.card/.badge/.kpi/.chip),
营销版式(nav/hero/section/footer)用同一批 token 组合。
============================================================ */
*{box-sizing:border-box;}
html{scroll-behavior:smooth;}
body{margin:0; font-family:var(--font); color:var(--text); background:var(--bg); font-size:var(--mk-body); line-height:1.7; -webkit-font-smoothing:antialiased;}
img{max-width:100%; display:block;}
a{color:var(--primary); text-decoration:none;}
a:hover{text-decoration:underline;}
h1,h2,h3{color:var(--heading); line-height:1.3; margin:0;}
.container{max-width:var(--mk-maxw); margin:0 auto; padding:0 24px;}
.ic{width:18px; height:18px; stroke:currentColor; stroke-width:1.8; fill:none; stroke-linecap:round; stroke-linejoin:round; flex:none;}
/* ---------- 原子:按钮(镜像 atoms .btn / .primary / .ghost / .lg / .sm ---------- */
.btn{height:38px; padding:0 16px; border-radius:var(--r-md); font-size:var(--fs-body); font-weight:600; display:inline-flex; align-items:center; justify-content:center; gap:7px; cursor:pointer; border:1px solid transparent; transition:background-color .15s,border-color .15s,color .15s; font-family:var(--font); text-decoration:none !important;}
.btn .ic{width:16px; height:16px;}
.btn.ghost{background:var(--surface); border-color:var(--border); color:var(--text);}
.btn.ghost:hover{background:var(--bg);}
.btn.primary{background:var(--primary); color:var(--on-primary);}
.btn.primary:hover{background:var(--primary-dark);}
.btn.lg{height:46px; padding:0 26px; font-size:15px; border-radius:var(--r-md);}
.btn.sm{height:32px; padding:0 12px; font-size:var(--fs-sm);}
.btn.onhero{background:var(--hero-glass-strong); color:var(--on-accent); border-color:transparent;}
.btn.onhero:hover{background:var(--hero-glass);}
.btn.wine{background:var(--accent); color:var(--on-accent);}
.btn.wine:hover{background:var(--brand-wine);}
/* ---------- 原子:卡片 / 徽章 / chip(镜像 atoms ---------- */
.card{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:22px;}
.badge{display:inline-flex; align-items:center; gap:5px; height:22px; padding:0 9px; border-radius:var(--r-pill); font-size:var(--fs-sm); font-weight:600;}
.badge::before{content:''; width:6px; height:6px; border-radius:50%; background:currentColor;}
.badge.ok{background:var(--success-bg); color:var(--success);}
.badge.warn{background:var(--warn-bg); color:var(--warn);}
.badge.info{background:var(--info-soft); color:var(--primary);}
.badge.wine{background:var(--accent-soft); color:var(--accent);}
.chip{display:inline-flex; align-items:center; gap:7px; height:34px; padding:0 12px; border:1px solid var(--border); border-radius:var(--r-md); background:var(--surface); font-size:var(--fs-sm); color:var(--text);}
/* ---------- 原子:KPI 卡(镜像 .kpi + .ic 色块) ---------- */
.kpi{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:15px 16px;}
.kpi .khead{display:flex; align-items:flex-start; justify-content:space-between;}
.kpi .t{font-size:var(--fs-sm); color:var(--muted); padding-top:2px;}
.kpi .icbox{width:30px; height:30px; border-radius:var(--r-md); display:flex; align-items:center; justify-content:center;}
.kpi .icbox .ic{width:17px; height:17px;}
.kpi .v{font-size:24px; font-weight:700; color:var(--heading); margin-top:7px; letter-spacing:.3px; font-family:var(--font-mono);}
.kpi .d{font-size:var(--fs-xs); color:var(--muted); margin-top:5px;}
.ic-info{background:var(--info-soft); color:var(--primary);}
.ic-ok{background:var(--ok-soft); color:var(--success);}
.ic-warn{background:var(--warn-bg); color:var(--warn);}
.ic-wine{background:var(--accent-soft); color:var(--accent);}
/* ---------- 顶部导航 ---------- */
.nav{position:sticky; top:0; z-index:40; background:rgba(255,255,255,.92); backdrop-filter:blur(10px); border-bottom:1px solid var(--border);}
.nav-in{max-width:var(--mk-maxw); margin:0 auto; padding:0 24px; height:60px; display:flex; align-items:center; gap:26px;}
.nav-logo{display:flex; align-items:center; gap:10px; font-weight:800; font-size:17px; color:var(--heading); text-decoration:none !important; letter-spacing:.5px;}
.nav-logo img{width:30px; height:30px; border-radius:7px;}
.nav-links{display:flex; gap:22px; margin-left:6px;}
.nav-links a{color:var(--muted); font-size:14px; font-weight:500; text-decoration:none !important;}
.nav-links a:hover{color:var(--primary);}
.nav-cta{margin-left:auto; display:flex; gap:10px; align-items:center;}
.nav-burger{display:none; margin-left:auto; background:none; border:none; color:var(--heading); cursor:pointer; padding:6px;}
.nav-mobile{display:none; border-top:1px solid var(--border); background:var(--surface); padding:10px 24px 16px;}
.nav-mobile a{display:block; padding:10px 0; color:var(--text); font-size:15px; border-bottom:1px solid var(--border-subtle); text-decoration:none !important;}
.nav-mobile.open{display:block;}
/* ---------- Hero(品牌渐变 = 登录页品牌面板同源) ---------- */
.hero{background:var(--mk-hero-grad); color:var(--on-accent); padding:84px 0 0; overflow:hidden;}
.hero-in{max-width:var(--mk-maxw); margin:0 auto; padding:0 24px; text-align:center;}
.hero .badge-line{display:inline-flex; align-items:center; gap:8px; background:var(--hero-glass); border-radius:var(--r-pill); padding:6px 14px; font-size:var(--fs-sm); color:var(--on-accent-faint);}
.hero .badge-line b{color:var(--on-accent); font-weight:600;}
.hero h1{color:var(--on-accent); font-size:var(--mk-display); font-weight:800; letter-spacing:1px; margin:26px auto 18px; max-width:17em;}
.hero .lead{color:var(--on-accent-faint); font-size:var(--mk-lead); max-width:36em; margin:0 auto 30px; line-height:1.8;}
.hero .cta-row{display:flex; gap:14px; justify-content:center; flex-wrap:wrap;}
.hero .mini{display:flex; gap:22px; justify-content:center; margin-top:22px; font-size:var(--fs-sm); color:var(--on-accent-faint); flex-wrap:wrap;}
.hero .mini span{display:inline-flex; align-items:center; gap:6px;}
.hero .shot{max-width:980px; margin:52px auto -2px; border-radius:var(--r-xl) var(--r-xl) 0 0; overflow:hidden; box-shadow:var(--sh-3); border:1px solid rgba(255,255,255,.18); border-bottom:none;}
.hero .shot img{width:100%;}
/* ---------- 区块 ---------- */
.section{padding:84px 0;}
.section.alt{background:var(--page);}
.sec-head{text-align:center; max-width:44em; margin:0 auto 46px;}
.sec-head .eyebrow{display:inline-flex; align-items:center; gap:7px; color:var(--accent); font-size:var(--fs-sm); font-weight:700; letter-spacing:2px; margin-bottom:12px;}
.sec-head .eyebrow::before,.sec-head .eyebrow::after{content:''; width:22px; height:1px; background:var(--accent); opacity:.5;}
.sec-head h2{font-size:var(--mk-h2); font-weight:800;}
.sec-head p{color:var(--muted); margin:14px 0 0;}
/* 模块卡网格 */
.grid{display:grid; gap:16px;}
.grid.c4{grid-template-columns:repeat(4,1fr);}
.grid.c3{grid-template-columns:repeat(3,1fr);}
.grid.c2{grid-template-columns:repeat(2,1fr);}
.feature .icbox{width:38px; height:38px; border-radius:var(--r-md); display:flex; align-items:center; justify-content:center; margin-bottom:14px;}
.feature .icbox .ic{width:20px; height:20px;}
.feature h3{font-size:var(--mk-h3); font-weight:700; margin-bottom:8px;}
.feature p{color:var(--muted); font-size:14px; margin:0; line-height:1.75;}
/* 截图对开区块 */
.split{display:grid; grid-template-columns:1fr 1fr; gap:48px; align-items:center;}
.split .shot{border-radius:var(--r-lg); overflow:hidden; border:1px solid var(--border); box-shadow:var(--sh-2);}
.split h2{font-size:var(--mk-h2); font-weight:800; margin-bottom:16px;}
.split .pts{display:flex; flex-direction:column; gap:14px; margin-top:22px;}
.split .pt{display:flex; gap:12px; align-items:flex-start;}
.split .pt .icbox{width:30px; height:30px; border-radius:var(--r-md); flex:none; display:flex; align-items:center; justify-content:center;}
.split .pt .icbox .ic{width:16px;height:16px;}
.split .pt b{display:block; color:var(--heading); font-size:15px;}
.split .pt span{color:var(--muted); font-size:14px;}
/* 审核流状态机 */
.flow{display:flex; align-items:stretch; gap:0; justify-content:center; flex-wrap:wrap;}
.flow .step{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:18px 20px; width:200px; text-align:center;}
.flow .arrow{align-self:center; color:var(--faint); padding:0 10px; font-size:20px;}
.flow .step .num{width:28px; height:28px; border-radius:50%; background:var(--info-soft); color:var(--primary); font-weight:700; display:flex; align-items:center; justify-content:center; margin:0 auto 10px; font-size:var(--fs-body);}
.flow .step b{display:block; color:var(--heading); font-size:15px; margin-bottom:5px;}
.flow .step span{color:var(--muted); font-size:var(--fs-sm);}
/* 角色权限表 */
table.roles{width:100%; border-collapse:collapse; background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); overflow:hidden; font-size:14px;}
table.roles th{background:var(--th-bg); color:var(--muted); font-size:var(--fs-sm); font-weight:600; text-align:left; padding:11px 14px; border-bottom:1px solid var(--border);}
table.roles td{padding:11px 14px; border-bottom:1px solid var(--border-subtle);}
table.roles tr:last-child td{border-bottom:none;}
table.roles .y{color:var(--success); font-weight:700;}
table.roles .n{color:var(--faint);}
/* 定价卡 */
.plans{display:grid; grid-template-columns:repeat(3,1fr); gap:18px; align-items:stretch;}
.plans.p4{grid-template-columns:repeat(4,1fr); gap:16px;}
.plans.p4 .plan{padding:26px 22px;}
.plans.p4 .plan .price{font-size:30px;}
.plan{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-xl); padding:28px 26px; display:flex; flex-direction:column;}
.plan.hot{border-color:var(--primary); box-shadow:var(--sh-2); position:relative;}
.plan.hot::before{content:'推荐'; position:absolute; top:-11px; left:50%; transform:translateX(-50%); background:var(--primary); color:var(--on-primary); font-size:var(--fs-xs); font-weight:700; padding:3px 12px; border-radius:var(--r-pill);}
.plan .pname{font-size:var(--fs-title); font-weight:700; color:var(--heading);}
.plan .price{font-size:34px; font-weight:800; color:var(--heading); margin:12px 0 2px; font-family:var(--font-mono);}
.plan .price small{font-size:14px; color:var(--muted); font-weight:400; font-family:var(--font);}
.plan .psub{color:var(--muted); font-size:var(--fs-sm); margin-bottom:18px;}
.plan ul{list-style:none; padding:0; margin:0 0 22px; display:flex; flex-direction:column; gap:9px; color:var(--text); font-size:14px;}
.plan ul li{display:flex; gap:8px; align-items:flex-start;}
.plan ul li .ic{width:15px; height:15px; color:var(--success); margin-top:4px;}
.plan .btn{margin-top:auto;}
/* FAQ */
.faq{max-width:760px; margin:0 auto; display:flex; flex-direction:column; gap:10px;}
.faq details{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:0 20px;}
.faq summary{cursor:pointer; font-weight:600; color:var(--heading); padding:16px 0; font-size:15px; list-style:none; display:flex; justify-content:space-between; align-items:center;}
.faq summary::after{content:'+'; color:var(--faint); font-size:18px;}
.faq details[open] summary::after{content:'';}
.faq details p{color:var(--muted); font-size:14px; margin:0 0 16px; line-height:1.8;}
/* CTA 横幅 */
.cta-banner{background:var(--mk-hero-grad); border-radius:var(--r-xl); padding:56px 32px; text-align:center; color:var(--on-accent);}
.cta-banner h2{color:var(--on-accent); font-size:var(--mk-h2); font-weight:800; margin-bottom:12px;}
.cta-banner p{color:var(--on-accent-faint); margin:0 0 26px;}
/* ---------- 页脚 ---------- */
.footer{background:var(--side-bg); color:var(--side-fg); padding:56px 0 30px; margin-top:84px;}
.footer a{color:var(--side-fg); text-decoration:none !important;}
.footer a:hover{color:#fff;}
.footer-grid{display:grid; grid-template-columns:2fr 1fr 1fr 1fr; gap:32px; max-width:var(--mk-maxw); margin:0 auto; padding:0 24px;}
.footer .fbrand{display:flex; align-items:center; gap:10px; color:#fff; font-weight:800; font-size:16px; margin-bottom:12px;}
.footer .fbrand img{width:28px; height:28px; border-radius:6px;}
.footer .fdesc{font-size:var(--fs-sm); line-height:1.8; max-width:26em;}
.footer h4{color:#fff; font-size:var(--fs-body); font-weight:700; margin:0 0 14px;}
.footer ul{list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:9px; font-size:var(--fs-sm);}
.footer-bottom{max-width:var(--mk-maxw); margin:40px auto 0; padding:22px 24px 0; border-top:1px solid rgba(255,255,255,.12); display:flex; justify-content:space-between; gap:12px; flex-wrap:wrap; font-size:var(--fs-xs); color:var(--side-fg);}
/* ---------- 响应式 ---------- */
@media (max-width:1024px){
.grid.c4{grid-template-columns:repeat(2,1fr);}
.plans,.plans.p4{grid-template-columns:repeat(2,1fr);}
.split{grid-template-columns:1fr;}
.footer-grid{grid-template-columns:1fr 1fr;}
}
@media (max-width:640px){
.plans,.plans.p4{grid-template-columns:1fr; max-width:440px; margin:0 auto;}
}
@media (max-width:720px){
.nav-links,.nav-cta .btn.ghost{display:none;}
.nav-burger{display:block;}
.grid.c4,.grid.c3,.grid.c2{grid-template-columns:1fr;}
.section{padding:56px 0;}
.hero{padding-top:56px;}
.flow{flex-direction:column; align-items:center;}
.flow .arrow{transform:rotate(90deg); padding:6px 0;}
.flow .step{width:100%; max-width:320px;}
}
/* 五端网格 */
.grid.plat5{grid-template-columns:repeat(5,1fr);}
@media (max-width:1024px){ .grid.plat5{grid-template-columns:repeat(3,1fr);} }
@media (max-width:720px){ .grid.plat5{grid-template-columns:repeat(2,1fr);} }
/* ---------- Auth 两栏卡片(镜像 App login.html/register.html 版式) ---------- */
.auth-wrap{min-height:calc(100vh - 60px); display:flex; align-items:center; justify-content:center; padding:40px 24px; background:var(--page);}
.auth{display:grid; grid-template-columns:minmax(0,1fr) 420px; width:100%; max-width:880px; background:var(--surface); border:1px solid var(--border); border-radius:var(--r-xl); overflow:hidden; box-shadow:var(--sh-3);}
.auth.reg{grid-template-columns:minmax(0,380px) minmax(0,1fr); max-width:940px;}
.auth-brand{background:var(--mk-hero-grad); color:var(--side-active-fg); padding:48px 40px; display:flex; flex-direction:column; min-height:480px;}
.auth-brand .mk{width:64px; height:64px; border-radius:15px;}
.auth-brand .bname{font-size:var(--fs-display); font-weight:800; letter-spacing:1px; margin:24px 0 8px; color:var(--side-active-fg);}
.auth-brand .btag{font-size:var(--fs-body); color:var(--side-fg); line-height:1.7; max-width:280px;}
.auth-brand .pts{margin-top:auto; display:flex; flex-direction:column; gap:14px;}
.auth-brand .pt{display:flex; align-items:center; gap:11px; font-size:var(--fs-sm); color:var(--side-fg);}
.auth-brand .pt .pi{width:30px; height:30px; border-radius:var(--r-md); background:var(--hero-glass); display:flex; align-items:center; justify-content:center; flex:none; color:var(--side-active-fg);}
.auth-brand .pt .ic{width:16px; height:16px;}
.auth-brand .steps{margin-top:36px; display:flex; flex-direction:column;}
.auth-brand .step{display:flex; gap:13px; position:relative; padding-bottom:22px;}
.auth-brand .step:last-child{padding-bottom:0;}
.auth-brand .step:not(:last-child)::before{content:''; position:absolute; left:14px; top:30px; bottom:0; width:2px; background:var(--hero-glass);}
.auth-brand .step .si{width:29px; height:29px; border-radius:50%; background:var(--hero-glass); color:var(--side-active-fg); display:flex; align-items:center; justify-content:center; font-weight:700; font-size:var(--fs-body); flex:none; z-index:1;}
.auth-brand .step.done .si{background:var(--side-active-fg); color:var(--side-active-bg);}
.auth-brand .step .st{font-size:var(--fs-sm); line-height:1.45; color:var(--side-fg);}
.auth-brand .step .st b{display:block; color:var(--side-active-fg); font-weight:600; font-size:var(--fs-body);}
.auth-form{padding:44px 40px; display:flex; flex-direction:column; justify-content:center;}
.auth-form h1{font-size:var(--fs-h1); color:var(--heading); font-weight:700; margin:0 0 6px;}
.auth-form .alead{font-size:var(--fs-sm); color:var(--muted); margin:0 0 24px;}
.field{margin-bottom:14px;}
.field label{display:block; font-size:var(--fs-sm); color:var(--muted); margin-bottom:6px;}
.field label .req{color:var(--danger); margin-left:2px;}
.input{height:38px; width:100%; background:var(--surface); border:1px solid var(--border); border-radius:var(--r-md); padding:0 11px; font-size:var(--fs-body); color:var(--text); outline:none; font-family:var(--font);}
.input:focus{border-color:var(--primary); box-shadow:0 0 0 3px var(--brand50);}
.input::placeholder{color:var(--faint);}
textarea.input{height:auto; min-height:64px; padding:9px 11px; resize:vertical;}
.grid2f{display:grid; grid-template-columns:1fr 1fr; gap:14px;}
.field .hint{font-size:var(--fs-xs); color:var(--faint); margin-top:5px;}
.field .ferr{font-size:var(--fs-xs); color:var(--danger); margin-top:5px; display:none;}
.field.error .input{border-color:var(--danger);}
.field.error .ferr{display:block;}
.auth-form .btn.primary{width:100%; justify-content:center;}
.auth-foot{margin-top:20px; text-align:center; font-size:var(--fs-sm); color:var(--muted);}
.auth-alert{display:none; background:var(--danger-bg); border:1px solid var(--danger); color:var(--danger); border-radius:var(--r-md); padding:10px 12px; font-size:var(--fs-body); margin-bottom:14px;}
.auth-alert.show{display:block;}
.auth-ok{background:var(--success-bg); border:1px solid var(--success); color:var(--success); border-radius:var(--r-md); padding:14px; font-size:var(--fs-body); margin-bottom:14px;}
.auth-ok code{font-family:var(--font-mono); font-weight:700; font-size:17px;}
@media (max-width:860px){ .auth,.auth.reg{grid-template-columns:1fr; max-width:480px;} .auth-brand{min-height:auto; padding:34px 32px;} .auth-brand .pts,.auth-brand .steps{display:none;} .auth-form{padding:32px 28px;} .grid2f{grid-template-columns:1fr;} }
/* ---------- Checkout(确认订单,仿原型 screens/checkout.html ---------- */
.crumb{display:flex; align-items:center; gap:7px; font-size:var(--fs-sm); color:var(--muted); margin-bottom:12px;}
.crumb a{color:var(--muted); text-decoration:none !important;}
.crumb a:hover{color:var(--primary);}
.co-title{font-size:var(--fs-h1); font-weight:700; color:var(--heading); margin:0 0 20px;}
.checkout{display:grid; grid-template-columns:minmax(0,1fr) 360px; gap:22px; align-items:start; max-width:1020px;}
.co-card{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:20px 22px; margin-bottom:18px;}
.co-card h2{margin:0 0 14px; font-size:var(--fs-h2); color:var(--heading);}
.seg{display:inline-flex; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-md); padding:3px; gap:3px; margin-bottom:6px;}
.seg-btn{border:none; background:transparent; padding:8px 18px; border-radius:calc(var(--r-md) - 3px); font-size:var(--fs-body); color:var(--muted); cursor:pointer; font-family:var(--font); display:inline-flex; align-items:center; gap:7px;}
.seg-btn.on{background:var(--surface); color:var(--heading); font-weight:600; box-shadow:var(--sh-1);}
.seg-tip{font-size:var(--fs-xs); color:var(--success); background:var(--ok-soft); padding:1px 8px; border-radius:var(--r-pill); font-weight:600;}
.seg-label{font-size:var(--fs-sm); color:var(--muted); margin:14px 0 8px;}
.co-feats{list-style:none; padding:0; margin:12px 0 4px; display:flex; flex-direction:column; gap:8px; font-size:14px; color:var(--text);}
.co-feats li{display:flex; gap:8px; align-items:flex-start;}
.co-feats li .ic{width:15px; height:15px; color:var(--success); margin-top:3px; flex:none;}
.co-item{display:flex; align-items:center; gap:14px; padding:4px 0;}
.co-item .co-ic{width:46px; height:46px; border-radius:var(--r-md); background:var(--brand50); color:var(--primary); display:flex; align-items:center; justify-content:center; flex:none;}
.co-item .co-ic .ic{width:24px; height:24px;}
.co-item .co-meta b{display:block; font-size:var(--fs-body); color:var(--heading); font-weight:600;}
.co-item .co-meta span{font-size:var(--fs-sm); color:var(--muted);}
.co-item .co-amt{margin-left:auto; font-size:18px; font-weight:700; color:var(--heading); font-family:var(--font-mono);}
.co-pay{display:flex; flex-direction:column; gap:10px;}
.co-pay .pm{display:flex; align-items:center; gap:12px; border:1.5px solid var(--border); border-radius:var(--r-md); padding:13px 15px;}
.co-pay .pm.on{border-color:var(--primary); box-shadow:0 0 0 3px var(--brand50);}
.co-pay .pm.off{opacity:.62;}
.co-pay .pic{width:34px; height:34px; border-radius:8px; display:flex; align-items:center; justify-content:center; flex:none; color:#fff;}
.co-pay .pic .ic{width:19px; height:19px;}
.co-pay .nm b{display:block; font-size:var(--fs-body); color:var(--heading); font-weight:600;}
.co-pay .nm span{font-size:var(--fs-xs); color:var(--muted);}
.co-pay .radio{margin-left:auto; width:18px; height:18px; border-radius:50%; border:2px solid var(--border); flex:none; position:relative;}
.co-pay .pm.on .radio{border-color:var(--primary);}
.co-pay .pm.on .radio::after{content:''; position:absolute; inset:3px; border-radius:50%; background:var(--primary);}
.co-summary{position:sticky; top:84px; margin-bottom:0;}
.sum-row{display:flex; justify-content:space-between; font-size:var(--fs-body); color:var(--text); padding:7px 0;}
.sum-row.muted{color:var(--muted); font-size:var(--fs-sm);}
.sum-div{height:1px; background:var(--border-subtle); margin:8px 0;}
.sum-total{display:flex; justify-content:space-between; align-items:baseline; padding-top:6px;}
.sum-total .lab{font-size:var(--fs-body); color:var(--heading); font-weight:600;}
.sum-total .val{font-size:var(--fs-display); font-weight:800; color:var(--primary); font-family:var(--font-mono); letter-spacing:-.5px;}
.co-summary .fine{font-size:var(--fs-xs); color:var(--faint); text-align:center; margin-top:12px; line-height:1.6;}
/* ---------- 支付结果页 ---------- */
.result{text-align:center; max-width:520px; margin:0 auto; padding:24px 16px;}
.result .ring{width:72px; height:72px; border-radius:50%; display:flex; align-items:center; justify-content:center; margin:0 auto 18px;}
.result .ring .ic{width:36px; height:36px;}
.result .ring.good{background:var(--ok-soft); color:var(--success);}
.result .ring.wait{background:var(--brand50); color:var(--primary);}
.result h2{margin:0 0 10px; font-size:var(--fs-h1); color:var(--heading);}
.result p{margin:0 0 22px; font-size:var(--fs-body); color:var(--muted); line-height:1.8;}
.result .ono{display:inline-flex; align-items:center; gap:8px; font-family:var(--font-mono); font-size:var(--fs-body); background:var(--bg); border:1px dashed var(--border); border-radius:var(--r-md); padding:10px 16px; color:var(--heading); margin-bottom:22px;}
.result .acts{display:flex; gap:10px; justify-content:center; flex-wrap:wrap;}
@media (max-width:900px){
.checkout{grid-template-columns:1fr;}
.co-summary{position:static;}
}
+54
View File
@@ -0,0 +1,54 @@
/* ============================================================
岩美酒库官网 · 令牌层
单一真相源:.superpowers/prototype/tokens.css 主题 A(经典蓝)1:1 拷贝
+ 「营销扩展」段(仅官网用的大字号/容器宽,App 无此场景)。
规则:页面与组件只引用 var(--token),禁硬编码 hex。
同步纪律:App 原型 token 变更时,此文件对应值需同步。
============================================================ */
:root{
/* —— 与原型 :root 完全一致 —— */
--sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px; --sp-8:32px; --sp-10:40px;
--r-sm:4px; --r-md:6px; --r-lg:10px; --r-xl:14px; --r-pill:999px;
--fs-display:28px; --fs-h1:21px; --fs-h2:17px; --fs-title:15px; --fs-body:13px; --fs-sm:12px; --fs-xs:11px;
--font:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei","Inter",sans-serif;
--font-mono:"JetBrains Mono","SF Mono",ui-monospace,monospace;
--sh-1:0 1px 3px var(--shadow); --sh-2:0 4px 14px var(--shadow); --sh-3:0 18px 50px var(--shadow);
--scrim:rgba(15,20,30,.42); --hero-glass:rgba(255,255,255,.16); --hero-glass-strong:rgba(255,255,255,.24);
--brand-deep:#0F3057; --brand-mid:#1B4F8E; --brand-wine:#8B2331; --brand-dot:#C97B86;
--on-accent:#FFFFFF; --on-accent-faint:rgba(255,255,255,.72);
--toast-bg:#1F2330;
/* —— 主题 A(经典蓝)—— */
--page:#E7EBF1; --bg:#F5F7FA; --surface:#FFFFFF; --border:#DCE2EB; --border-subtle:#ECEFF4;
--text:#232934; --heading:#1B2430; --title:#1B2430; --muted:#6E7888; --faint:#99A3B3;
--primary:#2563AC; --primary-dark:#154072; --on-primary:#FFFFFF; --brand400:#4F86C6; --brand50:#EEF4FB; --accent:#8B2331;
--success:#2E8B57; --danger:#D14343; --warn:#E08E00;
--info-soft:#EEF4FB; --ok-soft:#E8F5EE; --accent-soft:#FAEEF0;
--success-bg:#E8F5EE; --danger-bg:#FDECEC; --warn-bg:#FFF4DB;
--th-bg:#F7F9FC; --row-hover:#EEF4FB; --shadow:rgba(20,40,70,.20);
--side-bg:#163A6C; --side-fg:#C4D6ED; --side-active-bg:#2C6BB3; --side-active-fg:#FFFFFF;
/* —— 营销扩展(官网独有场景;App 端无对应 token)—— */
--mk-display:clamp(34px, 5vw, 54px); /* hero 大标题 */
--mk-h2:clamp(24px, 3.2vw, 34px); /* 区块标题 */
--mk-h3:19px;
--mk-lead:clamp(15px, 1.6vw, 18px); /* hero/区块引言 */
--mk-body:15px; /* 营销正文(App fs-body 13 在长文场景过小) */
--mk-maxw:1120px; /* 内容容器宽 */
--mk-hero-grad:linear-gradient(150deg, var(--side-bg), var(--side-active-bg)); /* 与登录页品牌面板同源 */
}
/* ---------- 兼容别名层:旧 color.css 词汇 → 真相源 token ----------
仅供 docs.css / legal.css 等二级页面样式过渡使用;新样式禁止引用这些别名。 */
:root{
--brand-50:var(--brand50); --brand-100:#DDE9F7; --brand-200:#C3D8F0; --brand-300:#9FC0E4;
--brand-400:var(--brand400); --brand-500:var(--primary); --brand-600:#1B4F8E; --brand-700:var(--primary-dark); --brand-900:#0A1F3B;
--gray-0:#FFFFFF; --gray-25:var(--bg); --gray-50:#EEF1F5; --gray-100:#E3E7ED; --gray-300:#C6CDD8;
--gray-500:var(--faint); --gray-700:var(--muted); --gray-800:var(--text); --gray-900:var(--heading);
--fg-default:var(--text); --fg-muted:var(--muted); --fg-subtle:var(--faint); --fg-link:var(--primary);
--border-default:var(--border); --info-50:var(--info-soft); --info-500:#2F7BD0; --info-700:#1D5A9E;
--radius-sm:var(--r-sm); --radius-md:var(--r-md); --radius-lg:var(--r-lg);
--text-xs:12px; --text-sm:13px; --text-md:15px; --text-lg:17px; --text-xl:19px; --text-2xl:24px;
--duration-fast:.15s; --ease-standard:ease; --ring-focus:0 0 0 3px var(--brand50);
}
+147
View File
@@ -0,0 +1,147 @@
---
layout: base.njk
title: 确认订单
description: 选择套餐与计费周期,在线支付开通或续费门店授权。
permalink: /checkout/
---
{# 官网内购买/续费(需登录):套餐+周期联动汇总 → POST /api/v1/license/purchase
拿 pay_url 跳收银台(契约 pay-contract v1.0.0)。版式仿原型 screens/checkout.html。 #}
<div class="container" style="padding:40px 24px 72px">
<div class="crumb"><a href="/#pricing">购买授权</a><span>/</span><span>确认订单</span></div>
<h1 class="co-title">确认订单</h1>
<div class="checkout">
<div>
<div class="co-card">
<h2>选择套餐</h2>
<div class="seg" id="planSeg">
<button class="seg-btn on" data-v="standard">标准版</button>
<button class="seg-btn" data-v="pro">高级版</button>
</div>
<ul class="co-feats" id="planFeats"></ul>
<div class="seg-label">计费周期</div>
<div class="seg" id="cycleSeg">
<button class="seg-btn on" data-v="annual">年付 <span class="seg-tip">省两个月</span></button>
<button class="seg-btn" data-v="monthly">月付</button>
</div>
</div>
<div class="co-card">
<h2>订单内容</h2>
<div class="co-item">
<div class="co-ic"><svg class="ic"><use href="#i-ic07"/></svg></div>
<div class="co-meta"><b id="itName">标准版授权</b><span id="itDur">365 天授权 · 到期时间自动叠加</span></div>
<div class="co-amt" id="itAmt">¥2,999</div>
</div>
</div>
<div class="co-card">
<h2>付款方式</h2>
<div class="co-pay">
<div class="pm on">
<div class="pic" style="background:#1677FF"><svg class="ic"><use href="#i-ic08"/></svg></div>
<div class="nm"><b>支付宝</b><span>电脑扫码 / 手机拉起支付宝,即时到账</span></div>
<div class="radio"></div>
</div>
<div class="pm off">
<div class="pic" style="background:#8A94A6"><svg class="ic"><use href="#i-ic11"/></svg></div>
<div class="nm"><b>对公转账</b><span>需要合同与对公收款请<a href="mailto:{{ site.support.email }}">联系我们</a></span></div>
</div>
</div>
</div>
</div>
<div class="co-card co-summary">
<h2>费用汇总</h2>
<div class="sum-row"><span id="sumName">标准版 · 年付</span><span id="sumBase">¥2,999</span></div>
<div class="sum-row muted"><span>优惠</span><span> ¥0</span></div>
<div class="sum-div"></div>
<div class="sum-total"><span class="lab">应付总额</span><span class="val" id="sumTotal">¥2,999</span></div>
<div class="auth-alert" id="coErr" style="margin-top:12px"></div>
<button class="btn primary lg" id="coBtn" style="width:100%;margin-top:16px">提交订单</button>
<div class="fine">提交后跳转支付宝收银台,支付成功即时续期到当前门店。时长在现有到期时间上叠加,不覆盖。</div>
</div>
</div>
</div>
<script>
(function () {
var API = '{{ site.appBaseUrl }}';
function $(id) { return document.getElementById(id); }
function token() {
var raw = localStorage.getItem('flutter.access_token');
if (!raw) return null;
try { return JSON.parse(raw); } catch (e) { return raw; }
}
function gotoLogin() { location.href = '/login/?next=' + encodeURIComponent('/checkout/' + location.search); }
if (!token()) { gotoLogin(); return; }
/* 套餐目录:价格仅前端展示,实付金额以 pay 套餐表为准(下单响应回传) */
var PLANS = {
standard: {
name: '标准版',
feats: ['单门店 · 单仓库', '2 台客户端同时使用', '1,000 张商品图片分享'],
monthly: { biz: 'monthly_standard', price: 299 },
annual: { biz: 'annual_standard', price: 2999 },
},
pro: {
name: '高级版',
feats: ['单门店 · 多仓库', '5 台客户端同时使用', '10,000 张商品图片分享', '免费 AI 周度 / 月度商业数据分析'],
monthly: { biz: 'monthly_pro', price: 599 },
annual: { biz: 'annual_pro', price: 5999 },
},
};
var q = new URLSearchParams(location.search);
var plan = PLANS[q.get('plan')] ? q.get('plan') : 'standard';
var cycle = 'annual';
function fmt(n) { return '¥' + n.toLocaleString('zh-CN'); }
function render() {
var p = PLANS[plan], c = p[cycle];
var days = cycle === 'annual' ? 365 : 30;
$('planFeats').innerHTML = p.feats.map(function (f) {
return '<li><svg class="ic"><use href="#i-check"/></svg>' + f + '</li>';
}).join('');
$('itName').textContent = p.name + '授权 · ' + (cycle === 'annual' ? '年付' : '月付');
$('itDur').textContent = days + ' 天授权 · 到期时间自动叠加';
$('itAmt').textContent = fmt(c.price);
$('sumName').textContent = p.name + ' · ' + (cycle === 'annual' ? '年付' : '月付');
$('sumBase').textContent = fmt(c.price);
$('sumTotal').textContent = fmt(c.price);
document.querySelectorAll('#planSeg .seg-btn').forEach(function (b) { b.classList.toggle('on', b.dataset.v === plan); });
document.querySelectorAll('#cycleSeg .seg-btn').forEach(function (b) { b.classList.toggle('on', b.dataset.v === cycle); });
}
document.querySelectorAll('#planSeg .seg-btn').forEach(function (b) {
b.addEventListener('click', function () { plan = b.dataset.v; render(); });
});
document.querySelectorAll('#cycleSeg .seg-btn').forEach(function (b) {
b.addEventListener('click', function () { cycle = b.dataset.v; render(); });
});
render();
function showErr(msg) { var el = $('coErr'); el.textContent = msg; el.classList.add('show'); }
$('coBtn').addEventListener('click', function () {
$('coErr').classList.remove('show');
var btn = $('coBtn'); btn.disabled = true; btn.textContent = '正在创建订单…';
fetch(API + '/api/v1/license/purchase', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token() },
body: JSON.stringify({ biz_code: PLANS[plan][cycle].biz }),
})
.then(function (r) {
if (r.status === 401) { gotoLogin(); throw new Error('登录已过期'); }
return r.json().then(function (j) { return { ok: r.ok, j: j }; });
})
.then(function (res) {
if (!res.ok) throw new Error((res.j && res.j.error) || '下单失败,请稍后重试');
var d = res.j.data || {};
if (!d.pay_url) throw new Error('下单响应异常');
location.href = d.pay_url;
})
.catch(function (e) {
showErr(e && e.message ? e.message : '无法连接服务器,请稍后重试');
btn.disabled = false; btn.textContent = '提交订单';
});
});
})();
</script>
+106 -428
View File
@@ -1,487 +1,165 @@
# 岩美酒库管理系统 · 使用手册
# 岩美酒库 · 使用手册
> 适用版本:v1.x · 受众:酒行管理员及工作人员
> 适用版本:客户端 v1.1.1 · 受众:酒行店员 / 店长
---
## 1. 系统概述
## 1. 快速上手
岩美酒库管理系统专为酒行门店设计,将入库、出库、库存查询、财务往来等日常业务整合到一个平台,支持多人协同操作,数据实时同步
**注册新门店**:登录页点「注册新门店」,三步开通——①填门店信息(编号自动分配)②创建管理员账号(密码至少 6 位)③激活授权。新门店自带 **30 天免费试用**,有兑换券登录后在「系统设置 → 授权兑换券」输入短码续期
**核心功能一览**
**登录**:输入门店编号、登录账号、密码。「记住我」会自动填入最近登录的门店和账号;账号框带历史下拉,多人共用电脑时点箭头切换。忘记密码请找本店管理员在「用户管理」中重置。
| 模块 | 主要用途 |
|------|---------|
| 入库管理 | 新建入库单、提交审核、审批通过后自动更新库存 |
| 出库管理 | 新建出库单、提交审核、审批时自动扣减库存 |
| 库存管理 | 查询实时库存、库存盘点、打印标签 |
| 财务管理 | 查看应付款(欠供应商)和应收款(客户欠款)、标记结清 |
| 往来单位 | 管理供应商和客户的基本信息 |
| 基础数据 | 维护商品名称、系列、规格字典 |
| 系统设置 | 用户管理、仓库管理、编号规则、数据导入 |
**五端支持**
**支持平台**:Web 浏览器(现已上线);Windows、macOS、iOS、Android 客户端即将推出。
---
## 2. 登录与退出
### 2.1 登录
1. 打开系统,进入登录页面。
2. 在「门店编号」栏输入酒行的门店代号(例如 `S001`)。
3. 输入「用户名」和「密码」。
4. 点击「登录」按钮。
登录成功后,系统自动跳转到「入库管理」页面。
**提示**:系统会记录最近登录过的用户名,点击输入框可从候选列表中快速选择。
### 2.2 退出登录
点击顶部右侧用户名旁的下拉箭头,选择「退出登录」。
### 2.3 查看当前登录信息
点击顶部左侧「岩美」标题或右侧「门店编号」区域,弹出门店信息面板,显示门店编号、登录账号、姓名、系统版本。
---
## 3. 界面说明
系统主界面由三个区域组成:
```
┌──────────────────────────────────────────────────────────┐
│ 顶部导航栏(标题、门店编号、用户名、退出) │
├──────────┬───────────────────────────────────────────────┤
│ │ │
│ 左侧 │ 主内容区域 │
│ 侧边栏 │ │
│ │ │
├──────────┴───────────────────────────────────────────────┤
│ 状态栏(门店、用户、登录时间、当前时间、连接状态) │
└──────────────────────────────────────────────────────────┘
```
**左侧侧边栏导航项**
| 导航项 | 功能 |
|--------|------|
| 入库管理 | 入库单录入与审核 |
| 出库管理 | 出库单录入与审核 |
| 库存管理 | 库存查询与盘点 |
| 财务管理 | 应付款和应收款 |
| 往来单位 | 供应商与客户 |
| 基础数据 | 商品名称、系列、规格字典 |
| 系统设置 | 用户、仓库、编号规则等配置 |
点击侧边栏顶部的菜单图标可收起或展开侧边栏,节省屏幕空间。
**更新提示**:有新版本时,内容区顶部会显示黄色提示横幅;如果是必须更新的版本,会弹出对话框要求立即更新。
**离线提示**:网络断开时,顶部显示红色横幅「网络连接已断开」,状态栏变红,此时展示上次加载的缓存数据。
---
## 4. 角色与权限
系统有四种角色,权限从高到低依次为:
| 角色 | 说明 | 可执行操作 |
|------|------|------------|
| 超级管理员 | 系统最高权限账号 | 全部操作,含数据清空 |
| 管理员 | 门店管理人员 | 全部操作,含用户管理、酒行信息编辑 |
| 操作员 | 日常录入审核人员 | 新建、编辑、提交、审核单据,查询数据 |
| 只读 | 仅查看数据 | 只能查看,不能新建、修改、删除任何数据 |
**权限说明**
- 只读账号点击任何写操作按钮时,系统将直接提示「无权限」。
- 新增或编辑用户、重置密码仅管理员可操作。
- 修改酒行基本信息仅管理员可操作。
- 数据清空仅超级管理员可操作。
---
## 5. 入库管理
入库管理页面包含两个标签页:**入库审核**(新建和待审核)和**入库单**(已完成记录)。
### 5.1 入库单状态说明
单据状态按以下流程流转:
**草稿****待审核****已审批**(库存增加)
待审核阶段也可被拒绝,变为**已拒绝**状态(库存不变)。
| 状态 | 含义 |
| 平台 | 说明 |
|------|------|
| 草稿 | 已保存但未提交,可继续编辑或删除 |
| 待审核 | 已提交等待审批,不可再编辑 |
| 已审批 | 审批通过,库存已增加,同时生成应付账款记录 |
| 已拒绝 | 审批不通过,库存不变 |
| Windows / macOS | 官网下载安装包(macOS 已通过 Apple 公证) |
| Web 网页版 | 浏览器直接打开,免安装 |
| Android | 官网下载 APK |
| iOS | 通过 TestFlight 安装 |
### 5.2 新建入库单
同一账号可多端同时登录,数据实时同步。
1. 点击左侧「入库管理」,切换到「入库审核」标签页
2. 点击右上角「新建入库审核单」按钮。
3. 填写入库基本信息:
- **仓库**(必填):选择货物入库的目标仓库。
- **供应商**(必填):选择供货的往来单位。
- **入库日期**(必填):默认为今天,可修改。
- **备注**(选填):填写本批次入库说明。
4. 在商品明细区域填写每一行商品:
- **商品名称**(必填):从字典中选择或直接输入。
- **系列**(必填):选择商品系列。
- **规格**(必填):选择商品规格,如 500ml×6。
- **生产日期**(必填):点击日历图标选择。
- **批次号**(选填):填写批次编号,用于后续追踪查询。
- **数量**(必填):填写入库数量。
- **单价**(必填):填写进货单价,系统自动计算总金额。
5. 点击「添加商品」可继续增加商品行,点击行末「删除」可移除该行。
6. 完成后选择操作:
- **保存草稿**:保存后可以继续修改。
- **保存并提交审核**:提交后进入待审核状态,不可再编辑。
### 5.3 提交审核
草稿状态的入库单,在列表中点击「提交」按钮,确认后单据进入「待审核」状态。
### 5.4 审批入库单
操作员及以上权限的用户均可审批。
1. 在「入库审核」标签页中找到待审核的入库单。
2. 点击「通过」:弹窗确认,确认后系统自动将商品入库,库存增加,同时生成一笔应付账款记录。
3. 点击「拒绝」:弹窗确认,拒绝后库存不变,单据进入「已拒绝」状态。
**注意**:审批通过后操作不可撤销,请仔细核对商品数量和单价后再确认。
### 5.5 查看入库单详情
点击入库单号(蓝色链接)可弹出详情页,显示完整的商品明细、数量、金额及审核信息。详情页右上角有打印按钮,可直接打印入库单。
### 5.6 打印入库单
在列表中点击对应行的「打印」按钮,直接调用打印机打印入库单据。
### 5.7 打印商品标签
1. 在列表中点击对应行的「打标签」按钮。
2. 弹出商品标签打印对话框,列出本次入库的所有商品。
3. 勾选需要打印标签的商品(默认全选)。
4. 点击「打印选中」,系统逐张打印选中商品的标签。
标签内容包括:商品名称、编码、系列、规格、批次号、生产日期、酒行名称及联系方式、二维码。
### 5.8 标记货款结清
审批通过的入库单会自动生成应付账款记录。货款付清后,在列表中点击「结清」按钮,确认后将对应财务记录标记为已结清。
### 5.9 筛选与导出
- **仓库筛选**:点击列头「仓库」的筛选图标,选择仓库进行过滤。
- **供应商筛选**:点击列头「供应商」的筛选图标,选择供应商进行过滤。
- **日期筛选**:点击「选择日期」按钮,选择日期范围。
- **导出**:点击「导出」按钮,将当前列表导出为 Excel 文件。
**主题切换**:点右上角的小衣服图标,三套配色随意换——A 经典蓝(浅色·默认)、B 琥珀(深色)、C 酒窖(暖浅色),也可在「系统设置 → 偏好设置」里选
---
## 6. 出库管理
## 2. 核心概念(必读)
出库管理与入库管理操作流程类似,包含**出库审核**(待处理)和**出库单**(已完成)两个标签页
**每瓶酒 = 一个独立编号**。系统按「件」记账:入库每录一行,就生成一个新的商品编号和专属二维码标签。字段含义:名称=品牌(茅台)、系列=型号(飞天 53°)、规格=版本(500ml)。库存里同一款酒出现多行是正常的——每行是不同批次的货,各自可追溯
### 6.1 出库单状态说明
**审核流**:草稿 → 提交 → 待审核 → 审核通过(此刻才增减库存、生成账款)/ 拒绝(什么都不变)。待审核的单可「撤回」退回草稿修改;已审核的单只能走「退单」。
出库单状态流转与入库单相同:草稿 → 待审核 → 已审批或已拒绝。
**四级角色**
审批通过后:
- 系统会先检查库存是否充足,库存不足时审批失败并给出提示,库存不变。
- 库存充足时自动扣减库存,并生成应收账款记录。
| 角色 | 权限要点 |
|------|---------|
| 超级管理员 | 全部操作 |
| 管理员 | 全部业务 + 用户管理;**可见成本价与利润** |
| 操作员 | 录单、审核、查询;看不到成本和利润,只能撤回自己的单 |
| 只读 | 只能查看,所有操作按钮隐藏 |
### 6.2 新建出库单
1. 点击左侧「出库管理」,切换到「出库审核」标签页。
2. 点击右上角「新建出库审核单」按钮。
3. 填写基本信息:
- **仓库**(必填):选择出货仓库。
- **客户**(必填):选择购买方(往来单位中的客户类型)。
- **出库日期**(必填):默认今天,可修改。
4. 填写商品明细:商品名称、系列、规格、数量、单价(操作方式同入库单)。
5. 保存草稿或保存并提交审核。
**注意**:出库数量不能超过当前库存,审批时系统会自动检查,库存不足会提示失败。
### 6.3 审批出库单
操作步骤与入库审批相同。审批通过后库存自动扣减,同时生成应收账款记录。
### 6.4 打印出库单
在列表中点击「打印」按钮,打印出库单据。
### 6.5 标记货款结清
出库单审批通过后生成应收账款。收到货款后,在列表中点击「结清」按钮,将对应财务记录标记为已结清。
成本与利润由服务器按角色控制,操作员端不下发数字。
---
## 7. 库管理
## 3. 库管理
### 7.1 查询库存
顶部四张统计卡:近 30 天笔数 / 金额、待审核单数、草稿单数——后两张**点击即筛选列表**。
点击左侧「库存管理」进入库存列表
**新建入库单**:选仓库、供应商、日期 → 明细表格逐行录(一行 = 一件独立编号商品):名称/系列/规格可搜索、没有的直接新增;生产日期滚轮选择;**批次号必填**;**进价(单瓶)可留空 = 待定价**;**参考售价**会存入商品档案、出库时自动带出;总进价自动合计。键盘:`Enter` 跳格、`⌘D / Ctrl+D` 复制上一行、`⌘Enter / Ctrl+Enter` 提交
列表显示每个商品的实时库存数量、所在仓库、单价、金额、生产日期、批次等信息
**审核**:通过 → 库存增加、生成应付账款;拒绝 → 不变。审核前核对好数量和进价
- **搜索**:在搜索框输入商品名称关键字(也支持拼音或拼音首字母),实时过滤结果
- **仓库筛选**:点击列头「仓库」的筛选图标,按仓库查看库存。
- **导出**:点击「导出」按钮,将当前库存列表导出为 Excel 文件。
**退单**(管理员):已审核单点红色「退单」,逐行退或全部退;库存删除、冲减应付,列表显示「部分退单 / 已退单」徽章
### 7.2 修改备注
**确认进价**:待定价的单在详情点「确认进价」补真实进价,成本与应付自动回填,售价/应收不受影响。
在库存列表中,点击「备注」列对应单元格(显示铅笔图标或现有备注内容),弹出编辑框,填写后点击「保存」即可
### 7.3 打印商品标签
在库存列表中点击某商品行的「打印标签」按钮,可单独打印该商品的标签。
### 7.4 库存盘点
1. 在库存管理页面切换到「库存盘点」标签页。
2. 选择要盘点的**仓库**,系统自动加载该仓库所有商品的账面库存数量。
3. 选择盘点类型(全盘)。
4. 逐行填写**实际盘点数量**(与账面不符时修改数量)。
5. 点击「提交盘点」完成。
盘点单编号由系统自动生成。
### 7.5 库存流水
「库存流水」标签页可查看每次库存变动记录,包括入库、出库的时间、变动数量等。
**找单**:搜索框支持单号、供应商、酒名(拼音);「详细搜索」可按商品编码、批次号、系列、规格、录单人、审核员、状态、日期区间等组合查询。打印带预览,针式打印机版式已优化
---
## 8. 财务管理
## 4. 出库管理
### 8.1 财务记录说明
**新建出库单**:选仓库、客户、日期 → 「添加商品」从库存选货(按编码/名称/系列搜索,分页加载)。选中后**数量自动填可用数量、售价自动带入库时的参考售价**。管理员可见每行实时利润 =(售价 − 进价)× 数量,底栏显示合计金额与合计利润。
系统在以下情况自动生成财务记录:
- 入库单审批通过 → 自动生成**应付账款**(欠供应商的货款)
- 出库单审批通过 → 自动生成**应收账款**(客户欠的货款)
**待定价出库**:售价可留空先发货,事后在详情点「确认售价」补填,自动补应收。
点击左侧「财务管理」进入财务页面,包含三个标签:
**详情**:管理员见成本价 / 售价 / 总售价 / 利润列;操作员只见售价和小计。
**审核**:出库数量不能超过库存,不足会拦下。通过后扣减库存、生成应收账款。
**退单**:客户退货点「退单」,商品加回库存、按售价冲减应收。可打印单据(带预览)和带二维码的商品标签。
---
## 5. 库存管理
库存按**批次维度**展示,同款酒不同批次分行。搜索支持商品名(汉字/全拼/首字母)和编码;「显示字段」可自定义列且会被记住。
主要列:商品/系列/规格/批次号、库存、成本价与总价(=库存×成本,仅管理员)、生产日期、入库日期、供应商、状态(在售/预警/缺货,「缺货预警」卡点击即筛选)。
**商品编辑抽屉**:点击库存行打开——上传商品图片(双击全屏预览、可缩放翻页)、从介绍库选择商品介绍、查看成本价(单瓶)/总成本价。
**盘点**:库存盘点页选仓库(账面自动加载)→ 选全盘/循环盘点 → 填实盘数量 → 提交。
**标签与扫码分享**:每件商品唯一二维码,标签含品名、编码、批次、生产日期、店名和二维码;扫码打开商品详情公开页,链接可直接分享给顾客与同行。
---
## 6. 财务管理
页面自上而下:时间范围筛选 → KPI 四卡(本月销售、本月采购、应收、应付,带未结清笔数)→ 收支趋势图 → 应收/应付按往来单位汇总(点击行看该单位流水和近期单据)→ 收支流水表(按收款/付款/应收/应付筛选)。
账款来源:入库审核通过自动生成**应付**,出库审核通过自动生成**应收**。「登记收支」可手动记一笔收付款(类型 + 往来单位 + 金额 + 日期 + 备注)。
**结清**:流水行单笔结清,或在往来单位抽屉整体处理;入库/出库列表也有结清入口。结清不可撤销,操作前确认款项已到。
---
## 7. 往来单位
供应商和客户统一管理,录单时的供应商/客户下拉来源于此。列表含名称、类型徽章、联系人、电话、**应收、应付**,类型 chips 一键筛选,搜索支持拼音。
新建/编辑:名称必填;**期初余额**用于把老账本上的旧欠款带进系统。点击任意行打开详情抽屉:联系信息、应收应付余额、近期单据(含入库应付、出库应收、收付款),可打对账单。已关联单据的单位不可删除。
---
## 8. 基础数据
五个标签页:
| 标签 | 内容 |
|------|------|
| 全部记录 | 所有财务流水 |
| 应付账款 | 需要向供应商付款的记录 |
| 应收账款 | 客户尚未付款的记录 |
| 商品档案 | 全店商品编号档案,由入库单自动建立(无手动新增),只查看和编辑 |
| 商品介绍 | 介绍库,供库存页商品编辑抽屉「从介绍库选择」 |
| 系列 | 型号/产品线字典 |
| 规格 | 版本/包装字典 |
| 仓库 | 仓库维护(默认仓库在「偏好设置」里选) |
### 8.2 查看财务记录
- 默认显示当前月份的记录,可通过月份选择器查看历史月份。
- 支持按「类型」和「往来单位」列头筛选。
- 点击「导出」可导出当月数据为 Excel 文件。
### 8.3 标记结清
**方式一**:在入库单或出库单列表中点击「结清」按钮,直接结清该单据对应的账款。
**方式二**:在财务管理列表中找到对应记录,点击「结清」按钮,单笔结清。
结清后该记录状态从「未结清」变为「已结清」。
### 8.4 财务汇总
财务管理页面显示当期应付和应收总额,便于掌握资金往来概况。
字典维护直接影响录单下拉候选;录单时输入新名字选「新增」也会自动进字典,这里主要做事后整理。
---
## 9. 往来单位
## 9. 设备与设置
往来单位管理**供应商**和**客户**两类信息,新建入库单时选择供应商,新建出库单时选择客户
**设备管理**:登录设备(本店所有在线会话,管理员可「强制下线」)、外设登记(打印机、扫码枪存档)、打印模板
### 9.1 查看往来单位
**系统设置**五个面板:
点击左侧「往来单位」,通过「供应商」和「客户」标签页分别查看。列表支持按名称搜索和导出。
| 面板 | 说明 |
|------|------|
| 门店信息 | 店名/地址/电话/负责人,管理员可编辑,编号不可改 |
| 用户管理 | 新增/编辑/重置密码/启停用户,仅管理员 |
| 编号规则 | 单号前缀与序号;序号切勿调小,会撞号 |
| 授权兑换券 | 输入 `JIUKU-XXXX-XXXX` 短码兑换续期,时长叠加;新店 30 天试用 |
| 偏好设置 | 主题切换 + 默认仓库 |
### 9.2 新建往来单位
授权过期:7 天内宽限照常用,7–15 天只读,满 15 天锁定;兑换新券立即恢复,数据不丢。
1. 在对应标签页点击右上角「新建」按钮
2. 填写信息:
- **名称**(必填)
- **联系电话**(选填)
- **地址**(选填)
- **卡号**(选填,如有账户绑定)
- **初始余额**(选填,导入历史数据时填写)
- **备注**(选填)
3. 点击「保存」。
### 9.3 编辑往来单位
在列表中点击「编辑」按钮,修改信息后保存。
### 9.4 删除往来单位
在列表中点击「删除」按钮,确认后删除。
**注意**:已关联入库单或出库单的往来单位不可删除。
「关于我们」页可查版本更新、授权状态、更新日志,并提交反馈
---
## 10. 基础数据(商品管理)
## 10. 常见问题
点击左侧「基础数据」,进入商品字典管理页面,包含三个标签:**商品名称**、**系列**、**规格**
**忘记密码?** 找管理员在「用户管理」重置;管理员本人忘记请联系技术支持
这些字典数据是新建入库或出库单时选择商品的基础,需要先在此处维护好商品信息
**单据录错了?** 待审核 → 「撤回」回草稿修改;已审核 → 「退单」退掉错的行再补单
### 10.1 商品名称管理
**库存对不上?** 记下商品编码,到入库/出库「详细搜索」按编码查所有相关单据核对,再做一次盘点调平。
商品名称是酒品的主名称,例如「茅台飞天 53°」
**同款酒库存好多行?** 设计如此:按件记账,每行是一个独立编号/批次,各自可追溯
1. 在「商品名称」标签页,点击「新建」按钮
2. 填写商品名称(必填)、编号(选填)、备注(选填)。
3. 点击「保存」。
**打印没反应/发糊?** 先升级到最新版;用「打印预览」确认;针打版式已做纯黑加粗优化
支持按名称或编号搜索,支持分页浏览,支持编辑和删除
**扫码打不开商品页?** 早期版本的扫码报错已修复;确认手机联网,很旧的标签可重打
### 10.2 系列管理
**授权到期变只读?** 「系统设置 → 授权兑换券」输入 JIUKU 兑换码立即恢复;过期 15 天内数据都在。
系列是商品所属的系列或品牌线,例如「飞天系列」「王子系列」。操作步骤与商品名称相同
**进价/售价还没谈好?** 价格留空即「待定价」,事后在单据详情「确认进价 / 确认售价」补填,账自动补齐
### 10.3 规格管理
**出库提示库存不足?** 数量超过在库数,检查是否录多,或进货入库单还没审核(未审核不算库存)。
规格用于描述包装规格,例如「500ml×6」「1000ml×1」。新建时可填写「单品数量」(即一箱包含的单瓶数量)
### 10.4 商品详情与二维码
每个商品都有唯一的二维码,可通过扫码访问商品公开信息页面。在商品详情页可查看或上传商品图片。
---
## 11. 系统设置
点击左侧「系统设置」,包含七个标签页。
### 11.1 酒行信息
显示本门店的基本信息:门店编号、门店名称、地址、联系电话、负责人。
**编辑**(仅管理员):点击「编辑信息」按钮,修改门店名称、地址、电话、负责人后保存。门店编号不可修改。
### 11.2 用户管理(仅管理员)
**查看用户**:显示本门店所有用户的姓名、用户名、角色、启用状态。
**新增用户**
1. 点击「新增用户」按钮。
2. 填写用户名(登录账号,不可重复)、姓名、手机号、初始密码。
3. 选择角色(操作员、管理员、只读、超级管理员)。
4. 点击「保存」。
**编辑用户**:点击用户行的「编辑」按钮,可修改姓名、手机号、角色(用户名不可修改)。
**重置密码**:点击「重置密码」按钮,输入新密码后确认,下次登录时使用新密码。
**启用或停用**:在用户列表中拨动状态开关。停用后该用户无法登录。
### 11.3 仓库管理
**查看仓库**:显示所有仓库名称、位置、是否为默认仓库。
**新建仓库**
1. 点击「新建」按钮。
2. 填写仓库名称(必填)、位置(选填)。
3. 可勾选「设为默认仓库」,新建单据时将自动选择此仓库。
4. 点击「保存」。
**编辑或删除**:点击对应按钮操作。已有库存的仓库不可删除。
### 11.4 编号规则
系统为每类单据自动生成唯一编号,格式由前缀、日期和序号组成,例如入库单编号 `RK20260523001`
**查看当前规则**:列表显示每种单据的前缀、当前序号及下一编号示例。
**修改规则**
1. 点击对应规则行的「编辑」按钮。
2. 可修改「前缀」和「当前序号」。
3. 点击「保存」,修改后对新建单据生效。
**注意**:请勿将序号调小至已使用过的范围,否则可能导致编号重复。
### 11.5 系统参数
配置系统的全局参数:
- **系统名称、货币单位、日期格式、时区**(通常无需修改)
- **入库单需要审核**:关闭后提交即视为通过,无需审批
- **出库单需要审核**:关闭后同上
- **允许超量出库**:开启后,出库数量超过库存时仍可审批通过
修改后点击「保存设置」生效,点击「重置默认」恢复出厂默认值。
### 11.6 数据导入
通过 Excel 文件批量导入历史数据。支持以下数据类型:往来单位、商品名称、商品系列、商品规格、商品编码、库存。
各类型的 Excel 格式要求详见系统内「系统设置 → 数据导入」页面说明。
**操作步骤**
1. 在对应数据类型行点击「选择文件」,选择 Excel(.xls 或 .xlsx)文件。
2. 全部文件选择完成后,点击「全部导入」按钮。
3. 系统依次处理每个文件并显示导入结果(新增、跳过重复、失败数量)。
4. 如有失败,根据错误提示修正文件后重新导入。
**导入规则**:以名称去重,已存在的记录不重复导入。导入过程中请勿切换页面。
**数据清空(仅超级管理员)**:在数据导入页面下方「危险操作」区域,选择要清空的数据表,点击确认后输入「确认清空」完成操作。**此操作不可撤销,请务必提前做好数据备份。**
### 11.7 关于
- **版本信息**:显示当前系统版本。有新版本时可点击「立即更新」。
- **授权信息**:显示授权类型(试用、月付、年付、买断)、授权状态、到期时间。授权剩余不足 30 天时会显示倒计时提醒。
- **意见反馈**:点击「反馈 Bug」或「功能建议」,通过邮件向开发团队反馈。
---
## 12. 常见问题
**Q:入库单提交后发现填错了,怎么办?**
单据提交(待审核状态)后不可再修改。请联系管理员将该单据拒绝,然后重新新建一张正确的入库单。
**Q:出库审批时提示「库存不足」,怎么处理?**
出库数量超过当前仓库库存时,系统会拒绝审批。请检查出库数量,或确认对应商品的入库单是否已审批通过、库存已更新。
**Q:结清了财务记录但发现结清错了,如何撤销?**
目前系统不支持撤销结清操作。如需处理,请联系技术支持。
**Q:如何查找某批商品是从哪张入库单入库的?**
在库存管理列表中查看商品的批次号,然后在入库管理中按批次号搜索对应的入库单。
**Q:导入数据时提示「格式错误」,怎么处理?**
请检查 Excel 文件的列顺序是否与「系统设置 → 数据导入」页面说明的格式一致,且第一行为数据行(无需填写表头)。如问题持续,可将文件发送给技术支持排查。
**Q:用户无法登录,怎么处理?**
请管理员在「系统设置 → 用户管理」中检查该用户是否处于启用状态,并使用「重置密码」功能为其设置新密码。
**Q:操作时提示「无权限」,是什么原因?**
当前账号角色为「只读」,不允许执行写操作。如需操作,请联系管理员调整账号角色。
**Q:系统显示「网络连接已断开」,数据还准确吗?**
网络断开时,系统展示上次加载的缓存数据,可能不是最新状态。请恢复网络连接后刷新页面获取最新数据。
**Q:如何联系技术支持?**
进入「系统设置 → 关于」查看联系邮箱和技术支持时间。工作日(周一至周五)9:00 - 18:00 可获得响应。
**员工离职/电脑丢失?** 「设备管理」强制下线该设备 + 「用户管理」停用账号或重置密码
+130 -456
View File
@@ -1,397 +1,113 @@
---
layout: base.njk
title: 下载
description: 下载岩美酒库管理系统Web 版现已上线,Windows / macOS / iOS / Android 客户端即将推出
description: 下载岩美酒库管理系统客户端:WindowsmacOS、Android、iOS 与免安装 Web 版
pageStyle: |
/* ── Page header ── */
.page-header {
padding: 72px 0 56px;
background:
radial-gradient(700px 360px at 80% 20%, rgba(37, 99, 172, 0.06), transparent 60%),
linear-gradient(180deg, #FBFCFD 0%, #FFFFFF 80%);
border-bottom: 1px solid var(--border-subtle);
}
.ph-inner {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 64px;
align-items: center;
}
.ph-eyebrow {
font-size: var(--text-xs); font-weight: 600;
letter-spacing: 0.12em; color: var(--brand-500);
text-transform: uppercase;
margin: 0 0 12px;
display: flex; align-items: center; gap: 8px;
}
.ph-eyebrow .version-badge {
background: var(--brand-500); color: #fff;
padding: 3px 8px; border-radius: var(--radius-sm);
font-family: var(--font-mono);
letter-spacing: 0.02em; font-size: 11px;
}
.ph-title {
font-size: 44px; font-weight: 700; line-height: 1.15;
letter-spacing: var(--tracking-cn-display);
color: var(--brand-900); margin: 0 0 18px;
}
.ph-lead {
font-size: var(--text-lg); color: var(--gray-600);
line-height: 1.6; margin: 0 0 24px; max-width: 480px;
}
.ph-meta {
display: flex; gap: 24px;
font-size: var(--text-sm); color: var(--fg-muted);
border-top: 1px solid var(--border-subtle);
padding-top: 16px;
}
.ph-meta-item { display: flex; flex-direction: column; gap: 2px; }
.ph-meta-label { font-size: 11px; color: var(--fg-subtle); letter-spacing: 0.04em; }
.ph-meta-val { font-size: var(--text-sm); color: var(--gray-800); font-weight: 500; }
/* ── Auto-detect card ── */
.detect-card {
background: var(--gray-0);
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
overflow: hidden;
}
.detect-card-head {
padding: 14px 20px;
background: var(--gray-50);
border-bottom: 1px solid var(--border-subtle);
font-size: var(--text-xs); color: var(--fg-muted);
display: flex; justify-content: space-between; align-items: center;
}
.detect-card-head .head-left { display: flex; align-items: center; gap: 6px; }
.detect-card-head .head-left svg { width: 14px; height: 14px; color: var(--success-500); }
.detect-card-body {
padding: 28px;
display: flex; gap: 24px; align-items: center;
}
.detect-icon {
width: 72px; height: 72px;
background: var(--brand-50); border-radius: 16px;
display: grid; place-items: center;
color: var(--brand-500); flex-shrink: 0;
}
.detect-icon svg { width: 38px; height: 38px; }
.detect-info { flex: 1; }
.detect-info h3 { margin: 0 0 4px; font-size: var(--text-xl); font-weight: 600; color: var(--brand-900); }
.detect-info p { margin: 0; font-size: var(--text-sm); color: var(--fg-muted); }
.detect-card-actions { padding: 0 28px 28px; display: flex; gap: 8px; }
.detect-card-actions .btn-primary { flex: 1; justify-content: center; }
/* ── Platform grid ── */
.dl-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;
margin-top: 32px;
}
.dl-card {
background: var(--gray-0);
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
padding: 20px;
display: flex; flex-direction: column;
transition: border-color var(--duration-base) var(--ease-standard),
box-shadow var(--duration-base) var(--ease-standard);
}
.dl-card:hover { border-color: var(--brand-300); box-shadow: var(--shadow-sm); }
.dl-card.detected { border-color: var(--brand-400); box-shadow: var(--shadow-md); }
.dl-card.coming-soon { opacity: 0.65; }
.dl-card-icon {
width: 48px; height: 48px;
background: var(--brand-50); border-radius: var(--radius-md);
display: grid; place-items: center; color: var(--brand-500);
margin-bottom: 12px;
}
.dl-card-icon svg { width: 24px; height: 24px; }
.dl-card-name { font-size: var(--text-md); font-weight: 600; color: var(--brand-900); margin: 0 0 4px; }
.dl-card-desc { font-size: var(--text-xs); color: var(--fg-muted); margin: 0 0 14px; line-height: 1.5; flex: 1; }
.dl-status {
display: inline-flex; align-items: center; gap: 4px;
font-size: 10px; font-weight: 600; letter-spacing: 0.04em;
padding: 3px 8px; border-radius: var(--radius-pill); margin-bottom: 12px;
}
.dl-status.available { background: var(--success-50); color: var(--success-700); }
.dl-status.soon { background: var(--gray-100); color: var(--fg-muted); }
.dl-card-btn {
display: flex; align-items: center; justify-content: center; gap: 6px;
height: 34px; padding: 0 14px;
border-radius: var(--radius-md);
font-size: var(--text-sm); font-weight: 500;
border: 1px solid transparent;
cursor: pointer; width: 100%;
transition: background var(--duration-fast) var(--ease-standard);
text-decoration: none;
}
.dl-card-btn.primary { background: var(--brand-500); color: #fff; }
.dl-card-btn.primary:hover { background: var(--brand-600); }
.dl-card-btn.disabled {
background: var(--gray-100); color: var(--fg-muted);
cursor: default; pointer-events: none;
border-color: var(--border-subtle);
}
.dl-card-btn svg { width: 14px; height: 14px; }
/* ── System requirements (rendered markdown) ── */
.sysreq-content h2 {
font-size: var(--text-xl); font-weight: 600;
color: var(--brand-900); margin: 28px 0 12px;
letter-spacing: var(--tracking-cn-display);
padding-bottom: 8px;
border-bottom: 1px solid var(--border-subtle);
}
.sysreq-content h2:first-child { margin-top: 0; }
.sysreq-content table {
width: 100%; border-collapse: collapse;
font-size: var(--text-sm); color: var(--gray-800);
margin-bottom: 8px;
}
.sysreq-content th {
text-align: left; font-size: 11px; font-weight: 600;
letter-spacing: 0.04em; text-transform: uppercase;
color: var(--fg-muted); padding: 8px 12px;
background: var(--gray-50);
border: 1px solid var(--border-subtle);
}
.sysreq-content td {
padding: 10px 12px;
border: 1px solid var(--border-subtle);
vertical-align: top;
}
.sysreq-content tr:hover td { background: var(--gray-25); }
.sysreq-content p { font-size: var(--text-sm); color: var(--fg-muted); margin: 8px 0 0; }
/* ── Changelog timeline ── */
.changelog {
display: grid;
grid-template-columns: 180px 1fr;
gap: 0;
margin-top: 40px;
border-left: 1px solid var(--border-subtle);
}
.changelog-entry { display: contents; }
.changelog-meta {
padding: 28px 24px 28px 32px;
position: relative;
border-bottom: 1px solid var(--border-subtle);
}
.changelog-meta::before {
content: '';
position: absolute; left: -7px; top: 36px;
width: 13px; height: 13px; border-radius: 50%;
background: var(--gray-0); border: 3px solid var(--gray-300);
}
.changelog-entry.current .changelog-meta::before {
background: var(--brand-500); border-color: var(--brand-100);
box-shadow: 0 0 0 4px var(--brand-50);
}
.changelog-version {
font-family: var(--font-mono); font-size: var(--text-md); font-weight: 600;
color: var(--brand-900); margin-bottom: 4px;
}
.changelog-date { font-size: var(--text-xs); color: var(--fg-muted); }
.changelog-tag {
display: inline-block; font-size: 10px;
background: var(--success-50); color: var(--success-700);
padding: 2px 8px; border-radius: var(--radius-pill);
font-weight: 500; margin-top: 8px;
}
.changelog-body {
padding: 28px 0 28px 32px;
border-bottom: 1px solid var(--border-subtle);
border-left: 1px solid var(--border-subtle);
margin-left: -1px;
}
.changelog-entry:last-child .changelog-meta,
.changelog-entry:last-child .changelog-body { border-bottom: none; }
.changelog-intro {
font-size: var(--text-sm); color: var(--gray-700);
line-height: 1.6; margin: 0 0 16px;
}
.changelog-cat { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.changelog-cat-label {
font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
color: var(--fg-muted); text-transform: uppercase;
display: flex; align-items: center; gap: 6px; margin-bottom: 2px;
}
.changelog-cat-label .dot { width: 6px; height: 6px; border-radius: 50%; }
.dot-feature { background: var(--brand-500); }
.dot-improve { background: var(--success-500); }
.dot-fix { background: var(--warning-500); }
.changelog-cat ul {
margin: 0; padding: 0; list-style: none;
font-size: var(--text-sm); color: var(--gray-700); line-height: 1.6;
}
.changelog-cat ul li {
padding: 3px 0 3px 16px; position: relative;
}
.changelog-cat ul li::before {
content: ''; position: absolute; left: 0; top: 11px;
width: 6px; height: 1px; background: var(--gray-400);
}
/* ── Responsive ── */
@media (max-width: 1024px) {
.ph-inner { grid-template-columns: 1fr; gap: 32px; }
.ph-title { font-size: 36px; }
.dl-grid { grid-template-columns: repeat(3, 1fr); }
.changelog { grid-template-columns: 120px 1fr; }
}
@media (max-width: 768px) {
.dl-grid { grid-template-columns: repeat(2, 1fr); }
.page-header { padding: 48px 0 40px; }
}
@media (max-width: 600px) {
.ph-meta { flex-wrap: wrap; gap: 12px; }
.dl-grid { grid-template-columns: 1fr; }
.changelog { grid-template-columns: 1fr; }
.changelog-version { font-size: 13px; }
}
.dl-hero{background:var(--mk-hero-grad); color:var(--on-accent); padding:64px 0 72px; text-align:center;}
.dl-hero h1{color:var(--on-accent); font-size:var(--mk-h2); font-weight:800;}
.dl-hero p{color:var(--on-accent-faint); margin:12px auto 0; max-width:36em;}
.detect{max-width:640px; margin:-40px auto 0; position:relative; z-index:2;}
.detect .card{display:flex; align-items:center; gap:18px; box-shadow:var(--sh-2); padding:20px 22px;}
.detect .icbox{width:44px; height:44px; border-radius:var(--r-lg); flex:none; display:flex; align-items:center; justify-content:center;}
.detect .icbox .ic{width:22px; height:22px;}
.detect b{display:block; color:var(--heading); font-size:16px;}
.detect span{color:var(--muted); font-size:var(--fs-sm);}
.detect .btn{margin-left:auto; flex:none;}
.dl-grid{display:grid; grid-template-columns:repeat(5,1fr); gap:14px; margin-top:46px;}
.dl-card{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:20px 18px; display:flex; flex-direction:column; text-align:center;}
.dl-card.detected{border-color:var(--primary); box-shadow:var(--sh-1);}
.dl-card .icbox{width:38px; height:38px; border-radius:var(--r-md); margin:0 auto 12px; display:flex; align-items:center; justify-content:center;}
.dl-card h3{font-size:15px; font-weight:700; margin-bottom:4px;}
.dl-card .req{color:var(--faint); font-size:var(--fs-xs); margin-bottom:14px;}
.dl-card .dl-status{font-size:var(--fs-xs); color:var(--success); margin-bottom:8px;}
.dl-card .dl-status.soon{color:var(--warn);}
.dl-card .btn{margin-top:auto; width:100%;}
.btn.disabled{opacity:.55; pointer-events:none; background:var(--bg); border:1px solid var(--border); color:var(--muted);}
/* 更新日志时间线 */
.changelog{max-width:820px; margin:0 auto;}
.changelog-entry{display:grid; grid-template-columns:150px 1fr; gap:20px; padding:22px 0; border-bottom:1px solid var(--border-subtle);}
.changelog-version{font-family:var(--font-mono); font-weight:700; color:var(--heading); font-size:16px;}
.changelog-date{color:var(--faint); font-size:var(--fs-sm); margin-top:3px;}
.changelog-tag{display:inline-block; margin-top:8px; background:var(--info-soft); color:var(--primary); font-size:var(--fs-xs); font-weight:700; padding:2px 9px; border-radius:var(--r-pill);}
.changelog-cat-label{display:flex; align-items:center; gap:7px; font-size:var(--fs-sm); font-weight:700; color:var(--heading); margin:10px 0 6px;}
.changelog-cat-label .dot{width:7px; height:7px; border-radius:50%;}
.dot-feature{background:var(--primary);} .dot-improve{background:var(--success);} .dot-fix{background:var(--warn);}
.changelog-body ul{margin:0; padding-left:20px; color:var(--muted); font-size:14px; line-height:1.8;}
.changelog-intro{color:var(--muted); font-size:14px; margin:0 0 4px;}
@media (max-width:900px){ .dl-grid{grid-template-columns:repeat(2,1fr);} }
@media (max-width:600px){ .dl-grid{grid-template-columns:1fr;} .detect .card{flex-wrap:wrap;} .changelog-entry{grid-template-columns:1fr;} }
---
<!-- ============================== PAGE HEADER ============================== -->
<section class="page-header">
<div class="container ph-inner">
<header class="dl-hero">
<div class="container">
<h1>下载岩美酒库</h1>
<p>当前版本 <b class="version-badge">v{{ changelog[0].version }}</b> · 一个账号五端同步 · 客户端内置自动更新</p>
</div>
</header>
<div class="detect container">
<div class="card">
<div class="icbox ic-info" id="detect-icon"><svg class="ic"><use href="#i-ic01"/></svg></div>
<div>
<div class="ph-eyebrow">
<span class="version-badge">v{{ changelog[0].version }}</span>
<span>下载岩美</span>
</div>
<h1 class="ph-title">随时随地,<br/>轻松管理酒库。</h1>
<p class="ph-lead">
Web 版、Windows / macOS 桌面版与 Android 客户端均已上线,iOS 客户端即将推出。
</p>
<div class="ph-meta">
<div class="ph-meta-item">
<div class="ph-meta-label">最新版本</div>
<div class="ph-meta-val version-badge">v{{ changelog[0].version }}</div>
</div>
<div class="ph-meta-item">
<div class="ph-meta-label">发布日期</div>
<div class="ph-meta-val">{{ changelog[0].date }}</div>
</div>
<div class="ph-meta-item">
<div class="ph-meta-label">可用平台</div>
<div class="ph-meta-val">Web / Windows / macOS / Android</div>
</div>
</div>
</div>
<!-- Auto-detect card -->
<div class="detect-card">
<div class="detect-card-head">
<span class="head-left">
<i data-lucide="zap" class="icon"></i>
已自动识别您的环境
</span>
<span style="font-family: var(--font-mono); color: var(--fg-subtle);">推荐</span>
</div>
<div class="detect-card-body">
<div class="detect-icon" id="detect-icon">
<i data-lucide="globe" class="icon"></i>
</div>
<div class="detect-info">
<h3 id="detect-name">Web 版</h3>
<p id="detect-desc">浏览器直接访问,功能完整,无需下载安装。</p>
</div>
</div>
<div class="detect-card-actions">
<a href="{{ site.appBaseUrl }}/app/" class="btn btn-primary btn-lg" id="detect-btn">
<i data-lucide="arrow-right" class="icon"></i>
立即使用 Web 版
</a>
</div>
<b id="detect-name">Web 版</b>
<span id="detect-desc">已自动识别你的设备,推荐以下版本。</span>
</div>
<a class="btn primary" id="detect-btn" href="{{ site.appBaseUrl }}/app/"><span>立即使用</span></a>
</div>
</section>
</div>
<!-- ============================== PLATFORM GRID ============================== -->
<section class="section">
<div class="container">
<h2 class="fs-3xl fw-6 text-brand-900 m-0 mb-6" style="letter-spacing: var(--tracking-cn-display);">支持的平台</h2>
<p class="fs-md text-muted m-0 mb-8">Web 版、Windows / macOS 桌面版与 Android 客户端现已可用,iOS 正在开发中。</p>
<div class="dl-grid" id="platform-grid">
<!-- Web -->
<div class="dl-card" data-platform="web">
<div class="dl-card-icon"><i data-lucide="globe" class="icon"></i></div>
<h3 class="dl-card-name">Web 版</h3>
<p class="dl-card-desc">浏览器直接访问,功能完整,无需安装,支持 PC 与平板。</p>
<span class="dl-status available">✓ 已上线</span>
<a href="{{ site.appBaseUrl }}/app/" class="dl-card-btn primary">
<i data-lucide="arrow-right" class="icon"></i>立即使用
</a>
</div>
<!-- Windows -->
<div class="dl-card" data-platform="windows">
<div class="dl-card-icon"><i data-lucide="monitor" class="icon"></i></div>
<h3 class="dl-card-name">Windows</h3>
<p class="dl-card-desc">原生桌面客户端,支持 Win 10/1164 位),离线模式与本地打印。</p>
<span class="dl-status available">✓ 已支持</span>
<a href="#" class="dl-card-btn primary" id="windows-dl-btn">
<i data-lucide="download" class="icon"></i>下载 Windows 版
</a>
<div class="icbox ic-info"><svg class="ic"><use href="#i-ic63"/></svg></div>
<h3>Windows</h3>
<div class="req">Win 10 / 1164 位)</div>
<div class="dl-status">✓ 安装包</div>
<a class="btn primary" id="windows-dl-btn" href="{{ site.appBaseUrl }}/downloads/jiu-windows-x64-setup.exe"><svg class="ic" style="width:15px;height:15px"><use href="#i-download"/></svg>下载</a>
</div>
<!-- macOS -->
<div class="dl-card" data-platform="macos">
<div class="dl-card-icon"><i data-lucide="laptop" class="icon"></i></div>
<h3 class="dl-card-name">macOS</h3>
<p class="dl-card-desc">支持 Apple Silicon Intel,解压即用,首次运行右键→打开。</p>
<span class="dl-status available">✓ 已支持</span>
<a href="#" class="dl-card-btn primary" id="macos-dl-btn">
<i data-lucide="download" class="icon"></i>下载 macOS 版
</a>
<div class="icbox ic-info"><svg class="ic"><use href="#i-ic25"/></svg></div>
<h3>macOS</h3>
<div class="req">Apple Silicon / Intel</div>
<div class="dl-status">✓ 已签名公证</div>
<a class="btn primary" id="macos-dl-btn" href="{{ site.appBaseUrl }}/downloads/jiu-macos-x64.zip"><svg class="ic" style="width:15px;height:15px"><use href="#i-download"/></svg>下载</a>
</div>
<!-- iOS -->
<div class="dl-card coming-soon" data-platform="ios" id="ios-card">
<div class="dl-card-icon"><i data-lucide="smartphone" class="icon"></i></div>
<h3 class="dl-card-name">iOS</h3>
<p class="dl-card-desc">iPhone 与 iPad 通用,通过 TestFlight 安装。</p>
<span class="dl-status soon" id="ios-status">敬请期待</span>
<a href="#" class="dl-card-btn disabled" id="ios-dl-btn">
<i data-lucide="clock" class="icon"></i><span>即将推出</span>
</a>
<div class="dl-card" data-platform="web">
<div class="icbox ic-ok"><svg class="ic"><use href="#i-ic01"/></svg></div>
<h3>Web 版</h3>
<div class="req">现代浏览器均可</div>
<div class="dl-status">✓ 免安装</div>
<a class="btn primary" href="{{ site.appBaseUrl }}/app/">立即使用</a>
</div>
<!-- Android -->
<div class="dl-card" data-platform="android">
<div class="dl-card-icon"><i data-lucide="tablet-smartphone" class="icon"></i></div>
<h3 class="dl-card-name">Android</h3>
<p class="dl-card-desc">手机与平板通用,APK 直接安装,支持 Android 6.0 及以上</p>
<span class="dl-status available">✓ 已支持</span>
<a href="#" class="dl-card-btn primary" id="android-dl-btn">
<i data-lucide="download" class="icon"></i>下载 Android 版
</a>
<div class="icbox ic-warn"><svg class="ic"><use href="#i-ic43"/></svg></div>
<h3>Android</h3>
<div class="req">Android 6.0 及以上</div>
<div class="dl-status">✓ APK 直装</div>
<a class="btn primary" id="android-dl-btn" href="{{ site.appBaseUrl }}/downloads/jiu-android.apk"><svg class="ic" style="width:15px;height:15px"><use href="#i-download"/></svg>下载</a>
</div>
<div class="dl-card" data-platform="ios" id="ios-card">
<div class="icbox ic-wine"><svg class="ic"><use href="#i-ic22"/></svg></div>
<h3>iOS</h3>
<div class="req">iPhone / iPad</div>
<div class="dl-status soon" id="ios-status">TestFlight 内测中</div>
<a class="btn ghost disabled" id="ios-dl-btn" href="#">敬请期待</a>
</div>
</div>
<p style="text-align:center;color:var(--faint);font-size:var(--fs-sm);margin-top:18px">
桌面与移动客户端内置自动更新 · 各端详细配置见<a href="/system-requirements/">系统要求</a>
</p>
</div>
</section>
<!-- ============================== SYSTEM REQUIREMENTS ============================== -->
<section class="section bg-25">
<section class="section alt" id="changelog">
<div class="container">
<h2 class="fs-3xl fw-6 text-brand-900 m-0 mb-6" style="letter-spacing: var(--tracking-cn-display);">系统要求</h2>
<p class="fs-md text-muted m-0 mb-32">在以下环境中均经过测试与验证。</p>
<div class="card p-32 sysreq-content">
{{ sysreq | safe }}
<div class="sec-head">
<div class="eyebrow">更新日志</div>
<h2>最近更新</h2>
<p id="changelog-count">最近 {{ changelog | length }} 个版本。</p>
</div>
</div>
</section>
<!-- ============================== CHANGELOG ============================== -->
<section class="section">
<div class="container">
<h2 class="fs-3xl fw-6 text-brand-900 m-0 mb-6" style="letter-spacing: var(--tracking-cn-display);">版本更新日志</h2>
<p class="fs-md text-muted m-0" id="changelog-count">最近 {{ changelog | length }} 个版本。</p>
<div class="changelog" id="changelog-list">
{% for entry in changelog %}
<div class="changelog-entry{% if loop.first %} current{% endif %}">
@@ -401,27 +117,14 @@ pageStyle: |
{% if loop.first %}<span class="changelog-tag">当前版本</span>{% endif %}
</div>
<div class="changelog-body">
{% if entry.intro %}
<p class="changelog-intro">{{ entry.intro }}</p>
{% endif %}
{% if entry.intro %}<p class="changelog-intro">{{ entry.intro }}</p>{% endif %}
{% for section in entry.sections %}
<div class="changelog-cat">
<div class="changelog-cat-label">
{% if section.type == "新功能" %}
<span class="dot dot-feature"></span>新功能
{% elif section.type == "改进" %}
<span class="dot dot-improve"></span>改进
{% elif section.type == "修复" %}
<span class="dot dot-fix"></span>问题修复
{% else %}
<span class="dot dot-improve"></span>{{ section.type }}
{% endif %}
<span class="dot {% if section.type == '新功能' %}dot-feature{% elif section.type == '修复' %}dot-fix{% else %}dot-improve{% endif %}"></span>
{{ section.type }}
</div>
<ul>
{% for item in section.items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<ul>{% for item in section.items %}<li>{{ item }}</li>{% endfor %}</ul>
</div>
{% endfor %}
</div>
@@ -434,12 +137,13 @@ pageStyle: |
<script>
(function() {
var APP_BASE = '{{ site.appBaseUrl }}';
var SPRITE = { web:'#i-ic01', ios:'#i-ic22', android:'#i-ic43', windows:'#i-ic63', macos:'#i-ic25' };
var platforms = {
web: { name: 'Web 版', desc: '浏览器直接访问,功能完整,无需下载安装。', icon: 'globe', btnText: '立即使用 Web 版', btnHref: APP_BASE + '/app/', available: true },
ios: { name: 'iOS', desc: 'iPhone 与 iPad 通用,通过 TestFlight 安装。', icon: 'smartphone', btnText: '敬请期待', available: false },
android: { name: 'Android', desc: '手机与平板通用,APK 直接安装,支持 Android 6.0 及以上。', icon: 'tablet-smartphone', btnText: '下载 Android 版', btnHref: '#', available: true },
windows: { name: 'Windows 桌面', desc: '原生桌面客户端,支持 Win 10/11(64 位),离线模式与本地打印。', icon: 'monitor', btnText: '下载 Windows 版', btnHref: '#', available: true },
macos: { name: 'macOS 桌面', desc: '支持 Apple Silicon 与 Intel,解压即用,首次运行右键→打开。', icon: 'laptop', btnText: '下载 macOS 版', btnHref: '#', available: true }
web: { name: 'Web 版', desc: '浏览器直接访问,功能完整,无需下载安装。', btnText: '立即使用 Web 版', btnHref: APP_BASE + '/app/', available: true },
ios: { name: 'iOS', desc: 'iPhone 与 iPad 通用,通过 TestFlight 安装。', btnText: '敬请期待', available: false },
android: { name: 'Android', desc: '手机与平板通用,APK 直接安装。', btnText: '下载 Android 版', btnHref: APP_BASE + '/downloads/jiu-android.apk', available: true },
windows: { name: 'Windows 桌面', desc: '原生桌面客户端,支持 Win 10/11(64 位)与本地打印。', btnText: '下载 Windows 版', btnHref: APP_BASE + '/downloads/jiu-windows-x64-setup.exe', available: true },
macos: { name: 'macOS 桌面', desc: '支持 Apple Silicon 与 Intel,解压即用', btnText: '下载 macOS 版', btnHref: APP_BASE + '/downloads/jiu-macos-x64.zip', available: true }
};
function detectPlatform() {
@@ -461,27 +165,18 @@ pageStyle: |
if (nameEl) nameEl.textContent = info.name;
if (descEl) descEl.textContent = info.desc;
if (btnEl) {
if (info.available) {
if (info.available && info.btnHref && info.btnHref !== '#') {
btnEl.href = info.btnHref;
btnEl.style.opacity = '';
btnEl.style.pointerEvents = '';
btnEl.querySelector('span') && (btnEl.querySelector('span').textContent = info.btnText);
} else {
btnEl.classList.remove('disabled');
btnEl.innerHTML = '<span>' + info.btnText + '</span>';
} else if (!info.available) {
btnEl.href = '#';
btnEl.style.opacity = '0.5';
btnEl.style.pointerEvents = 'none';
btnEl.classList.add('disabled');
btnEl.innerHTML = '<span>' + info.btnText + '</span>';
}
btnEl.childNodes.forEach(function(n) {
if (n.nodeType === 3) n.textContent = ' ' + info.btnText;
});
}
if (iconEl && window.lucide) {
iconEl.innerHTML = '';
var svg = document.createElement('i');
svg.setAttribute('data-lucide', info.icon);
svg.className = 'icon';
iconEl.appendChild(svg);
lucide.createIcons();
if (iconEl) {
iconEl.innerHTML = '<svg class="ic"><use href="' + (SPRITE[p] || '#i-ic01') + '"/></svg>';
}
}
@@ -499,13 +194,11 @@ pageStyle: |
});
}
// 用 /api/v1/public/release 返回的 changelog 重渲染更新日志时间线,使客户端
// 发版无需重建官网即可同步。结构与构建时 web/_data/changelog.js 一致;接口
// 不可达或字段缺失时保留构建时静态时间线作 fallback。
// 用 /api/v1/public/release 的 changelog 重渲染时间线客户端发版无需重建官网。
// 接口不可达或字段缺失时保留构建时静态时间线作 fallback。
function renderChangelog(entries) {
if (!Array.isArray(entries) || entries.length === 0) return;
var dotClass = { '新功能': 'dot-feature', '改进': 'dot-improve', '修复': 'dot-fix' };
var label = { '修复': '问题修复' };
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"]/g, function(c) {
return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c];
@@ -513,22 +206,16 @@ pageStyle: |
}
var html = '';
entries.forEach(function(entry, i) {
var current = i === 0;
html += '<div class="changelog-entry' + (current ? ' current' : '') + '">';
html += '<div class="changelog-meta">';
html += '<div class="changelog-version">v' + esc(entry.version) + '</div>';
html += '<div class="changelog-entry' + (i === 0 ? ' current' : '') + '">';
html += '<div class="changelog-meta"><div class="changelog-version">v' + esc(entry.version) + '</div>';
html += '<div class="changelog-date">' + esc(entry.date) + '</div>';
if (current) html += '<span class="changelog-tag">当前版本</span>';
if (i === 0) html += '<span class="changelog-tag">当前版本</span>';
html += '</div><div class="changelog-body">';
if (entry.intro) html += '<p class="changelog-intro">' + esc(entry.intro) + '</p>';
(entry.sections || []).forEach(function(section) {
var dot = dotClass[section.type] || 'dot-improve';
var name = label[section.type] || section.type;
html += '<div class="changelog-cat"><div class="changelog-cat-label">';
html += '<span class="dot ' + dot + '"></span>' + esc(name) + '</div><ul>';
(section.items || []).forEach(function(item) {
html += '<li>' + esc(item) + '</li>';
});
html += '<div class="changelog-cat"><div class="changelog-cat-label"><span class="dot ' + dot + '"></span>' + esc(section.type) + '</div><ul>';
(section.items || []).forEach(function(item) { html += '<li>' + esc(item) + '</li>'; });
html += '</ul></div>';
});
html += '</div></div>';
@@ -544,63 +231,50 @@ pageStyle: |
updateDetectCard(detected);
highlightPlatformCard(detected);
fetch('{{ site.appBaseUrl }}/api/v1/public/release')
fetch(APP_BASE + '/api/v1/public/release')
.then(function(r) { return r.json(); })
.then(function(data) {
if (data && data.version) updateVersionBadges(data.version);
if (data && data.changelog) renderChangelog(data.changelog);
if (data && data.download_urls && data.download_urls.macos) {
platforms.macos.btnHref = data.download_urls.macos;
var macBtn = document.getElementById('macos-dl-btn');
if (macBtn) macBtn.href = data.download_urls.macos;
if (detected === 'macos') updateDetectCard(detected);
}
if (data && data.download_urls && data.download_urls.windows) {
platforms.windows.btnHref = data.download_urls.windows;
var btn = document.getElementById('windows-dl-btn');
if (btn) btn.href = data.download_urls.windows;
if (detected === 'windows') updateDetectCard(detected);
}
if (data && data.download_urls && data.download_urls.android) {
platforms.android.btnHref = data.download_urls.android;
var andBtn = document.getElementById('android-dl-btn');
if (andBtn) andBtn.href = data.download_urls.android;
if (detected === 'android') updateDetectCard(detected);
}
// iOS 走 TestFlight:仅当配置了公开 TestFlight 链接时激活卡片
if (data && data.download_urls && data.download_urls.ios) {
var urls = (data && data.download_urls) || {};
[['macos','macos-dl-btn'],['windows','windows-dl-btn'],['android','android-dl-btn']].forEach(function(pair) {
if (urls[pair[0]]) {
platforms[pair[0]].btnHref = urls[pair[0]];
var btn = document.getElementById(pair[1]);
if (btn) btn.href = urls[pair[0]];
if (detected === pair[0]) updateDetectCard(detected);
}
});
// iOS 走 TestFlight:仅当返回公开链接时激活
if (urls.ios) {
platforms.ios.available = true;
platforms.ios.btnHref = data.download_urls.ios;
platforms.ios.btnHref = urls.ios;
platforms.ios.btnText = '通过 TestFlight 安装';
var iosCard = document.getElementById('ios-card');
var iosStatus = document.getElementById('ios-status');
var iosBtn = document.getElementById('ios-dl-btn');
if (iosCard) iosCard.classList.remove('coming-soon');
if (iosStatus) { iosStatus.className = 'dl-status available'; iosStatus.textContent = '✓ 已上线'; }
if (iosStatus) { iosStatus.className = 'dl-status'; iosStatus.textContent = '✓ 已上线'; }
if (iosBtn) {
iosBtn.className = 'dl-card-btn primary';
iosBtn.href = data.download_urls.ios;
iosBtn.className = 'btn primary';
iosBtn.href = urls.ios;
iosBtn.target = '_blank';
iosBtn.rel = 'noopener';
iosBtn.innerHTML = '<i data-lucide="download" class="icon"></i>通过 TestFlight 安装';
if (window.lucide) lucide.createIcons();
iosBtn.textContent = 'TestFlight 安装';
}
if (detected === 'ios') updateDetectCard(detected);
}
})
.catch(function() {
// API 不可达时,将仍为 href="#" 的下载按钮改为禁用态
['windows-dl-btn', 'macos-dl-btn', 'android-dl-btn'].forEach(function(id) {
var btn = document.getElementById(id);
if (btn && btn.getAttribute('href') === '#') {
btn.className = 'dl-card-btn disabled';
btn.innerHTML = '暂时无法获取下载地址';
btn.className = 'btn ghost disabled';
btn.textContent = '暂时无法获取下载地址';
}
});
var detectBtn = document.getElementById('detect-btn');
if (detectBtn && detectBtn.getAttribute('href') === '#') {
detectBtn.className = 'btn btn-secondary btn-lg';
detectBtn.innerHTML = '暂时无法连接服务器';
detectBtn.classList.add('disabled');
detectBtn.innerHTML = '<span>暂时无法连接服务器</span>';
}
});
});
+227 -582
View File
@@ -1,646 +1,291 @@
---
layout: base.njk
title: 为酒行与酒店设计的库存管理平台
description: 岩美酒库管理系统,审核驱动业务流,库存与账款自动同步,五端无缝接入
description: 岩美酒库管理系统——每一瓶酒一个编号,审核驱动业务流,成本售价利润一目了然,五端同步
---
<style>
/* HERO --------------------------------------------------------- */
.hero {
position: relative; overflow: hidden; padding: 88px 0 64px;
background:
radial-gradient(900px 480px at 78% 12%, rgba(37,99,172,0.06), transparent 60%),
linear-gradient(180deg,#FBFCFD 0%,#FFFFFF 80%);
}
.hero-inner { display: grid; grid-template-columns: 1.05fr 1.25fr; gap: 64px; align-items: center; }
.hero h1 { font-size: 52px; line-height: 1.12; letter-spacing: var(--tracking-cn-display); color: var(--brand-900); font-weight: 700; margin: 0 0 20px; }
.hero h1 em { font-style: normal; color: var(--brand-500); position: relative; }
.hero h1 em::after { content: ''; position: absolute; left: 0; right: 0; bottom: 4px; height: 10px; background: var(--brand-100); z-index: -1; border-radius: 2px; }
.hero p.lead { font-size: var(--text-xl); line-height: 1.6; color: var(--gray-600); margin: 0 0 32px; }
.hero-mini-item .icon { width: 14px; height: 14px; color: var(--success-500); }
/* Preview mock UI ---------------------------------------------- */
.preview-shell { background: var(--gray-0); border: 1px solid var(--border-default); border-radius: 14px; box-shadow: var(--shadow-xl); overflow: hidden; }
.preview-titlebar { height: 36px; display: flex; align-items: center; padding: 0 14px; background: var(--gray-50); border-bottom: 1px solid var(--border-subtle); gap: 8px; }
.preview-dots { display: flex; gap: 6px; }
.preview-dots span { width: 10px; height: 10px; border-radius: 50%; background: var(--gray-300); }
.preview-url { flex: 1; text-align: center; font-size: var(--text-xs); color: var(--fg-subtle); font-family: var(--font-mono); }
.mock-app { display: grid; grid-template-columns: 180px 1fr; height: 540px; }
.mock-sidebar { background: var(--brand-900); color: #DCE2EB; padding: 20px 12px; font-size: var(--text-sm); }
.mock-sidebar-brand { font-size: 13px; font-weight: 600; letter-spacing: 0.1em; color: #fff; padding: 0 8px 16px; border-bottom: 1px solid rgba(255,255,255,0.08); margin-bottom: 12px; }
.mock-nav-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--radius-sm); margin-bottom: 2px; color: #ADC9EA; font-size: 13px; }
.mock-nav-item .icon { width: 16px; height: 16px; }
.mock-nav-item.active { background: rgba(255,255,255,0.08); color: #fff; }
.mock-main { background: var(--gray-50); padding: 20px; overflow: hidden; }
.mock-topbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; }
.mock-tabs { display: flex; gap: 4px; background: var(--gray-0); padding: 4px; border-radius: var(--radius-md); border: 1px solid var(--border-default); }
.mock-tab { padding: 6px 14px; font-size: 13px; color: var(--gray-600); border-radius: var(--radius-sm); }
.mock-tab.active { background: var(--brand-500); color: #fff; font-weight: 500; }
.mock-actions { display: flex; gap: 8px; }
.mock-mini-btn { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; background: var(--gray-0); border: 1px solid var(--border-default); border-radius: var(--radius-sm); font-size: 12px; color: var(--gray-700); }
.mock-mini-btn .icon { width: 13px; height: 13px; }
.mock-mini-btn.primary { background: var(--brand-500); color: #fff; border-color: var(--brand-500); }
.mock-stats { display: grid; grid-template-columns: repeat(4,1fr); gap: 10px; margin-bottom: 16px; }
.mock-stat { background: var(--gray-0); border: 1px solid var(--border-default); border-radius: var(--radius-md); padding: 12px 14px; }
.mock-stat-label { font-size: 11px; color: var(--fg-muted); margin-bottom: 4px; }
.mock-stat-value { font-size: 20px; font-weight: 600; color: var(--brand-900); font-variant-numeric: tabular-nums; }
.mock-stat-delta { font-size: 11px; margin-top: 2px; font-variant-numeric: tabular-nums; }
.mock-stat-delta.up { color: var(--success-500); }
.mock-stat-delta.down { color: var(--danger-500); }
.mock-table-card { background: var(--gray-0); border: 1px solid var(--border-default); border-radius: var(--radius-md); overflow: hidden; }
.mock-table-head { display: grid; grid-template-columns: 110px 1fr 60px 70px 80px 70px; padding: 10px 14px; background: var(--gray-50); border-bottom: 1px solid var(--border-subtle); font-size: 11px; color: var(--fg-muted); font-weight: 500; }
.mock-table-row { display: grid; grid-template-columns: 110px 1fr 60px 70px 80px 70px; padding: 10px 14px; border-bottom: 1px solid var(--border-subtle); font-size: 12px; color: var(--gray-800); align-items: center; font-variant-numeric: tabular-nums; }
.mock-table-row:last-child { border-bottom: none; }
.mock-table-row .sku { font-family: var(--font-mono); color: var(--brand-500); font-size: 11px; }
.mock-pill { display: inline-block; padding: 1px 8px; border-radius: var(--radius-pill); font-size: 10px; font-weight: 500; }
.mock-pill.ok { background: var(--success-50); color: var(--success-700); }
.mock-pill.warn { background: var(--warning-50); color: var(--warning-700); }
.mock-pill.pending { background: var(--info-50); color: var(--info-700); }
.hero-toast { position: absolute; bottom: -28px; right: -36px; background: var(--gray-0); border: 1px solid var(--border-default); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: 14px 16px; width: 260px; display: flex; gap: 12px; align-items: flex-start; z-index: 2; }
.hero-toast-icon { width: 36px; height: 36px; border-radius: 8px; background: var(--success-50); display: grid; place-items: center; flex-shrink: 0; }
.hero-toast-icon .icon { color: var(--success-500); }
/* Trust strip -------------------------------------------------- */
.trust { border-top: 1px solid var(--border-subtle); border-bottom: 1px solid var(--border-subtle); background: var(--gray-25); padding: 32px 0; }
.trust-grid { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr; align-items: center; gap: 32px; }
.trust-stat-num { font-size: 28px; font-weight: 600; color: var(--brand-900); font-variant-numeric: tabular-nums; line-height: 1; }
.trust-stat-num sup { font-size: 14px; color: var(--brand-500); margin-left: 2px; font-weight: 500; }
/* Modules ------------------------------------------------------ */
.module-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
.module-card.feature-card { grid-column: span 2; background: linear-gradient(180deg,#FBFCFD 0%,#FFFFFF 100%); }
.module-card.feature-card .icon-box-lg { background: var(--brand-500); color: #fff; }
.module-card ul { margin: 16px 0 0; padding: 0; list-style: none; font-size: var(--text-sm); color: var(--gray-700); }
.module-card ul li { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid var(--border-subtle); }
.module-card ul li:first-child { border-top: none; }
.module-card ul li .icon { width: 14px; height: 14px; color: var(--brand-500); }
/* Workflow ----------------------------------------------------- */
.workflow-grid { display: grid; grid-template-columns: 1fr 1.1fr; gap: 64px; align-items: center; margin-top: 48px; }
.wf-step { display: grid; grid-template-columns: 32px 1fr; gap: 14px; align-items: start; }
.wf-num { width: 32px; height: 32px; border-radius: var(--radius-pill); background: var(--brand-50); color: var(--brand-700); display: grid; place-items: center; font-size: 13px; font-weight: 600; }
.wf-step.active .wf-num { background: var(--brand-500); color: #fff; }
.flow-vis { background: var(--gray-0); border: 1px solid var(--border-default); border-radius: var(--radius-lg); box-shadow: var(--shadow-md); padding: 28px; }
.flow-doc { background: var(--gray-50); border: 1px solid var(--border-default); border-radius: var(--radius-md); padding: 16px 18px; margin-bottom: 14px; }
.flow-doc-head { display: flex; justify-content: space-between; margin-bottom: 12px; }
.flow-doc-id { font-family: var(--font-mono); font-size: 12px; color: var(--brand-500); }
.flow-doc-status { font-size: 11px; font-weight: 500; padding: 2px 10px; border-radius: var(--radius-pill); background: var(--info-50); color: var(--info-700); }
.flow-doc-status.approved { background: var(--success-50); color: var(--success-700); }
.flow-row { display: grid; grid-template-columns: 70px 1fr 70px 70px; gap: 8px; font-size: 12px; padding: 6px 0; color: var(--gray-700); font-variant-numeric: tabular-nums; }
.flow-row.head { font-weight: 500; color: var(--fg-muted); font-size: 11px; padding: 0 0 6px; border-bottom: 1px solid var(--border-subtle); margin-bottom: 4px; }
.flow-row.foot { border-top: 1px solid var(--border-default); margin-top: 6px; padding-top: 8px; font-weight: 600; color: var(--brand-900); }
.flow-arrow { display: flex; align-items: center; justify-content: center; padding: 12px 0; color: var(--gray-400); }
.flow-arrow .icon { width: 18px; height: 18px; }
.flow-effects { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.flow-effect { background: #fff; border: 1px solid var(--border-default); border-radius: var(--radius-md); padding: 12px 14px; }
.flow-effect-label { font-size: 11px; color: var(--fg-muted); display: flex; align-items: center; gap: 6px; }
.flow-effect-label .icon { width: 12px; height: 12px; color: var(--success-500); }
.flow-effect-val { font-size: 18px; font-weight: 600; color: var(--brand-900); margin-top: 4px; font-variant-numeric: tabular-nums; }
/* Platforms ---------------------------------------------------- */
.platform-card:hover { border-color: var(--brand-300); }
.platforms-visual { margin-top: 56px; background: var(--brand-900); border-radius: var(--radius-xl); padding: 56px 48px; position: relative; overflow: hidden; color: #fff; display: grid; grid-template-columns: 1fr 1.2fr; gap: 48px; align-items: center; }
.platforms-visual::before { content: ''; position: absolute; inset: 0; background: radial-gradient(600px 320px at 80% 10%,rgba(79,134,198,0.20),transparent 60%); pointer-events: none; }
.pv-text h3 { font-size: 32px; font-weight: 600; letter-spacing: var(--tracking-cn-display); margin: 0 0 16px; }
.pv-text p { font-size: var(--text-lg); color: #ADC9EA; margin: 0 0 24px; line-height: 1.6; }
.pv-feature { display: flex; align-items: center; gap: 10px; font-size: var(--text-md); color: #DCE2EB; }
.pv-feature .icon { width: 16px; height: 16px; color: var(--brand-300); }
.device-stack { position: relative; height: 320px; z-index: 1; }
.device-laptop { position: absolute; width: 100%; bottom: 0; background: #0A1F3B; border: 1px solid rgba(255,255,255,0.08); border-radius: 8px 8px 4px 4px; box-shadow: var(--shadow-xl); }
.device-laptop::after { content: ''; position: absolute; bottom: -10px; left: -16px; right: -16px; height: 10px; background: linear-gradient(180deg,#232934 0%,#141821 100%); border-radius: 0 0 8px 8px; }
.device-laptop-screen { background: var(--gray-50); margin: 8px; height: 200px; border-radius: 4px; padding: 10px; overflow: hidden; }
.device-laptop-mock-row { height: 12px; background: var(--gray-200); border-radius: 2px; margin-bottom: 6px; }
.device-laptop-mock-row.short { width: 60%; }
.device-laptop-mock-row.brand { background: var(--brand-300); width: 30%; }
.device-phone { position: absolute; right: 32px; bottom: -8px; width: 110px; height: 220px; background: #0A1F3B; border: 2px solid rgba(255,255,255,0.10); border-radius: 16px; padding: 4px; box-shadow: var(--shadow-xl); z-index: 2; }
.device-phone-screen { background: var(--gray-50); border-radius: 12px; height: 100%; padding: 8px; overflow: hidden; font-size: 8px; }
.device-phone-screen .top { height: 14px; background: var(--brand-500); border-radius: 4px; margin-bottom: 6px; }
/* Reports ------------------------------------------------------ */
.reports-grid { display: grid; grid-template-columns: 1.05fr 1fr; gap: 64px; align-items: center; margin-top: 48px; }
.report-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 18px; }
.report-tabs { display: flex; gap: 4px; }
.report-tab { padding: 4px 10px; font-size: 11px; color: var(--fg-muted); border-radius: var(--radius-sm); cursor: pointer; }
.report-tab.active { background: var(--gray-100); color: var(--gray-900); }
.chart { position: relative; height: 200px; margin: 8px 0 0; }
.chart svg { width: 100%; height: 100%; }
.chart-stats { display: grid; grid-template-columns: repeat(3,1fr); gap: 12px; border-top: 1px solid var(--border-subtle); padding-top: 18px; margin-top: 8px; }
.chart-stat-val small { font-size: var(--text-xs); color: var(--success-500); margin-left: 4px; font-weight: 500; }
/* Security ----------------------------------------------------- */
.roles-table { margin-top: 32px; background: var(--gray-0); border: 1px solid var(--border-default); border-radius: var(--radius-lg); overflow: hidden; }
.roles-row { display: grid; grid-template-columns: 180px 1fr 80px 80px 80px 80px; align-items: center; padding: 14px 24px; border-bottom: 1px solid var(--border-subtle); font-size: var(--text-sm); }
.roles-row:last-child { border-bottom: none; }
.roles-row.head { background: var(--gray-50); color: var(--fg-muted); font-size: 12px; font-weight: 500; }
.role-check { text-align: center; }
.role-check .icon { width: 16px; height: 16px; color: var(--success-500); display: inline-block; }
.role-check.no .icon { color: var(--gray-300); }
/* Pricing ------------------------------------------------------ */
.price-card.featured { border-color: var(--brand-500); box-shadow: 0 0 0 4px rgba(37,99,172,0.08), var(--shadow-md); position: relative; }
.price-card.featured::before { content: '推荐'; position: absolute; top: -1px; right: 24px; background: var(--brand-500); color: #fff; padding: 4px 12px; border-radius: 0 0 var(--radius-sm) var(--radius-sm); font-size: 11px; font-weight: 500; letter-spacing: 0.06em; }
.price-amount { display: flex; align-items: baseline; gap: 4px; margin-bottom: 8px; }
.price-amount .currency { font-size: 18px; color: var(--gray-700); font-weight: 500; }
.price-amount .num { font-size: 42px; font-weight: 700; color: var(--brand-900); font-variant-numeric: tabular-nums; line-height: 1; }
.price-amount .unit { font-size: 14px; color: var(--fg-muted); margin-left: 4px; }
.price-card.custom .num { font-size: 32px; }
/* FAQ ---------------------------------------------------------- */
.faq-q { display: flex; justify-content: space-between; align-items: center; padding: 20px 24px; cursor: pointer; font-size: var(--text-lg); font-weight: 500; color: var(--brand-900); list-style: none; }
.faq-q::-webkit-details-marker { display: none; }
.faq-q .icon { width: 18px; height: 18px; color: var(--fg-muted); transition: transform var(--duration-base) var(--ease-standard); }
details[open] .faq-q .icon { transform: rotate(45deg); }
/* CTA banner --------------------------------------------------- */
.cta-banner {
background: radial-gradient(800px 360px at 80% 30%,rgba(37,99,172,0.40),transparent 60%), linear-gradient(135deg,#0A1F3B 0%,#15407D 100%);
color: #fff; border-radius: var(--radius-xl); padding: 64px 56px;
display: grid; grid-template-columns: 1.4fr 1fr; gap: 48px; align-items: center; margin: 96px 0;
}
.cta-banner h2 { font-size: 36px; font-weight: 600; letter-spacing: var(--tracking-cn-display); margin: 0 0 16px; line-height: 1.25; }
.cta-banner p { font-size: var(--text-lg); color: #ADC9EA; margin: 0; line-height: 1.6; }
.cta-banner .btn-primary { background: #fff; color: var(--brand-700); }
.cta-banner .btn-primary:hover { background: #DCE2EB; }
.cta-banner .btn-secondary { background: transparent; color: #fff; border-color: rgba(255,255,255,0.30); }
.cta-banner .btn-secondary:hover { background: rgba(255,255,255,0.10); }
/* Responsive --------------------------------------------------- */
@media (max-width: 1024px) {
.hero-inner, .workflow-grid, .reports-grid, .platforms-visual, .cta-banner { grid-template-columns: 1fr; }
.module-card.feature-card { grid-column: span 2; }
.hero h1 { font-size: 40px; }
.roles-row { grid-template-columns: 140px 1fr repeat(4, 60px); padding: 12px 16px; }
}
@media (max-width: 768px) {
/* Hide decorative app preview on mobile to prevent overflow */
.preview-shell { display: none; }
}
@media (max-width: 600px) {
.hero h1 { font-size: 28px; }
.hero p.lead { font-size: var(--text-md); }
.hero-cta { flex-direction: column; }
.hero-cta .btn { width: 100%; justify-content: center; }
.module-card.feature-card { grid-column: span 1; }
.roles-row { grid-template-columns: 1fr; }
.roles-table .roles-row > *:nth-child(n+3) { display: none; }
}
</style>
<!-- ====================== HERO ====================== -->
<section class="hero">
<div class="container hero-inner">
<div>
<div class="d-inline-flex items-center gap-8 bg-0 border rounded-pill fs-sm text-gray-7 mb-24" style="padding: 6px 14px 6px 8px;">
<span class="badge badge-brand">v{{ changelog[0].version }}</span>
<span>{{ changelog[0].date }} 更新 ·</span>
<span class="text-brand fw-5">查看更新</span>
</div>
<h1>从入库到出库,<br/><em>一套系统</em>管到底。</h1>
<p class="lead">岩美酒库管理系统,为酒行与酒店设计的库存、审核、财务一体化平台。审核驱动业务流,库存与账款自动同步,五端无缝接入。</p>
<div class="d-flex gap-12 mb-28">
<a href="/register/" class="btn btn-primary btn-lg">免费试用 30 天<i data-lucide="arrow-right" class="icon"></i></a>
<a href="/docs/" class="btn btn-secondary btn-lg"><i data-lucide="book-open" class="icon"></i>查看使用手册</a>
</div>
<div class="d-flex items-center gap-18 text-muted fs-sm">
<div class="hero-mini-item d-flex items-center gap-6"><i data-lucide="check" class="icon"></i>无需安装</div>
<div class="hero-mini-item d-flex items-center gap-6"><i data-lucide="check" class="icon"></i>30 天免费</div>
<div class="hero-mini-item d-flex items-center gap-6"><i data-lucide="check" class="icon"></i>支持私有部署</div>
</div>
{# ============ Hero(品牌渐变 = App 登录页品牌面板同源,真实截图) ============ #}
<header class="hero">
<div class="hero-in">
<div class="badge-line">
<b>v{{ changelog[0].version }}</b> · {{ changelog[0].date }} 更新
<a href="/download/#changelog" style="color:inherit;text-decoration:underline">查看更新日志</a>
</div>
<div class="pos-relative">
<div class="preview-shell">
<div class="preview-titlebar">
<div class="preview-dots"><span></span><span></span><span></span></div>
<div class="preview-url">yanmei.app/inventory</div>
</div>
<div class="mock-app">
<aside class="mock-sidebar">
<div class="mock-sidebar-brand">金樽酒行 · S001</div>
<div class="mock-nav-item"><i data-lucide="package-plus" class="icon"></i>入库管理</div>
<div class="mock-nav-item"><i data-lucide="package-minus" class="icon"></i>出库管理</div>
<div class="mock-nav-item active"><i data-lucide="package" class="icon"></i>库存管理</div>
<div class="mock-nav-item"><i data-lucide="wallet" class="icon"></i>财务管理</div>
<div class="mock-nav-item"><i data-lucide="truck" class="icon"></i>往来单位</div>
<div class="mock-nav-item"><i data-lucide="database" class="icon"></i>基础数据</div>
<div class="mock-nav-item"><i data-lucide="settings" class="icon"></i>系统设置</div>
</aside>
<main class="mock-main">
<div class="mock-topbar">
<div class="mock-tabs">
<div class="mock-tab active">库存列表</div>
<div class="mock-tab">库存盘点</div>
<div class="mock-tab">库存流水</div>
</div>
<div class="mock-actions">
<div class="mock-mini-btn"><i data-lucide="download" class="icon"></i>导出</div>
<div class="mock-mini-btn primary"><i data-lucide="clipboard-list" class="icon"></i>新建盘点</div>
</div>
</div>
<div class="mock-stats">
<div class="mock-stat">
<div class="mock-stat-label">库存总量</div>
<div class="mock-stat-value">2,816 <span style="font-size:12px;color:var(--fg-muted);font-weight:400">瓶</span></div>
<div class="mock-stat-delta up">↑ 4.2% 较上月</div>
</div>
<div class="mock-stat">
<div class="mock-stat-label">库存金额</div>
<div class="mock-stat-value">¥486,290</div>
<div class="mock-stat-delta up">↑ 2.8% 较上月</div>
</div>
<div class="mock-stat">
<div class="mock-stat-label">本月入库</div>
<div class="mock-stat-value">128 <span style="font-size:12px;color:var(--fg-muted);font-weight:400">单</span></div>
<div class="mock-stat-delta up">↑ 12 单</div>
</div>
<div class="mock-stat">
<div class="mock-stat-label">预警 SKU</div>
<div class="mock-stat-value" style="color:var(--warning-500)">7</div>
<div class="mock-stat-delta down">↓ 库存不足</div>
</div>
</div>
<div class="mock-table-card">
<div class="mock-table-head">
<div>SKU</div><div>商品名称</div><div>仓库</div><div>库存</div><div>金额</div><div>状态</div>
</div>
<div class="mock-table-row"><div class="sku">WT-501-006</div><div>茅台飞天 53° · 500ml×6</div><div>A 仓</div><div>148</div><div>¥298,400</div><div><span class="mock-pill ok">充足</span></div></div>
<div class="mock-table-row"><div class="sku">XJ-468-001</div><div>五粮液第八代 52° · 500ml</div><div>A 仓</div><div>92</div><div>¥92,920</div><div><span class="mock-pill ok">充足</span></div></div>
<div class="mock-table-row"><div class="sku">XJ-330-024</div><div>剑南春水晶剑 52° · 500ml×6</div><div>B 仓</div><div style="color:var(--warning-700);font-weight:500">12</div><div>¥4,560</div><div><span class="mock-pill warn">偏低</span></div></div>
<div class="mock-table-row"><div class="sku">PJ-750-012</div><div>拉菲传奇波尔多 · 750ml</div><div>A 仓</div><div>64</div><div>¥18,560</div><div><span class="mock-pill ok">充足</span></div></div>
<div class="mock-table-row"><div class="sku">PJ-700-008</div><div>轩尼诗 VSOP · 700ml</div><div>B 仓</div><div>38</div><div>¥22,420</div><div><span class="mock-pill pending">入库中</span></div></div>
</div>
</main>
</div>
</div>
<div class="hero-toast">
<div class="hero-toast-icon"><i data-lucide="check-circle-2" class="icon"></i></div>
<div>
<div class="fw-6 fs-sm text-gray-9 mb-2">入库单已审批</div>
<div class="fs-sm text-muted" style="line-height:1.45">RK20260523000007 库存 +24 瓶<br/>生成应付 ¥48,400</div>
</div>
</div>
<h1>每一瓶酒一个编号,<br/>从入库到利润一套系统管到底。</h1>
<p class="lead">岩美酒库管理系统,为酒行与酒店设计的库存、审核、财务一体化平台。序列号级追溯、审核驱动业务流、成本售价利润实时可见,五端无缝接入。</p>
<div class="cta-row">
<a class="btn primary lg" href="/register/">免费试用 30 天</a>
<a class="btn onhero lg" href="/docs/">查看使用手册</a>
</div>
<div class="mini">
<span><svg class="ic" style="width:14px;height:14px"><use href="#i-check"/></svg>无需绑卡</span>
<span><svg class="ic" style="width:14px;height:14px"><use href="#i-check"/></svg>Web 版免安装</span>
<span><svg class="ic" style="width:14px;height:14px"><use href="#i-check"/></svg>数据多租户隔离</span>
<span><svg class="ic" style="width:14px;height:14px"><use href="#i-check"/></svg>HTTPS 全程加密</span>
</div>
<div class="shot"><img src="/assets/shots/inventory_list.png" alt="库存管理界面 — 真实产品截图" loading="eager" /></div>
</div>
</section>
</header>
<!-- ====================== TRUST ====================== -->
<section class="trust">
<div class="container">
<div class="trust-grid">
<div class="fs-sm text-muted">专为酒水经营场所设计</div>
<div><div class="trust-stat-num">5<sup>端</sup></div><div class="fs-xs text-muted mt-4">Web / iOS / Android / Win / Mac</div></div>
<div><div class="trust-stat-num">多<sup>仓</sup></div><div class="fs-xs text-muted mt-4">支持多仓库独立管理</div></div>
<div><div class="trust-stat-num">FIFO</div><div class="fs-xs text-muted mt-4">批次先进先出追溯</div></div>
<div><div class="trust-stat-num">0</div><div class="fs-xs text-muted mt-4">需安装依赖,Web 即用</div></div>
</div>
</div>
</section>
<!-- ====================== MODULES ====================== -->
{# ============ 核心模块 ============ #}
<section class="section" id="modules">
<div class="container">
<p class="eyebrow">核心模块</p>
<h2 class="section-title">全流程模块,覆盖酒水经营每一个环节</h2>
<p class="section-sub">从商品建档到入库出库、从财务结算到扫码防伪,岩美一站式承载日常运营。</p>
<div class="grid-3 gap-16 mt-48">
<div class="module-card feature-card card p-24 transition">
<div class="icon-box-lg bg-brand-50 text-brand mb-16"><i data-lucide="package-plus" class="icon-md"></i></div>
<h3 class="fs-xl fw-6 text-brand-900 m-0 mb-6 ls-1">入库管理</h3>
<p class="fs-sm lh-16 text-muted m-0">审核驱动的入库流程。草稿、提交、审批、库存更新、应付账款生成 — 一气呵成。</p>
<ul>
<li><i data-lucide="check" class="icon"></i>多商品行明细录入,自动算金额</li>
<li><i data-lucide="check" class="icon"></i>审批通过即同步库存与应付账款</li>
<li><i data-lucide="check" class="icon"></i>支持单据打印、商品标签打印</li>
<li><i data-lucide="check" class="icon"></i>批次号追踪,生产日期可追溯</li>
</ul>
<a href="/features/approval.html" class="fs-xs text-brand mt-12 d-inline-block">了解审核流详情 →</a>
<div class="sec-head">
<div class="eyebrow">核心模块</div>
<h2>全流程覆盖酒水经营的每一个环节</h2>
<p>从供应商进货到客户出库,从批次库存到应收应付,一个系统闭环。</p>
</div>
<div class="grid c4">
<div class="card feature">
<div class="icbox ic-info"><svg class="ic"><use href="#i-download"/></svg></div>
<h3>入库管理</h3>
<p>键盘流内联录单,名称/系列/规格边搜边建;进价可暂估、事后确认自动回填成本与应付。</p>
</div>
<div class="module-card card p-24 transition">
<div class="icon-box-lg bg-brand-50 text-brand mb-16"><i data-lucide="package-minus" class="icon-md"></i></div>
<h3 class="fs-xl fw-6 text-brand-900 m-0 mb-6 ls-1">出库管理</h3>
<p class="fs-sm lh-16 text-muted m-0">出库前自动校验库存,审批通过即扣减库存并生成应收。库存不足,单据无法通过。</p>
<div class="card feature">
<div class="icbox ic-info"><svg class="ic"><use href="#i-upload"/></svg></div>
<h3>出库管理</h3>
<p>从库存选货自动带出可用数量与参考售价,每行利润实时计算;支持先出库后定价。</p>
</div>
<div class="module-card card p-24 transition">
<div class="icon-box-lg bg-brand-50 text-brand mb-16"><i data-lucide="package" class="icon-md"></i></div>
<h3 class="fs-xl fw-6 text-brand-900 m-0 mb-6 ls-1">库存管理</h3>
<p class="fs-sm lh-16 text-muted m-0">实时查询每个 SKU 的库存数量、所在仓库、批次。支持全仓盘点与库存流水。</p>
<a href="/features/inventory.html" class="fs-xs text-brand mt-12 d-inline-block">了解更多 →</a>
<div class="card feature">
<div class="icbox ic-ok"><svg class="ic"><use href="#i-box"/></svg></div>
<h3>批次库存</h3>
<p>每瓶酒独立编号、每批独立成本,库存货值实时汇总;盘点单一键对平账实差异。</p>
</div>
<div class="module-card card p-24 transition">
<div class="icon-box-lg bg-brand-50 text-brand mb-16"><i data-lucide="wallet" class="icon-md"></i></div>
<h3 class="fs-xl fw-6 text-brand-900 m-0 mb-6 ls-1">财务管理</h3>
<p class="fs-sm lh-16 text-muted m-0">审批同步生成应付应收,月度汇总。结清一键完成,可按往来单位筛选导出。</p>
<div class="card feature">
<div class="icbox ic-ok"><svg class="ic"><use href="#i-ic47"/></svg></div>
<h3>财务管理</h3>
<p>应收应付按往来单位自动挂账,收支趋势一图看清;结清、退单自动冲账。</p>
</div>
<div class="module-card card p-24 transition">
<div class="icon-box-lg bg-brand-50 text-brand mb-16"><i data-lucide="truck" class="icon-md"></i></div>
<h3 class="fs-xl fw-6 text-brand-900 m-0 mb-6 ls-1">往来单位</h3>
<p class="fs-sm lh-16 text-muted m-0">供应商与客户统一档案管理。卡号、初始余额、联系信息集中维护。</p>
<div class="card feature">
<div class="icbox ic-wine"><svg class="ic"><use href="#i-ic56"/></svg></div>
<h3>往来单位</h3>
<p>供应商与客户统一管理,拼音首字母秒搜;每家单位的历史单据与欠款一屏尽览。</p>
</div>
<div class="module-card card p-24 transition">
<div class="icon-box-lg bg-brand-50 text-brand mb-16"><i data-lucide="database" class="icon-md"></i></div>
<h3 class="fs-xl fw-6 text-brand-900 m-0 mb-6 ls-1">基础数据</h3>
<p class="fs-sm lh-16 text-muted m-0">商品名称、系列、规格三级字典,单品数量配置。先建字典,后录单据。</p>
<div class="card feature">
<div class="icbox ic-wine"><svg class="ic"><use href="#i-ic57"/></svg></div>
<h3>基础数据</h3>
<p>品牌/型号/版本字典化维护,录单下拉即选;商品图片、介绍库集中管理。</p>
</div>
<div class="module-card card p-24 transition">
<div class="icon-box-lg bg-brand-50 text-brand mb-16"><i data-lucide="settings" class="icon-md"></i></div>
<h3 class="fs-xl fw-6 text-brand-900 m-0 mb-6 ls-1">系统设置</h3>
<p class="fs-sm lh-16 text-muted m-0">用户与权限、多仓库、编号规则、参数与数据导入 — 自助配置,无需开发。</p>
<div class="card feature">
<div class="icbox ic-warn"><svg class="ic"><use href="#i-ic31"/></svg></div>
<h3>扫码看详情</h3>
<p>每瓶酒可打印专属二维码标签,扫码直达商品详情页,链接随手分享给顾客与同行。</p>
</div>
<div class="module-card card p-24 transition">
<div class="icon-box-lg bg-brand-50 text-brand mb-16"><i data-lucide="qr-code" class="icon-md"></i></div>
<h3 class="fs-xl fw-6 text-brand-900 m-0 mb-6 ls-1">扫码防伪</h3>
<p class="fs-sm lh-16 text-muted m-0">每件商品生成专属二维码,顾客扫码可查看商品名称、批次、出售门店与「已通过岩美防伪验证」标识。</p>
<div class="card feature">
<div class="icbox ic-warn"><svg class="ic"><use href="#i-gear"/></svg></div>
<h3>门店设置</h3>
<p>四级角色权限、在线设备管理与强制下线、单号规则、三套界面主题随心切换。</p>
</div>
</div>
</div>
</section>
<!-- ====================== WORKFLOW ====================== -->
<section class="section bg-25" id="workflow">
{# ============ 审核驱动业务流 ============ #}
<section class="section alt" id="workflow">
<div class="container">
<p class="eyebrow">业务流程</p>
<h2 class="section-title">审核驱动业务流,每一笔交易都可追溯</h2>
<p class="section-sub">从单据录入到库存变动、账款生成,每一步都有清晰状态与经办人记录。</p>
<div class="workflow-grid">
<div>
<h3 class="fs-2xl fw-6 text-brand-900 m-0 mb-12">一张入库单的完整生命周期</h3>
<div class="d-flex flex-col gap-14 mt-24">
<div class="wf-step"><div class="wf-num">1</div><div><h4 class="mt-4 mb-4 fs-md fw-6 text-gray-9">录入草稿</h4><p class="m-0 fs-sm text-muted lh-15">选择仓库、供应商、入库日期,逐行录入商品明细。系统自动算金额,可随时保存草稿。</p></div></div>
<div class="wf-step"><div class="wf-num">2</div><div><h4 class="mt-4 mb-4 fs-md fw-6 text-gray-9">提交审核</h4><p class="m-0 fs-sm text-muted lh-15">确认无误后提交。单据进入待审核」,此时不可再编辑,保证数据完整。</p></div></div>
<div class="wf-step active"><div class="wf-num">3</div><div><h4 class="mt-4 mb-4 fs-md fw-6 text-gray-9">审批通过</h4><p class="m-0 fs-sm text-muted lh-15">操作员及以上权限审批。通过后库存自动 +N,同时生成一笔应付账款,账款挂在对应供应商名下。</p></div></div>
<div class="wf-step"><div class="wf-num">4</div><div><h4 class="mt-4 mb-4 fs-md fw-6 text-gray-9">结清账款</h4><p class="m-0 fs-sm text-muted lh-15">财务结算时一键「结清」,财务记录由「未结清」变为「已结清」。账目清晰,按月汇总。</p></div></div>
</div>
<div class="sec-head">
<div class="eyebrow">审核驱动</div>
<h2>每一笔出入库,都要过审核这道关</h2>
<p>草稿随便改,审核过了才动库存与账款——录错不怕,责任清晰。</p>
</div>
<div class="flow">
<div class="step"><div class="num">1</div><b>草稿</b><span>操作员录单,可反复修改、复制行、暂估价</span></div>
<div class="arrow">→</div>
<div class="step"><div class="num">2</div><b>提交审核</b><span>单据锁定进入待审核,支持撤回修改</span></div>
<div class="arrow">→</div>
<div class="step"><div class="num">3</div><b>审核通过</b><span>同一事务更新库存并写入流水、挂应收应付</span></div>
<div class="arrow">→</div>
<div class="step"><div class="num">4</div><b>事后可纠</b><span>退单自动回滚库存与账款,确认进价/售价按差额补账</span></div>
</div>
</div>
</section>
{# ============ 定价与利润 ============ #}
<section class="section">
<div class="container split">
<div>
<div class="sec-head" style="text-align:left;margin:0 0 8px">
<div class="eyebrow" style="justify-content:flex-start">经营看得清</div>
</div>
<div class="flow-vis">
<div class="flow-doc">
<div class="flow-doc-head">
<div class="flow-doc-id">RK20260523000007</div>
<div class="flow-doc-status approved">已审批</div>
</div>
<div class="flow-row head"><div>商品</div><div>规格</div><div>数量</div><div>金额</div></div>
<div class="flow-row"><div>茅台飞天</div><div>500ml×6</div><div>4</div><div>¥8,000</div></div>
<div class="flow-row"><div>五粮液</div><div>500ml</div><div>20</div><div>¥18,400</div></div>
<div class="flow-row"><div>剑南春</div><div>500ml×6</div><div>2</div><div>¥4,200</div></div>
<div class="flow-row"><div>拉菲传奇</div><div>750ml</div><div>10</div><div>¥17,800</div></div>
<div class="flow-row foot"><div>合计</div><div></div><div>36</div><div>¥48,400</div></div>
</div>
<div class="flow-arrow"><i data-lucide="arrow-down" class="icon"></i></div>
<div class="flow-effects">
<div class="flow-effect"><div class="flow-effect-label"><i data-lucide="check-circle-2" class="icon"></i>库存更新</div><div class="flow-effect-val">+36 瓶</div></div>
<div class="flow-effect"><div class="flow-effect-label"><i data-lucide="check-circle-2" class="icon"></i>应付账款</div><div class="flow-effect-val">¥48,400</div></div>
</div>
<h2>成本、售价、利润,<br/>每一单算得明明白白</h2>
<div class="pts">
<div class="pt"><div class="icbox ic-ok"><svg class="ic"><use href="#i-ic47"/></svg></div>
<div><b>建单即见利润</b><span>出库录单时每行利润实时计算,合计利润随手可见。</span></div></div>
<div class="pt"><div class="icbox ic-info"><svg class="ic"><use href="#i-ic03"/></svg></div>
<div><b>成本仅管理员可见</b><span>操作员只看到售价,成本与利润在服务端就被隔离。</span></div></div>
<div class="pt"><div class="icbox ic-warn"><svg class="ic"><use href="#i-ic04"/></svg></div>
<div><b>先出库后定价</b><span>急单先走货,售价确定后一键补录,应收自动按差额调整。</span></div></div>
</div>
</div>
<div class="shot"><img src="/assets/shots/stock_out_list.png" alt="出库管理 — 真实产品截图" loading="lazy" /></div>
</div>
</section>
{# ============ 财务 ============ #}
<section class="section alt">
<div class="container split">
<div class="shot"><img src="/assets/shots/finance.png" alt="财务管理 — 真实产品截图" loading="lazy" /></div>
<div>
<div class="sec-head" style="text-align:left;margin:0 0 8px">
<div class="eyebrow" style="justify-content:flex-start">账款不再靠记</div>
</div>
<h2>应收应付自动挂账,<br/>收支趋势一图看清</h2>
<div class="pts">
<div class="pt"><div class="icbox ic-info"><svg class="ic"><use href="#i-ic07"/></svg></div>
<div><b>出入库自动记账</b><span>审核通过即挂应收/应付,退单自动冲账,无需二次录入。</span></div></div>
<div class="pt"><div class="icbox ic-ok"><svg class="ic"><use href="#i-ic08"/></svg></div>
<div><b>按往来单位对账</b><span>每家供应商、每个客户的未结清金额与流水随点随查。</span></div></div>
<div class="pt"><div class="icbox ic-wine"><svg class="ic"><use href="#i-ic16"/></svg></div>
<div><b>近 30 天滚动口径</b><span>销售、采购、待审核、草稿等经营指标环比一望即知。</span></div></div>
</div>
</div>
</div>
</section>
<!-- ====================== PLATFORMS ====================== -->
{# ============ 五端 ============ #}
<section class="section" id="platforms">
<div class="container">
<p class="eyebrow">多端覆盖</p>
<h2 class="section-title">一个账号,五端同步</h2>
<p class="section-sub">办公室开台账,仓库扫码盘点,老板出差查看实时报表。同一份数据,任意终端访问。</p>
<div class="grid-5 gap-12 mt-48">
<div class="platform-card card py-24 px-16 text-center transition"><i data-lucide="globe" class="icon-xl text-brand mx-auto mb-12"></i><h4 class="m-0 mb-4 fs-md fw-6 text-brand-900">Web</h4><p class="m-0 fs-xs text-muted">主流浏览器,无需安装</p></div>
<div class="platform-card card py-24 px-16 text-center transition"><i data-lucide="monitor" class="icon-xl text-brand mx-auto mb-12"></i><h4 class="m-0 mb-4 fs-md fw-6 text-brand-900">Windows</h4><p class="m-0 fs-xs text-muted">桌面客户端</p></div>
<div class="platform-card card py-24 px-16 text-center transition"><i data-lucide="laptop" class="icon-xl text-brand mx-auto mb-12"></i><h4 class="m-0 mb-4 fs-md fw-6 text-brand-900">macOS</h4><p class="m-0 fs-xs text-muted">原生应用</p></div>
<div class="platform-card card py-24 px-16 text-center transition"><i data-lucide="smartphone" class="icon-xl text-brand mx-auto mb-12"></i><h4 class="m-0 mb-4 fs-md fw-6 text-brand-900">iOS</h4><p class="m-0 fs-xs text-muted">TestFlight 内测中</p></div>
<div class="platform-card card py-24 px-16 text-center transition"><i data-lucide="tablet-smartphone" class="icon-xl text-brand mx-auto mb-12"></i><h4 class="m-0 mb-4 fs-md fw-6 text-brand-900">Android</h4><p class="m-0 fs-xs text-muted">手机 / 平板</p></div>
<div class="sec-head">
<div class="eyebrow">多端同步</div>
<h2>一个账号,五端同步</h2>
<p>店里用 Windows 收银机、仓库用手机、老板在家用 Mac——数据实时一致。</p>
</div>
<div class="platforms-visual">
<div class="pv-text">
<h3>仓库里也能用得顺手</h3>
<p>移动端扫码即查库存、入库、出库,断网也能继续工作,恢复网络后自动同步。</p>
<div class="d-flex flex-col gap-10">
<div class="pv-feature"><i data-lucide="scan-line" class="icon"></i>扫码即查商品 · 商品二维码防伪</div>
<div class="pv-feature"><i data-lucide="wifi-off" class="icon"></i>离线缓存,断网继续作业</div>
<div class="pv-feature"><i data-lucide="layout-grid" class="icon"></i>窄屏自动切换卡片流布局</div>
<div class="pv-feature"><i data-lucide="camera" class="icon"></i>手机拍照直接添加商品图片</div>
</div>
</div>
<div class="device-stack">
<div class="device-laptop">
<div class="device-laptop-screen">
<div class="device-laptop-mock-row brand"></div>
<div class="device-laptop-mock-row short"></div>
<div class="device-laptop-mock-row"></div>
<div class="device-laptop-mock-row short"></div>
<div class="device-laptop-mock-row"></div>
<div class="device-laptop-mock-row short"></div>
<div class="device-laptop-mock-row"></div>
<div class="device-laptop-mock-row short"></div>
<div class="d-flex gap-6 mt-10">
<div style="flex:1;height:50px;background:var(--gray-100);border-radius:3px"></div>
<div style="flex:1;height:50px;background:var(--brand-100);border-radius:3px"></div>
<div style="flex:1;height:50px;background:var(--gray-100);border-radius:3px"></div>
</div>
</div>
</div>
<div class="device-phone">
<div class="device-phone-screen">
<div class="top"></div>
<div style="height:8px;background:var(--gray-200);border-radius:2px;margin-bottom:4px;width:70%"></div>
<div style="height:8px;background:var(--gray-200);border-radius:2px;margin-bottom:8px;width:50%"></div>
<div style="background:var(--gray-0);border:1px solid var(--gray-200);border-radius:4px;padding:6px;margin-bottom:4px"><div style="height:6px;background:var(--brand-300);border-radius:1px;width:60%;margin-bottom:3px"></div><div style="height:4px;background:var(--gray-200);border-radius:1px;width:80%"></div></div>
<div style="background:var(--gray-0);border:1px solid var(--gray-200);border-radius:4px;padding:6px;margin-bottom:4px"><div style="height:6px;background:var(--brand-300);border-radius:1px;width:50%;margin-bottom:3px"></div><div style="height:4px;background:var(--gray-200);border-radius:1px;width:70%"></div></div>
<div style="background:var(--gray-0);border:1px solid var(--gray-200);border-radius:4px;padding:6px;margin-bottom:4px"><div style="height:6px;background:var(--brand-300);border-radius:1px;width:55%;margin-bottom:3px"></div><div style="height:4px;background:var(--gray-200);border-radius:1px;width:75%"></div></div>
<div style="position:absolute;bottom:8px;left:8px;right:8px;background:var(--brand-500);height:22px;border-radius:4px;display:grid;place-items:center"><div style="height:6px;background:rgba(255,255,255,0.9);border-radius:1px;width:50%"></div></div>
</div>
</div>
</div>
<div class="grid plat5">
<div class="card feature" style="text-align:center"><div class="icbox ic-info" style="margin:0 auto 12px"><svg class="ic"><use href="#i-ic63"/></svg></div><h3>Windows</h3><p>安装包一键装机</p></div>
<div class="card feature" style="text-align:center"><div class="icbox ic-info" style="margin:0 auto 12px"><svg class="ic"><use href="#i-ic25"/></svg></div><h3>macOS</h3><p>已签名公证</p></div>
<div class="card feature" style="text-align:center"><div class="icbox ic-ok" style="margin:0 auto 12px"><svg class="ic"><use href="#i-ic01"/></svg></div><h3>Web 版</h3><p>浏览器直接用,零安装</p></div>
<div class="card feature" style="text-align:center"><div class="icbox ic-warn" style="margin:0 auto 12px"><svg class="ic"><use href="#i-ic43"/></svg></div><h3>Android</h3><p>APK 直装</p></div>
<div class="card feature" style="text-align:center"><div class="icbox ic-wine" style="margin:0 auto 12px"><svg class="ic"><use href="#i-ic22"/></svg></div><h3>iOS</h3><p>TestFlight 内测中</p></div>
</div>
<div style="text-align:center;margin-top:28px"><a class="btn primary" href="/download/">前往下载 <svg class="ic" style="width:15px;height:15px"><use href="#i-chevron-right"/></svg></a></div>
</div>
</section>
<!-- ====================== REPORTS ====================== -->
<section class="section bg-25" id="reports">
{# ============ 安全与权限 ============ #}
<section class="section alt" id="security">
<div class="container">
<p class="eyebrow">数据洞察</p>
<h2 class="section-title">把每一瓶酒的进出,转成可分析的数据</h2>
<p class="section-sub">报表自动生成,可按日、按月、按往来单位维度切换。导出 Excel 一键完成。</p>
<div class="reports-grid">
<div class="card p-24 shadow-sm">
<div class="report-head">
<div><h4 class="fs-md fw-6 text-gray-9 m-0">本月财务流水</h4><p class="fs-xs text-muted mt-2 m-0">2026 年 5 月 · 应付与应收对比</p></div>
<div class="report-tabs">
<div class="report-tab">日</div>
<div class="report-tab active">月</div>
<div class="report-tab">季</div>
</div>
</div>
<div class="chart">
<svg viewBox="0 0 600 200" preserveAspectRatio="none">
<line x1="0" y1="50" x2="600" y2="50" stroke="#ECEFF4" stroke-width="1"/>
<line x1="0" y1="100" x2="600" y2="100" stroke="#ECEFF4" stroke-width="1"/>
<line x1="0" y1="150" x2="600" y2="150" stroke="#ECEFF4" stroke-width="1"/>
<path d="M 20 140 L 80 110 L 140 130 L 200 90 L 260 100 L 320 70 L 380 85 L 440 50 L 500 65 L 560 40 L 560 190 L 20 190 Z" fill="rgba(37,99,172,0.10)" />
<path d="M 20 140 L 80 110 L 140 130 L 200 90 L 260 100 L 320 70 L 380 85 L 440 50 L 500 65 L 560 40" fill="none" stroke="#2563AC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M 20 160 L 80 140 L 140 145 L 200 120 L 260 130 L 320 105 L 380 115 L 440 90 L 500 95 L 560 70 L 560 190 L 20 190 Z" fill="rgba(139,35,49,0.08)" />
<path d="M 20 160 L 80 140 L 140 145 L 200 120 L 260 130 L 320 105 L 380 115 L 440 90 L 500 95 L 560 70" fill="none" stroke="#8B2331" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="560" cy="40" r="4" fill="#2563AC" />
<circle cx="560" cy="40" r="8" fill="rgba(37,99,172,0.20)" />
<circle cx="560" cy="70" r="4" fill="#8B2331" />
</svg>
<div class="d-flex gap-16 fs-xs text-muted mt-8">
<span class="d-flex items-center gap-4"><span style="width:8px;height:8px;background:var(--brand-500);border-radius:50%"></span>应付账款</span>
<span class="d-flex items-center gap-4"><span style="width:8px;height:8px;background:var(--accent-500);border-radius:50%"></span>应收账款</span>
</div>
</div>
<div class="chart-stats">
<div><div class="fs-xs text-muted mb-4">本月应付</div><div class="fs-lg fw-6 text-brand-900 tabular">¥248,600<small>↑ 12%</small></div></div>
<div><div class="fs-xs text-muted mb-4">本月应收</div><div class="fs-lg fw-6 text-brand-900 tabular">¥182,400<small>↑ 8%</small></div></div>
<div><div class="fs-xs text-muted mb-4">已结清率</div><div class="fs-lg fw-6 text-brand-900 tabular">86.4%<small>↑ 4%</small></div></div>
</div>
</div>
<div class="d-flex flex-col gap-24">
<div class="d-grid gap-16" style="grid-template-columns:36px 1fr">
<div class="icon-box-md bg-brand-50 text-brand"><i data-lucide="bar-chart-3" class="icon-md"></i></div>
<div><h4 class="m-0 mb-4 fs-lg fw-6 text-brand-900">财务汇总自动生成</h4><p class="m-0 fs-sm text-muted lh-16">每月应付应收按往来单位、按时间维度自动汇总。无需手工统计,月底一键导出。</p></div>
</div>
<div class="d-grid gap-16" style="grid-template-columns:36px 1fr">
<div class="icon-box-md bg-brand-50 text-brand"><i data-lucide="trending-down" class="icon-md"></i></div>
<div><h4 class="m-0 mb-4 fs-lg fw-6 text-brand-900">损耗趋势可见</h4><p class="m-0 fs-sm text-muted lh-16">每次盘点的账实差异自动累计为损耗数据,结合周转率帮你发现异常 SKU。</p></div>
</div>
<div class="d-grid gap-16" style="grid-template-columns:36px 1fr">
<div class="icon-box-md bg-brand-50 text-brand"><i data-lucide="file-spreadsheet" class="icon-md"></i></div>
<div><h4 class="m-0 mb-4 fs-lg fw-6 text-brand-900">Excel 进出自由</h4><p class="m-0 fs-sm text-muted lh-16">历史数据可批量导入,月度报表可一键导出。与会计、税务系统无缝衔接。</p></div>
</div>
<div class="d-grid gap-16" style="grid-template-columns:36px 1fr">
<div class="icon-box-md bg-brand-50 text-brand"><i data-lucide="alert-triangle" class="icon-md"></i></div>
<div><h4 class="m-0 mb-4 fs-lg fw-6 text-brand-900">库存状态实时标识</h4><p class="m-0 fs-sm text-muted lh-16">设置商品最低库存量,系统自动标记「充足 / 偏低 / 告急」,一眼识别需补货的 SKU。</p></div>
</div>
</div>
<div class="sec-head">
<div class="eyebrow">安全与权限</div>
<h2>四级角色,数据各归其位</h2>
<p>多租户隔离、HTTPS 全程加密、登录设备可管控,敏感经营数据只给该看的人。</p>
</div>
<table class="roles">
<thead><tr><th>能力</th><th>超级管理员</th><th>管理员</th><th>操作员</th><th>只读</th></tr></thead>
<tbody>
<tr><td>录单 / 提交审核</td><td class="y">✓</td><td class="y">✓</td><td class="y">✓</td><td class="n">—</td></tr>
<tr><td>审核 / 退单 / 确认价格</td><td class="y">✓</td><td class="y">✓</td><td class="n">—</td><td class="n">—</td></tr>
<tr><td>查看成本与利润</td><td class="y">✓</td><td class="y">✓</td><td class="n">—</td><td class="n">—</td></tr>
<tr><td>用户与设备管理</td><td class="y">✓</td><td class="y">✓</td><td class="n">—</td><td class="n">—</td></tr>
<tr><td>查看单据与库存</td><td class="y">✓</td><td class="y">✓</td><td class="y">✓</td><td class="y">✓</td></tr>
</tbody>
</table>
</div>
</section>
<!-- ====================== SECURITY ====================== -->
<section class="section bg-25">
<div class="container">
<p class="eyebrow">安全与权限</p>
<h2 class="section-title">企业级权限控制,操作可追溯</h2>
<p class="section-sub">四级角色细分权限,每一次单据操作都有经办人记录。数据备份与审批留痕,按合规要求设计。</p>
<div class="grid-4 gap-16 mt-48">
<div class="card p-24"><i data-lucide="users" class="icon-lg text-brand mb-14"></i><h4 class="fs-lg fw-6 text-brand-900 m-0 mb-6">四级角色</h4><p class="m-0 fs-sm text-muted lh-15">超级管理员、管理员、操作员、只读。按岗位分配,最小权限原则。</p></div>
<div class="card p-24"><i data-lucide="file-clock" class="icon-lg text-brand mb-14"></i><h4 class="fs-lg fw-6 text-brand-900 m-0 mb-6">操作审计</h4><p class="m-0 fs-sm text-muted lh-15">谁、什么时候、做了什么 — 每一笔单据都有经办人与时间戳。</p></div>
<div class="card p-24"><i data-lucide="shield-check" class="icon-lg text-brand mb-14"></i><h4 class="fs-lg fw-6 text-brand-900 m-0 mb-6">审批留痕</h4><p class="m-0 fs-sm text-muted lh-15">审批不可撤销,单据从草稿到结清的每一步状态完整保留。</p></div>
<div class="card p-24"><i data-lucide="hard-drive" class="icon-lg text-brand mb-14"></i><h4 class="fs-lg fw-6 text-brand-900 m-0 mb-6">每日备份</h4><p class="m-0 fs-sm text-muted lh-15">云端每日自动备份,支持私有部署。数据始终在你的控制下。</p></div>
</div>
<div class="roles-table">
<div class="roles-row head">
<div>角色</div><div>说明</div>
<div class="text-center">查看</div><div class="text-center">录入</div>
<div class="text-center">审批</div><div class="text-center">设置</div>
</div>
<div class="roles-row"><div class="fw-6 text-brand-900">超级管理员</div><div class="text-muted fs-sm">系统最高权限,含数据清空</div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check"><i data-lucide="check" class="icon"></i></div></div>
<div class="roles-row"><div class="fw-6 text-brand-900">管理员</div><div class="text-muted fs-sm">门店管理人员,含用户与门店设置</div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check"><i data-lucide="check" class="icon"></i></div></div>
<div class="roles-row"><div class="fw-6 text-brand-900">操作员</div><div class="text-muted fs-sm">日常录入与审核单据</div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check no"><i data-lucide="minus" class="icon"></i></div></div>
<div class="roles-row"><div class="fw-6 text-brand-900">只读</div><div class="text-muted fs-sm">仅查看,不可修改任何数据</div><div class="role-check"><i data-lucide="check" class="icon"></i></div><div class="role-check no"><i data-lucide="minus" class="icon"></i></div><div class="role-check no"><i data-lucide="minus" class="icon"></i></div><div class="role-check no"><i data-lucide="minus" class="icon"></i></div></div>
</div>
</div>
</section>
<!-- ====================== PRICING ====================== -->
{# ============ 定价 ============ #}
<section class="section" id="pricing">
<div class="container">
<p class="eyebrow">价格</p>
<h2 class="section-title">按门店付费,明码标价</h2>
<p class="section-sub">所有方案均含全功能、五端同步、无限单据、技术支持。仅在用户数、门店数、部署方式上区分。</p>
<div class="grid-3 gap-16 mt-48">
<div class="card d-flex flex-col" style="padding:32px 28px">
<div class="fs-sm text-muted fw-5 mb-4">入门方案</div>
<div class="fs-2xl fw-6 text-brand-900 mb-16">试用版</div>
<div class="price-amount"><span class="currency">¥</span><span class="num">0</span><span class="unit">/ 30 天</span></div>
<div class="fs-sm text-muted mb-24">体验完整功能,无需绑卡</div>
<ul class="price-features d-flex flex-col gap-10 p-0 m-0 mb-32" style="list-style:none">
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>单门店 · 最多 3 用户</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>全部模块开放</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>Web 端 + 移动端</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>微信技术支持</li>
<div class="sec-head">
<div class="eyebrow">价格方案</div>
<h2>按门店付费,明码标价</h2>
<p>先免费试用,再按需选档;时长兑换券续期,无隐藏费用、无自动扣款。</p>
</div>
<div class="plans p4">
<div class="plan">
<div class="pname">免费版</div>
<div class="price">¥0<small> / 30 天</small></div>
<div class="psub">注册即开通,无需绑卡</div>
<ul>
<li><svg class="ic"><use href="#i-check"/></svg>全功能试用 30 天</li>
<li><svg class="ic"><use href="#i-check"/></svg>五端同步</li>
<li><svg class="ic"><use href="#i-check"/></svg>到期数据保留 90 天</li>
</ul>
<a href="/register/" class="btn btn-secondary w-full justify-center mt-auto">免费开通</a>
<a class="btn ghost" data-plan="free" href="/register/">立即试用</a>
</div>
<div class="price-card featured card d-flex flex-col" style="padding:32px 28px">
<div class="fs-sm text-muted fw-5 mb-4">标准方案</div>
<div class="fs-2xl fw-6 text-brand-900 mb-16">单店版</div>
<div class="price-amount"><span class="currency">¥</span><span class="num">299</span><span class="unit">/ 月</span></div>
<div class="fs-sm text-muted mb-24">年付折扣 ¥2,988 / 年(约省 ¥600</div>
<ul class="price-features d-flex flex-col gap-10 p-0 m-0 mb-32" style="list-style:none">
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>单门店 · 最多 10 用户</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>全部模块 + 五端同步</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>无限单据、无限商品</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>云端每日备份</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>工作日 9-18 点支持</li>
<div class="plan hot">
<div class="pname">标准</div>
<div class="price">¥299<small> / 月</small></div>
<div class="psub">年付 ¥2,999,立省两个月</div>
<ul>
<li><svg class="ic"><use href="#i-check"/></svg>单门店 · 单仓库</li>
<li><svg class="ic"><use href="#i-check"/></svg>2 台客户端同时使用</li>
<li><svg class="ic"><use href="#i-check"/></svg>1,000 张商品图片分享</li>
<li><svg class="ic"><use href="#i-check"/></svg>扫码详情分享 + 标签打印</li>
</ul>
<a href="/register/" class="btn btn-primary w-full justify-center mt-auto">免费试用 30 天</a>
<a class="btn primary" data-plan="standard" href="/register/">开通门店</a>
</div>
<div class="price-card custom card d-flex flex-col" style="padding:32px 28px">
<div class="fs-sm text-muted fw-5 mb-4">企业方案</div>
<div class="fs-2xl fw-6 text-brand-900 mb-16">连锁版</div>
<div class="price-amount"><span class="num">定制</span></div>
<div class="fs-sm text-muted mb-24">多门店连锁,含私有部署</div>
<ul class="price-features d-flex flex-col gap-10 p-0 m-0 mb-32" style="list-style:none">
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>多门店 · 总部数据汇总</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>不限用户数</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>支持私有部署 / 内网</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>专属实施与培训服务</li>
<li class="d-flex items-start gap-8 fs-sm text-gray-7"><i data-lucide="check" class="icon-sm text-brand flex-shrink-0 mt-2"></i>7×24 专属客户经理</li>
<div class="plan">
<div class="pname">高级版</div>
<div class="price">¥599<small> / 月</small></div>
<div class="psub">年付 ¥5,999,立省两个月</div>
<ul>
<li><svg class="ic"><use href="#i-check"/></svg>单门店 · 多仓库</li>
<li><svg class="ic"><use href="#i-check"/></svg>5 台客户端同时使用</li>
<li><svg class="ic"><use href="#i-check"/></svg>10,000 张商品图片分享</li>
<li><svg class="ic"><use href="#i-check"/></svg>免费 AI 周度 / 月度商业数据分析</li>
</ul>
{% if site.support.wechat %}<a href="#" class="btn btn-secondary w-full justify-center mt-auto">微信咨询:{{ site.support.wechat }}</a>{% else %}<a href="mailto:{{ site.support.email }}" class="btn btn-secondary w-full justify-center mt-auto">联系销售</a>{% endif %}
<a class="btn primary" data-plan="pro" href="/register/">开通门店</a>
</div>
<div class="plan">
<div class="pname">定制版</div>
<div class="price">定制</div>
<div class="psub">连锁多店、私有部署、专属支持</div>
<ul>
<li><svg class="ic"><use href="#i-check"/></svg>多门店数据隔离</li>
<li><svg class="ic"><use href="#i-check"/></svg>客户端与图片配额按需扩展</li>
<li><svg class="ic"><use href="#i-check"/></svg>可私有化部署</li>
<li><svg class="ic"><use href="#i-check"/></svg>一对一迁移协助</li>
</ul>
<a class="btn ghost" href="mailto:{{ site.support.email }}">联系我们</a>
</div>
</div>
</div>
</section>
<!-- ====================== FAQ ====================== -->
<section class="section bg-25" id="faq">
{# ============ FAQ ============ #}
<section class="section alt" id="faq">
<div class="container">
<p class="eyebrow">常见问题</p>
<h2 class="section-title">购买前常见问题</h2>
<div class="mt-48" style="max-width:880px">
<details class="card mb-12 overflow-hidden" open>
<summary class="faq-q">支持哪些操作系统和设备?<i data-lucide="plus" class="icon"></i></summary>
<div class="fs-md text-muted lh-17" style="padding:0 24px 22px">系统支持 Windows、macOS 桌面客户端,iOS 与 Android 移动 App,以及主流浏览器的 Web 版本。同一账号可在五端同步使用,数据实时更新。</div>
</details>
<details class="card mb-12 overflow-hidden">
<summary class="faq-q">没有网络时还能操作吗?<i data-lucide="plus" class="icon"></i></summary>
<div class="fs-md text-muted lh-17" style="padding:0 24px 22px">网络中断时,系统会自动切换到离线模式,展示上次加载的缓存数据。库存查询与单据录入可继续,恢复网络后自动同步至云端。审批与生成账款类操作需要网络。</div>
</details>
<details class="card mb-12 overflow-hidden">
<summary class="faq-q">能否从原有 Excel / 旧系统迁移数据?<i data-lucide="plus" class="icon"></i></summary>
<div class="fs-md text-muted lh-17" style="padding:0 24px 22px">可以。「系统设置 → 数据导入」支持往来单位、商品名称/系列/规格、库存等数据的批量 Excel 导入,按提供的模板填写即可。导入过程中会显示成功条数与失败原因。</div>
</details>
<details class="card mb-12 overflow-hidden">
<summary class="faq-q">审批通过后发现错误,怎么办?<i data-lucide="plus" class="icon"></i></summary>
<div class="fs-md text-muted lh-17" style="padding:0 24px 22px">审批通过后操作不可撤销。如发现录入错误,可由具备权限的用户新建一张反向单据(如:误入 10 箱则再出库 10 箱)来修正库存与应收应付,审批链完整保留。</div>
</details>
<details class="card mb-12 overflow-hidden">
<summary class="faq-q">数据安全如何保障?<i data-lucide="plus" class="icon"></i></summary>
<div class="fs-md text-muted lh-17" style="padding:0 24px 22px">云端版采用每日自动备份策略,数据传输全程 HTTPS 加密。企业版支持私有部署,数据存放在你自己的服务器,岩美不接触任何业务数据。</div>
</details>
<details class="card mb-12 overflow-hidden">
<summary class="faq-q">可以试用多长时间?<i data-lucide="plus" class="icon"></i></summary>
<div class="fs-md text-muted lh-17" style="padding:0 24px 22px">注册后即可免费试用 30 天,全部功能开放、五端可用,无需绑定支付方式。30 天到期后,升级到单店版(¥299/月)即可继续使用,已录入的数据完整保留。</div>
</details>
<details class="card mb-12 overflow-hidden">
<summary class="faq-q">只读账号能做什么?<i data-lucide="plus" class="icon"></i></summary>
<div class="fs-md text-muted lh-17" style="padding:0 24px 22px">只读账号可查看全部数据,包括单据、库存、财务、报表,并可导出 Excel。但不能新建、修改、审批、删除任何数据。适合财务、税务、审计岗位的查阅需求。</div>
</details>
<div class="sec-head">
<div class="eyebrow">常见问题</div>
<h2>你可能想问</h2>
</div>
<div class="faq">
<details><summary>支持哪些设备和系统?</summary><p>Windows、macOS、Android、iOSTestFlight 内测)以及免安装的 Web 版,一个账号五端数据实时同步。详见<a href="/system-requirements/">系统要求</a>。</p></details>
<details><summary>老系统的数据能搬过来吗?</summary><p>支持 Excel 批量导入商品、往来单位、历史出入库单与库存;定制版提供一对一迁移协助。</p></details>
<details><summary>录错单怎么办?</summary><p>草稿随时改;已提交可撤回;审核通过后支持整单或按行退单,库存与账款自动回滚——不需要手工冲账。</p></details>
<details><summary>进货价还没谈妥能先入库吗?</summary><p>可以。进价留空按暂估入库,价格确定后用「确认进价」补录,系统自动回填库存成本、已出库成本快照与应付差额。出库同理支持先出货后定价。</p></details>
<details><summary>店员能看到我的进货成本吗?</summary><p>不能。成本价与利润仅管理员可见,操作员的界面和接口数据都不包含成本——这是服务端级别的隔离,不是前端隐藏。</p></details>
<details><summary>数据安全吗?</summary><p>每家门店数据完全隔离,全程 HTTPS 加密传输,登录设备可管控、可强制下线;数据库每日备份。</p></details>
<details><summary>试用到期后数据会丢吗?</summary><p>不会。到期后进入只读模式,数据保留 90 天,兑换续期后立即恢复完整功能。</p></details>
</div>
</div>
</section>
<!-- ====================== CTA ====================== -->
<section>
{# ============ CTA ============ #}
<section class="section">
<div class="container">
<div class="cta-banner">
<div>
<h2>用一杯咖啡的时间,<br/>看看你的酒库到底有多少瓶。</h2>
<p>30 天免费试用,无需绑卡。任何时间可取消,已录入数据保留 90 天。</p>
</div>
<div class="d-flex flex-col gap-12 items-start">
<a href="/register/" class="btn btn-primary btn-lg">立即开通试用<i data-lucide="arrow-right" class="icon"></i></a>
{% if site.support.wechat %}<a href="#" class="btn btn-secondary btn-lg"><i data-lucide="message-circle" class="icon"></i>微信咨询:{{ site.support.wechat }}</a>{% else %}<a href="mailto:{{ site.support.email }}?subject=咨询岩美连锁版" class="btn btn-secondary btn-lg"><i data-lucide="mail" class="icon"></i>邮件咨询</a>{% endif %}
</div>
<h2>用一杯咖啡的时间,看清你的酒库到底值多少钱。</h2>
<p>30 天免费试用 · 无需绑卡 · 任何时间可取消,已录入数据保留 90 天</p>
<a class="btn lg" style="background:#fff;color:var(--primary-dark)" href="/register/">免费注册门店</a>
</div>
</div>
</section>
<script>
/* 价格区登录态:已登录(与 Web 版 App 共享 localStorage flutter.*)时,
标准/高级按钮 → 「购买 / 续费」进 /checkout/;免费版按钮 → 「进入系统」。 */
(function () {
if (!localStorage.getItem('flutter.access_token')) return;
document.querySelectorAll('[data-plan]').forEach(function (a) {
var plan = a.dataset.plan;
if (plan === 'free') {
a.textContent = '进入系统';
a.href = '/app/';
} else {
a.textContent = '购买 / 续费';
a.href = '/checkout/?plan=' + plan;
}
});
})();
</script>
+77
View File
@@ -0,0 +1,77 @@
---
layout: base.njk
title: 支付结果
description: 支付宝支付完成后的到账确认页。
permalink: /license/result/
---
{# pay 回跳页(return_urlpay 拼 out_trade_no):轮询购买单状态,到账以 webhook 续期为准。 #}
<div class="container" style="padding:64px 24px 96px">
<div class="result" id="waiting">
<div class="ring wait"><svg class="ic"><use href="#i-ic11"/></svg></div>
<h2>确认到账中…</h2>
<p>支付完成后到账通常在数秒内确认,请稍候。<br/>本页会自动刷新结果,无需手动操作。</p>
</div>
<div class="result" id="ok" style="display:none">
<div class="ring good"><svg class="ic"><use href="#i-check"/></svg></div>
<h2>支付成功,授权已续期</h2>
<p id="okDetail">门店授权已更新。</p>
<div class="ono">订单号 <b id="orderNo">—</b></div>
<div class="acts">
<a class="btn primary lg" href="/app/">进入系统</a>
<a class="btn ghost lg" href="/#pricing">返回套餐</a>
</div>
</div>
<div class="result" id="slow" style="display:none">
<div class="ring wait"><svg class="ic"><use href="#i-ic11"/></svg></div>
<h2>到账确认中</h2>
<p>付款已提交,但到账确认比平时慢一些。<br/>授权会在到账后自动续期——稍后可在 App「系统设置 → 授权」查看,无需重复付款。</p>
<div class="acts">
<a class="btn primary lg" href="/app/">进入系统</a>
<a class="btn ghost lg" href="mailto:{{ site.support.email }}">联系支持</a>
</div>
</div>
</div>
<script>
(function () {
var API = '{{ site.appBaseUrl }}';
function $(id) { return document.getElementById(id); }
function token() {
var raw = localStorage.getItem('flutter.access_token');
if (!raw) return null;
try { return JSON.parse(raw); } catch (e) { return raw; }
}
var otn = new URLSearchParams(location.search).get('out_trade_no');
if (!otn || !token()) { $('waiting').style.display = 'none'; $('slow').style.display = 'block'; return; }
var tries = 0, maxTries = 30; // 2s × 30 = 60s
function poll() {
tries++;
fetch(API + '/api/v1/license/purchase/' + encodeURIComponent(otn), {
headers: { 'Authorization': 'Bearer ' + token() },
})
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (j) {
var d = j && j.data;
if (d && d.status === 'paid') {
$('orderNo').textContent = otn;
if (d.expires_at) {
$('okDetail').textContent = '门店授权已续期至 ' + new Date(d.expires_at).toLocaleDateString('zh-CN') + '。';
}
$('waiting').style.display = 'none';
$('ok').style.display = 'block';
return;
}
next();
})
.catch(next);
}
function next() {
if (tries >= maxTries) { $('waiting').style.display = 'none'; $('slow').style.display = 'block'; return; }
setTimeout(poll, 2000);
}
poll();
})();
</script>
+84
View File
@@ -0,0 +1,84 @@
---
layout: base.njk
title: 登录
description: 登录岩美酒库管理系统。
permalink: /login/
---
{# 官网内登录(不跳 App):POST /api/v1/auth/login,成功后把凭证写入与
Web 版 Appshared_preferences_web)共享的 localStorage['flutter.*']
(值为 JSON 编码,与插件格式一致),再进入 /app/ 即已登录。 #}
<div class="auth-wrap">
<div class="auth">
<div class="auth-brand">
<img class="mk" src="/assets/logo-mark.svg" alt="岩美酒库" />
<div class="bname">岩美酒库</div>
<div class="btag">酒水进销存 · 审核驱动业务流</div>
<div class="pts">
<div class="pt"><span class="pi"><svg class="ic"><use href="#i-box"/></svg></span>入库出库 · 审核后实时改库存</div>
<div class="pt"><span class="pi"><svg class="ic"><use href="#i-ic08"/></svg></span>往来对账 · 财务一目了然</div>
<div class="pt"><span class="pi"><svg class="ic"><use href="#i-ic23"/></svg></span>多端协同 · 多租户安全隔离</div>
</div>
</div>
<div class="auth-form">
<h1>登录</h1>
<p class="alead">欢迎回来,请输入账号信息</p>
<div class="auth-alert" id="loginErr"></div>
<div class="field"><label>门店编号<span class="req">*</span></label><input class="input" id="shop" placeholder="例如 S000001" autocomplete="organization" /></div>
<div class="field"><label>登录账号<span class="req">*</span></label><input class="input" id="user" placeholder="请输入登录账号" autocomplete="username" /></div>
<div class="field"><label>密码<span class="req">*</span></label><input class="input" id="pass" type="password" placeholder="请输入密码" autocomplete="current-password" /></div>
<button class="btn primary lg" id="loginBtn" style="margin-top:8px">登 录</button>
<div class="auth-foot">还没有门店账号?<a href="/register/" style="font-weight:600">注册新门店 →</a></div>
<div class="auth-foot" style="margin-top:6px;color:var(--faint);font-size:var(--fs-xs)">岩美酒库 © 2026 · 仅授权门店可登录</div>
</div>
</div>
</div>
<script>
(function () {
var API = '{{ site.appBaseUrl }}';
function $(id) { return document.getElementById(id); }
function showErr(msg) { var el = $('loginErr'); el.textContent = msg; el.classList.add('show'); }
// 与 shared_preferences_web 同格式(值 JSON 编码),Web 版 App 可直接恢复会话
function persist(data, shopNo) {
var u = data.user || {};
var kv = {
access_token: data.access_token, refresh_token: data.refresh_token,
user_id: u.id || 0, username: u.username || '', real_name: u.real_name || '',
shop_no: shopNo, shop_id: String(data.shop_id || ''), role: u.role || 'operator',
};
Object.keys(kv).forEach(function (k) {
localStorage.setItem('flutter.' + k, JSON.stringify(kv[k]));
});
}
function doLogin() {
var shop = $('shop').value.trim(), user = $('user').value.trim(), pass = $('pass').value;
if (!shop) return showErr('请输入门店编号');
if (!user) return showErr('请输入登录账号');
if (!pass) return showErr('请输入密码');
$('loginErr').classList.remove('show');
var btn = $('loginBtn'); btn.disabled = true; btn.textContent = '登录中…';
fetch(API + '/api/v1/auth/login', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ shop_code: shop, username: user, password: pass }),
})
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, j: j }; }); })
.then(function (res) {
if (!res.ok) throw new Error((res.j && res.j.error) || '登录失败,请检查账号信息');
persist(res.j.data || {}, shop);
// ?next= 仅允许站内路径(防开放跳转)
var next = new URLSearchParams(location.search).get('next') || '';
location.href = (next.charAt(0) === '/' && next.indexOf('//') !== 0) ? next : '/app/';
})
.catch(function (e) {
showErr(e && e.message ? e.message : '无法连接服务器,请稍后重试');
btn.disabled = false; btn.textContent = '登 录';
});
}
$('loginBtn').addEventListener('click', doLogin);
['shop', 'user', 'pass'].forEach(function (id) {
$(id).addEventListener('keydown', function (e) { if (e.key === 'Enter') doLogin(); });
});
})();
</script>
+92 -309
View File
@@ -1,327 +1,110 @@
---
layout: base.njk
title: 注册新门店
description: 填写门店信息,创建岩美酒库管理账户,注册完成后获取门店编用于登录客户端
pageStyle: |
.reg-page { padding: var(--space-16) 0 var(--space-24); }
.reg-wrap { max-width: 640px; margin: 0 auto; padding: 0 var(--space-4); }
.reg-hero { text-align: center; margin-bottom: var(--space-8); }
.reg-hero h1 { font-size: var(--text-3xl); font-weight: 700; margin-bottom: var(--space-2); }
.reg-hero p { color: var(--neutral-500); font-size: var(--text-md); max-width: 480px; margin: 0 auto; }
.reg-card { background: var(--neutral-0); border: 1px solid var(--neutral-200); border-radius: var(--radius-xl); padding: var(--space-8); }
.reg-fieldset { border: none; padding: 0; margin: 0 0 var(--space-8); }
.reg-legend { font-size: var(--text-sm); font-weight: 600; color: var(--neutral-500); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: var(--space-5); padding-bottom: var(--space-3); border-bottom: 1px solid var(--neutral-100); display: block; width: 100%; }
.form-row { margin-bottom: var(--space-5); }
.form-label { display: block; font-size: var(--text-sm); font-weight: 500; color: var(--neutral-700); margin-bottom: var(--space-1-5); }
.form-label .req { color: var(--error-500); margin-left: 2px; }
.form-input, .form-textarea { width: 100%; padding: var(--space-2-5) var(--space-3); border: 1.5px solid var(--neutral-300); border-radius: var(--radius-md); font-size: var(--text-md); font-family: inherit; background: var(--neutral-0); color: var(--neutral-900); transition: border-color .15s, box-shadow .15s; box-sizing: border-box; }
.form-input:focus, .form-textarea:focus { outline: none; border-color: var(--brand-500); box-shadow: 0 0 0 3px var(--brand-100); }
.form-input.error, .form-textarea.error { border-color: var(--error-500); box-shadow: 0 0 0 3px rgba(239,68,68,.12); }
.form-textarea { resize: vertical; min-height: 80px; }
.form-helper { font-size: var(--text-xs); color: var(--neutral-400); margin-top: var(--space-1); }
.field-error { font-size: var(--text-xs); color: var(--error-600); margin-top: 4px; display: none; }
.field-error.visible { display: block; }
.reg-submit { width: 100%; padding: var(--space-3) var(--space-4); font-size: var(--text-md); font-weight: 600; margin-top: var(--space-2); }
.reg-login-hint { text-align: center; margin-top: var(--space-4); font-size: var(--text-sm); color: var(--neutral-500); }
.reg-login-hint a { color: var(--brand-600); }
.reg-error { display: none; background: var(--error-50); border: 1px solid var(--error-200); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); margin-bottom: var(--space-5); font-size: var(--text-sm); color: var(--error-700); }
.reg-error.visible { display: block; }
.success-panel { display: none; text-align: center; }
.success-panel.visible { display: block; }
.success-icon { color: var(--success-500); margin-bottom: var(--space-4); }
.success-panel h2 { font-size: var(--text-2xl); font-weight: 700; margin-bottom: var(--space-2); }
.success-panel > p { color: var(--neutral-500); margin-bottom: var(--space-6); }
.shop-code-box { background: var(--brand-50); border: 1px solid var(--brand-200); border-radius: var(--radius-lg); padding: var(--space-5) var(--space-6); margin-bottom: var(--space-6); display: flex; align-items: center; justify-content: center; gap: var(--space-3); }
.shop-code-text { font-size: var(--text-3xl); font-weight: 700; font-family: 'JetBrains Mono', monospace; color: var(--brand-700); letter-spacing: 0.05em; }
.copy-btn { display: inline-flex; align-items: center; gap: var(--space-1-5); padding: var(--space-2) var(--space-3); background: var(--brand-600); color: #fff; border: none; border-radius: var(--radius-md); font-size: var(--text-sm); font-weight: 500; cursor: pointer; transition: background .15s; }
.copy-btn:hover { background: var(--brand-700); }
.login-hint-card { background: var(--neutral-50); border: 1px solid var(--neutral-200); border-radius: var(--radius-md); padding: var(--space-4) var(--space-5); margin-bottom: var(--space-6); text-align: left; }
.login-hint-card p { font-size: var(--text-sm); font-weight: 600; color: var(--neutral-600); margin-bottom: var(--space-3); }
.login-hint-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.login-hint-table td { padding: var(--space-1-5) var(--space-2); }
.login-hint-table td:first-child { color: var(--neutral-500); width: 80px; }
.login-hint-table td:last-child { font-weight: 500; color: var(--neutral-800); font-family: 'JetBrains Mono', monospace; }
.success-note { font-size: var(--text-xs); color: var(--neutral-400); margin-bottom: var(--space-6); }
.btn-loading { opacity: .7; cursor: not-allowed; }
description: 几分钟创建你的酒水进销存门店,注册完成后获取门店编用于登录。
permalink: /register/
---
<section class="reg-page">
<div class="reg-wrap">
<div class="reg-hero">
<h1>注册新门店</h1>
<p>填写以下信息,创建您的专属酒库管理账户。注册完成后系统自动分配门店编码,用于登录客户端。</p>
</div>
<!-- 注册表单 -->
<div class="reg-card" id="reg-form-card">
<div class="reg-error" id="reg-error"></div>
<!-- 门店信息 -->
<fieldset class="reg-fieldset">
<legend class="reg-legend">门店信息</legend>
<div class="form-row">
<label class="form-label" for="shop_name">店铺名称<span class="req">*</span></label>
<input class="form-input" type="text" id="shop_name" placeholder="例:强朋友名酒行" autocomplete="organization" />
<div class="field-error" id="err-shop_name"></div>
<div class="form-helper">您的酒行正式名称,将显示在所有单据和商品标签上</div>
</div>
<div class="form-row">
<label class="form-label" for="address">门店地址<span class="req">*</span></label>
<input class="form-input" type="text" id="address" placeholder="例:贵州省贵阳市云岩区中华北路 88 号" autocomplete="street-address" />
<div class="field-error" id="err-address"></div>
<div class="form-helper">实际经营地址,打印标签和单据时展示</div>
</div>
<div class="form-row">
<label class="form-label" for="manager_name">负责人姓名<span class="req">*</span></label>
<input class="form-input" type="text" id="manager_name" placeholder="例:张三" autocomplete="name" />
<div class="field-error" id="err-manager_name"></div>
<div class="form-helper">主要负责人真实姓名,也是首个管理员账号的姓名</div>
</div>
<div class="form-row">
<label class="form-label" for="phone">联系电话<span class="req">*</span></label>
<input class="form-input" type="tel" id="phone" placeholder="例:0851-12345678" autocomplete="tel" />
<div class="field-error" id="err-phone"></div>
<div class="form-helper">对外联系电话,打印商品标签时会展示</div>
</div>
<div class="form-row">
<label class="form-label" for="description">店铺简介</label>
<textarea class="form-textarea" id="description" placeholder="简短介绍您的门店,例:专注高端白酒批发零售,主营茅台系列" maxlength="200"></textarea>
<div class="form-helper">选填,最多 200 字;客户扫商品码时可见</div>
</div>
</fieldset>
<!-- 管理员账号 -->
<fieldset class="reg-fieldset" style="margin-bottom:0">
<legend class="reg-legend">管理员账号</legend>
<div class="form-row">
<label class="form-label" for="username">用户名<span class="req">*</span></label>
<input class="form-input" type="text" id="username" placeholder="例:admin" autocomplete="username" />
<div class="field-error" id="err-username"></div>
<div class="form-helper">登录账号(3–30 位,字母/数字/下划线),创建后不可修改</div>
</div>
<div class="form-row">
<label class="form-label" for="password">密码<span class="req">*</span></label>
<input class="form-input" type="password" id="password" placeholder="至少 6 位" autocomplete="new-password" />
<div class="field-error" id="err-password"></div>
<div class="form-helper">至少 6 位,建议字母数字组合</div>
</div>
<div class="form-row">
<label class="form-label" for="password_confirm">确认密码<span class="req">*</span></label>
<input class="form-input" type="password" id="password_confirm" placeholder="再次输入密码" autocomplete="new-password" />
<div class="field-error" id="err-password_confirm"></div>
</div>
</fieldset>
<button class="btn btn-primary reg-submit" id="reg-btn">
<i data-lucide="store" class="icon"></i>
立即注册
</button>
<p class="reg-login-hint">已有账号?<a href="/app/">直接登录</a></p>
</div>
<!-- 注册成功面板 -->
<div class="reg-card success-panel" id="success-panel">
<div class="success-icon">
<i data-lucide="check-circle" style="width:56px;height:56px;"></i>
{# 官网内注册(不跳 App):POST /api/v1/public/register,成功显示门店编号并引导登录。
版式与 App 注册页同款(App 真相源两栏卡片 + 三步时间线)。 #}
<div class="auth-wrap">
<div class="auth reg">
<div class="auth-brand">
<img class="mk" src="/assets/logo-mark.svg" alt="岩美酒库" style="width:60px;height:60px" />
<div class="bname">开通门店</div>
<div class="btag">几分钟创建你的酒水进销存门店,立即开始入库出库</div>
<div class="steps">
<div class="step done"><span class="si">1</span><div class="st"><b>填写门店信息</b><span>店名、地址与联系人</span></div></div>
<div class="step"><span class="si">2</span><div class="st"><b>创建管理员账号</b><span>用于登录与管理成员</span></div></div>
<div class="step"><span class="si">3</span><div class="st"><b>激活授权</b><span>先享 30 天试用,兑换券随时续期</span></div></div>
</div>
<h2>注册成功!</h2>
<p>您的门店编码已生成,请妥善保存:</p>
<div class="shop-code-box">
<span class="shop-code-text" id="result-shop-code">—</span>
<button class="copy-btn" id="copy-code-btn">
<i data-lucide="copy" class="icon"></i>复制
</button>
</div>
<div class="auth-form">
<div id="regForm">
<h1>注册新门店</h1>
<p class="alead">已有门店账号?<a href="/login/" style="font-weight:600">直接登录 →</a></p>
<div class="auth-alert" id="regErr"></div>
<div class="field"><label>门店名称<span class="req">*</span></label>
<input class="input" id="shop_name" placeholder="例如 强朋友名酒行" autocomplete="organization" />
<div class="hint">酒行正式名称,将显示在单据与商品标签上</div></div>
<div class="grid2f">
<div class="field"><label>联系电话<span class="req">*</span></label>
<input class="input" id="phone" type="tel" placeholder="手机或座机" autocomplete="tel" /></div>
<div class="field"><label>负责人姓名<span class="req">*</span></label>
<input class="input" id="manager_name" placeholder="例如 王经理" autocomplete="name" /></div>
</div>
<div class="field"><label>门店地址<span class="req">*</span></label>
<input class="input" id="address" placeholder="省 / 市 / 区 详细地址" autocomplete="street-address" /></div>
<div class="field"><label>店铺简介</label>
<textarea class="input" id="description" maxlength="200" placeholder="选填,最多 200 字;顾客扫商品码时可见"></textarea></div>
<div style="height:1px;background:var(--border-subtle);margin:6px 0 16px"></div>
<div class="field"><label>登录账号<span class="req">*</span></label>
<input class="input" id="username" placeholder="330 位字母/数字/下划线" autocomplete="username" />
<div class="hint">创建后不可修改</div></div>
<div class="grid2f">
<div class="field"><label>登录密码<span class="req">*</span></label>
<input class="input" id="password" type="password" placeholder="至少 6 位" autocomplete="new-password" /></div>
<div class="field"><label>确认密码<span class="req">*</span></label>
<input class="input" id="password_confirm" type="password" placeholder="再次输入" autocomplete="new-password" /></div>
</div>
<button class="btn primary lg" id="reg-btn" style="margin-top:8px">创建门店并开始试用</button>
<div class="auth-foot">创建即代表你将成为该门店的管理员,并同意<a href="/terms/">服务条款</a>与<a href="/privacy/">隐私政策</a></div>
</div>
<div class="login-hint-card">
<p>登录客户端时请输入:</p>
<table class="login-hint-table">
<tr>
<td>门店编码</td>
<td id="hint-code"></td>
</tr>
<tr>
<td>用户名</td>
<td id="hint-username">—</td>
</tr>
<tr>
<td>密码</td>
<td>您设置的密码</td>
</tr>
</table>
<div id="regDone" style="display:none">
<h1>门店创建成功 🎉</h1>
<p class="alead">请记下你的门店编号,登录时需要用到。</p>
<div class="auth-ok">
门店编号:<code id="done-code">—</code><br/>
<span style="color:var(--muted);font-size:var(--fs-sm)" id="done-detail"></span>
</div>
<a class="btn primary lg" href="/login/">前往登录</a>
<div class="auth-foot">已自动开通 30 天全功能试用 · 到期可在 系统设置 → 授权兑换券 续期</div>
</div>
<p class="success-note">如忘记门店编码,登录后可在「系统设置」中查看。</p>
<a href="/app/" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:8px;">
<i data-lucide="log-in" class="icon"></i>前往登录
</a>
</div>
</div>
</section>
</div>
<script>
(function() {
var API_URL = "{{ site.appBaseUrl }}/api/v1/public/register";
(function () {
var API = '{{ site.appBaseUrl }}';
function $(id) { return document.getElementById(id); }
function showErr(msg) { var el = $('regErr'); el.textContent = msg; el.classList.add('show'); }
function val(id) { return $(id).value.trim(); }
function getVal(id) { return $(id).value.trim(); }
function submit() {
$('regErr').classList.remove('show');
if (!val('shop_name')) return showErr('请填写门店名称');
if (!val('phone')) return showErr('请填写联系电话');
if (!val('manager_name')) return showErr('请填写负责人姓名');
if (!val('address')) return showErr('请填写门店地址');
if (!/^[A-Za-z0-9_]{3,30}$/.test(val('username'))) return showErr('登录账号需 330 位字母/数字/下划线');
if ($('password').value.length < 6) return showErr('密码至少 6 位');
if ($('password').value !== $('password_confirm').value) return showErr('两次密码不一致');
function setFieldError(id, msg) {
var input = $(id);
var errEl = $('err-' + id);
if (msg) {
input.classList.add('error');
if (errEl) { errEl.textContent = msg; errEl.classList.add('visible'); }
} else {
input.classList.remove('error');
if (errEl) { errEl.textContent = ''; errEl.classList.remove('visible'); }
}
}
function clearAllErrors() {
['shop_name','address','manager_name','phone','username','password','password_confirm'].forEach(function(id) {
setFieldError(id, '');
});
}
// 用户修改字段后清除该字段的错误提示
['shop_name','address','manager_name','phone','username','password','password_confirm'].forEach(function(id) {
var el = $(id);
if (el) el.addEventListener('input', function() { setFieldError(id, ''); });
});
function validate() {
clearAllErrors();
var ok = true;
var firstErr = null;
function require(id, msg) {
if (!getVal(id)) { setFieldError(id, msg); if (!firstErr) firstErr = id; ok = false; }
}
require('shop_name', '请填写店铺名称');
require('address', '请填写门店地址');
require('manager_name', '请填写负责人姓名');
require('phone', '请填写联系电话');
var username = getVal('username');
if (!username) {
setFieldError('username', '请填写用户名'); if (!firstErr) firstErr = 'username'; ok = false;
} else if (username.length < 3 || username.length > 30) {
setFieldError('username', '用户名长度须在 330 位之间'); if (!firstErr) firstErr = 'username'; ok = false;
} else if (!/^[a-zA-Z0-9_]+$/.test(username)) {
setFieldError('username', '用户名只能包含字母、数字和下划线'); if (!firstErr) firstErr = 'username'; ok = false;
}
var password = $('password').value;
if (!password) {
setFieldError('password', '请填写密码'); if (!firstErr) firstErr = 'password'; ok = false;
} else if (password.length < 6) {
setFieldError('password', '密码至少 6 位'); if (!firstErr) firstErr = 'password'; ok = false;
}
var confirm = $('password_confirm').value;
if (!confirm) {
setFieldError('password_confirm', '请再次输入密码'); if (!firstErr) firstErr = 'password_confirm'; ok = false;
} else if (password && password !== confirm) {
setFieldError('password_confirm', '两次输入的密码不一致'); if (!firstErr) firstErr = 'password_confirm'; ok = false;
}
if (firstErr) { $(firstErr).focus(); $(firstErr).scrollIntoView({ behavior: 'smooth', block: 'center' }); }
return ok;
}
function showTopError(msg) {
var el = $('reg-error');
el.textContent = msg;
el.classList.add('visible');
el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
$('reg-btn').addEventListener('click', function() {
$('reg-error').classList.remove('visible');
if (!validate()) return;
var btn = $('reg-btn');
btn.disabled = true;
btn.classList.add('btn-loading');
btn.textContent = '注册中…';
var payload = {
shop_name: getVal('shop_name'),
address: getVal('address'),
manager_name: getVal('manager_name'),
phone: getVal('phone'),
description: getVal('description'),
username: getVal('username'),
password: $('password').value
};
fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
var btn = $('reg-btn'); btn.disabled = true; btn.textContent = '创建中…';
fetch(API + '/api/v1/public/register', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
shop_name: val('shop_name'), phone: val('phone'), manager_name: val('manager_name'),
address: val('address'), description: val('description'),
username: val('username'), password: $('password').value,
}),
})
.then(function(res) { return res.json().then(function(data) { return { ok: res.ok, data: data }; }); })
.then(function(r) {
if (!r.ok) {
var msg = (r.data && r.data.error) ? r.data.error : '注册失败,请稍后重试';
if (msg.indexOf('Duplicate') !== -1 || msg.indexOf('duplicate') !== -1) {
msg = '该用户名在此门店已存在,请换一个用户名';
}
showTopError(msg);
btn.disabled = false;
btn.classList.remove('btn-loading');
btn.innerHTML = '<i data-lucide="store" class="icon"></i>立即注册';
if (window.lucide) lucide.createIcons();
return;
}
var d = r.data.data;
$('result-shop-code').textContent = d.shop_code;
$('hint-code').textContent = d.shop_code;
$('hint-username').textContent = d.username;
$('reg-form-card').style.display = 'none';
var panel = $('success-panel');
panel.classList.add('visible');
if (window.lucide) lucide.createIcons();
panel.scrollIntoView({ behavior: 'smooth', block: 'start' });
})
.catch(function() {
showTopError('网络异常,请检查网络连接后重试');
btn.disabled = false;
btn.classList.remove('btn-loading');
btn.innerHTML = '<i data-lucide="store" class="icon"></i>立即注册';
if (window.lucide) lucide.createIcons();
});
});
$('copy-code-btn').addEventListener('click', function() {
var code = $('result-shop-code').textContent;
navigator.clipboard.writeText(code).then(function() {
var btn = $('copy-code-btn');
btn.innerHTML = '<i data-lucide="check" class="icon"></i>已复制';
if (window.lucide) lucide.createIcons();
setTimeout(function() {
btn.innerHTML = '<i data-lucide="copy" class="icon"></i>复制';
if (window.lucide) lucide.createIcons();
}, 2000);
});
});
document.addEventListener('DOMContentLoaded', function() {
if (window.lucide) lucide.createIcons();
});
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, j: j }; }); })
.then(function (res) {
if (!res.ok) throw new Error((res.j && res.j.error) || '注册失败,请稍后重试');
var d = res.j.data || {};
$('done-code').textContent = d.shop_code || '—';
$('done-detail').textContent = '门店「' + (d.shop_name || val('shop_name')) + '」已创建,管理员账号 ' + (d.username || val('username'));
$('regForm').style.display = 'none';
$('regDone').style.display = 'block';
window.scrollTo(0, 0);
})
.catch(function (e) {
showErr(e && e.message ? e.message : '无法连接服务器,请稍后重试');
btn.disabled = false; btn.textContent = '创建门店并开始试用';
});
}
$('reg-btn').addEventListener('click', submit);
})();
</script>