Files
a_long/miniprogram/pages/dashoupaihang/dashoupaihang.js
2026-06-27 00:40:42 +08:00

263 lines
7.5 KiB
JavaScript

/**
* 统一排行榜(打手/管事/组长/商家)
* POST /yonghu/phbhqsj
*/
import request from '../../utils/request.js'
import { resolveAvatarUrl, getDefaultAvatarUrl } from '../../utils/avatar.js'
const RANK_API = '/yonghu/phbhqsj'
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: '成交总额',
mainType: 'money',
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: '收入总额',
mainType: 'money',
metrics: [
{ field: 'yaoqing_dashou_shu', label: '邀请', type: 'int' },
{ field: 'chongzhi_dashou_shu', label: '充值', type: 'int' },
],
},
zuzhang: {
title: '组长排行榜',
sortField: 'shouru_zonge',
sortLabel: '收入总额',
mainType: 'money',
metrics: [
{ field: 'yaoqing_guanshi_shu', label: '邀请', type: 'int' },
{ field: 'fenyong_jine', label: '分佣', type: 'money' },
],
},
shangjia: {
title: '商家排行榜',
sortField: 'jiesuan_jine',
sortLabel: '成交总额',
mainType: 'money',
metrics: [
{ field: 'jiesuan_dingdan_shu', label: '成交量', type: 'int' },
{ field: 'paifa_dingdan_shu', label: '派单量', type: 'int' },
{ field: 'paifa_jine', label: '派单额', type: 'money' },
],
},
}
function buildAvatar(path, app) {
const def = getDefaultAvatarUrl(app)
if (!path) return def
return resolveAvatarUrl(path, app) || def
}
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.initPageAssets()
this.applyRole(validRole, false)
this.fetchRankList()
},
onPullDownRefresh() {
this.fetchRankList().finally(() => wx.stopPullDownRefresh())
},
initPageAssets() {
this.syncDefaultAvatar()
const g = getApp().globalData
const assets = g.paihangAssets || {}
const base = (g.ossImageUrl || '') + (assets.basePath || 'beijing/paihangbang/')
this.setData({
imgUrls: {
pageBg: base + (assets.pageBg || 'page-bg.jpg'),
cardBg: base + (assets.cardBg || 'list-card-bg.png'),
emptyIcon: base + (assets.emptyIcon || 'empty.png'),
refreshIcon: base + (assets.refreshIcon || 'icon-refresh.png'),
},
})
},
syncDefaultAvatar() {
const def = getDefaultAvatarUrl()
if (def) this.setData({ defaultAvatar: def })
},
ensureAvatars() {
const def = getDefaultAvatarUrl()
if (!def) return
const oss = getApp().globalData.ossImageUrl || ''
const { rankList, topThree, restList } = this.data
if (!rankList.length) {
this.setData({ defaultAvatar: def })
return
}
const isValid = (url) => url && typeof url === 'string' && (url.startsWith('http') || (oss && url.indexOf(oss) === 0))
const fix = (item) => ({ ...item, avatar: isValid(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 avatar = buildAvatar(raw.touxiang || raw.avatar, app)
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),
}
})
const sortRaw = raw[meta.sortField]
const mainType = meta.mainType || 'money'
const mainValue = mainType === 'money' ? this.formatMoney(sortRaw) : this.formatInt(sortRaw)
return {
mingci: raw.mingci || index + 1,
uid,
nicheng,
avatar,
mainType,
mainPrefix: mainType === 'money' ? '¥' : '',
mainValue,
mainLabel: meta.sortLabel,
metrics,
}
},
async fetchRankList() {
this.setData({ isLoading: true, isEmpty: false })
try {
const res = await request({
url: RANK_API,
method: 'POST',
data: { shenfen: this.data.currentRole, riqi: this.data.currentDate },
})
const body = res?.data || {}
const ok = body.code === 0 || body.code === 200
let list = ok && body.data ? (body.data.list || body.data.paihang_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 = getDefaultAvatarUrl()
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()
},
})