优化了样式,抽象出了 WXSS/JS 库
This commit is contained in:
362
utils/base-page.js
Normal file
362
utils/base-page.js
Normal file
@@ -0,0 +1,362 @@
|
||||
// utils/base-page.js - 页面公共框架
|
||||
import request from './request.js';
|
||||
import popupService from '../services/popupService.js';
|
||||
import {
|
||||
isRoleStatusActive,
|
||||
isCenterPageActive,
|
||||
syncRoleStatuses,
|
||||
syncProfileFields,
|
||||
syncGroupFields,
|
||||
ensureRoleOnCenterPage,
|
||||
clearCacheAndEnterNormal,
|
||||
refreshTabBar,
|
||||
} from './role-tab-bar.js';
|
||||
import { lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole } from './primary-role.js';
|
||||
import { resolveAvatarFromStorage } from './avatar.js';
|
||||
import { isApiSuccess, getApiMsg, extractUserData, parseSceneOptions } from './api-helper.js';
|
||||
|
||||
// ===== 基类数据 =====
|
||||
const BASE_DATA = {
|
||||
isLoading: false,
|
||||
lastRefreshTime: 0,
|
||||
canRefresh: true,
|
||||
};
|
||||
|
||||
// ===== 基类方法 =====
|
||||
const BASE_METHODS = {
|
||||
|
||||
// ---------- 通知组件 ----------
|
||||
registerNotificationComponent() {
|
||||
const app = getApp();
|
||||
const comp = this.selectComponent('#global-notification');
|
||||
if (comp && comp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => comp.showNotification(data),
|
||||
hide: () => comp.hideNotification(),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 弹窗清理 ----------
|
||||
cleanupPopup() {
|
||||
const comp = this.selectComponent('#popupNotice');
|
||||
if (comp && comp.cleanup) comp.cleanup();
|
||||
},
|
||||
|
||||
// ---------- 冷启动弹窗 ----------
|
||||
checkColdStartPopup(pageId) {
|
||||
const app = getApp();
|
||||
if (!app.globalData.hasShownPopupOnColdStart) {
|
||||
app.globalData.hasShownPopupOnColdStart = true;
|
||||
setTimeout(() => popupService.checkAndShow(this, pageId), 300);
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 头像加载 ----------
|
||||
loadAvatar() {
|
||||
this.setData({ avatarUrl: resolveAvatarFromStorage(getApp()) });
|
||||
},
|
||||
|
||||
// ---------- 通用用户信息加载 ----------
|
||||
loadUserInfo() {
|
||||
const app = getApp();
|
||||
const { nick, uid, avatarUrl } = (app.getUserProfileForDisplay && app.getUserProfileForDisplay()) || {};
|
||||
this.setData({
|
||||
avatarUrl: avatarUrl || resolveAvatarFromStorage(app),
|
||||
uid: uid || wx.getStorageSync('uid') || '',
|
||||
nicheng: nick || wx.getStorageSync('nicheng') || '',
|
||||
});
|
||||
},
|
||||
|
||||
// ---------- 通用数据加载 ----------
|
||||
async loadData(opts) {
|
||||
const {
|
||||
url, method = 'POST', data: reqData,
|
||||
fields, loadingKey = 'isLoading',
|
||||
successCode = 200, errorMsg = '加载失败',
|
||||
onSuccess, onError,
|
||||
} = opts;
|
||||
|
||||
this.setData({ [loadingKey]: true });
|
||||
try {
|
||||
const res = await request({ url, method, data: reqData });
|
||||
if (res && (res.data.code === successCode || (successCode === 0 && res.data.code === 0))) {
|
||||
const resData = res.data.data || {};
|
||||
if (fields) {
|
||||
const updateData = {};
|
||||
Object.keys(fields).forEach((pageKey) => {
|
||||
const apiField = fields[pageKey];
|
||||
updateData[pageKey] = apiField in resData ? resData[apiField] : (typeof apiField === 'object' ? apiField.default : '');
|
||||
});
|
||||
this.setData({ ...updateData, [loadingKey]: false });
|
||||
}
|
||||
if (onSuccess) onSuccess(resData, res);
|
||||
return resData;
|
||||
}
|
||||
throw new Error(res?.data?.msg || res?.data?.message || errorMsg);
|
||||
} catch (error) {
|
||||
this.setData({ [loadingKey]: false });
|
||||
if (onError) {
|
||||
onError(error);
|
||||
} else {
|
||||
wx.showToast({ title: error.message || errorMsg, icon: 'none' });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 节流刷新 ----------
|
||||
throttledRefresh(fn, interval = 3000) {
|
||||
const now = Date.now();
|
||||
if (!this.data.canRefresh || (now - this.data.lastRefreshTime < interval)) {
|
||||
wx.showToast({ title: '操作过快', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({ lastRefreshTime: now, canRefresh: false });
|
||||
fn.call(this);
|
||||
setTimeout(() => this.setData({ canRefresh: true }), interval);
|
||||
},
|
||||
|
||||
// ---------- 复制 UID ----------
|
||||
copyUid() {
|
||||
const uid = this.data.uid;
|
||||
if (!uid) {
|
||||
wx.showToast({ title: 'UID不存在', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.setClipboardData({
|
||||
data: uid,
|
||||
success: () => wx.showToast({ title: '已复制UID', icon: 'success' }),
|
||||
fail: () => wx.showToast({ title: '复制失败', icon: 'none' }),
|
||||
});
|
||||
},
|
||||
|
||||
// ---------- 清除缓存 ----------
|
||||
clearCache() {
|
||||
wx.showModal({
|
||||
title: '清除缓存',
|
||||
content: '将清除所有登录与身份数据,并回到点单端,确定继续?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: (r) => { if (r.confirm) clearCacheAndEnterNormal(this); },
|
||||
});
|
||||
},
|
||||
|
||||
// ---------- 返回点单端 ----------
|
||||
switchToNormal() {
|
||||
lockPrimaryRole('normal');
|
||||
wx.reLaunch({ url: '/pages/mine/mine' });
|
||||
},
|
||||
|
||||
// ---------- 客服 ----------
|
||||
goToKefu() {
|
||||
const app = getApp();
|
||||
const { link, enterpriseId } = app.globalData.kefuConfig || {};
|
||||
if (!link || !enterpriseId) {
|
||||
wx.showToast({ title: '客服配置未加载', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.openCustomerServiceChat({ extInfo: { url: link }, corpId: enterpriseId });
|
||||
},
|
||||
|
||||
// ---------- API 辅助(快捷访问) ----------
|
||||
_isApiSuccess: isApiSuccess,
|
||||
_getApiMsg: getApiMsg,
|
||||
_extractUserData: extractUserData,
|
||||
|
||||
// ---------- 邀请码注册通用流程 ----------
|
||||
async registerWithInviteCode(opts) {
|
||||
const {
|
||||
inviteCode, apiUrl, role, statusKey,
|
||||
alreadyMsg, successMsg = '注册成功',
|
||||
onSuccess,
|
||||
} = opts;
|
||||
|
||||
if (!inviteCode) {
|
||||
wx.showToast({ title: '请输入邀请码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (inviteCode.length > 100) {
|
||||
wx.showToast({ title: '邀请码不能超过100字符', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
const res = await request({ url: apiUrl, method: 'POST', data: { inviteCode } });
|
||||
const body = res?.data || {};
|
||||
|
||||
if (isApiSuccess(body)) {
|
||||
const userData = extractUserData(body);
|
||||
syncRoleStatuses(userData);
|
||||
syncProfileFields(userData);
|
||||
syncGroupFields(userData);
|
||||
if (userData.token) {
|
||||
wx.setStorageSync('token', userData.token);
|
||||
getApp().globalData.token = userData.token;
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(userData, statusKey) &&
|
||||
!isRoleStatusActive(wx.getStorageSync(statusKey))) {
|
||||
wx.setStorageSync(statusKey, 1);
|
||||
getApp().globalData[statusKey] = 1;
|
||||
}
|
||||
if (onSuccess) {
|
||||
await onSuccess(userData, body);
|
||||
}
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: successMsg, icon: 'success' });
|
||||
} else {
|
||||
const msg = getApiMsg(body);
|
||||
if (alreadyMsg && msg && msg.includes(alreadyMsg)) {
|
||||
if (onSuccess) {
|
||||
await onSuccess(extractUserData(body), body);
|
||||
}
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: msg, icon: 'success' });
|
||||
} else {
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: msg || '注册失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: error.message || '注册失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 邀请码 + 登录检查 ----------
|
||||
handleInviteCodeWithLoginCheck(opts) {
|
||||
const { inviteCode, role, statusKey, alreadyMsg, onNeedLogin, onAlreadyRegistered } = opts;
|
||||
const token = wx.getStorageSync('token');
|
||||
const uid = wx.getStorageSync('uid');
|
||||
|
||||
if (!token || !uid) {
|
||||
if (onNeedLogin) onNeedLogin(inviteCode);
|
||||
return;
|
||||
}
|
||||
|
||||
const isActive = isRoleStatusActive(wx.getStorageSync(statusKey));
|
||||
if (!isActive) {
|
||||
wx.showLoading({ title: '注册中...', mask: true });
|
||||
this.registerWithInviteCode(opts);
|
||||
} else {
|
||||
if (onAlreadyRegistered) onAlreadyRegistered();
|
||||
else wx.showToast({ title: alreadyMsg || '您已注册', icon: 'success' });
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 微信登录 + 注册 ----------
|
||||
loginAndRegisterWithInviteCode(opts) {
|
||||
const { inviteCode, onLoginSuccess } = opts;
|
||||
wx.showLoading({ title: '登录注册中...', mask: true });
|
||||
wx.login({
|
||||
success: (loginRes) => {
|
||||
if (loginRes.code) {
|
||||
if (onLoginSuccess) onLoginSuccess(loginRes.code, inviteCode);
|
||||
} else {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '微信登录失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '微信登录失败', icon: 'none' });
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// ---------- 图片 URL 初始化 ----------
|
||||
initImageUrls(urlMap) {
|
||||
const ossImageUrl = getApp().globalData.ossImageUrl || '';
|
||||
const result = {};
|
||||
Object.keys(urlMap).forEach((key) => {
|
||||
const path = urlMap[key];
|
||||
result[key] = path.startsWith('http') || path.startsWith('/') ? path : ossImageUrl + path;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
// ===== 角色中心页增强 =====
|
||||
function createRoleCenterMixin(roleConfig) {
|
||||
const { role, statusKey, dataFlag, pageId, apiUrl } = roleConfig;
|
||||
|
||||
return {
|
||||
onReady() {
|
||||
if (isCenterPageActive(this, dataFlag, statusKey)) {
|
||||
ensureRoleOnCenterPage(this, role);
|
||||
}
|
||||
},
|
||||
|
||||
onHide() {
|
||||
this.cleanupPopup();
|
||||
},
|
||||
|
||||
checkRoleStatus() {
|
||||
try {
|
||||
const status = wx.getStorageSync(statusKey);
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (isRoleStatusActive(status)) {
|
||||
this.setData({ [dataFlag]: true, uid: uid || '' });
|
||||
this.loadAvatar();
|
||||
if (this.checkGlobalData) this.checkGlobalData();
|
||||
ensureRoleOnCenterPage(this, role);
|
||||
} else {
|
||||
this.setData({ [dataFlag]: false });
|
||||
}
|
||||
} catch (error) {
|
||||
this.setData({ [dataFlag]: false });
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建页面配置(合并基类方法)
|
||||
* @param {Object} pageConfig - 页面特有配置
|
||||
* @param {Object} [options] - 可选配置
|
||||
* @param {Object} [options.roleConfig] - 角色中心页配置 { role, statusKey, dataFlag, pageId, apiUrl }
|
||||
* @returns {Object} 完整的 Page 配置
|
||||
*/
|
||||
export function createPage(pageConfig, options = {}) {
|
||||
const { roleConfig } = options;
|
||||
|
||||
// 合并 data
|
||||
const data = { ...BASE_DATA, ...(pageConfig.data || {}) };
|
||||
|
||||
// 合并方法
|
||||
const methods = { ...BASE_METHODS };
|
||||
|
||||
// 如果是角色中心页,混入角色相关方法
|
||||
if (roleConfig) {
|
||||
Object.assign(methods, createRoleCenterMixin(roleConfig));
|
||||
}
|
||||
|
||||
// 页面特有方法覆盖基类方法
|
||||
const allMethods = { ...methods, ...pageConfig };
|
||||
|
||||
// 确保 data 正确
|
||||
allMethods.data = data;
|
||||
|
||||
return allMethods;
|
||||
}
|
||||
|
||||
// 导出工具函数供页面直接使用
|
||||
export {
|
||||
request,
|
||||
isRoleStatusActive,
|
||||
isCenterPageActive,
|
||||
syncRoleStatuses,
|
||||
syncProfileFields,
|
||||
syncGroupFields,
|
||||
ensureRoleOnCenterPage,
|
||||
clearCacheAndEnterNormal,
|
||||
refreshTabBar,
|
||||
lockPrimaryRole,
|
||||
migrateLegacyCenterRole,
|
||||
enterLockedRole,
|
||||
isApiSuccess,
|
||||
getApiMsg,
|
||||
extractUserData,
|
||||
parseSceneOptions,
|
||||
};
|
||||
Reference in New Issue
Block a user