feat(dashboard): add daily and monthly recharge rankings

This commit is contained in:
zhx 2026-07-22 12:03:44 +08:00
parent 8e2e3feaca
commit 62632adc7e
4 changed files with 181 additions and 98 deletions

View File

@ -80,6 +80,15 @@ export function listDailyUserRechargeTop(dateNumber) {
}) })
} }
// 用户日/月充值排行榜(服务端分页)
export function pageUserRechargeRank(params) {
return request({
url: '/count/currency/user-recharge-rank/page',
method: 'get',
params
})
}
// 最新平台内购情况 // 最新平台内购情况
export function latestPurchaseAmountPlatform(areaCode) { export function latestPurchaseAmountPlatform(areaCode) {
return request({ return request({

View File

@ -4199,7 +4199,7 @@ export default {
dailyGoldAnalysis: 'Daily Gold Analysis', dailyGoldAnalysis: 'Daily Gold Analysis',
dailyGoldSourceTop: 'Daily Gold Source Top', dailyGoldSourceTop: 'Daily Gold Source Top',
userGoldTop: 'User Gold Top 50', userGoldTop: 'User Gold Top 50',
dailyRechargeTop: 'Daily Recharge Top 50' dailyRechargeTop: 'Recharge Ranking'
}, },
account: { account: {
accountLabel: 'Account' accountLabel: 'Account'
@ -4332,8 +4332,14 @@ export default {
}, },
rechargeTop: { rechargeTop: {
datePlaceholder: 'Select date', datePlaceholder: 'Select date',
title: 'Recharge Top 50 ({sysOriginName})', monthPlaceholder: 'Select month',
description: 'Same-day total recharge amount, including refunds', platformPlaceholder: 'Select platform',
title: 'Recharge Ranking ({sysOriginName})',
descriptionDaily: 'Recharge total from 08:00 on the selected day to 08:00 the next day, including refunds',
descriptionMonthly: 'Net recharge total for the selected month, with refunds deducted',
daily: 'Daily',
monthly: 'Monthly',
rank: 'Rank',
user: 'User', user: 'User',
amount: 'Amount' amount: 'Amount'
}, },

View File

@ -4196,7 +4196,7 @@ export default {
dailyGoldAnalysis: '每日金币(分析)', dailyGoldAnalysis: '每日金币(分析)',
dailyGoldSourceTop: '每日金币(来源榜)', dailyGoldSourceTop: '每日金币(来源榜)',
userGoldTop: '用户金币(Top50)', userGoldTop: '用户金币(Top50)',
dailyRechargeTop: '每日充值(Top50)' dailyRechargeTop: '充值排行榜'
}, },
account: { account: {
accountLabel: '账号' accountLabel: '账号'
@ -4329,8 +4329,14 @@ export default {
}, },
rechargeTop: { rechargeTop: {
datePlaceholder: '选择日期', datePlaceholder: '选择日期',
title: '充值Top50({sysOriginName})', monthPlaceholder: '选择月份',
description: '当日充值总额,包含退款', platformPlaceholder: '选择平台',
title: '充值排行榜({sysOriginName})',
descriptionDaily: '所选统计日08:00至次日08:00的充值总额包含退款',
descriptionMonthly: '所选月份的净充值总额,退款金额会扣除',
daily: '日榜',
monthly: '月榜',
rank: '排名',
user: '用户', user: '用户',
amount: '金额' amount: '金额'
}, },

View File

@ -1,134 +1,196 @@
<template> <template>
<div v-loading="loading" class="dily-register-user" :data-language="language"> <div v-loading="loading" class="user-recharge-rank" :data-language="language">
<div class="query-bar">
<el-radio-group v-model="listQuery.periodType" size="mini" @change="changePeriodType">
<el-radio-button label="DAILY">{{ $t('pages.dashboard.rechargeTop.daily') }}</el-radio-button>
<el-radio-button label="MONTHLY">{{ $t('pages.dashboard.rechargeTop.monthly') }}</el-radio-button>
</el-radio-group>
<el-select
v-model="listQuery.sysOrigin"
size="mini"
:placeholder="$t('pages.dashboard.rechargeTop.platformPlaceholder')"
@change="handleSearch"
>
<el-option
v-for="item in permissionsSysOriginPlatformAlls"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
<el-date-picker <el-date-picker
v-model="dateNumber" v-if="listQuery.periodType === 'DAILY'"
style="margin-bottom: 10px;" v-model="listQuery.periodNumber"
type="date" type="date"
:placeholder="$t('pages.dashboard.rechargeTop.datePlaceholder')"
value-format="yyyyMMdd" value-format="yyyyMMdd"
size="mini" size="mini"
:picker-options="pickerOptions"
:clearable="false" :clearable="false"
@change="changeDateNumber" :picker-options="dailyPickerOptions"
:placeholder="$t('pages.dashboard.rechargeTop.datePlaceholder')"
@change="handleSearch"
/>
<el-date-picker
v-else
v-model="listQuery.periodNumber"
type="month"
value-format="yyyyMM"
size="mini"
:clearable="false"
:picker-options="monthlyPickerOptions"
:placeholder="$t('pages.dashboard.rechargeTop.monthPlaceholder')"
@change="handleSearch"
/> />
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>{{ $t('pages.dashboard.rechargeTop.title', { sysOriginName: item.sysOriginName }) }}</span>
</div> </div>
<div class="content">
<div style="font-size: 12px;color: #666;margin-bottom: 10px;">{{ $t('pages.dashboard.rechargeTop.description') }}</div> <el-card class="box-card">
<el-table <div slot="header" class="clearfix">
:data="item.items" <span>{{ $t('pages.dashboard.rechargeTop.title', { sysOriginName: selectedOriginName }) }}</span>
element-loading-text="Loading" </div>
fit <div class="description">
highlight-current-row {{ listQuery.periodType === 'DAILY'
> ? $t('pages.dashboard.rechargeTop.descriptionDaily')
<el-table-column label="No" width="50" align="center"> : $t('pages.dashboard.rechargeTop.descriptionMonthly') }}
<template slot-scope="scope">{{ scope.$index + 1 }}</template> </div>
</el-table-column> <el-table :data="list" element-loading-text="Loading" fit highlight-current-row>
<el-table-column :label="$t('pages.dashboard.rechargeTop.user')" prop="roomProfile.roomName" align="center" min-width="200"> <el-table-column :label="$t('pages.dashboard.rechargeTop.rank')" width="90" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<user-table-exhibit :user-profile="scope.row.userProfile" :query-details="true" /> {{ (listQuery.cursor - 1) * listQuery.limit + scope.$index + 1 }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('pages.dashboard.rechargeTop.amount')" min-width="100" align="center"> <el-table-column :label="$t('pages.dashboard.rechargeTop.user')" align="center" min-width="220">
<template slot-scope="scope"> <template slot-scope="scope">
<div>{{ scope.row.quantity || 0 }}</div> <user-table-exhibit
v-if="scope.row.userProfile"
:user-profile="scope.row.userProfile"
:query-details="true"
/>
<span v-else>{{ scope.row.userId }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('pages.dashboard.rechargeTop.amount')" min-width="120" align="center">
<template slot-scope="scope">{{ scope.row.quantity || 0 }}</template>
</el-table-column>
</el-table> </el-table>
</div>
<pagination
v-show="total > 0"
:total="total"
:page.sync="listQuery.cursor"
:limit.sync="listQuery.limit"
@pagination="loadRank"
/>
</el-card> </el-card>
</div> </div>
</div>
</template> </template>
<script> <script>
import { listDailyUserRechargeTop } from '@/api/statistics' import { pageUserRechargeRank } from '@/api/statistics'
import { currencyOrigins } from '@/constant/type' import Pagination from '@/components/Pagination'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { formatDate, beforeDateObject } from '@/utils' import { formatDate, beforeDateObject } from '@/utils'
export default { export default {
name: 'DilyRegisterUser', name: 'UserRechargeRank',
components: { Pagination },
data() { data() {
return { return {
pickerOptions: { loading: false,
list: [],
total: 0,
dailyPeriodNumber: formatDate(beforeDateObject(1), 'yyyyMMdd'),
monthlyPeriodNumber: formatDate(new Date(), 'yyyyMM'),
dailyPickerOptions: {
disabledDate(date) { disabledDate(date) {
return date.getTime() > beforeDateObject(1).getTime() return date.getTime() > beforeDateObject(1).getTime()
} }
}, },
dateNumber: formatDate(beforeDateObject(new Date().getHours() >= 8 ? 1 : 2), 'yyyyMMdd'), monthlyPickerOptions: {
loading: false, disabledDate(date) {
list: [], const now = new Date()
queryDetailsRow: {}, return date.getFullYear() > now.getFullYear() ||
selectOrigin: currencyOrigins[0].value, (date.getFullYear() === now.getFullYear() && date.getMonth() > now.getMonth())
selectOriginName: currencyOrigins[0].name }
},
listQuery: {
cursor: 1,
limit: 20,
sysOrigin: '',
periodType: 'DAILY',
periodNumber: ''
}
} }
}, },
computed: { computed: {
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']), ...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
sysOriginPlatforms() { selectedOriginName() {
if (!this.permissionsSysOriginPlatformAlls || this.permissionsSysOriginPlatformAlls.length <= 0) { const selected = (this.permissionsSysOriginPlatformAlls || [])
return [] .find(item => item.value === this.listQuery.sysOrigin)
} return selected ? selected.name : this.listQuery.sysOrigin
return this.permissionsSysOriginPlatformAlls.map(item => item.value)
} }
}, },
created() { created() {
this.loadOrigin() const firstOrigin = (this.permissionsSysOriginPlatformAlls || [])[0]
if (!firstOrigin) {
return
}
this.listQuery.sysOrigin = firstOrigin.value
this.listQuery.periodNumber = this.dailyPeriodNumber
this.loadRank()
}, },
methods: { methods: {
loadOrigin() { changePeriodType(periodType) {
// yyyyMMdd yyyyMM
this.listQuery.periodNumber = periodType === 'DAILY'
? this.dailyPeriodNumber
: this.monthlyPeriodNumber
this.handleSearch()
},
handleSearch() {
if (this.listQuery.periodType === 'DAILY') {
this.dailyPeriodNumber = this.listQuery.periodNumber
} else {
this.monthlyPeriodNumber = this.listQuery.periodNumber
}
this.listQuery.cursor = 1
this.loadRank()
},
loadRank() {
if (!this.listQuery.sysOrigin || !this.listQuery.periodNumber) {
return
}
this.loading = true this.loading = true
listDailyUserRechargeTop(this.dateNumber).then(res => { pageUserRechargeRank(this.listQuery).then(res => {
const body = res.body || {}
this.list = body.records || []
this.total = Number(body.total || 0)
this.loading = false this.loading = false
const { body } = res
const list = body || []
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
}).catch(() => { }).catch(() => {
this.loading = false this.loading = false
}) })
},
querySearch(queryString, cb) {
const restaurants = currencyOrigins
const results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
cb(results)
},
createFilter(queryString) {
return restaurant => {
return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0 ||
restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0
}
},
handleSelect(item) {
const selectedItem = item || currencyOrigins[0]
this.selectOrigin = selectedItem.value
this.selectOriginName = selectedItem.name
this.loadOrigin()
},
changeDateNumber() {
this.loadOrigin()
} }
} }
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.box-card { .user-recharge-rank {
margin-bottom: 10px; .query-bar {
padding: 10px; display: flex;
flex-wrap: wrap;
.card-col { gap: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
}
.register-logout-count { .box-card {
.date { padding: 10px;
font-weight: bold; }
line-height: 30px;
.description {
margin-bottom: 10px;
color: #666;
font-size: 12px;
} }
} }
</style> </style>