From e54846df026860474555c3018eb253a83fc55db5 Mon Sep 17 00:00:00 2001 From: XingQue Date: Mon, 22 Jun 2026 16:42:36 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E6=89=93=E6=89=8B=E7=AB=AF=E6=AF=8F?= =?UTF-8?q?=E6=AC=A1=E8=BF=9B=E5=85=A5=E5=9D=87=E5=90=91=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=89=8B=E6=9C=BA=E5=8F=B7=E8=AE=A4=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 25 +-- pages/accept-order/accept-order.js | 6 +- pages/fighter-recharge/fighter-recharge.js | 7 +- pages/fighter/fighter.js | 6 +- pages/phone-auth/phone-auth.js | 7 +- utils/phone-auth.js | 174 +++++++++++---------- utils/primary-role.js | 16 +- 7 files changed, 137 insertions(+), 104 deletions(-) diff --git a/app.js b/app.js index 811ef86..e5bb6b6 100644 --- a/app.js +++ b/app.js @@ -180,6 +180,22 @@ App({ const savedRole = getPrimaryRole(this); lockPrimaryRole(savedRole, this); + // ②b 进入各端前先问后端是否需要手机号认证 + if (wx.getStorageSync('token')) { + try { + const needAuth = await check(); + if (needAuth) { + if (savedRole === 'dashou') { + wx.setStorageSync('phone_auth_return_role', 'dashou'); + } else if (savedRole === 'shangjia') { + wx.setStorageSync('phone_auth_return_role', 'shangjia'); + } + wx.reLaunch({ url: '/pages/phone-auth/phone-auth' }); + return; + } + } catch (e) {} + } + const targetPage = roleDefaultPage[savedRole] || '/pages/index/index'; if (savedRole !== 'normal') { setTimeout(() => { @@ -221,15 +237,6 @@ App({ .catch(err => { console.warn('远程配置获取失败,继续使用本地缓存', err); }); - - // ⑦ 手机号认证检查 - try { - const needAuth = await check(); - if (needAuth) { - wx.reLaunch({ url: '/pages/phone-auth/phone-auth' }); - return; - } - } catch (e) {} }, // 连接管理方法 diff --git a/pages/accept-order/accept-order.js b/pages/accept-order/accept-order.js index d06af4a..e27a9ed 100644 --- a/pages/accept-order/accept-order.js +++ b/pages/accept-order/accept-order.js @@ -3,6 +3,7 @@ const app = getApp(); import request from '../../utils/request.js'; import PopupService from '../../services/popupService.js'; import { reconnectForRole } from '../../utils/role-tab-bar.js'; +import { ensurePhoneAuth } from '../../utils/phone-auth.js'; Page({ data: { @@ -58,7 +59,10 @@ Page({ } }, - onShow() { + async onShow() { + const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' }); + if (!phoneOk) return; + this.registerNotificationComponent(); this.loadGlobalStatus(); if (wx.getStorageSync('uid')) { diff --git a/pages/fighter-recharge/fighter-recharge.js b/pages/fighter-recharge/fighter-recharge.js index ebb1da2..d52fe34 100644 --- a/pages/fighter-recharge/fighter-recharge.js +++ b/pages/fighter-recharge/fighter-recharge.js @@ -1,7 +1,7 @@ // pages/dashou-chongzhi/index.js import request from '../../utils/request'; -// 引入弹窗服务(路径根据实际调整) import PopupService from '../../services/popupService.js'; +import { ensurePhoneAuth } from '../../utils/phone-auth.js'; Page({ data: { @@ -83,7 +83,10 @@ onHide() { } }, - onShow() { + async onShow() { + const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' }); + if (!phoneOk) return; + // 每次显示页面都刷新数据 this.registerNotificationComponent(); this.loadFromGlobalData(); diff --git a/pages/fighter/fighter.js b/pages/fighter/fighter.js index beed7f0..e49f58b 100644 --- a/pages/fighter/fighter.js +++ b/pages/fighter/fighter.js @@ -2,6 +2,7 @@ import { createPage, request, isRoleStatusActive, isCenterPageActive, syncRoleStatuses, syncProfileFields, syncGroupFields, ensureRoleOnCenterPage, clearCacheAndEnterNormal, lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole, parseSceneOptions } from '../../utils/base-page.js' import { resolveAvatarUrl } from '../../utils/avatar.js' import { openPrivateChat, buildInviterPeerId } from '../../utils/im-user.js' +import { ensurePhoneAuth } from '../../utils/phone-auth.js' const PLATFORM_INVITE_CODE = '0000008RffVgKHMj7kQC' @@ -171,7 +172,10 @@ Page(createPage({ this.checkColdStartPopup('dashouduan'); }, - onShow() { + async onShow() { + const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' }); + if (!phoneOk) return; + migrateLegacyCenterRole(getApp()); lockPrimaryRole('dashou', getApp()); wx.setStorageSync('isJinpai', 0); diff --git a/pages/phone-auth/phone-auth.js b/pages/phone-auth/phone-auth.js index d61714f..b3abe21 100644 --- a/pages/phone-auth/phone-auth.js +++ b/pages/phone-auth/phone-auth.js @@ -1,4 +1,4 @@ -import { submit } from '../../utils/phone-auth'; +import { submit, getPhoneAuthReturnPage } from '../../utils/phone-auth'; Page({ data: { @@ -27,8 +27,7 @@ Page({ wx.showLoading({ title: '认证中...', mask: true }); await submit(phoneCode, avatarFile); wx.hideLoading(); - // 成功,重启小程序,此时check返回false,用户无感进入 - wx.reLaunch({ url: '/pages/index/index' }); + wx.reLaunch({ url: getPhoneAuthReturnPage() }); } catch (e) { wx.hideLoading(); wx.showToast({ title: e.message, icon: 'none' }); @@ -37,4 +36,4 @@ Page({ exit() { wx.exitMiniProgram(); } -}); \ No newline at end of file +}); diff --git a/utils/phone-auth.js b/utils/phone-auth.js index 01c463f..f34d990 100644 --- a/utils/phone-auth.js +++ b/utils/phone-auth.js @@ -1,57 +1,32 @@ /** * 手机号强制认证 - 完全自包含逻辑模块 - * - * 特点: - * 1. 不引用项目中的 request.js / upload.js,避免其顶层 getApp() 带来的加载顺序错误 - * 2. 所有请求直接使用 wx.request / wx.uploadFile,基础 URL 写死(与 app.globalData.apiBaseUrl 保持一致) - * 3. check() 内部捕获所有异常,永远返回 false 或 true,不会抛出错误 - * 4. submit() 抛出错误由认证页面自己处理,不影响主流程 - * - * 用法: - * app.js 中: - * try { - * const { check } = require('./utils/phone-auth'); - * if (await check()) { wx.reLaunch(...); return; } - * } catch(e) {} - * - * 认证页面中: - * const { submit } = require('../../utils/phone-auth'); - * await submit(phoneCode, avatarPath); + * + * check / ensurePhoneAuth 每次均请求后端 /peizhi/yhbdsjh 判断 need_auth */ -// ==================== 基础配置 ==================== -const API_BASE = 'https://www.abas.asia/hqhd'; // 与你 app.js 里的 apiBaseUrl 一致 -const AUTH_CHECK_URL = '/peizhi/yhbdsjh'; // 检查是否需要认证 -const AUTH_SUBMIT_URL = '/yonghu/xiugai'; // 提交认证(复用修改信息接口) +const API_BASE = 'https://www.abas.asia/hqhd'; +const AUTH_CHECK_URL = '/peizhi/yhbdsjh'; +const AUTH_SUBMIT_URL = '/yonghu/xiugai'; +const AUTH_PAGE = '/pages/phone-auth/phone-auth'; +const RETURN_ROLE_KEY = 'phone_auth_return_role'; -// ==================== 内部封装:wx.request ==================== function innerRequest(url, data = {}) { return new Promise((resolve, reject) => { - const token = wx.getStorageSync('token'); // 运行时获取 token,不提前取 - const header = { - 'Content-Type': 'application/json' - }; - if (token) { - header['Authorization'] = 'Bearer ' + token; - } + const token = wx.getStorageSync('token'); + const header = { 'Content-Type': 'application/json' }; + if (token) header['Authorization'] = 'Bearer ' + token; wx.request({ url: API_BASE + url, method: 'POST', data, header, - success: (res) => { - // 正常响应直接 resolve 整个响应对象,让上层自己解析 - resolve(res); - }, - fail: (err) => { - reject(err); - } + success: (res) => resolve(res), + fail: (err) => reject(err), }); }); } -// ==================== 内部封装:wx.uploadFile ==================== function innerUpload(url, filePath, formData = {}, fileName = 'avatar') { return new Promise((resolve, reject) => { const token = wx.getStorageSync('token'); @@ -60,7 +35,6 @@ function innerUpload(url, filePath, formData = {}, fileName = 'avatar') { return; } - // 先验证文件是否存在 wx.getFileInfo({ filePath, success: () => { @@ -69,15 +43,11 @@ function innerUpload(url, filePath, formData = {}, fileName = 'avatar') { filePath, name: fileName, formData, - header: { - 'Authorization': 'Bearer ' + token - }, + header: { Authorization: 'Bearer ' + token }, success: (res) => { - // ✅ 修复:wx.uploadFile 成功时 statusCode 为 200 if (res.statusCode === 200) { try { - const data = JSON.parse(res.data); - resolve(data); // 直接返回解析后的对象,方便下游使用 + resolve(JSON.parse(res.data)); } catch (e) { reject(new Error('解析响应数据失败')); } @@ -88,24 +58,43 @@ function innerUpload(url, filePath, formData = {}, fileName = 'avatar') { reject(new Error(`上传失败,状态码:${res.statusCode}`)); } }, - fail: (err) => { - reject(new Error('网络请求失败')); - } + fail: () => reject(new Error('网络请求失败')), }); }, - fail: () => { - reject(new Error('文件不存在或无法访问')); - } + fail: () => reject(new Error('文件不存在或无法访问')), }); }); } -// ==================== 公开方法 ==================== +function isOnPhoneAuthPage() { + const pages = getCurrentPages(); + const cur = pages[pages.length - 1]; + return !!(cur && cur.route === 'pages/phone-auth/phone-auth'); +} + +function parseCheckResponse(res) { + if (!res || res.statusCode === 401) { + return { ok: false, needAuth: false }; + } + if (res.statusCode !== 200) { + return { ok: false, needAuth: false }; + } + const body = res.data; + if (!body || typeof body !== 'object') { + return { ok: false, needAuth: false }; + } + const code = Number(body.code); + if (code !== 0 && code !== 200) { + return { ok: false, needAuth: false }; + } + const needAuth = body.need_auth === true + || (body.data && body.data.need_auth === true); + return { ok: true, needAuth: !!needAuth }; +} /** - * 检查是否需要手机号认证 - * - 无 token 或 任何异常 → 返回 false - * - 后端明确 need_auth: true → 返回 true + * 询问后端是否需要手机号认证 + * @returns {Promise} true=需要认证 */ async function check() { const token = wx.getStorageSync('token'); @@ -113,53 +102,68 @@ async function check() { try { const res = await innerRequest(AUTH_CHECK_URL, {}); - // 假设后端返回格式:{ data: { need_auth: true/false } } 或直接在顶层 - const body = res.data; - return body && body.need_auth === true; + const parsed = parseCheckResponse(res); + if (!parsed.ok) return false; + return parsed.needAuth; } catch (e) { - // 任何错误(网络、后端异常)都吞掉,不影响主流程 return false; } } +function redirectToPhoneAuth(returnRole) { + if (returnRole) { + wx.setStorageSync(RETURN_ROLE_KEY, returnRole); + } + if (!isOnPhoneAuthPage()) { + wx.reLaunch({ url: AUTH_PAGE }); + } +} + /** - * 提交认证(手机号 code + 头像文件) - * @param {string} phoneCode 微信 getPhoneNumber 返回的 code - * @param {string} avatarPath 头像临时路径 - * @returns {Promise} 后端返回的完整数据 + * 打手端等场景:向后端确认,未认证则跳转认证页 + * @returns {Promise} true=已通过或无需认证;false=已跳转认证页 */ +async function ensurePhoneAuth(options = {}) { + const { redirect = true, role = 'dashou' } = options; + const token = wx.getStorageSync('token'); + if (!token) return true; + + const needAuth = await check(); + if (!needAuth) return true; + if (redirect) redirectToPhoneAuth(role); + return false; +} + +function getPhoneAuthReturnPage() { + const role = wx.getStorageSync(RETURN_ROLE_KEY) || ''; + wx.removeStorageSync(RETURN_ROLE_KEY); + if (role === 'dashou') return '/pages/accept-order/accept-order'; + if (role === 'shangjia') return '/pages/merchant-orders/merchant-orders'; + return '/pages/mine/mine'; +} + async function submit(phoneCode, avatarPath) { if (!phoneCode || !avatarPath) { throw new Error('缺少参数'); } - // 将手机号 code 放入 formData - const formData = { - shoujihao_code: phoneCode - }; + const data = await innerUpload(AUTH_SUBMIT_URL, avatarPath, { + shoujihao_code: phoneCode, + }); - // 调用上传 - const data = await innerUpload(AUTH_SUBMIT_URL, avatarPath, formData); - - // ✅ 修复:正确判断后端返回的 code 字段,后端成功时 code === 0 if (data && data.code === 0) { - // data.data 包含 { touxiang: '相对路径', phone: '...' } const respData = data.data || data; - - // 存储手机号 - if (respData.phone) { - wx.setStorageSync('phone', respData.phone); - } - - // ✅ 修复:存储头像相对路径,与 xiugai 页面处理方式一致 - if (respData.touxiang) { - wx.setStorageSync('touxiang', respData.touxiang); - } - + if (respData.phone) wx.setStorageSync('phone', respData.phone); + if (respData.touxiang) wx.setStorageSync('touxiang', respData.touxiang); return respData; - } else { - throw new Error((data && data.msg) || '认证失败'); } + throw new Error((data && data.msg) || '认证失败'); } -module.exports = { check, submit }; \ No newline at end of file +module.exports = { + check, + submit, + ensurePhoneAuth, + redirectToPhoneAuth, + getPhoneAuthReturnPage, +}; diff --git a/utils/primary-role.js b/utils/primary-role.js index 29005ba..e502c0a 100644 --- a/utils/primary-role.js +++ b/utils/primary-role.js @@ -1,5 +1,7 @@ /** 三端固定:normal | dashou | shangjia,认证后不可回退 */ +import { ensurePhoneAuth } from './phone-auth.js'; + const VALID = ['normal', 'dashou', 'shangjia']; const LEGACY_CENTER_ROLES = ['guanshi', 'zuzhang', 'kaoheguan']; @@ -87,8 +89,7 @@ export const PRIMARY_DEFAULT_PAGES = { }; /** 锁定主端并跳转到该端默认首页,同时建立 IM 长连接 */ -export function enterLockedRole(role, app) { - if (!VALID.includes(role) || role === 'normal') return false; +function finishEnterLockedRole(role, app) { app = app || getApp(); lockPrimaryRole(role, app); wx.reLaunch({ url: PRIMARY_DEFAULT_PAGES[role] }); @@ -103,3 +104,14 @@ export function enterLockedRole(role, app) { }, 600); return true; } + +export function enterLockedRole(role, app) { + if (!VALID.includes(role) || role === 'normal') return false; + if (role === 'dashou') { + ensurePhoneAuth({ redirect: true, role: 'dashou' }).then((ok) => { + if (ok) finishEnterLockedRole(role, app); + }); + return true; + } + return finishEnterLockedRole(role, app); +} From 32f89529c66a860ce043dd6fbaf39d8882b2a244 Mon Sep 17 00:00:00 2001 From: XingQue Date: Tue, 7 Jul 2026 21:49:36 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E6=98=9F=E4=B9=8B=E7=95=8C=E6=8E=92?= =?UTF-8?q?=E8=A1=8C=E6=A6=9C=E5=B1=95=E7=A4=BA=E5=AD=97=E6=AE=B5=E4=B8=8E?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E8=A7=84=E5=88=99=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 管事主指标改为有效人数,组长主指标改为收益总额,副指标展示无效/有效邀请与收益。 Co-authored-by: Cursor --- pages/fighter-rank/fighter-rank.js | 321 +++++++++++++++++++++++------ 1 file changed, 263 insertions(+), 58 deletions(-) diff --git a/pages/fighter-rank/fighter-rank.js b/pages/fighter-rank/fighter-rank.js index 7d72942..baac5bf 100644 --- a/pages/fighter-rank/fighter-rank.js +++ b/pages/fighter-rank/fighter-rank.js @@ -1,68 +1,125 @@ -/** - * 排行榜 POST /yonghu/phbhqsj - * 参数: shenfen(dashou|guanshi|zuzhang|shangjia), riqi(今日|本周|本月|总榜|昨日|上周|上月) - */ +/** + * 鎺掕姒? * 榛樿 POST /yonghu/phbhqsj + * 鏄熶箣鐣?xzj) POST /yonghu/xzjphbhqsj锛堥個璇蜂汉鏁版帓搴忕瓑涓撶敤瑙勫垯锛? */ import request from '../../utils/request.js' import { resolveAvatarUrl, getDefaultAvatarUrl } from '../../utils/avatar.js' +import { CLUB_ID } from '../../config/club-config.js' + +const IS_XZJ = CLUB_ID === 'xzj' +const RANK_API = IS_XZJ ? '/yonghu/xzjphbhqsj' : '/yonghu/phbhqsj' +const CLOSED_DATES = ['鏄ㄦ棩', '涓婂懆', '涓婃湀'] const ROLE_LIST = [ - { key: 'dashou', label: '接单员' }, - { key: 'guanshi', label: '管事' }, - { key: 'zuzhang', label: '组长' }, - { key: 'shangjia', label: '商家' }, + { key: 'dashou', label: '鎺ュ崟鍛? }, + { key: 'guanshi', label: '绠′簨' }, + { key: 'zuzhang', label: '缁勯暱' }, + { key: 'shangjia', label: '鍟嗗' }, ] const DATE_LIST = [ - { key: '今日', label: '今日' }, - { key: '本周', label: '本周' }, - { key: '本月', label: '本月' }, - { key: '总榜', label: '总榜' }, - { key: '昨日', label: '昨日' }, - { key: '上周', label: '上周' }, - { key: '上月', label: '上月' }, + { key: '浠婃棩', label: '浠婃棩' }, + { key: '鏄ㄦ棩', label: '鏄ㄦ棩' }, + { key: '鏈懆', label: '鏈懆' }, + { key: '涓婂懆', label: '涓婂懆' }, + { key: '鏈湀', label: '鏈湀' }, + { key: '涓婃湀', label: '涓婃湀' }, + { key: '鎬绘', label: '鎬绘' }, ] const ROLE_META = { dashou: { - title: '接单员排行榜', + title: '鎺ュ崟鍛樻帓琛屾', sortField: 'chengjiao_zonge', - sortLabel: '分红总额', + sortLabel: '鍒嗙孩鎬婚', + mainType: 'money', metrics: [ - { field: 'jiedan_zongliang', label: '接单量', type: 'int' }, - { field: 'jiedan_zonge', label: '接单额', type: 'money' }, - { field: 'chengjiao_zongliang', label: '成交量', type: 'int' }, + { field: 'jiedan_zongliang', label: '鎺ュ崟閲?, type: 'int' }, + { field: 'jiedan_zonge', label: '鎺ュ崟棰?, type: 'money' }, + { field: 'chengjiao_zongliang', label: '鎴愪氦閲?, type: 'int' }, ], }, guanshi: { - title: '管事排行榜', + title: '绠′簨鎺掕姒?, sortField: 'shouru_zonge', - sortLabel: '收入总额', + sortLabel: '鏀跺叆鎬婚', + mainType: 'money', metrics: [ - { field: 'yaoqing_dashou_shu', label: '邀请', type: 'int' }, - { field: 'chongzhi_dashou_shu', label: '充值', type: 'int' }, + { field: 'yaoqing_dashou_shu', label: '閭€璇?, type: 'int' }, + { field: 'chongzhi_dashou_shu', label: '鍏呭€?, type: 'int' }, ], }, zuzhang: { - title: '组长排行榜', + title: '缁勯暱鎺掕姒?, sortField: 'shouru_zonge', - sortLabel: '收入总额', + sortLabel: '鏀跺叆鎬婚', + mainType: 'money', metrics: [ - { field: 'yaoqing_guanshi_shu', label: '邀请', type: 'int' }, - { field: 'fenyong_jine', label: '分佣', type: 'money' }, + { field: 'yaoqing_guanshi_shu', label: '閭€璇?, type: 'int' }, + { field: 'fenyong_jine', label: '鍒嗕剑', type: 'money' }, ], }, shangjia: { - title: '商家排行榜', + title: '鍟嗗鎺掕姒?, sortField: 'jiesuan_jine', - sortLabel: '成交总额', + sortLabel: '鎴愪氦鎬婚', + mainType: 'money', metrics: [ - { field: 'jiesuan_dingdan_shu', label: '成交量', type: 'int' }, - { field: 'paifa_dingdan_shu', label: '派单量', type: 'int' }, - { field: 'paifa_jine', label: '派单额', type: 'money' }, + { field: 'jiesuan_dingdan_shu', label: '鎴愪氦閲?, type: 'int' }, + { field: 'paifa_dingdan_shu', label: '娲惧崟閲?, type: 'int' }, + { field: 'paifa_jine', label: '娲惧崟棰?, type: 'money' }, ], }, } +/** 鏄熶箣鐣屼笓鐢ㄥ睍绀轰笌鎺掑簭瀛楁 */ +const XZJ_ROLE_META = { + dashou: { + title: '鎺ュ崟鍛樻帓琛屾', + sortField: 'chengjiao_zonge', + sortLabel: '鎴愪氦閲戦', + mainType: 'money', + metrics: [ + { field: 'jiedan_zongliang', label: '鎺ュ崟閲?, type: 'int' }, + { field: 'jiedan_zonge', label: '鎺ュ崟棰?, type: 'money' }, + { field: 'chengjiao_zongliang', label: '鎴愪氦閲?, type: 'int' }, + ], + }, + guanshi: { + title: '绠′簨鎺掕姒?, + sortField: 'chongzhi_dashou_shu', + sortLabel: '鏈夋晥浜烘暟', + mainType: 'int', + metrics: [ + { field: 'wuxiao_yaoqing_dashou_shu', label: '鏃犳晥閭€璇?, type: 'int' }, + { field: 'yaoqing_dashou_shu', label: '閭€璇蜂汉鏁?, type: 'int' }, + { field: 'shouru_zonge', label: '鏀剁泭鎬婚', type: 'money' }, + ], + }, + zuzhang: { + title: '缁勯暱鎺掕姒?, + sortField: 'shouru_zonge', + sortLabel: '鏀剁泭鎬婚', + mainType: 'money', + metrics: [ + { field: 'wuxiao_yaoqing_guanshi_shu', label: '鏃犳晥閭€璇?, type: 'int' }, + { field: 'youxiao_guanshi_shu', label: '鏈夋晥浜烘暟', type: 'int' }, + { field: 'yaoqing_guanshi_shu', label: '閭€璇蜂汉鏁?, type: 'int' }, + ], + }, + shangjia: { + title: '鍟嗗鎺掕姒?, + sortField: 'paifa_jine', + sortLabel: '娲惧崟娴佹按', + mainType: 'money', + metrics: [ + { field: 'paifa_dingdan_shu', label: '娲惧崟閲?, type: 'int' }, + { field: 'paifa_jine', label: '娲惧崟娴佹按', type: 'money' }, + ], + }, +} + +const ACTIVE_ROLE_META = IS_XZJ ? XZJ_ROLE_META : ROLE_META + function isInvalidAvatarPath(path) { if (path === null || path === undefined) return true if (typeof path !== 'string') return true @@ -86,20 +143,25 @@ Page({ roleList: ROLE_LIST, dateList: DATE_LIST, currentRole: 'dashou', - currentDate: '今日', + currentDate: '浠婃棩', + fanwei: 'club', + showJituanScope: false, // 涓存椂闅愯棌闆嗗洟鎬绘锛屾仮澶嶆椂鏀?true 骞惰皟鏁?fanwei 榛樿鍊? myClubId: CLUB_ID || 'xq', roleMeta: ROLE_META.dashou, - sortLabel: '分红总额', + sortLabel: '鍒嗙孩鎬婚', isLoading: true, rankList: [], topThree: [], restList: [], isEmpty: false, defaultAvatar: '', + rewardInfo: null, + showClaimBar: false, + claimLoading: false, }, onLoad(options) { const type = options.type || options.rankType || 'dashou' - const validRole = ROLE_META[type] ? type : 'dashou' + const validRole = ACTIVE_ROLE_META[type] ? type : 'dashou' this.initPage() this.applyRole(validRole, false) this.fetchRankList() @@ -115,15 +177,13 @@ Page({ const base = (g.ossImageUrl || '') + 'beijing/paihangbang/' this.setData({ imgUrls: { - pageBg: base + 'page-bg.jpg', - cardBg: base + 'list-card-bg.png', emptyIcon: base + 'empty.png', refreshIcon: base + 'icon-refresh.png', }, }) }, - /** 与 app.js 一致:ossImageUrl + morentouxiang,配置异步加载后 onShow 再同步 */ + /** 涓?app.js 涓€鑷达細ossImageUrl + morentouxiang锛岄厤缃紓姝ュ姞杞藉悗 onShow 鍐嶅悓姝?*/ syncDefaultAvatar() { const def = getDefaultAvatar() if (def && def !== this.data.defaultAvatar) { @@ -133,7 +193,7 @@ Page({ } }, - /** 配置晚于页面加载时,补全空头像 */ + /** 閰嶇疆鏅氫簬椤甸潰鍔犺浇鏃讹紝琛ュ叏绌哄ご鍍?*/ ensureAvatars() { const def = getDefaultAvatar() if (!def) return @@ -162,8 +222,8 @@ Page({ }, applyRole(role, reload = true) { - const meta = ROLE_META[role] - wx.setNavigationBarTitle({ title: meta.title }) + const meta = ACTIVE_ROLE_META[role] + wx.setNavigationBarTitle({ title: '鎬绘帓琛屾' }) this.setData({ currentRole: role, roleMeta: meta, sortLabel: meta.sortLabel }) if (reload) this.fetchRankList() }, @@ -181,6 +241,61 @@ Page({ this.fetchRankList() }, + onScopeTap(e) { + const fanwei = e.currentTarget.dataset.fanwei + if (!fanwei || fanwei === this.data.fanwei) return + if (fanwei === 'jituan' && !this.data.showJituanScope) return + this.setData({ fanwei }) + this.fetchRankList() + }, + + onShowRules() { + const r = this.data.rewardInfo + if (!r || !r.tiers || !r.tiers.length) { + wx.showToast({ title: '鏆傛棤濂栧姳瑙勫垯', icon: 'none' }) + return + } + const lines = r.tiers.map((t) => { + const label = t.label || `绗?{t.rank_from}${t.rank_to > t.rank_from ? '-' + t.rank_to : ''}鍚峘 + return `${label}锛毬?{t.amount}` + }) + const tip = r.period_status === 'open' ? '锛堟湰鏈熻繘琛屼腑锛岀粨鏉熷悗鍙鍙栵級' : '' + wx.showModal({ + title: `${r.title || '鎺掕姒滃鍔?}${tip}`, + content: [ + `淇变箰閮細${r.club_id || this.data.myClubId}`, + `鎺掑簭锛?{r.sort_label || ''}`, + ...lines, + r.description || '', + ].filter(Boolean).join('\n'), + showCancel: false, + }) + }, + + async onClaimTap() { + const claim = this.data.rewardInfo && this.data.rewardInfo.my_claim + if (!claim || !claim.claim_id || this.data.claimLoading) return + this.setData({ claimLoading: true }) + try { + const res = await request({ + url: '/yonghu/phbjllq', + method: 'POST', + data: { claim_id: claim.claim_id }, + }) + const body = res?.data || {} + if (body.code === 200) { + wx.showToast({ title: `宸查鍙?楼${claim.amount}`, icon: 'success' }) + await this.fetchRewardInfo() + } else { + wx.showToast({ title: body.msg || '棰嗗彇澶辫触', icon: 'none' }) + } + } catch (e) { + wx.showToast({ title: '棰嗗彇澶辫触', icon: 'none' }) + } finally { + this.setData({ claimLoading: false }) + } + }, + onRefreshTap() { this.fetchRankList() }, @@ -196,10 +311,10 @@ Page({ }, normalizeItem(raw, index, role) { - const meta = ROLE_META[role] + const meta = ACTIVE_ROLE_META[role] const app = getApp() const uid = raw.yonghuid || raw.uid || '' - const nicheng = raw.nicheng || raw.nick || '用户' + const nicheng = raw.nicheng || raw.nick || (role === 'dashou' ? '鎵撴墜' : role === 'guanshi' ? '绠′簨' : role === 'zuzhang' ? '缁勯暱' : role === 'shangjia' ? '鍟嗗' : '鐢ㄦ埛') const def = getDefaultAvatar(app) const avatar = buildAvatar(raw.touxiang || raw.avatar, app) || def const metrics = meta.metrics.map((m) => { @@ -209,33 +324,120 @@ Page({ value: m.type === 'money' ? this.formatMoney(v) : this.formatInt(v), } }) + const hasBackendMetric = raw.metric_value !== undefined && raw.metric_value !== null + const sortRaw = IS_XZJ + ? (raw[meta.sortField] ?? raw.metric_value) + : (hasBackendMetric ? raw.metric_value : (raw[meta.sortField] ?? raw.metric_value)) + let mainType = meta.mainType || 'money' + if (hasBackendMetric && raw.metric_is_int !== undefined) { + mainType = raw.metric_is_int ? 'int' : 'money' + } else if (raw.metric_label) { + mainType = (Number.isInteger(Number(sortRaw)) && !String(sortRaw).includes('.')) ? 'int' : 'money' + } + const mainValue = mainType === 'money' + ? this.formatMoney(sortRaw) + : this.formatInt(sortRaw) return { mingci: raw.mingci || index + 1, uid, nicheng, avatar, - mainValue: this.formatMoney(raw[meta.sortField]), - mainLabel: meta.sortLabel, + clubName: raw.club_name || raw.club_id || '', + rewardAmount: raw.reward_amount || 0, + mainType, + mainPrefix: mainType === 'money' ? '楼' : '', + mainValue, + mainLabel: (IS_XZJ ? meta.sortLabel : (raw.metric_label || meta.sortLabel)), metrics, } }, - async fetchRankList() { - this.setData({ isLoading: true, isEmpty: false }) + applyRewardToList(list, rewardInfo, fanwei) { + if (fanwei !== 'club') return list + if (!rewardInfo || !rewardInfo.rank_rewards) return list + const map = rewardInfo.rank_rewards + return list.map((item) => ({ + ...item, + rewardAmount: map[item.mingci] || item.rewardAmount || 0, + })) + }, + + async fetchRewardInfo() { try { const res = await request({ - url: '/yonghu/phbhqsj', + url: '/yonghu/phbjlxx', method: 'POST', - data: { shenfen: this.data.currentRole, riqi: this.data.currentDate }, + data: { + shenfen: this.data.currentRole, + riqi: this.data.currentDate, + club_id: this.data.myClubId, + }, }) const body = res?.data || {} - const ok = body.code === 200 || body.code === 0 - let list = ok && body.data - ? (body.data.list || body.data.paihang_list || body.data.rank_list || []) - : [] + if (body.code !== 200 || !body.data) return + const rewardInfo = body.data + const showClaimBar = !!(rewardInfo.my_claim && rewardInfo.my_claim.status === 'pending') + const { rankList, fanwei } = this.data + const merged = this.applyRewardToList(rankList, rewardInfo, fanwei) + this.setData({ + rewardInfo, + showClaimBar, + rankList: merged, + topThree: merged.slice(0, 3), + restList: merged.slice(3), + }) + } catch (e) { + console.warn('濂栧姳淇℃伅鍔犺浇澶辫触', e) + } + }, + + async fetchRankList() { + this.setData({ isLoading: true, isEmpty: false, showClaimBar: false }) + try { + const { currentRole, currentDate, fanwei, myClubId } = this.data + let list = [] + let rewardFromRank = null + + if (fanwei === 'club') { + const res = await request({ + url: '/yonghu/phbjlphb', + method: 'POST', + data: { + shenfen: currentRole, + riqi: currentDate, + fanwei: 'club', + club_id: myClubId, + }, + }) + const body = res?.data || {} + if (body.code === 200 && body.data) { + list = body.data.list || [] + rewardFromRank = body.data.reward || null + if (body.data.sort_label) { + this.setData({ sortLabel: body.data.sort_label }) + } + } + } else { + const res = await request({ + url: RANK_API, + method: 'POST', + data: { shenfen: currentRole, riqi: currentDate }, + }) + const body = res?.data || {} + const ok = body.code === 200 || body.code === 0 + list = ok && body.data + ? (body.data.list || body.data.paihang_list || body.data.rank_list || []) + : [] + } + if (!Array.isArray(list)) list = [] - const role = this.data.currentRole - const normalized = list.slice(0, 50).map((item, i) => this.normalizeItem(item, i, role)) + const role = currentRole + // 鍚嶆椤哄簭瀹屽叏娌跨敤鍚庣杩斿洖锛屽墠绔笉鍋?sort / 閲嶆帓 + let normalized = list.slice(0, 50).map((item, i) => this.normalizeItem(item, i, role)) + if (rewardFromRank) { + normalized = this.applyRewardToList(normalized, rewardFromRank, 'club') + this.setData({ rewardInfo: rewardFromRank, showClaimBar: !!(rewardFromRank.my_claim && rewardFromRank.my_claim.status === 'pending') }) + } this.setData({ rankList: normalized, topThree: normalized.slice(0, 3), @@ -244,9 +446,12 @@ Page({ isLoading: false, }) this.ensureAvatars() + if (!rewardFromRank) { + await this.fetchRewardInfo() + } } catch (e) { - console.error('排行榜加载失败', e) - wx.showToast({ title: '加载失败', icon: 'none' }) + console.error('鎺掕姒滃姞杞藉け璐?, e) + wx.showToast({ title: '鍔犺浇澶辫触', icon: 'none' }) this.setData({ rankList: [], topThree: [], restList: [], isEmpty: true, isLoading: false }) } },