first commit

This commit is contained in:
2026-09-21 19:21:35 +08:00
commit 6c944c4ffd
2733 changed files with 296956 additions and 0 deletions
@@ -0,0 +1,208 @@
<template>
<div class="app-container">
<!-- 搜索栏 -->
<el-row :gutter="10" style="margin-bottom:16px">
<el-input v-model="queryParams.userId" placeholder="用户ID" clearable @clear="handleSearch" @keyup.enter.native="handleSearch" style="width:130px" />
<el-input v-model="queryParams.nickName" placeholder="昵称" clearable @clear="handleSearch" @keyup.enter.native="handleSearch" style="width:130px" />
<el-input v-model="queryParams.phone" placeholder="手机号" clearable @clear="handleSearch" @keyup.enter.native="handleSearch" style="width:150px" />
<el-date-picker
v-model="dateRange"
type="datetimerange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="yyyy-MM-dd HH:mm:ss"
:default-time="['00:00:00', '23:59:59']"
:picker-options="pickerOptions"
style="width:380px"
/>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-row>
<!-- 排序提示 -->
<el-alert :closable="false" style="margin-bottom:12px" type="info">
当前排序:
<b>{{ sortLabels[queryParams.sortField] || '总流水' }}</b>
<span style="margin-left:4px">{{ queryParams.sortOrder === 'asc' ? '↑ 升序' : '↓ 降序' }}</span>
<span style="margin-left:12px;color:#909399">点击表头可切换排序字段和方向</span>
</el-alert>
<!-- 数据表格 -->
<div style="overflow-x:auto">
<el-table v-loading="loading" :data="tableData" border @sort-change="handleSortChange" style="width:100%">
<el-table-column prop="userId" label="用户ID" width="90" align="center" fixed sortable="custom" />
<el-table-column prop="nickName" label="昵称" width="120" show-overflow-tooltip fixed />
<el-table-column prop="phone" label="手机号" min-width="120" align="center" />
<el-table-column label="用户类型" min-width="80" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="userTypeTag(scope.row.userType)">{{ userTypeLabel(scope.row.userType) }}</el-tag>
</template>
</el-table-column>
<!-- 充值 -->
<el-table-column prop="totalRecharge" label="总充值" min-width="110" align="right" sortable="custom">
<template slot-scope="scope"><span style="color:#67c23a">{{ formatMoney(scope.row.totalRecharge) }}</span></template>
</el-table-column>
<!-- 消费流水 -->
<el-table-column prop="totalTurnover" label="总流水" min-width="110" align="right" sortable="custom">
<template slot-scope="scope"><span style="color:#f56c6c">{{ formatMoney(scope.row.totalTurnover) }}</span></template>
</el-table-column>
<!-- 游戏分类消费 -->
<el-table-column label="游戏分类消费" align="center">
<el-table-column prop="classicBox" label="经典开箱" min-width="100" align="right" sortable="custom">
<template slot-scope="scope">{{ formatMoney(scope.row.classicBox) }}</template>
</el-table-column>
<el-table-column prop="fightGame" label="对战" min-width="80" align="right" sortable="custom">
<template slot-scope="scope">{{ formatMoney(scope.row.fightGame) }}</template>
</el-table-column>
<el-table-column prop="luckyUpgrade" label="幸运升级" min-width="100" align="right" sortable="custom">
<template slot-scope="scope">{{ formatMoney(scope.row.luckyUpgrade) }}</template>
</el-table-column>
<el-table-column prop="wheelGame" label="疯狂转盘" min-width="100" align="right" sortable="custom">
<template slot-scope="scope">{{ formatMoney(scope.row.wheelGame) }}</template>
</el-table-column>
<el-table-column prop="olympusGame" label="玫瑰女郎" min-width="100" align="right" sortable="custom">
<template slot-scope="scope">{{ formatMoney(scope.row.olympusGame) }}</template>
</el-table-column>
<el-table-column prop="rollRoom" label="roll房" min-width="90" align="right" sortable="custom">
<template slot-scope="scope">{{ formatMoney(scope.row.rollRoom) }}</template>
</el-table-column>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<el-pagination
style="margin-top:16px;text-align:right"
background
layout="total, prev, pager, next, sizes"
:total="total"
:page-size.sync="queryParams.pageSize"
:current-page.sync="queryParams.pageNum"
:page-sizes="[5,10,20,50]"
@size-change="loadData"
@current-change="loadData"
/>
</div>
</template>
<script>
import { getUserTurnover } from '@/api/skins/userTurnover'
const sortLabels = {
totalRecharge: '充值', totalTurnover: '总流水',
classicBox: '经典开箱', fightGame: '对战', luckyUpgrade: '幸运升级',
wheelGame: '疯狂转盘', olympusGame: '玫瑰女郎', rollRoom: 'roll房',
userId: '用户ID'
}
/** 格式化日期为 yyyy-MM-dd HH:mm:ss */
function fmtDate(d) {
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const h = String(d.getHours()).padStart(2, '0')
const min = String(d.getMinutes()).padStart(2, '0')
const s = String(d.getSeconds()).padStart(2, '0')
return `${y}-${m}-${day} ${h}:${min}:${s}`
}
/** 获取默认日期范围:昨天00:00:00 至 今天当前时间 */
function getDefaultDateRange() {
const now = new Date()
const yesterday = new Date(now)
yesterday.setDate(yesterday.getDate() - 1)
yesterday.setHours(0, 0, 0, 0)
return [fmtDate(yesterday), fmtDate(now)]
}
export default {
name: 'UserTurnover',
data() {
return {
loading: false,
tableData: [],
total: 0,
sortLabels,
// 日期范围,默认昨天00:00:00 至 今天当前时间
dateRange: getDefaultDateRange(),
pickerOptions: {
shortcuts: [
{ text: '今日', onClick(picker) {
const now = new Date(); const start = new Date(now); start.setHours(0, 0, 0, 0)
picker.$emit('pick', [start, now])
}},
{ text: '昨日', onClick(picker) {
const yd = new Date(); yd.setDate(yd.getDate() - 1); yd.setHours(0, 0, 0, 0)
const ydEnd = new Date(yd); ydEnd.setHours(23, 59, 59, 999)
picker.$emit('pick', [yd, ydEnd])
}},
{ text: '本周', onClick(picker) {
const now = new Date(); const day = now.getDay(); const diff = day === 0 ? 6 : day - 1
const monday = new Date(now); monday.setDate(monday.getDate() - diff); monday.setHours(0, 0, 0, 0)
picker.$emit('pick', [monday, now])
}},
{ text: '上周', onClick(picker) {
const now = new Date(); const day = now.getDay(); const diff = day === 0 ? 6 : day - 1
const lastMonday = new Date(now); lastMonday.setDate(lastMonday.getDate() - diff - 7); lastMonday.setHours(0, 0, 0, 0)
const lastSunday = new Date(lastMonday); lastSunday.setDate(lastSunday.getDate() + 6); lastSunday.setHours(23, 59, 59, 999)
picker.$emit('pick', [lastMonday, lastSunday])
}}
]
},
queryParams: {
userId: null,
nickName: null,
phone: null,
sortField: 'totalTurnover',
sortOrder: 'desc',
pageNum: 1,
pageSize: 10
}
}
},
created() { this.loadData() },
methods: {
loadData() {
this.loading = true
const params = { ...this.queryParams }
if (params.userId) params.userId = Number(params.userId)
// 日期范围(已是完整的 yyyy-MM-dd HH:mm:ss 格式)
if (this.dateRange && this.dateRange.length === 2) {
params.startTime = this.dateRange[0]
params.endTime = this.dateRange[1]
}
getUserTurnover(params).then(res => {
this.tableData = res.data.rows || []
this.total = res.data.total || 0
}).finally(() => { this.loading = false })
},
handleSearch() { this.queryParams.pageNum = 1; this.loadData() },
handleReset() {
this.dateRange = getDefaultDateRange()
this.queryParams = { userId: null, nickName: null, phone: null, sortField: 'totalTurnover', sortOrder: 'desc', pageNum: 1, pageSize: 10 }
this.loadData()
},
handleSortChange({ prop, order }) {
this.queryParams.sortField = prop || 'totalTurnover'
this.queryParams.sortOrder = order === 'ascending' ? 'asc' : 'desc'
this.loadData()
},
formatMoney(v) {
if (v == null) return '0.00'
return Number(v).toFixed(2)
},
userTypeLabel(type) {
const map = { '01': '主播', '02': '普通用户', '03': '机器人', '04': '招商', '05': '卡商', '99': '超级管理员' }
return map[type] || type || '-'
},
userTypeTag(type) {
const map = { '01': 'success', '02': '', '03': 'info', '04': 'warning', '05': 'danger', '99': 'danger' }
return map[type] || ''
}
}
}
</script>