|
@ -1,11 +1,16 @@ |
|
|
<script setup> |
|
|
<script setup> |
|
|
import { ref, onMounted, reactive, computed } from 'vue' |
|
|
|
|
|
|
|
|
import { ref, onMounted, reactive, computed, nextTick } from 'vue' |
|
|
import ElementPlus from 'element-plus' |
|
|
import ElementPlus from 'element-plus' |
|
|
import { ElMessage, ElMessageBox } from 'element-plus' |
|
|
import { ElMessage, ElMessageBox } from 'element-plus' |
|
|
import { AiFillRead } from 'vue-icons-plus/ai' |
|
|
import { AiFillRead } from 'vue-icons-plus/ai' |
|
|
import axios from 'axios' |
|
|
import axios from 'axios' |
|
|
import moment from 'moment' |
|
|
import moment from 'moment' |
|
|
import API from '@/util/http' |
|
|
import API from '@/util/http' |
|
|
|
|
|
import * as XLSX from 'xlsx' // 引入 xlsx 库 |
|
|
|
|
|
|
|
|
|
|
|
// 提取 XLSX 工具函数 |
|
|
|
|
|
const { utils, writeFile } = XLSX |
|
|
|
|
|
|
|
|
// 变量 |
|
|
// 变量 |
|
|
//这是获取用户信息的接口 |
|
|
//这是获取用户信息的接口 |
|
|
const adminData = ref({}) |
|
|
const adminData = ref({}) |
|
@ -23,7 +28,14 @@ const getAdminData = async function () { |
|
|
const tableData = ref([]) |
|
|
const tableData = ref([]) |
|
|
// 搜索====================================== |
|
|
// 搜索====================================== |
|
|
// 搜索rechargeVo |
|
|
// 搜索rechargeVo |
|
|
const rechargeVo = ref({}) |
|
|
|
|
|
|
|
|
const rechargeVo = ref({ |
|
|
|
|
|
activityId: '', |
|
|
|
|
|
rechargeWay: '', |
|
|
|
|
|
area: '', |
|
|
|
|
|
startDate: '', |
|
|
|
|
|
endDate: '', |
|
|
|
|
|
status: '' |
|
|
|
|
|
}) |
|
|
// 搜索对象 |
|
|
// 搜索对象 |
|
|
const getObj = ref({ |
|
|
const getObj = ref({ |
|
|
pageNum: 1, |
|
|
pageNum: 1, |
|
@ -57,14 +69,8 @@ const getPayWay = async function () { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// //表格高度 |
|
|
|
|
|
// const tableHeight = computed(function () { |
|
|
|
|
|
// return (getObj.value.pageSize + 2) * 60 + "px"; |
|
|
|
|
|
// }); |
|
|
|
|
|
|
|
|
|
|
|
// 方法 |
|
|
// 方法 |
|
|
// 合计数存储 |
|
|
// 合计数存储 |
|
|
// 合计数存储 |
|
|
|
|
|
const trueGold = ref(0) |
|
|
const trueGold = ref(0) |
|
|
const trueRGold = ref(0) |
|
|
const trueRGold = ref(0) |
|
|
const trueFGold = ref(0) |
|
|
const trueFGold = ref(0) |
|
@ -100,7 +106,7 @@ const get = async function (val) { |
|
|
} |
|
|
} |
|
|
// 搜索参数时间赋值 |
|
|
// 搜索参数时间赋值 |
|
|
if (getTime.value != null) { |
|
|
if (getTime.value != null) { |
|
|
if (getTime.value.startDate != '' && getTime.value.endDate != '') { |
|
|
|
|
|
|
|
|
if (getTime.value[0] && getTime.value[1]) { |
|
|
rechargeVo.value.startDate = getTime.value[0] |
|
|
rechargeVo.value.startDate = getTime.value[0] |
|
|
rechargeVo.value.endDate = getTime.value[1] |
|
|
rechargeVo.value.endDate = getTime.value[1] |
|
|
} |
|
|
} |
|
@ -194,7 +200,7 @@ const reset = function () { |
|
|
rechargeVo.value.endDate = '' |
|
|
rechargeVo.value.endDate = '' |
|
|
sortField.value = '' |
|
|
sortField.value = '' |
|
|
sortOrder.value = '' |
|
|
sortOrder.value = '' |
|
|
getTime.value = {} |
|
|
|
|
|
|
|
|
getTime.value = [] |
|
|
} |
|
|
} |
|
|
// 今天 |
|
|
// 今天 |
|
|
const getToday = function () { |
|
|
const getToday = function () { |
|
@ -314,7 +320,6 @@ const getActivity = async function () { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// 获取地区 |
|
|
// 获取地区 |
|
|
// 获取地区 |
|
|
|
|
|
const getArea = async function () { |
|
|
const getArea = async function () { |
|
|
console.log('888888888880000000') |
|
|
console.log('888888888880000000') |
|
|
try { |
|
|
try { |
|
@ -364,10 +369,10 @@ onMounted(async function () { |
|
|
getAdminData() |
|
|
getAdminData() |
|
|
get() |
|
|
get() |
|
|
getActivity() |
|
|
getActivity() |
|
|
|
|
|
|
|
|
getPayWay() |
|
|
getPayWay() |
|
|
}) |
|
|
|
|
|
getArea() |
|
|
getArea() |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
const handlePageSizeChange = function (val) { |
|
|
const handlePageSizeChange = function (val) { |
|
|
getObj.value.pageSize = val |
|
|
getObj.value.pageSize = val |
|
|
get() |
|
|
get() |
|
@ -397,6 +402,207 @@ const handleSortChange = (column) => { |
|
|
sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC' |
|
|
sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC' |
|
|
get() |
|
|
get() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 导出Excel相关变量和方法 |
|
|
|
|
|
const headers = [ |
|
|
|
|
|
'序号', |
|
|
|
|
|
'姓名', |
|
|
|
|
|
'精网号', |
|
|
|
|
|
'所属地区', |
|
|
|
|
|
'活动名称', |
|
|
|
|
|
'货币名称', |
|
|
|
|
|
'充值金额', |
|
|
|
|
|
'充值方式', |
|
|
|
|
|
'永久金币', |
|
|
|
|
|
'免费金币', |
|
|
|
|
|
'备注', |
|
|
|
|
|
'支付方式', |
|
|
|
|
|
'提交人', |
|
|
|
|
|
'状态', |
|
|
|
|
|
'驳回理由', |
|
|
|
|
|
'交款时间' |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
const showExportInfoPanel = ref(false) |
|
|
|
|
|
|
|
|
|
|
|
// 点击导出按钮直接显示信息面板 |
|
|
|
|
|
const exportExcel = async () => { |
|
|
|
|
|
try { |
|
|
|
|
|
console.log('点击导出按钮,尝试显示信息面板'); |
|
|
|
|
|
showExportInfoPanel.value = true; |
|
|
|
|
|
await nextTick(); // 组件更新显示信息面板 |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('显示信息面板失败:', error); |
|
|
|
|
|
ElMessage.error('显示信息面板失败,请稍后重试'); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 新增导出状态相关变量 |
|
|
|
|
|
const exportProgress = ref(0) |
|
|
|
|
|
const isExporting = ref(false) |
|
|
|
|
|
const exportCancelToken = ref(null) |
|
|
|
|
|
|
|
|
|
|
|
// 移除未使用的映射 |
|
|
|
|
|
// const platformMap = { |
|
|
|
|
|
// 0: '初始化金币', |
|
|
|
|
|
// 1: 'ERP系统', |
|
|
|
|
|
// 2: 'Homily Chart', |
|
|
|
|
|
// 3: 'Homily Link', |
|
|
|
|
|
// 4: '金币系统' |
|
|
|
|
|
// }; |
|
|
|
|
|
// const updateTypeMap = { |
|
|
|
|
|
// 0: '充值', |
|
|
|
|
|
// 1: '消费', |
|
|
|
|
|
// 2: '退款', |
|
|
|
|
|
// 3: '其他' |
|
|
|
|
|
// }; |
|
|
|
|
|
|
|
|
|
|
|
// 优化后的导出方法 |
|
|
|
|
|
const doExportExcel = async () => { |
|
|
|
|
|
try { |
|
|
|
|
|
isExporting.value = true |
|
|
|
|
|
exportProgress.value = 0 |
|
|
|
|
|
showExportInfoPanel.value = false |
|
|
|
|
|
|
|
|
|
|
|
// 初始化 Excel |
|
|
|
|
|
const wb = utils.book_new() |
|
|
|
|
|
const ws = utils.aoa_to_sheet([headers]) |
|
|
|
|
|
utils.book_append_sheet(wb, ws, 'Sheet1') |
|
|
|
|
|
|
|
|
|
|
|
// 流式写入配置 |
|
|
|
|
|
const writer = { |
|
|
|
|
|
write: (d, o) => { |
|
|
|
|
|
if (!d) return |
|
|
|
|
|
utils.sheet_add_aoa(ws, d, { origin: -1 }) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let page = 1 |
|
|
|
|
|
let totalExported = 0 |
|
|
|
|
|
const pageSize = 5000 // 每次请求 5000 条 |
|
|
|
|
|
let totalRecords = 0 |
|
|
|
|
|
|
|
|
|
|
|
// 首次请求获取总记录数 |
|
|
|
|
|
const firstResult = await API({ |
|
|
|
|
|
url: '/recharge/recharge', |
|
|
|
|
|
method: 'post', |
|
|
|
|
|
data: { |
|
|
|
|
|
pageNum: 1, |
|
|
|
|
|
pageSize, |
|
|
|
|
|
rechargeVo: { ...rechargeVo.value } |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
totalRecords = firstResult.data.total |
|
|
|
|
|
|
|
|
|
|
|
// 创建取消令牌 |
|
|
|
|
|
const CancelToken = axios.CancelToken |
|
|
|
|
|
exportCancelToken.value = CancelToken.source() |
|
|
|
|
|
|
|
|
|
|
|
// 处理首次请求的数据 |
|
|
|
|
|
const firstData = firstResult.data.list |
|
|
|
|
|
if (firstData.length) { |
|
|
|
|
|
const rows = firstData.map((row, index) => [ |
|
|
|
|
|
totalExported + index + 1, |
|
|
|
|
|
row.username || '', |
|
|
|
|
|
row.jwcode || '', |
|
|
|
|
|
row.area || '', |
|
|
|
|
|
row.activityName || '', |
|
|
|
|
|
// 假设货币名称字段在数据中为 currencyName,若不存在请替换为实际字段 |
|
|
|
|
|
row.currencyName || '', |
|
|
|
|
|
(row.paidGold / 100).toFixed(2) || '0.00', |
|
|
|
|
|
row.rechargeWay || '', |
|
|
|
|
|
(row.paidGold / 100).toFixed(2) || '0.00', |
|
|
|
|
|
(row.freeGold / 100).toFixed(2) || '0.00', |
|
|
|
|
|
row.remark || '', |
|
|
|
|
|
row.payWay || '', |
|
|
|
|
|
row.name || '', |
|
|
|
|
|
// 根据状态值显示对应文本 |
|
|
|
|
|
row.status === 1 ? '已通过' : row.status === 0 ? '待审核' : row.status === 2 ? '已驳回' : '', |
|
|
|
|
|
row.reson || '', |
|
|
|
|
|
moment(row.rechargeTime).format('YYYY-MM-DD HH:mm:ss') || '' |
|
|
|
|
|
]) |
|
|
|
|
|
writer.write(rows) |
|
|
|
|
|
totalExported += firstData.length |
|
|
|
|
|
exportProgress.value = Math.round((totalExported / totalRecords) * 100) |
|
|
|
|
|
page++ |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
while (totalExported < totalRecords) { |
|
|
|
|
|
const result = await API({ |
|
|
|
|
|
url: '/recharge/recharge', |
|
|
|
|
|
method: 'post', |
|
|
|
|
|
data: { |
|
|
|
|
|
pageNum: page, |
|
|
|
|
|
pageSize, |
|
|
|
|
|
rechargeVo: { ...rechargeVo.value } |
|
|
|
|
|
}, |
|
|
|
|
|
cancelToken: exportCancelToken.value.token |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const data = result.data.list |
|
|
|
|
|
if (!data.length) break |
|
|
|
|
|
|
|
|
|
|
|
// 转换数据 |
|
|
|
|
|
const rows = data.map((row, index) => [ |
|
|
|
|
|
totalExported + index + 1, |
|
|
|
|
|
row.username || '', |
|
|
|
|
|
row.jwcode || '', |
|
|
|
|
|
row.area || '', |
|
|
|
|
|
row.activityName || '', |
|
|
|
|
|
// 假设货币名称字段在数据中为 currencyName,若不存在请替换为实际字段 |
|
|
|
|
|
row.currencyName || '', |
|
|
|
|
|
(row.paidGold / 100).toFixed(2) || '0.00', |
|
|
|
|
|
row.rechargeWay || '', |
|
|
|
|
|
(row.paidGold / 100).toFixed(2) || '0.00', |
|
|
|
|
|
(row.freeGold / 100).toFixed(2) || '0.00', |
|
|
|
|
|
row.remark || '', |
|
|
|
|
|
row.payWay || '', |
|
|
|
|
|
row.name || '', |
|
|
|
|
|
// 根据状态值显示对应文本 |
|
|
|
|
|
row.status === 1 ? '已通过' : row.status === 0 ? '待审核' : row.status === 2 ? '已驳回' : '', |
|
|
|
|
|
row.reson || '', |
|
|
|
|
|
moment(row.rechargeTime).format('YYYY-MM-DD HH:mm:ss') || '' |
|
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
|
|
// 流式写入 |
|
|
|
|
|
writer.write(rows) |
|
|
|
|
|
totalExported += data.length |
|
|
|
|
|
exportProgress.value = Math.round((totalExported / totalRecords) * 100) |
|
|
|
|
|
|
|
|
|
|
|
// 内存控制:每 500 页释放内存 |
|
|
|
|
|
if (page % 500 === 0) { |
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 0)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
page++ |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 生成最终文件 |
|
|
|
|
|
writeFile(wb, '客户金币明细.xlsx') |
|
|
|
|
|
ElMessage.success(`导出成功,共${totalExported}条数据`) |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
if (!axios.isCancel(error)) { |
|
|
|
|
|
ElMessage.error(`导出失败: ${error.message}`) |
|
|
|
|
|
} |
|
|
|
|
|
} finally { |
|
|
|
|
|
isExporting.value = false |
|
|
|
|
|
exportCancelToken.value = null |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 新增取消导出方法 |
|
|
|
|
|
const cancelExport = () => { |
|
|
|
|
|
if (exportCancelToken.value) { |
|
|
|
|
|
exportCancelToken.value.cancel('用户取消导出') |
|
|
|
|
|
ElMessage.warning('导出已取消') |
|
|
|
|
|
isExporting.value = false |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const putExcel = ref({ |
|
|
|
|
|
startDate: new Date(), |
|
|
|
|
|
endDate: new Date(new Date().setDate(new Date().getDate() + 1)) |
|
|
|
|
|
}) |
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
<template> |
|
|
<template> |
|
@ -463,7 +669,7 @@ const handleSortChange = (column) => { |
|
|
</el-col> |
|
|
</el-col> |
|
|
</el-row> |
|
|
</el-row> |
|
|
<el-row> |
|
|
<el-row> |
|
|
<el-col :span="21"> |
|
|
|
|
|
|
|
|
<el-col :span="16"> |
|
|
<div class="head-card-element"> |
|
|
<div class="head-card-element"> |
|
|
<el-text class="mx-1" size="large">充值时间:</el-text> |
|
|
<el-text class="mx-1" size="large">充值时间:</el-text> |
|
|
<el-date-picker |
|
|
<el-date-picker |
|
@ -480,10 +686,12 @@ const handleSortChange = (column) => { |
|
|
<el-button @click="get7Days()">近7天</el-button> |
|
|
<el-button @click="get7Days()">近7天</el-button> |
|
|
</div> |
|
|
</div> |
|
|
</el-col> |
|
|
</el-col> |
|
|
<el-col :span="3"> |
|
|
|
|
|
|
|
|
<el-col :span="6"> |
|
|
<div class="head-card-btn"> |
|
|
<div class="head-card-btn"> |
|
|
<el-button type="success" @click="reset()">重置</el-button> |
|
|
<el-button type="success" @click="reset()">重置</el-button> |
|
|
<el-button type="primary" @click="search()">查询</el-button> |
|
|
<el-button type="primary" @click="search()">查询</el-button> |
|
|
|
|
|
<!-- 新增导出按钮 --> |
|
|
|
|
|
<el-button type="primary" @click="exportExcel">导出excel</el-button> |
|
|
</div> |
|
|
</div> |
|
|
</el-col> |
|
|
</el-col> |
|
|
</el-row> |
|
|
</el-row> |
|
@ -670,6 +878,53 @@ const handleSortChange = (column) => { |
|
|
</el-card> |
|
|
</el-card> |
|
|
</el-col> |
|
|
</el-col> |
|
|
</el-row> |
|
|
</el-row> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 导出excel提前展示的信息面板 --> |
|
|
|
|
|
<el-dialog |
|
|
|
|
|
v-model="showExportInfoPanel" |
|
|
|
|
|
title="导出信息确认" |
|
|
|
|
|
width="400px" |
|
|
|
|
|
:close-on-click-modal="false" |
|
|
|
|
|
> |
|
|
|
|
|
<div class="info-panel-header">导出信息</div> |
|
|
|
|
|
<div v-if="!rechargeVo.activityId && !rechargeVo.area && !rechargeVo.startDate && !rechargeVo.endDate"> |
|
|
|
|
|
你正在导出所有数据 |
|
|
|
|
|
</div> |
|
|
|
|
|
<div v-else> |
|
|
|
|
|
你正在导出以下数据 |
|
|
|
|
|
</div> |
|
|
|
|
|
<div v-if="rechargeVo.activityId">活动名称:{{ rechargeVo.activityId || '' }}</div> |
|
|
|
|
|
<div v-if="rechargeVo.area">所属地区:{{ rechargeVo.area || '' }}</div> |
|
|
|
|
|
<div v-if="rechargeVo.startDate || rechargeVo.endDate"> |
|
|
|
|
|
<span>充值时间:</span> |
|
|
|
|
|
<span>{{ rechargeVo.startDate ? moment(rechargeVo.startDate).format('YYYY-MM-DD HH:mm:ss') : '无起始时间' }} 至 {{ rechargeVo.endDate ? moment(rechargeVo.endDate).format('YYYY-MM-DD HH:mm:ss') : '无结束时间' }}</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
<template #footer> |
|
|
|
|
|
<span class="dialog-footer"> |
|
|
|
|
|
<el-button @click="showExportInfoPanel = false">取消</el-button> |
|
|
|
|
|
<el-button type="primary" @click="doExportExcel">导出</el-button> |
|
|
|
|
|
</span> |
|
|
|
|
|
</template> |
|
|
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 导出进度弹窗 --> |
|
|
|
|
|
<el-dialog |
|
|
|
|
|
v-model="isExporting" |
|
|
|
|
|
title="正在导出" |
|
|
|
|
|
width="400px" |
|
|
|
|
|
:close-on-click-modal="false" |
|
|
|
|
|
:show-close="false" |
|
|
|
|
|
> |
|
|
|
|
|
<el-progress |
|
|
|
|
|
:percentage="exportProgress" |
|
|
|
|
|
:format="(percentage) => `${percentage}%`" |
|
|
|
|
|
/> |
|
|
|
|
|
<template #footer> |
|
|
|
|
|
<span class="dialog-footer"> |
|
|
|
|
|
<el-button type="danger" @click="cancelExport">取消导出</el-button> |
|
|
|
|
|
</span> |
|
|
|
|
|
</template> |
|
|
|
|
|
</el-dialog> |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<style scoped> |
|
|
<style scoped> |
|
|