/** * 排行榜 POST /yonghu/phbhqsj * 参数: shenfen(dashou|guanshi|zuzhang|shangjia), riqi(今日|本周|本月|总榜|昨日|上周|上月) */ import request from '../../utils/request.js' import { resolveAvatarUrl, getDefaultAvatarUrl } from '../../utils/avatar.js' const ROLE_LIST = [ { 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: '上月' }, ] const ROLE_META = { dashou: { title: '接单员排行榜', sortField: 'chengjiao_zonge', sortLabel: '分红总额', metrics: [ { field: 'jiedan_zongliang', label: '接单量', type: 'int' }, { field: 'jiedan_zonge', label: '接单额', type: 'money' }, { field: 'chengjiao_zongliang', label: '成交量', type: 'int' }, ], }, guanshi: { title: '管事排行榜', sortField: 'shouru_zonge', sortLabel: '收入总额', metrics: [ { field: 'yaoqing_dashou_shu', label: '邀请', type: 'int' }, { field: 'chongzhi_dashou_shu', label: '充值', type: 'int' }, ], }, zuzhang: { title: '组长排行榜', sortField: 'shouru_zonge', sortLabel: '收入总额', metrics: [ { field: 'yaoqing_guanshi_shu', label: '邀请', type: 'int' }, { field: 'fenyong_jine', label: '分佣', type: 'money' }, ], }, shangjia: { title: '商家排行榜', sortField: 'jiesuan_jine', sortLabel: '成交总额', metrics: [ { field: 'jiesuan_dingdan_shu', label: '成交量', type: 'int' }, { field: 'paifa_dingdan_shu', label: '派单量', type: 'int' }, { field: 'paifa_jine', label: '派单额', type: 'money' }, ], }, } function isInvalidAvatarPath(path) { if (path === null || path === undefined) return true if (typeof path !== 'string') return true const s = path.trim() return !s || s === 'null' || s === 'undefined' || s === 'none' } function getDefaultAvatar(app) { return getDefaultAvatarUrl(app) } function buildAvatar(path, app) { app = app || getApp() if (isInvalidAvatarPath(path)) return getDefaultAvatar(app) return resolveAvatarUrl(path, app) || getDefaultAvatar(app) } Page({ data: { imgUrls: {}, roleList: ROLE_LIST, dateList: DATE_LIST, currentRole: 'dashou', currentDate: '今日', roleMeta: ROLE_META.dashou, sortLabel: '分红总额', isLoading: true, rankList: [], topThree: [], restList: [], isEmpty: false, defaultAvatar: '', }, onLoad(options) { const type = options.type || options.rankType || 'dashou' const validRole = ROLE_META[type] ? type : 'dashou' this.initPage() this.applyRole(validRole, false) this.fetchRankList() }, onPullDownRefresh() { this.fetchRankList().finally(() => wx.stopPullDownRefresh()) }, initPage() { this.syncDefaultAvatar() const g = getApp().globalData 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 再同步 */ syncDefaultAvatar() { const def = getDefaultAvatar() if (def && def !== this.data.defaultAvatar) { this.setData({ defaultAvatar: def }) } else if (!this.data.defaultAvatar && def) { this.setData({ defaultAvatar: def }) } }, /** 配置晚于页面加载时,补全空头像 */ ensureAvatars() { const def = getDefaultAvatar() if (!def) return const g = getApp().globalData const oss = g.ossImageUrl || '' const { rankList, topThree, restList } = this.data if (!rankList.length) { if (def !== this.data.defaultAvatar) this.setData({ defaultAvatar: def }) return } const isValidUrl = (url) => { if (!url || typeof url !== 'string') return false if (url.startsWith('http://') || url.startsWith('https://')) return true return !!(oss && url.indexOf(oss) === 0) } const fix = (item) => ({ ...item, avatar: isValidUrl(item.avatar) ? item.avatar : def, }) this.setData({ defaultAvatar: def, rankList: rankList.map(fix), topThree: topThree.map(fix), restList: restList.map(fix), }) }, applyRole(role, reload = true) { const meta = ROLE_META[role] wx.setNavigationBarTitle({ title: meta.title }) this.setData({ currentRole: role, roleMeta: meta, sortLabel: meta.sortLabel }) if (reload) this.fetchRankList() }, onRoleTap(e) { const role = e.currentTarget.dataset.role if (!role || role === this.data.currentRole) return this.applyRole(role) }, onDateTap(e) { const date = e.currentTarget.dataset.date if (!date || date === this.data.currentDate) return this.setData({ currentDate: date }) this.fetchRankList() }, onRefreshTap() { this.fetchRankList() }, formatMoney(val) { const n = parseFloat(val) return Number.isNaN(n) ? '0.00' : n.toFixed(2) }, formatInt(val) { const n = parseInt(val, 10) return Number.isNaN(n) ? '0' : String(n) }, normalizeItem(raw, index, role) { const meta = ROLE_META[role] const app = getApp() const uid = raw.yonghuid || raw.uid || '' const nicheng = raw.nicheng || raw.nick || '用户' const def = getDefaultAvatar(app) const avatar = buildAvatar(raw.touxiang || raw.avatar, app) || def const metrics = meta.metrics.map((m) => { const v = raw[m.field] return { label: m.label, value: m.type === 'money' ? this.formatMoney(v) : this.formatInt(v), } }) return { mingci: raw.mingci || index + 1, uid, nicheng, avatar, mainValue: this.formatMoney(raw[meta.sortField]), mainLabel: meta.sortLabel, metrics, } }, async fetchRankList() { this.setData({ isLoading: true, isEmpty: false }) try { const res = await request({ url: '/yonghu/phbhqsj', method: 'POST', data: { shenfen: this.data.currentRole, riqi: this.data.currentDate }, }) 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 (!Array.isArray(list)) list = [] const role = this.data.currentRole const normalized = list.slice(0, 50).map((item, i) => this.normalizeItem(item, i, role)) this.setData({ rankList: normalized, topThree: normalized.slice(0, 3), restList: normalized.slice(3), isEmpty: normalized.length === 0, isLoading: false, }) this.ensureAvatars() } catch (e) { console.error('排行榜加载失败', e) wx.showToast({ title: '加载失败', icon: 'none' }) this.setData({ rankList: [], topThree: [], restList: [], isEmpty: true, isLoading: false }) } }, onAvatarError(e) { const { zone, index } = e.currentTarget.dataset const fallback = getDefaultAvatar() if (!fallback) return const key = zone === 'top' ? `topThree[${index}].avatar` : `restList[${index}].avatar` this.setData({ [key]: fallback }) }, registerNotificationComponent() { const app = getApp() const comp = this.selectComponent('#global-notification') if (comp?.showNotification) { app.globalData.globalNotification = { show: (d) => comp.showNotification(d), hide: () => comp.hideNotification(), } } }, onShow() { this.syncDefaultAvatar() this.ensureAvatars() this.registerNotificationComponent() }, })