Files
jiu/web/login.njk
T
wangjia c010d8b416 fix(site): 官网 401 自动用 refresh_token 续签重试;资产加构建版本号破缓存
- 新增共享 auth.js(jiuAuth):access token 60 分钟过期后,checkout 提交/
  结果页轮询遇 401 自动 POST /auth/refresh 换新 token 并重试一次,仍失败
  才跳登录——修复「已登录点提交订单却被踢回登录页」
- 登录判定放宽为 access 或 refresh token 任一存在(refresh 7 天有效)
- 所有 JS/CSS 引用加 ?v=构建版本号:修复旧版 nav.js 被浏览器缓存后在新
  页面执行(右上角错位的旧用户面板、带引号的 "张三" 显示)
- nav.js 改经 jiuAuth 读 localStorage(JSON 解码,去掉引号显示)
- 登录页回填上次的门店编号与账号

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
2026-07-03 21:08:13 +08:00

92 lines
4.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 = '登 录';
});
}
// 登录过的浏览器回填门店编号/账号(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);
['shop', 'user', 'pass'].forEach(function (id) {
$(id).addEventListener('keydown', function (e) { if (e.key === 'Enter') doLogin(); });
});
})();
</script>