feat: 迁移注册充值明细和筛选导出
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
<template><ledger-details kind="spend" /></template>
|
||||
<script setup>
|
||||
import LedgerDetails from './components/LedgerDetails.vue'
|
||||
defineOptions({ name: 'SwRechargeDetails' })
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template><ledger-details kind="register" /></template>
|
||||
<script setup>
|
||||
import LedgerDetails from './components/LedgerDetails.vue'
|
||||
defineOptions({ name: 'SwRegistrationDetails' })
|
||||
</script>
|
||||
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="kra-search-box">
|
||||
<el-form :model="query" :inline="true" @submit.prevent="search">
|
||||
<el-form-item label="所属平台"><el-select v-model="sources" multiple collapse-tags clearable placeholder="选择平台" style="width: 210px" @change="sourceChanged"><el-option v-for="(label, value) in platforms" :key="value" :label="label" :value="value" /></el-select></el-form-item>
|
||||
<el-form-item label="商务账号"><el-input v-model="query.busierAccount" clearable placeholder="商务账号" /></el-form-item>
|
||||
<el-form-item label="玩家ID"><el-input v-model="playerID" clearable placeholder="玩家ID" /></el-form-item>
|
||||
<el-form-item v-if="spend" label="角色名"><el-input v-model="query.gamePlayerName" clearable placeholder="角色名" /></el-form-item>
|
||||
<el-form-item label="渠道账号"><el-input v-model="query.promoteAccount" clearable placeholder="渠道账号(精确匹配)" /></el-form-item>
|
||||
<el-form-item v-if="spend" label="订单号"><el-input v-model="query.payOrderNumber" clearable placeholder="订单号" /></el-form-item>
|
||||
<el-form-item label="游戏"><el-select v-model="gameNames" multiple collapse-tags clearable filterable remote reserve-keyword :remote-method="loadGames" :loading="gamesLoading" :disabled="!sources.length" placeholder="全部游戏" style="width: 200px" @visible-change="open => open && loadGames('')"><el-option v-for="name in gameOptions" :key="name" :label="name" :value="name" /></el-select></el-form-item>
|
||||
<el-form-item v-if="spend" label="方式"><el-select v-model="query.payWay" clearable placeholder="全部方式" style="width: 140px"><el-option v-for="(label, value) in payWays" :key="value" :label="label" :value="Number(value)" /></el-select></el-form-item>
|
||||
<el-form-item v-if="spend" label="状态"><el-select v-model="query.payStatus" clearable placeholder="全部状态" style="width: 140px"><el-option v-for="(label, value) in statuses" :key="value" :label="label" :value="Number(value)" /></el-select></el-form-item>
|
||||
<el-form-item label="日期"><el-date-picker v-model="dates" type="daterange" value-format="YYYY-MM-DD" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :clearable="false" :shortcuts="shortcuts" style="width: 300px" /></el-form-item>
|
||||
<el-form-item><el-button type="primary" icon="search" native-type="submit">搜索</el-button></el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="kra-table-box">
|
||||
<div class="kra-btn-list"><el-button v-if="canExport" type="warning" plain icon="download" :loading="exporting" @click="download">导出</el-button></div>
|
||||
<el-alert v-if="error" class="mb-4" type="error" :closable="false" :title="error" />
|
||||
<el-alert v-else-if="!available" class="mb-4" type="warning" :closable="false" title="明细所需业务数据尚未接入。" />
|
||||
<el-table v-loading="loading" :data="rows" :row-key="row => `${row.sourceDb}:${row.id}`" :row-class-name="rowClass" :default-sort="{ prop: spend ? 'payTime' : 'registerTime', order: 'descending' }" @sort-change="sortChanged">
|
||||
<el-table-column prop="sourceDbLabel" label="所属平台" min-width="100" />
|
||||
<el-table-column v-if="spend" prop="id" label="ID" min-width="90" />
|
||||
<el-table-column prop="userId" label="玩家ID" min-width="110" />
|
||||
<el-table-column v-if="!spend" prop="registerTime" label="注册时间" min-width="145" sortable="custom"><template #default="{ row }">{{ unixTime(row.registerTime) }}</template></el-table-column>
|
||||
<el-table-column v-if="spend" prop="payOrderNumber" label="订单号" min-width="190" show-overflow-tooltip />
|
||||
<el-table-column :prop="spend ? 'gameName' : 'fgameName'" label="游戏" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column v-if="spend" prop="serverName" label="区服" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column v-if="spend" prop="gamePlayerName" label="角色" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column :prop="spend ? 'payAmount' : 'totalPayAmount'" :label="spend ? '实付金额' : '累计充值'" min-width="150" sortable="custom"><template #default="{ row }">{{ channelMoney(spend ? row.payAmount ?? '0.00' : row.totalPayAmount) }}</template></el-table-column>
|
||||
<el-table-column v-if="spend" prop="payTime" label="充值时间" min-width="145" sortable="custom"><template #default="{ row }">{{ unixTime(row.payTime) }}</template></el-table-column>
|
||||
<el-table-column v-if="spend" label="方式" min-width="100"><template #default="{ row }">{{ payWays[row.payWay] ?? row.payWay ?? '—' }}</template></el-table-column>
|
||||
<el-table-column v-if="spend" label="状态" min-width="110"><template #default="{ row }"><el-tag :type="{ 1: 'success', 2: 'warning', 0: 'danger' }[row.payStatus] || 'info'">{{ statuses[row.payStatus] ?? row.payStatus ?? '—' }}</el-tag></template></el-table-column>
|
||||
<el-table-column :label="spend ? 'IP' : '注册IP'" min-width="130"><template #default="{ row }">{{ (spend ? row.spendIp : row.registerIp) || '—' }}</template></el-table-column>
|
||||
<el-table-column v-if="!spend" prop="deviceName" label="机型" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="promoteAccount" label="渠道" min-width="170" show-overflow-tooltip />
|
||||
<el-table-column prop="busierName" label="归属商务" min-width="190" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<p v-if="spend" class="mt-4 text-right">合计:成功支付总额 <strong>{{ error ? '未获取' : available ? channelMoney(totalPayAmount) : '未接入' }}</strong></p>
|
||||
<div class="kra-pagination"><el-pagination v-model:current-page="query.pageNum" v-model:page-size="query.pageSize" :total="total" :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" @size-change="search" @current-change="load" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useUserStore } from '@/pinia/modules/user'
|
||||
import { useBtnAuth } from '@/utils/btnAuth'
|
||||
import { listLedger, exportLedger, listLedgerGames } from '@/api/ledger'
|
||||
import { channelMoney } from '../channel-display'
|
||||
const props = defineProps({ kind: { type: String, required: true } })
|
||||
const spend = computed(() => props.kind === 'spend')
|
||||
const user = useUserStore(), buttons = useBtnAuth()
|
||||
const canExport = computed(() => Number(user.userInfo.authorityId) === 888 || !!buttons[`sw:${props.kind}:export`])
|
||||
const platforms = { lx: '龙翔', hw: '汇玩', yq: '游趣' }
|
||||
const payWays = { 1: '绑币', 2: '平台币', 3: '支付宝', 4: '微信', 5: '金猪', 6: '苹果支付', 11: '盒子币', 12: '龙翔币', 14: '抖音小游戏' }
|
||||
const statuses = { 1: '成功', 2: '处理中', 0: '失败' }
|
||||
const today = () => { const now = new Date(); return new Date(now.getFullYear(), now.getMonth(), now.getDate()) }
|
||||
const ymd = date => `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
||||
const end = today(), begin = today(); begin.setDate(begin.getDate() - 29)
|
||||
const dates = ref([ymd(begin), ymd(end)])
|
||||
const shortcuts = [
|
||||
{ text: '今日', value: () => [today(), today()] },
|
||||
{ text: '昨日', value: () => { const d = today(); d.setDate(d.getDate() - 1); return [d, new Date(d)] } },
|
||||
{ text: '本周', value: () => { const e = today(), b = today(); b.setDate(b.getDate() - (b.getDay() || 7) + 1); return [b, e] } },
|
||||
{ text: '本月', value: () => { const e = today(); return [new Date(e.getFullYear(), e.getMonth(), 1), e] } }
|
||||
]
|
||||
const sources = ref(Object.keys(platforms)), gameNames = ref([]), gameOptions = ref([]), playerID = ref('')
|
||||
const gamesLoading = ref(false), exporting = ref(false), loading = ref(false), available = ref(true)
|
||||
const error = ref('')
|
||||
const rows = ref([]), total = ref(0), totalPayAmount = ref('0.00')
|
||||
const query = reactive({ pageNum: 1, pageSize: 10, busierAccount: '', promoteAccount: '', gamePlayerName: '', payOrderNumber: '', payStatus: undefined, payWay: undefined, sortField: spend.value ? 'payTime' : 'registerTime', sortOrder: 'desc' })
|
||||
let listVersion = 0, gameVersion = 0
|
||||
const buildQuery = () => {
|
||||
const player = playerID.value.trim()
|
||||
if (player && !/^\d+$/.test(player)) { ElMessage.warning('玩家ID应为整数'); return null }
|
||||
const result = { ...query, sourceDb: sources.value.join(','), gameName: gameNames.value.join(','), userId: player || undefined, busierAccount: query.busierAccount.trim(), promoteAccount: query.promoteAccount.trim(), gamePlayerName: query.gamePlayerName.trim(), payOrderNumber: query.payOrderNumber.trim() }
|
||||
result[spend.value ? 'beginPayTime' : 'beginRegisterTime'] = dates.value?.[0]
|
||||
result[spend.value ? 'endPayTime' : 'endRegisterTime'] = dates.value?.[1]
|
||||
return result
|
||||
}
|
||||
const load = async () => {
|
||||
const version = ++listVersion
|
||||
if (!sources.value.length) { rows.value = []; total.value = 0; totalPayAmount.value = '0.00'; available.value = true; loading.value = false; error.value = ''; return }
|
||||
const params = buildQuery(); if (!params) { loading.value = false; return }
|
||||
loading.value = true; error.value = ''
|
||||
try {
|
||||
const res = await listLedger(props.kind, params)
|
||||
if (version !== listVersion) return
|
||||
if (res.code === 0) { rows.value = res.data.rows; total.value = res.data.total; totalPayAmount.value = res.data.totalPayAmount; available.value = res.data.available !== false }
|
||||
else { rows.value = []; total.value = 0; totalPayAmount.value = null; error.value = res.msg || '明细查询失败' }
|
||||
} catch { if (version === listVersion) { rows.value = []; total.value = 0; totalPayAmount.value = null; error.value = '明细查询失败,请重试' } } finally { if (version === listVersion) loading.value = false }
|
||||
}
|
||||
const search = () => { query.pageNum = 1; if (!sources.value.length) ElMessage.warning('请选择平台'); return load() }
|
||||
const sortChanged = ({ prop, order }) => { query.sortField = prop || (spend.value ? 'payTime' : 'registerTime'); query.sortOrder = order === 'ascending' ? 'asc' : 'desc'; return search() }
|
||||
const sourceChanged = () => { ++gameVersion; gameNames.value = []; gameOptions.value = []; gamesLoading.value = false }
|
||||
const loadGames = async (keyword = '') => {
|
||||
const version = ++gameVersion; if (!sources.value.length) return
|
||||
gamesLoading.value = true
|
||||
try { const res = await listLedgerGames({ sourceDb: sources.value.join(','), keyword, limit: 50 }); if (version === gameVersion && res.code === 0) gameOptions.value = [...new Set([...gameNames.value, ...res.data.map(row => row.gameName).filter(Boolean)])] } finally { if (version === gameVersion) gamesLoading.value = false }
|
||||
}
|
||||
const unixTime = value => {
|
||||
if (value == null) return '—'
|
||||
const date = new Date(Number(value) * 1000)
|
||||
if (Number.isNaN(date.getTime())) return '—'
|
||||
return `${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
||||
}
|
||||
const rowClass = ({ row }) => spend.value && Number(row.payAmount) >= 5000 ? 'large-recharge-row' : ''
|
||||
const download = async () => {
|
||||
if (!sources.value.length) { ElMessage.warning('请选择平台'); return }
|
||||
const params = buildQuery(); if (!params) return
|
||||
exporting.value = true
|
||||
try {
|
||||
const result = await exportLedger(props.kind, params)
|
||||
const blob = result instanceof Blob ? result : result.data
|
||||
if (!(blob instanceof Blob)) return
|
||||
if (blob.type.includes('json')) return
|
||||
const url = URL.createObjectURL(blob), link = document.createElement('a')
|
||||
link.href = url; link.download = spend.value ? '充值明细.xlsx' : '注册明细.xlsx'; link.click(); setTimeout(() => URL.revokeObjectURL(url), 1000)
|
||||
} finally { exporting.value = false }
|
||||
}
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.large-recharge-row) { --el-table-tr-bg-color: var(--el-color-warning-light-9); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user