Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0f721117f | |||
| c010d8b416 | |||
| 71909a76d7 | |||
| a79a4d4468 |
@@ -6,6 +6,28 @@
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.1.1] - 2026-07-03
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
- 登录超过 1 小时后点「提交订单」被踢回登录页:官网现在会自动续签登录态(7 天内免重登)。
|
||||||
|
- 浏览器缓存旧版脚本导致页面右上角出现错位的旧用户面板、用户名显示多余引号。
|
||||||
|
|
||||||
|
### 改进
|
||||||
|
- 重新登录时自动回填上次的门店编号与账号。
|
||||||
|
|
||||||
|
## [1.1.0] - 2026-07-03
|
||||||
|
|
||||||
|
### 新功能
|
||||||
|
- 注册与登录直接在官网完成,登录后无缝进入 Web 版系统,不再跳转 App 页面。
|
||||||
|
- 价格方案改为四档:免费版 / 标准版 ¥299 / 高级版 ¥599 / 定制版;登录后可直接
|
||||||
|
在线购买或续费(支付宝),新增确认订单页与支付结果页。
|
||||||
|
- 帮助中心改版:与 App 同步的十章使用手册。
|
||||||
|
|
||||||
|
### 改进
|
||||||
|
- 全站视觉重建,与 App 界面同一套设计体系(配色/组件/图标),首页展示真实产品截图。
|
||||||
|
- 下载页接入真实下载链接与动态更新日志;页面零第三方外链,大陆访问更快。
|
||||||
|
- 商品二维码定位更正:扫码看详情、可分享(非防伪)。
|
||||||
|
|
||||||
## [1.0.55] - 2026-06-17
|
## [1.0.55] - 2026-06-17
|
||||||
|
|
||||||
### 新功能
|
### 新功能
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
/* 构建期资产版本号:每次 build 变化,模板里作 ?v= 后缀破浏览器缓存,
|
||||||
|
避免旧版 JS/CSS(同 URL)被缓存后在新版页面上执行。 */
|
||||||
|
module.exports = { rev: Date.now().toString(36) };
|
||||||
@@ -8,10 +8,12 @@
|
|||||||
{% if description %}<meta name="description" content="{{ description }}" />{% endif %}
|
{% if description %}<meta name="description" content="{{ description }}" />{% endif %}
|
||||||
{# 真相源:tokens.css = App 原型 tokens.css 主题A 拷贝;site.css 镜像 atoms.css。
|
{# 真相源:tokens.css = App 原型 tokens.css 主题A 拷贝;site.css 镜像 atoms.css。
|
||||||
全站零外链(无 Google Fonts / unpkg):字体走系统栈(与 App 真机一致),图标走内联雪碧图。 #}
|
全站零外链(无 Google Fonts / unpkg):字体走系统栈(与 App 真机一致),图标走内联雪碧图。 #}
|
||||||
<link rel="stylesheet" href="/assets/tokens.css" />
|
<link rel="stylesheet" href="/assets/tokens.css?v={{ build.rev }}" />
|
||||||
<link rel="stylesheet" href="/assets/site.css" />
|
<link rel="stylesheet" href="/assets/site.css?v={{ build.rev }}" />
|
||||||
{% if pageExtraCss %}<link rel="stylesheet" href="{{ pageExtraCss }}" />{% endif %}
|
{% if pageExtraCss %}<link rel="stylesheet" href="{{ pageExtraCss }}" />{% endif %}
|
||||||
<link rel="icon" href="/assets/logo-mark.svg" type="image/svg+xml" />
|
<link rel="icon" href="/assets/logo-mark.svg" type="image/svg+xml" />
|
||||||
|
{# auth.js 放 head 同步加载:页面内联脚本(checkout/登录态判断)在 body 中就要用 jiuAuth #}
|
||||||
|
<script src="/assets/auth.js?v={{ build.rev }}"></script>
|
||||||
{% if pageStyle %}<style>{{ pageStyle | safe }}</style>{% endif %}
|
{% if pageStyle %}<style>{{ pageStyle | safe }}</style>{% endif %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -22,6 +24,6 @@
|
|||||||
|
|
||||||
{% include "footer.njk" %}
|
{% include "footer.njk" %}
|
||||||
|
|
||||||
<script src="/assets/nav.js"></script>
|
<script src="/assets/nav.js?v={{ build.rev }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/* 官网共享登录态助手:与 Web 版 App(shared_preferences_web)共享
|
||||||
|
localStorage['flutter.*'](值为 JSON 编码)。access token 60 分钟过期,
|
||||||
|
官网页面不常驻刷新,所以请求 401 时用 refresh_token 自动续签并重试一次,
|
||||||
|
仍失败才交回调用方跳登录。零依赖。 */
|
||||||
|
(function () {
|
||||||
|
function get(key) {
|
||||||
|
var raw = localStorage.getItem('flutter.' + key);
|
||||||
|
if (raw === null) return null;
|
||||||
|
try { return JSON.parse(raw); } catch (e) { return raw; }
|
||||||
|
}
|
||||||
|
function set(key, val) { localStorage.setItem('flutter.' + key, JSON.stringify(val)); }
|
||||||
|
|
||||||
|
function refresh(apiBase) {
|
||||||
|
var rt = get('refresh_token');
|
||||||
|
if (!rt) return Promise.resolve(false);
|
||||||
|
return fetch(apiBase + '/api/v1/auth/refresh', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ refresh_token: rt }),
|
||||||
|
})
|
||||||
|
.then(function (r) { return r.ok ? r.json() : null; })
|
||||||
|
.then(function (j) {
|
||||||
|
var d = j && j.data;
|
||||||
|
if (!d || !d.access_token) return false;
|
||||||
|
set('access_token', d.access_token);
|
||||||
|
if (d.refresh_token) set('refresh_token', d.refresh_token); // refresh token 会轮换
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(function () { return false; });
|
||||||
|
}
|
||||||
|
|
||||||
|
function authFetch(apiBase, path, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
function doFetch() {
|
||||||
|
var headers = Object.assign({}, opts.headers, { Authorization: 'Bearer ' + get('access_token') });
|
||||||
|
return fetch(apiBase + path, Object.assign({}, opts, { headers: headers }));
|
||||||
|
}
|
||||||
|
return doFetch().then(function (r) {
|
||||||
|
if (r.status !== 401) return r;
|
||||||
|
return refresh(apiBase).then(function (ok) { return ok ? doFetch() : r; });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 是否可视为登录:access 过期但 refresh 还在(7 天),authFetch 会自动续 */
|
||||||
|
function loggedIn() { return !!(get('access_token') || get('refresh_token')); }
|
||||||
|
|
||||||
|
window.jiuAuth = { get: get, set: set, refresh: refresh, authFetch: authFetch, loggedIn: loggedIn };
|
||||||
|
})();
|
||||||
+5
-5
@@ -2,13 +2,13 @@
|
|||||||
localStorage['flutter.*'] 有登录信息 → CTA 变「进入系统」)。零依赖。 */
|
localStorage['flutter.*'] 有登录信息 → CTA 变「进入系统」)。零依赖。 */
|
||||||
(function () {
|
(function () {
|
||||||
function getAuthUser() {
|
function getAuthUser() {
|
||||||
var token = localStorage.getItem('flutter.access_token');
|
if (!window.jiuAuth || !jiuAuth.loggedIn()) return null;
|
||||||
var username = localStorage.getItem('flutter.username');
|
var username = jiuAuth.get('username');
|
||||||
if (!token || !username) return null;
|
if (!username) return null;
|
||||||
return {
|
return {
|
||||||
username: username,
|
username: username,
|
||||||
realName: localStorage.getItem('flutter.real_name') || '',
|
realName: jiuAuth.get('real_name') || '',
|
||||||
shopNo: localStorage.getItem('flutter.shop_no') || '',
|
shopNo: jiuAuth.get('shop_no') || '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-8
@@ -68,13 +68,9 @@ permalink: /checkout/
|
|||||||
(function () {
|
(function () {
|
||||||
var API = '{{ site.appBaseUrl }}';
|
var API = '{{ site.appBaseUrl }}';
|
||||||
function $(id) { return document.getElementById(id); }
|
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); }
|
function gotoLogin() { location.href = '/login/?next=' + encodeURIComponent('/checkout/' + location.search); }
|
||||||
if (!token()) { gotoLogin(); return; }
|
// access token 只有 60 分钟,只要 refresh token(7 天)还在就算登录,请求时自动续签
|
||||||
|
if (!jiuAuth.loggedIn()) { gotoLogin(); return; }
|
||||||
|
|
||||||
/* 套餐目录:价格仅前端展示,实付金额以 pay 套餐表为准(下单响应回传) */
|
/* 套餐目录:价格仅前端展示,实付金额以 pay 套餐表为准(下单响应回传) */
|
||||||
var PLANS = {
|
var PLANS = {
|
||||||
@@ -123,12 +119,13 @@ permalink: /checkout/
|
|||||||
$('coBtn').addEventListener('click', function () {
|
$('coBtn').addEventListener('click', function () {
|
||||||
$('coErr').classList.remove('show');
|
$('coErr').classList.remove('show');
|
||||||
var btn = $('coBtn'); btn.disabled = true; btn.textContent = '正在创建订单…';
|
var btn = $('coBtn'); btn.disabled = true; btn.textContent = '正在创建订单…';
|
||||||
fetch(API + '/api/v1/license/purchase', {
|
jiuAuth.authFetch(API, '/api/v1/license/purchase', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token() },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ biz_code: PLANS[plan][cycle].biz }),
|
body: JSON.stringify({ biz_code: PLANS[plan][cycle].biz }),
|
||||||
})
|
})
|
||||||
.then(function (r) {
|
.then(function (r) {
|
||||||
|
// authFetch 已自动用 refresh_token 续签重试过,仍 401 才是真过期
|
||||||
if (r.status === 401) { gotoLogin(); throw new Error('登录已过期'); }
|
if (r.status === 401) { gotoLogin(); throw new Error('登录已过期'); }
|
||||||
return r.json().then(function (j) { return { ok: r.ok, j: j }; });
|
return r.json().then(function (j) { return { ok: r.ok, j: j }; });
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -276,7 +276,7 @@ description: 岩美酒库管理系统——每一瓶酒一个编号,审核驱
|
|||||||
/* 价格区登录态:已登录(与 Web 版 App 共享 localStorage flutter.*)时,
|
/* 价格区登录态:已登录(与 Web 版 App 共享 localStorage flutter.*)时,
|
||||||
标准/高级按钮 → 「购买 / 续费」进 /checkout/;免费版按钮 → 「进入系统」。 */
|
标准/高级按钮 → 「购买 / 续费」进 /checkout/;免费版按钮 → 「进入系统」。 */
|
||||||
(function () {
|
(function () {
|
||||||
if (!localStorage.getItem('flutter.access_token')) return;
|
if (!window.jiuAuth || !jiuAuth.loggedIn()) return;
|
||||||
document.querySelectorAll('[data-plan]').forEach(function (a) {
|
document.querySelectorAll('[data-plan]').forEach(function (a) {
|
||||||
var plan = a.dataset.plan;
|
var plan = a.dataset.plan;
|
||||||
if (plan === 'free') {
|
if (plan === 'free') {
|
||||||
|
|||||||
@@ -38,20 +38,13 @@ permalink: /license/result/
|
|||||||
(function () {
|
(function () {
|
||||||
var API = '{{ site.appBaseUrl }}';
|
var API = '{{ site.appBaseUrl }}';
|
||||||
function $(id) { return document.getElementById(id); }
|
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');
|
var otn = new URLSearchParams(location.search).get('out_trade_no');
|
||||||
if (!otn || !token()) { $('waiting').style.display = 'none'; $('slow').style.display = 'block'; return; }
|
if (!otn || !jiuAuth.loggedIn()) { $('waiting').style.display = 'none'; $('slow').style.display = 'block'; return; }
|
||||||
|
|
||||||
var tries = 0, maxTries = 30; // 2s × 30 = 60s
|
var tries = 0, maxTries = 30; // 2s × 30 = 60s
|
||||||
function poll() {
|
function poll() {
|
||||||
tries++;
|
tries++;
|
||||||
fetch(API + '/api/v1/license/purchase/' + encodeURIComponent(otn), {
|
jiuAuth.authFetch(API, '/api/v1/license/purchase/' + encodeURIComponent(otn), {})
|
||||||
headers: { 'Authorization': 'Bearer ' + token() },
|
|
||||||
})
|
|
||||||
.then(function (r) { return r.ok ? r.json() : null; })
|
.then(function (r) { return r.ok ? r.json() : null; })
|
||||||
.then(function (j) {
|
.then(function (j) {
|
||||||
var d = j && j.data;
|
var d = j && j.data;
|
||||||
|
|||||||
@@ -76,6 +76,13 @@ permalink: /login/
|
|||||||
btn.disabled = false; btn.textContent = '登 录';
|
btn.disabled = false; btn.textContent = '登 录';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 登录过的浏览器回填门店编号/账号(token 过期重登时少敲两栏)
|
||||||
|
if (window.jiuAuth) {
|
||||||
|
var savedShop = jiuAuth.get('shop_no'), savedUser = jiuAuth.get('username');
|
||||||
|
if (savedShop && !$('shop').value) $('shop').value = savedShop;
|
||||||
|
if (savedUser && !$('user').value) $('user').value = savedUser;
|
||||||
|
}
|
||||||
|
|
||||||
$('loginBtn').addEventListener('click', doLogin);
|
$('loginBtn').addEventListener('click', doLogin);
|
||||||
['shop', 'user', 'pass'].forEach(function (id) {
|
['shop', 'user', 'pass'].forEach(function (id) {
|
||||||
$(id).addEventListener('keydown', function (e) { if (e.key === 'Enter') doLogin(); });
|
$(id).addEventListener('keydown', function (e) { if (e.key === 'Enter') doLogin(); });
|
||||||
|
|||||||
Reference in New Issue
Block a user