151 lines
5.1 KiB
JavaScript
151 lines
5.1 KiB
JavaScript
/**
|
||||
|
|
* AppStore(规范 §131)
|
|||
|
|
*
|
|||
|
|
* 职责:
|
|||
|
|
* - 维护运行时业务配置(来自服务端,覆盖 BUSINESS_LIMITS 默认值)
|
|||
|
|
* - 维护平台维护态(503 时由 apiClient 注入)
|
|||
|
|
* - 维护网络状态(断网/弱网提示)
|
|||
|
|
* - 提供 toast / loading / modal 统一封装,避免页面散落 wx.* UI 调用
|
|||
|
|
*
|
|||
|
|
* 不变量(AGENTS.md 第 9 条):PostgreSQL 是 Source of Truth——
|
|||
|
|
* 本 Store 持有的 businessConfig 仅是服务端配置的运行时缓存,
|
|||
|
|
* 任何前端缓存的配置都不能覆盖后端校验。
|
|||
|
|
*/
|
|||
|
|
const BaseStore = require('./baseStore');
|
|||
|
|
const api = require('../utils/api');
|
|||
|
|
const { appConfigService } = require('../services');
|
|||
|
|
const { BUSINESS_LIMITS, mapErrorMessage } = require('../constants');
|
|||
|
|
|
|||
|
|
class AppStore extends BaseStore {
|
|||
|
|
constructor() {
|
|||
|
|
super({
|
|||
|
|
/** @type {typeof BUSINESS_LIMITS} 运行时合并后的业务配置 */
|
|||
|
|
businessConfig: { ...BUSINESS_LIMITS },
|
|||
|
|
|
|||
|
|
/** @type {boolean} 平台维护态 */
|
|||
|
|
maintenance: false,
|
|||
|
|
maintenanceMessage: '',
|
|||
|
|
|
|||
|
|
/** @type {boolean} 网络在线 */
|
|||
|
|
networkOnline: true,
|
|||
|
|
|
|||
|
|
/** @type {boolean} 全局 loading 显示中 */
|
|||
|
|
loadingVisible: false,
|
|||
|
|
loadingText: '',
|
|||
|
|
|
|||
|
|
/** @type {boolean} 新手引导是否已关闭(按 storage 记忆) */
|
|||
|
|
newbieTipsClosed: false,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 注入维护态拦截回调(apiClient 503 时调用)
|
|||
|
|
api.onMaintenance = (config) => this.handleMaintenance(config);
|
|||
|
|
// 监听网络状态
|
|||
|
|
if (typeof wx !== 'undefined' && wx.onNetworkStatusChange) {
|
|||
|
|
wx.onNetworkStatusChange((res) => {
|
|||
|
|
this.setState({ networkOnline: !!res.isConnected });
|
|||
|
|
});
|
|||
|
|
// 启动时同步一次当前网络状态
|
|||
|
|
if (wx.getNetworkType) {
|
|||
|
|
wx.getNetworkType({
|
|||
|
|
success: (res) => {
|
|||
|
|
this.setState({ networkOnline: res.networkType !== 'none' });
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 冷启动初始化:拉取服务端配置覆盖默认值(规范 §118) */
|
|||
|
|
async init() {
|
|||
|
|
try {
|
|||
|
|
const config = await appConfigService.fetchAppConfig();
|
|||
|
|
this.setState({ businessConfig: config });
|
|||
|
|
} catch (e) {
|
|||
|
|
// 接口失败时使用默认值(规范 §119),不阻塞启动
|
|||
|
|
this.setState({ businessConfig: { ...BUSINESS_LIMITS } });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 503 维护态进入:弹维护提示,后续请求会被 apiClient 自动拦截 */
|
|||
|
|
handleMaintenance(config = {}) {
|
|||
|
|
this.setState({
|
|||
|
|
maintenance: true,
|
|||
|
|
maintenanceMessage: config.message || '平台正在维护,请稍后再试',
|
|||
|
|
});
|
|||
|
|
if (typeof wx !== 'undefined' && wx.showToast) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: this.state.maintenanceMessage,
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 清除维护态(一般由刷新页面后重新拉取 /me/status 时恢复) */
|
|||
|
|
clearMaintenance() {
|
|||
|
|
this.setState({ maintenance: false, maintenanceMessage: '' });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 统一 Toast(规范 §56:1.5~2 秒)
|
|||
|
|
* @param {string} message
|
|||
|
|
* @param {'success'|'error'|'warning'|'none'} [type='none']
|
|||
|
|
*/
|
|||
|
|
toast(message, type = 'none') {
|
|||
|
|
if (typeof wx === 'undefined' || !wx.showToast) return;
|
|||
|
|
const iconMap = { success: 'success', error: 'error', warning: 'none', none: 'none' };
|
|||
|
|
wx.showToast({
|
|||
|
|
title: message || '',
|
|||
|
|
icon: iconMap[type] || 'none',
|
|||
|
|
duration: this.state.businessConfig.TOAST_DURATION || BUSINESS_LIMITS.TOAST_DURATION,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 成功提示 */
|
|||
|
|
toastSuccess(message) { this.toast(message, 'success'); }
|
|||
|
|
/** 错误提示(自动从 err 中提取用户可理解文案,规范 §80-84) */
|
|||
|
|
toastError(err, fallback = '操作失败,请稍后再试') {
|
|||
|
|
const code = err && err.code;
|
|||
|
|
const message = code ? mapErrorMessage(code, fallback) : (err && err.message) || fallback;
|
|||
|
|
this.toast(message, 'none');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 全局 loading */
|
|||
|
|
showLoading(text = '加载中...') {
|
|||
|
|
if (this.state.loadingVisible) return;
|
|||
|
|
this.setState({ loadingVisible: true, loadingText: text });
|
|||
|
|
if (typeof wx !== 'undefined' && wx.showLoading) {
|
|||
|
|
wx.showLoading({ title: text, mask: true });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
hideLoading() {
|
|||
|
|
if (!this.state.loadingVisible) return;
|
|||
|
|
this.setState({ loadingVisible: false, loadingText: '' });
|
|||
|
|
if (typeof wx !== 'undefined' && wx.hideLoading) wx.hideLoading();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 统一确认对话框(用于危险操作:拉黑/解除匹配/暂停寻找/成功牵手确认)
|
|||
|
|
* @param {{ title?: string, content: string, confirmText?: string, cancelText?: string, danger?: boolean }} opts
|
|||
|
|
* @returns {Promise<boolean>} 用户是否确认
|
|||
|
|
*/
|
|||
|
|
confirm(opts) {
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
if (typeof wx === 'undefined' || !wx.showModal) return resolve(false);
|
|||
|
|
wx.showModal({
|
|||
|
|
title: opts.title || (opts.danger ? '请确认' : '提示'),
|
|||
|
|
content: opts.content,
|
|||
|
|
confirmText: opts.confirmText || '确认',
|
|||
|
|
cancelText: opts.cancelText || '取消',
|
|||
|
|
confirmColor: opts.danger ? '#E04C4C' : '#1A6B3E',
|
|||
|
|
success: (res) => resolve(!!res.confirm),
|
|||
|
|
fail: () => resolve(false),
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const appStore = new AppStore();
|
|||
|
|
module.exports = appStore;
|