7093e6517d
token 过期后小程序静默白屏(影响所有用户) - JWT 有效期 7 天,过期后 40100 只 clearToken 不重新登录,而 store.ready 的 promise 已缓存不会重跑,加上 23 处静默 catch, 用户看到的是「什么都没有、也没有任何提示」,只能重启小程序 - request 层加 token 失效钩子:自动重新登录并重试一次(只重试一次, 防止登录接口本身故障时死循环);store 注册钩子重置引导缓存 - 补 15s 请求超时、60s 上传超时,超时给明确文案(原先默认 60s 卡住无反馈) 请求翻倍 - 每个 tab 页同时用 store.subscribe 和 onShow→ready().then() 加载, 而 ready() 内部 loadPets 会 notify 触发 subscriber,首次进入请求翻倍 - subscriber 改为只处理「切换宠物 / 数据变更」,首次加载交给 onShow - 首页 bind:save 与组件实际触发的 saved 事件名不一致,onSheetSave 从未生效;一并修正,并去掉关闭弹层时的全量重载 失败伪装成空态 - 社区请求失败时显示「还没有帖子,来发第一条吧」,把故障说成没数据。 改为区分「加载失败(可点击重试)」「关注 tab 为空」「确实没有帖子」 - 新增 utils/ui.js 统一错误提示(同文案 3 秒内去重,避免并发失败刷屏), 首页与记录页主数据加载失败不再静默 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
108 lines
2.1 KiB
JavaScript
108 lines
2.1 KiB
JavaScript
// 全局状态:登录、宠物列表、当前宠物;服务端驱动 + 发布订阅
|
|
const api = require('./api.js');
|
|
const { setReAuthHandler } = require('./request.js');
|
|
|
|
const state = {
|
|
user: null,
|
|
pets: [],
|
|
currentId: 0,
|
|
};
|
|
|
|
const listeners = new Set();
|
|
let bootstrapPromise = null;
|
|
|
|
function getPets() {
|
|
return state.pets;
|
|
}
|
|
function getPet() {
|
|
return state.pets.find((p) => p.id === state.currentId) || state.pets[0] || {};
|
|
}
|
|
function currentPetId() {
|
|
const p = getPet();
|
|
return p && p.id;
|
|
}
|
|
|
|
function notify() {
|
|
const pet = getPet();
|
|
listeners.forEach((fn) => {
|
|
try {
|
|
fn(pet);
|
|
} catch (e) {
|
|
/* noop */
|
|
}
|
|
});
|
|
}
|
|
|
|
function subscribe(fn) {
|
|
listeners.add(fn);
|
|
return () => listeners.delete(fn);
|
|
}
|
|
|
|
// 登录 + 拉宠物列表(只跑一次)
|
|
// token 失效时清掉引导缓存并重新登录一次,让 request 层能自动重试
|
|
setReAuthHandler(async () => {
|
|
bootstrapPromise = null;
|
|
await ready();
|
|
});
|
|
|
|
function ready() {
|
|
if (!bootstrapPromise) {
|
|
bootstrapPromise = (async () => {
|
|
const res = await api.login();
|
|
state.user = res.user;
|
|
await loadPets();
|
|
})().catch((e) => {
|
|
bootstrapPromise = null; // 允许重试
|
|
throw e;
|
|
});
|
|
}
|
|
return bootstrapPromise;
|
|
}
|
|
|
|
async function loadPets() {
|
|
const pets = await api.getPets();
|
|
state.pets = pets || [];
|
|
if (!state.currentId && state.pets[0]) {
|
|
state.currentId = state.pets[0].id;
|
|
}
|
|
// 当前宠物被删或不存在时回退第一只
|
|
if (!state.pets.find((p) => p.id === state.currentId) && state.pets[0]) {
|
|
state.currentId = state.pets[0].id;
|
|
}
|
|
notify();
|
|
return state.pets;
|
|
}
|
|
|
|
function switchPet(id) {
|
|
if (state.currentId === id) return;
|
|
state.currentId = id;
|
|
notify();
|
|
}
|
|
|
|
// 本地更新当前宠物字段(服务端已改动后同步 UI)
|
|
function patchCurrent(patch) {
|
|
const pet = getPet();
|
|
Object.assign(pet, patch);
|
|
notify();
|
|
}
|
|
|
|
function setUser(u) {
|
|
state.user = u;
|
|
}
|
|
function getUser() {
|
|
return state.user;
|
|
}
|
|
|
|
module.exports = {
|
|
ready,
|
|
loadPets,
|
|
getPets,
|
|
getPet,
|
|
currentPetId,
|
|
switchPet,
|
|
patchCurrent,
|
|
subscribe,
|
|
setUser,
|
|
getUser,
|
|
};
|