3 changed files with 1035 additions and 8 deletions
-
19src/api/cash/financialAccount.js
-
510src/views/moneyManage/financialAccount/cashFlow.vue
-
510src/views/moneyManage/financialAccount/performanceAttribution.vue
@ -0,0 +1,19 @@ |
|||||
|
import http from '@/util/http.js' |
||||
|
|
||||
|
//查询资金流水
|
||||
|
export const Moneyfunds = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/Money/funds', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 新增线上退款
|
||||
|
export const refundOnline = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/Money/addOnline', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
@ -1,5 +1,509 @@ |
|||||
|
<script setup> |
||||
|
import { ref, reactive, onMounted } from 'vue' |
||||
|
import { ElMessage, ElMessageBox } from 'element-plus' |
||||
|
import request from '@/util/http.js' |
||||
|
import dayjs from 'dayjs' |
||||
|
import { useI18n } from 'vue-i18n' |
||||
|
import { Moneyfunds,refundOnline } from '@/api/cash/financialAccount.js' |
||||
|
|
||||
|
const { t } = useI18n() |
||||
|
|
||||
|
const paytypeList = [ |
||||
|
t('cash.payMethods.stripe'), |
||||
|
t('cash.payMethods.paymentAsia'), |
||||
|
t('cash.payMethods.ipay88'), |
||||
|
t('cash.payMethods.bankTransfer'), |
||||
|
t('cash.payMethods.card'), |
||||
|
t('cash.payMethods.cash'), |
||||
|
t('cash.payMethods.check'), |
||||
|
t('cash.payMethods.grabpay'), |
||||
|
t('cash.payMethods.nets'), |
||||
|
t('cash.payMethods.transfer'), |
||||
|
t('cash.payMethods.paypal'), |
||||
|
] |
||||
|
|
||||
|
const payPlatformOptions = ref([...paytypeList]) |
||||
|
|
||||
|
const statusOptions = [ |
||||
|
{ label: '已到账', value: 4 }, |
||||
|
{ label: '已退款', value: 6 } |
||||
|
] |
||||
|
|
||||
|
// 地区树 |
||||
|
const marketOptions = ref([]) |
||||
|
|
||||
|
// 查询参数 |
||||
|
const queryParams = reactive({ |
||||
|
jwcode: '', |
||||
|
markets: [], // 下拉多选 |
||||
|
timeRange: [], // [startTime, endTime] |
||||
|
payType: '', |
||||
|
orderCode: '', |
||||
|
statuses: [], |
||||
|
pageNum: 1, |
||||
|
pageSize: 50 |
||||
|
}) |
||||
|
|
||||
|
const total = ref(0) |
||||
|
const tableData = ref([]) |
||||
|
const loading = ref(false) |
||||
|
|
||||
|
// 转换树形结构(参考 coinConsumeDetail.vue) |
||||
|
const transformTree = (nodes) => { |
||||
|
const allChildren = nodes.flatMap(node => node.children || []); |
||||
|
return allChildren.map(child => { |
||||
|
const grandchildren = child.children && child.children.length |
||||
|
? transformTree([child]) |
||||
|
: null; |
||||
|
return { |
||||
|
value: child.name, |
||||
|
label: child.name, |
||||
|
children: grandchildren |
||||
|
}; |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 获取地区数据 |
||||
|
const getMarket = async () => { |
||||
|
try { |
||||
|
const result = await request({ url: '/market/selectMarket' }); |
||||
|
if (result && result.data) { |
||||
|
marketOptions.value = transformTree(result.data) |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取地区失败', error) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 查询列表 |
||||
|
const fetchData = async () => { |
||||
|
loading.value = true |
||||
|
try { |
||||
|
// 构建请求参数 |
||||
|
const params = { |
||||
|
pageNum: queryParams.pageNum, |
||||
|
pageSize: queryParams.pageSize, |
||||
|
fundsDTO:{ |
||||
|
jwcode: queryParams.jwcode, |
||||
|
markets: queryParams.markets, |
||||
|
startTime: queryParams.timeRange?.[0] || '', |
||||
|
endTime: queryParams.timeRange?.[1] || '', |
||||
|
payType: queryParams.payType, |
||||
|
orderCode: queryParams.orderCode, |
||||
|
statuses: queryParams.statuses, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
console.log('查询参数:', params) |
||||
|
const res = await Moneyfunds(params) |
||||
|
if (res.code == 200) { |
||||
|
tableData.value = res.data.list || [] |
||||
|
total.value = res.data.total || 0 |
||||
|
loading.value = false |
||||
|
} else { |
||||
|
ElMessage.error(res.msg || '获取数据失败') |
||||
|
loading.value = false |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error(error) |
||||
|
loading.value = false |
||||
|
ElMessage.error('获取数据失败') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleSearch = () => { |
||||
|
queryParams.pageNum = 1 |
||||
|
fetchData() |
||||
|
} |
||||
|
|
||||
|
const handleReset = () => { |
||||
|
queryParams.jwcode = '' |
||||
|
queryParams.markets = [] |
||||
|
queryParams.timeRange = [] |
||||
|
queryParams.payType = '' |
||||
|
queryParams.orderCode = '' |
||||
|
queryParams.statuses = [] |
||||
|
handleSearch() |
||||
|
} |
||||
|
|
||||
|
const handlePageSizeChange = (val) => { |
||||
|
queryParams.pageSize = val |
||||
|
fetchData() |
||||
|
} |
||||
|
|
||||
|
const handleCurrentChange = (val) => { |
||||
|
queryParams.pageNum = val |
||||
|
fetchData() |
||||
|
} |
||||
|
|
||||
|
// 退款操作 |
||||
|
const handleRefund = (row) => { |
||||
|
ElMessageBox.confirm(`确定要对订单 ${row.systemTradeNo} 进行退款吗?`, '退款确认', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
|
||||
|
|
||||
|
ElMessage.success('退款申请已提交') |
||||
|
// 刷新列表 |
||||
|
fetchData() |
||||
|
}).catch(() => {}) |
||||
|
} |
||||
|
|
||||
|
// ==================== 导出相关逻辑 ==================== |
||||
|
|
||||
|
const exportListVisible = ref(false) |
||||
|
const exportList = ref([]) |
||||
|
const exportListLoading = ref(false) |
||||
|
|
||||
|
// 导出Excel |
||||
|
const handleExport = async () => { |
||||
|
try { |
||||
|
const params = { |
||||
|
pageNum: queryParams.pageNum, |
||||
|
pageSize: queryParams.pageSize, |
||||
|
fundsDTO:{ |
||||
|
jwcode: queryParams.jwcode, |
||||
|
markets: queryParams.markets, |
||||
|
startTime: queryParams.timeRange?.[0] || '', |
||||
|
endTime: queryParams.timeRange?.[1] || '', |
||||
|
payType: queryParams.payType, |
||||
|
orderCode: queryParams.orderCode, |
||||
|
statuses: queryParams.statuses, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// TODO: 确认导出接口 URL |
||||
|
const res = await request({ url: '/export/exportCash', data: params }) |
||||
|
if(res.code == 200){ |
||||
|
|
||||
|
console.log('导出参数', params) |
||||
|
ElMessage.success(t('elmessage.exportSuccess')) |
||||
|
} |
||||
|
|
||||
|
} catch (error) { |
||||
|
console.error(error) |
||||
|
ElMessage.error('导出失败') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 打开导出列表弹窗 |
||||
|
const openExportList = () => { |
||||
|
getExportList() |
||||
|
exportListVisible.value = true |
||||
|
} |
||||
|
|
||||
|
// 获取导出列表 |
||||
|
const getExportList = async () => { |
||||
|
exportListLoading.value = true |
||||
|
try { |
||||
|
const result = await request({ url: '/export/export' }) |
||||
|
if (result.code === 200) { |
||||
|
const filteredData = result.data.filter(item => item.type == 13); |
||||
|
exportList.value = filteredData || [] |
||||
|
} else { |
||||
|
ElMessage.error(result.msg || t('elmessage.getExportListError')) |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取导出列表出错:', error) |
||||
|
ElMessage.error(t('elmessage.getExportListError')) |
||||
|
} finally { |
||||
|
exportListLoading.value = false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 下载导出文件 |
||||
|
const downloadExportFile = (item) => { |
||||
|
if (item.state === 2) { |
||||
|
const link = document.createElement('a') |
||||
|
link.href = item.url |
||||
|
link.download = item.fileName |
||||
|
link.click() |
||||
|
} else { |
||||
|
ElMessage.warning(t('elmessage.exportingInProgress')) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 根据状态返回对应的标签类型 |
||||
|
const getTagType = (state) => { |
||||
|
switch (state) { |
||||
|
case 0: return 'info'; |
||||
|
case 1: return 'primary'; |
||||
|
case 2: return 'success'; |
||||
|
case 3: return 'danger'; |
||||
|
default: return 'info'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 根据状态返回对应的标签文案 |
||||
|
const getTagText = (state) => { |
||||
|
switch (state) { |
||||
|
case 0: return t('elmessage.pendingExecution'); |
||||
|
case 1: return t('elmessage.executing'); |
||||
|
case 2: return t('elmessage.executed'); |
||||
|
case 3: return t('elmessage.errorExecution'); |
||||
|
default: return t('elmessage.unknownStatus'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
getMarket() |
||||
|
fetchData() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<div> |
|
||||
<h1>资金流水</h1> |
|
||||
</div> |
|
||||
|
<div class="cash-flow-container"> |
||||
|
<!-- 搜索区域 --> |
||||
|
<el-card class="search-card"> |
||||
|
<div class="search-bar"> |
||||
|
<!-- 第一行 --> |
||||
|
<div class="search-row"> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">精网号:</span> |
||||
|
<el-input v-model="queryParams.jwcode" placeholder="请输入精网号" clearable /> |
||||
|
</div> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">所属地区:</span> |
||||
|
<!-- 下拉多选,使用 el-cascader 匹配地区树结构 --> |
||||
|
<el-cascader |
||||
|
v-model="queryParams.markets" |
||||
|
:options="marketOptions" |
||||
|
:props="{ multiple: true, emitPath: false }" |
||||
|
collapse-tags |
||||
|
collapse-tags-tooltip |
||||
|
placeholder="请选择地区" |
||||
|
clearable |
||||
|
style="width: 220px;" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">支付平台:</span> |
||||
|
<el-select v-model="queryParams.payType" placeholder="请选择" clearable> |
||||
|
<el-option v-for="item in payPlatformOptions" :key="item" :label="item" :value="item" /> |
||||
|
</el-select> |
||||
|
</div> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">状态:</span> |
||||
|
<el-select v-model="queryParams.statuses[0]" placeholder="请选择" clearable> |
||||
|
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" /> |
||||
|
</el-select> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 第二行 --> |
||||
|
<div class="search-row"> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">订单号:</span> |
||||
|
<el-input v-model="queryParams.orderCode" placeholder="请输入订单号" clearable /> |
||||
|
</div> |
||||
|
<div class="search-item" style="width: auto;"> |
||||
|
<span class="label">付款时间:</span> |
||||
|
<el-date-picker |
||||
|
v-model="queryParams.timeRange" |
||||
|
type="datetimerange" |
||||
|
range-separator="至" |
||||
|
start-placeholder="开始时间" |
||||
|
end-placeholder="结束时间" |
||||
|
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" |
||||
|
style="width: 350px;" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="search-btn-group"> |
||||
|
<el-button type="primary" @click="handleSearch">{{ t('common.search') }}</el-button> |
||||
|
<el-button type="primary" @click="handleExport">{{ t('common.exportExcel') }}</el-button> |
||||
|
<el-button type="primary" @click="openExportList">{{ t('common.viewExportList') }}</el-button> |
||||
|
<el-button type="success" @click="handleReset">{{ t('common.reset') }}</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
|
||||
|
<!-- 表格区域 --> |
||||
|
<el-card class="table-card"> |
||||
|
<el-table :data="tableData" v-loading="loading" style="width: 100%; flex: 1;" :header-cell-style="{ background: '#F3FAFE', color: '#333' }"> |
||||
|
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" /> |
||||
|
<el-table-column prop="jwcode" label="精网号" width="120" fixed="left" /> |
||||
|
<el-table-column prop="name" label="姓名" width="120" show-overflow-tooltip /> |
||||
|
<el-table-column prop="market" label="所属地区" width="120" show-overflow-tooltip /> |
||||
|
<el-table-column prop="orderCode" label="系统交易号" width="180" show-overflow-tooltip /> |
||||
|
|
||||
|
<el-table-column prop="paymentAmount" label="付款金额" width="150" align="right"> |
||||
|
<!-- <template #default="{ row }"> |
||||
|
{{ row.paymentAmount }} {{ row.paymentCurrency }} |
||||
|
</template> --> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="paymentCurrency" label="付款币种" width="180" show-overflow-tooltip /> |
||||
|
|
||||
|
<el-table-column prop="receivedAmount" label="到账金额" width="150" align="right"> |
||||
|
<!-- <template #default="{ row }"> |
||||
|
{{ row.receivedAmount }} {{ row.receivedCurrency }} |
||||
|
</template> --> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="receivedCurrency" label="到账币种" width="180" show-overflow-tooltip /> |
||||
|
|
||||
|
<el-table-column prop="handlingCharge" label="手续费" width="100" align="right" /> |
||||
|
<el-table-column prop="payType" label="支付方式" width="120" align="center" /> |
||||
|
<el-table-column prop="payTime" label="付款时间" width="180" align="center" /> |
||||
|
|
||||
|
<el-table-column prop="status" label="状态" width="100" align="center" fixed="right"> |
||||
|
<template #default="{ row }"> |
||||
|
<el-tag :type="row.status === 4 ? 'success' : 'warning'" effect="plain"> |
||||
|
{{ row.status === 4 ? '已到账' : '已退款' }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column label="操作" width="100" fixed="right" align="center"> |
||||
|
<template #default="{ row }"> |
||||
|
<el-button |
||||
|
v-if="row.orderCode.slice(0,4) == 'GOLD'" |
||||
|
type="danger" |
||||
|
link |
||||
|
size="small" |
||||
|
@click="handleRefund(row)" |
||||
|
> |
||||
|
退款 |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 分页 --> |
||||
|
<div class="pagination-container"> |
||||
|
<el-pagination |
||||
|
background |
||||
|
layout="total, sizes, prev, pager, next, jumper" |
||||
|
:total="total" |
||||
|
:current-page="queryParams.pageNum" |
||||
|
:page-size="queryParams.pageSize" |
||||
|
:page-sizes="[10, 20, 50, 100]" |
||||
|
@size-change="handlePageSizeChange" |
||||
|
@current-change="handleCurrentChange" |
||||
|
/> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
|
||||
|
<!-- 导出列表弹窗 --> |
||||
|
<el-dialog v-model="exportListVisible" :title="t('common_export.exportList')" width="80%"> |
||||
|
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading"> |
||||
|
<el-table-column prop="fileName" :label="t('common_export.fileName')" /> |
||||
|
<el-table-column prop="state" :label="t('common_export.status')"> |
||||
|
<template #default="scope"> |
||||
|
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'"> |
||||
|
{{ getTagText(scope.row.state) }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="createTime" :label="t('common_export.createTime')"> |
||||
|
<template #default="scope"> |
||||
|
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column :label="t('common_export.operation')"> |
||||
|
<template #default="scope"> |
||||
|
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)" |
||||
|
:disabled="scope.row.state !== 2"> |
||||
|
{{ t('common_export.download') }} |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<template #footer> |
||||
|
<div class="dialog-footer"> |
||||
|
<el-button text @click="exportListVisible = false">{{ t('common_export.close') }}</el-button> |
||||
|
</div> |
||||
|
</template> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
</template> |
</template> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.cash-flow-container { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.search-card { |
||||
|
margin-bottom: 10px; |
||||
|
background: #F3FAFE; // 浅蓝背景 |
||||
|
border: none; |
||||
|
|
||||
|
:deep(.el-card__body) { |
||||
|
padding: 15px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.search-bar { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 15px; |
||||
|
} |
||||
|
|
||||
|
.search-row { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
gap: 20px; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.search-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
.label { |
||||
|
font-size: 15px; // 参考 coinConsumeDetail 的 .text size="large" |
||||
|
color: #000; // 或 #606266 |
||||
|
white-space: nowrap; |
||||
|
margin-right: 8px; |
||||
|
min-width: 60px; |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.el-input, .el-select { |
||||
|
width: 200px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.search-btn-group { |
||||
|
margin-left: auto; // 靠右对齐 |
||||
|
display: flex; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
|
||||
|
.table-card { |
||||
|
background: #E7F4FD; |
||||
|
flex: 1; |
||||
|
border: none; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
|
||||
|
:deep(.el-card__body) { |
||||
|
padding: 20px; |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.pagination-container { |
||||
|
margin-top: 15px; |
||||
|
display: flex; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
|
||||
|
// 表格样式覆盖 (参考 coinConsumeDetail) |
||||
|
:deep(.el-table__header-wrapper), |
||||
|
:deep(.el-table__body-wrapper), |
||||
|
:deep(.el-table__cell), |
||||
|
:deep(.el-table__body td) { |
||||
|
background-color: #F3FAFE !important; // 如果想完全一致可以加这个,但有时候会影响阅读,暂保留头部颜色 |
||||
|
} |
||||
|
|
||||
|
:deep(.el-table__row:hover > .el-table__cell) { |
||||
|
background-color: #E5EBFE !important; |
||||
|
} |
||||
|
</style> |
||||
@ -1,5 +1,509 @@ |
|||||
|
<script setup> |
||||
|
import { ref, reactive, onMounted } from 'vue' |
||||
|
import { ElMessage, ElMessageBox } from 'element-plus' |
||||
|
import request from '@/util/http.js' |
||||
|
import dayjs from 'dayjs' |
||||
|
import { useI18n } from 'vue-i18n' |
||||
|
import { Moneyfunds,refundOnline } from '@/api/cash/financialAccount.js' |
||||
|
|
||||
|
const { t } = useI18n() |
||||
|
|
||||
|
const paytypeList = [ |
||||
|
t('cash.payMethods.stripe'), |
||||
|
t('cash.payMethods.paymentAsia'), |
||||
|
t('cash.payMethods.ipay88'), |
||||
|
t('cash.payMethods.bankTransfer'), |
||||
|
t('cash.payMethods.card'), |
||||
|
t('cash.payMethods.cash'), |
||||
|
t('cash.payMethods.check'), |
||||
|
t('cash.payMethods.grabpay'), |
||||
|
t('cash.payMethods.nets'), |
||||
|
t('cash.payMethods.transfer'), |
||||
|
t('cash.payMethods.paypal'), |
||||
|
] |
||||
|
|
||||
|
const payPlatformOptions = ref([...paytypeList]) |
||||
|
|
||||
|
const statusOptions = [ |
||||
|
{ label: '已到账', value: 4 }, |
||||
|
{ label: '已退款', value: 6 } |
||||
|
] |
||||
|
|
||||
|
// 地区树 |
||||
|
const marketOptions = ref([]) |
||||
|
|
||||
|
// 查询参数 |
||||
|
const queryParams = reactive({ |
||||
|
jwcode: '', |
||||
|
markets: [], // 下拉多选 |
||||
|
timeRange: [], // [startTime, endTime] |
||||
|
payType: '', |
||||
|
orderCode: '', |
||||
|
statuses: [], |
||||
|
pageNum: 1, |
||||
|
pageSize: 50 |
||||
|
}) |
||||
|
|
||||
|
const total = ref(0) |
||||
|
const tableData = ref([]) |
||||
|
const loading = ref(false) |
||||
|
|
||||
|
// 转换树形结构(参考 coinConsumeDetail.vue) |
||||
|
const transformTree = (nodes) => { |
||||
|
const allChildren = nodes.flatMap(node => node.children || []); |
||||
|
return allChildren.map(child => { |
||||
|
const grandchildren = child.children && child.children.length |
||||
|
? transformTree([child]) |
||||
|
: null; |
||||
|
return { |
||||
|
value: child.name, |
||||
|
label: child.name, |
||||
|
children: grandchildren |
||||
|
}; |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 获取地区数据 |
||||
|
const getMarket = async () => { |
||||
|
try { |
||||
|
const result = await request({ url: '/market/selectMarket' }); |
||||
|
if (result && result.data) { |
||||
|
marketOptions.value = transformTree(result.data) |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取地区失败', error) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 查询列表 |
||||
|
const fetchData = async () => { |
||||
|
loading.value = true |
||||
|
try { |
||||
|
// 构建请求参数 |
||||
|
const params = { |
||||
|
pageNum: queryParams.pageNum, |
||||
|
pageSize: queryParams.pageSize, |
||||
|
fundsDTO:{ |
||||
|
jwcode: queryParams.jwcode, |
||||
|
markets: queryParams.markets, |
||||
|
startTime: queryParams.timeRange?.[0] || '', |
||||
|
endTime: queryParams.timeRange?.[1] || '', |
||||
|
payType: queryParams.payType, |
||||
|
orderCode: queryParams.orderCode, |
||||
|
statuses: queryParams.statuses, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
console.log('查询参数:', params) |
||||
|
const res = await Moneyfunds(params) |
||||
|
if (res.code == 200) { |
||||
|
tableData.value = res.data.list || [] |
||||
|
total.value = res.data.total || 0 |
||||
|
loading.value = false |
||||
|
} else { |
||||
|
ElMessage.error(res.msg || '获取数据失败') |
||||
|
loading.value = false |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error(error) |
||||
|
loading.value = false |
||||
|
ElMessage.error('获取数据失败') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleSearch = () => { |
||||
|
queryParams.pageNum = 1 |
||||
|
fetchData() |
||||
|
} |
||||
|
|
||||
|
const handleReset = () => { |
||||
|
queryParams.jwcode = '' |
||||
|
queryParams.markets = [] |
||||
|
queryParams.timeRange = [] |
||||
|
queryParams.payType = '' |
||||
|
queryParams.orderCode = '' |
||||
|
queryParams.statuses = [] |
||||
|
handleSearch() |
||||
|
} |
||||
|
|
||||
|
const handlePageSizeChange = (val) => { |
||||
|
queryParams.pageSize = val |
||||
|
fetchData() |
||||
|
} |
||||
|
|
||||
|
const handleCurrentChange = (val) => { |
||||
|
queryParams.pageNum = val |
||||
|
fetchData() |
||||
|
} |
||||
|
|
||||
|
// 退款操作 |
||||
|
const handleRefund = (row) => { |
||||
|
ElMessageBox.confirm(`确定要对订单 ${row.systemTradeNo} 进行退款吗?`, '退款确认', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
|
||||
|
|
||||
|
ElMessage.success('退款申请已提交') |
||||
|
// 刷新列表 |
||||
|
fetchData() |
||||
|
}).catch(() => {}) |
||||
|
} |
||||
|
|
||||
|
// ==================== 导出相关逻辑 ==================== |
||||
|
|
||||
|
const exportListVisible = ref(false) |
||||
|
const exportList = ref([]) |
||||
|
const exportListLoading = ref(false) |
||||
|
|
||||
|
// 导出Excel |
||||
|
const handleExport = async () => { |
||||
|
try { |
||||
|
const params = { |
||||
|
pageNum: queryParams.pageNum, |
||||
|
pageSize: queryParams.pageSize, |
||||
|
fundsDTO:{ |
||||
|
jwcode: queryParams.jwcode, |
||||
|
markets: queryParams.markets, |
||||
|
startTime: queryParams.timeRange?.[0] || '', |
||||
|
endTime: queryParams.timeRange?.[1] || '', |
||||
|
payType: queryParams.payType, |
||||
|
orderCode: queryParams.orderCode, |
||||
|
statuses: queryParams.statuses, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// TODO: 确认导出接口 URL |
||||
|
const res = await request({ url: '/export/exportCash', data: params }) |
||||
|
if(res.code == 200){ |
||||
|
|
||||
|
console.log('导出参数', params) |
||||
|
ElMessage.success(t('elmessage.exportSuccess')) |
||||
|
} |
||||
|
|
||||
|
} catch (error) { |
||||
|
console.error(error) |
||||
|
ElMessage.error('导出失败') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 打开导出列表弹窗 |
||||
|
const openExportList = () => { |
||||
|
getExportList() |
||||
|
exportListVisible.value = true |
||||
|
} |
||||
|
|
||||
|
// 获取导出列表 |
||||
|
const getExportList = async () => { |
||||
|
exportListLoading.value = true |
||||
|
try { |
||||
|
const result = await request({ url: '/export/export' }) |
||||
|
if (result.code === 200) { |
||||
|
const filteredData = result.data.filter(item => item.type == 13); |
||||
|
exportList.value = filteredData || [] |
||||
|
} else { |
||||
|
ElMessage.error(result.msg || t('elmessage.getExportListError')) |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取导出列表出错:', error) |
||||
|
ElMessage.error(t('elmessage.getExportListError')) |
||||
|
} finally { |
||||
|
exportListLoading.value = false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 下载导出文件 |
||||
|
const downloadExportFile = (item) => { |
||||
|
if (item.state === 2) { |
||||
|
const link = document.createElement('a') |
||||
|
link.href = item.url |
||||
|
link.download = item.fileName |
||||
|
link.click() |
||||
|
} else { |
||||
|
ElMessage.warning(t('elmessage.exportingInProgress')) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 根据状态返回对应的标签类型 |
||||
|
const getTagType = (state) => { |
||||
|
switch (state) { |
||||
|
case 0: return 'info'; |
||||
|
case 1: return 'primary'; |
||||
|
case 2: return 'success'; |
||||
|
case 3: return 'danger'; |
||||
|
default: return 'info'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 根据状态返回对应的标签文案 |
||||
|
const getTagText = (state) => { |
||||
|
switch (state) { |
||||
|
case 0: return t('elmessage.pendingExecution'); |
||||
|
case 1: return t('elmessage.executing'); |
||||
|
case 2: return t('elmessage.executed'); |
||||
|
case 3: return t('elmessage.errorExecution'); |
||||
|
default: return t('elmessage.unknownStatus'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
getMarket() |
||||
|
fetchData() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<div> |
|
||||
<h1>业绩归属</h1> |
|
||||
</div> |
|
||||
|
<div class="cash-flow-container"> |
||||
|
<!-- 搜索区域 --> |
||||
|
<el-card class="search-card"> |
||||
|
<div class="search-bar"> |
||||
|
<!-- 第一行 --> |
||||
|
<div class="search-row"> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">精网号:</span> |
||||
|
<el-input v-model="queryParams.jwcode" placeholder="请输入精网号" clearable /> |
||||
|
</div> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">所属地区:</span> |
||||
|
<!-- 下拉多选,使用 el-cascader 匹配地区树结构 --> |
||||
|
<el-cascader |
||||
|
v-model="queryParams.markets" |
||||
|
:options="marketOptions" |
||||
|
:props="{ multiple: true, emitPath: false }" |
||||
|
collapse-tags |
||||
|
collapse-tags-tooltip |
||||
|
placeholder="请选择地区" |
||||
|
clearable |
||||
|
style="width: 220px;" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">支付平台:</span> |
||||
|
<el-select v-model="queryParams.payType" placeholder="请选择" clearable> |
||||
|
<el-option v-for="item in payPlatformOptions" :key="item" :label="item" :value="item" /> |
||||
|
</el-select> |
||||
|
</div> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">状态:</span> |
||||
|
<el-select v-model="queryParams.statuses[0]" placeholder="请选择" clearable> |
||||
|
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" /> |
||||
|
</el-select> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 第二行 --> |
||||
|
<div class="search-row"> |
||||
|
<div class="search-item"> |
||||
|
<span class="label">订单号:</span> |
||||
|
<el-input v-model="queryParams.orderCode" placeholder="请输入订单号" clearable /> |
||||
|
</div> |
||||
|
<div class="search-item" style="width: auto;"> |
||||
|
<span class="label">付款时间:</span> |
||||
|
<el-date-picker |
||||
|
v-model="queryParams.timeRange" |
||||
|
type="datetimerange" |
||||
|
range-separator="至" |
||||
|
start-placeholder="开始时间" |
||||
|
end-placeholder="结束时间" |
||||
|
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" |
||||
|
style="width: 350px;" |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="search-btn-group"> |
||||
|
<el-button type="primary" @click="handleSearch">{{ t('common.search') }}</el-button> |
||||
|
<el-button type="primary" @click="handleExport">{{ t('common.exportExcel') }}</el-button> |
||||
|
<el-button type="primary" @click="openExportList">{{ t('common.viewExportList') }}</el-button> |
||||
|
<el-button type="success" @click="handleReset">{{ t('common.reset') }}</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
|
||||
|
<!-- 表格区域 --> |
||||
|
<el-card class="table-card"> |
||||
|
<el-table :data="tableData" v-loading="loading" style="width: 100%; flex: 1;" :header-cell-style="{ background: '#F3FAFE', color: '#333' }"> |
||||
|
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" /> |
||||
|
<el-table-column prop="jwcode" label="精网号" width="120" fixed="left" /> |
||||
|
<el-table-column prop="name" label="姓名" width="120" show-overflow-tooltip /> |
||||
|
<el-table-column prop="market" label="所属地区" width="120" show-overflow-tooltip /> |
||||
|
<el-table-column prop="orderCode" label="系统交易号" width="180" show-overflow-tooltip /> |
||||
|
|
||||
|
<el-table-column prop="paymentAmount" label="付款金额" width="150" align="right"> |
||||
|
<!-- <template #default="{ row }"> |
||||
|
{{ row.paymentAmount }} {{ row.paymentCurrency }} |
||||
|
</template> --> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="paymentCurrency" label="付款币种" width="180" show-overflow-tooltip /> |
||||
|
|
||||
|
<el-table-column prop="receivedAmount" label="到账金额" width="150" align="right"> |
||||
|
<!-- <template #default="{ row }"> |
||||
|
{{ row.receivedAmount }} {{ row.receivedCurrency }} |
||||
|
</template> --> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="receivedCurrency" label="到账币种" width="180" show-overflow-tooltip /> |
||||
|
|
||||
|
<el-table-column prop="handlingCharge" label="手续费" width="100" align="right" /> |
||||
|
<el-table-column prop="payType" label="支付方式" width="120" align="center" /> |
||||
|
<el-table-column prop="payTime" label="付款时间" width="180" align="center" /> |
||||
|
|
||||
|
<el-table-column prop="status" label="状态" width="100" align="center" fixed="right"> |
||||
|
<template #default="{ row }"> |
||||
|
<el-tag :type="row.status === 4 ? 'success' : 'warning'" effect="plain"> |
||||
|
{{ row.status === 4 ? '已到账' : '已退款' }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column label="操作" width="100" fixed="right" align="center"> |
||||
|
<template #default="{ row }"> |
||||
|
<el-button |
||||
|
v-if="row.orderCode.slice(0,4) == 'GOLD'" |
||||
|
type="danger" |
||||
|
link |
||||
|
size="small" |
||||
|
@click="handleRefund(row)" |
||||
|
> |
||||
|
退款 |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 分页 --> |
||||
|
<div class="pagination-container"> |
||||
|
<el-pagination |
||||
|
background |
||||
|
layout="total, sizes, prev, pager, next, jumper" |
||||
|
:total="total" |
||||
|
:current-page="queryParams.pageNum" |
||||
|
:page-size="queryParams.pageSize" |
||||
|
:page-sizes="[10, 20, 50, 100]" |
||||
|
@size-change="handlePageSizeChange" |
||||
|
@current-change="handleCurrentChange" |
||||
|
/> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
|
||||
|
<!-- 导出列表弹窗 --> |
||||
|
<el-dialog v-model="exportListVisible" :title="t('common_export.exportList')" width="80%"> |
||||
|
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading"> |
||||
|
<el-table-column prop="fileName" :label="t('common_export.fileName')" /> |
||||
|
<el-table-column prop="state" :label="t('common_export.status')"> |
||||
|
<template #default="scope"> |
||||
|
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'"> |
||||
|
{{ getTagText(scope.row.state) }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="createTime" :label="t('common_export.createTime')"> |
||||
|
<template #default="scope"> |
||||
|
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column :label="t('common_export.operation')"> |
||||
|
<template #default="scope"> |
||||
|
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)" |
||||
|
:disabled="scope.row.state !== 2"> |
||||
|
{{ t('common_export.download') }} |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
<template #footer> |
||||
|
<div class="dialog-footer"> |
||||
|
<el-button text @click="exportListVisible = false">{{ t('common_export.close') }}</el-button> |
||||
|
</div> |
||||
|
</template> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
</template> |
</template> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.cash-flow-container { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.search-card { |
||||
|
margin-bottom: 10px; |
||||
|
background: #F3FAFE; // 浅蓝背景 |
||||
|
border: none; |
||||
|
|
||||
|
:deep(.el-card__body) { |
||||
|
padding: 15px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.search-bar { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 15px; |
||||
|
} |
||||
|
|
||||
|
.search-row { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
gap: 20px; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.search-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
.label { |
||||
|
font-size: 15px; // 参考 coinConsumeDetail 的 .text size="large" |
||||
|
color: #000; // 或 #606266 |
||||
|
white-space: nowrap; |
||||
|
margin-right: 8px; |
||||
|
min-width: 60px; |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.el-input, .el-select { |
||||
|
width: 200px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.search-btn-group { |
||||
|
margin-left: auto; // 靠右对齐 |
||||
|
display: flex; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
|
||||
|
.table-card { |
||||
|
background: #E7F4FD; |
||||
|
flex: 1; |
||||
|
border: none; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
|
||||
|
:deep(.el-card__body) { |
||||
|
padding: 20px; |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.pagination-container { |
||||
|
margin-top: 15px; |
||||
|
display: flex; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
|
||||
|
// 表格样式覆盖 (参考 coinConsumeDetail) |
||||
|
:deep(.el-table__header-wrapper), |
||||
|
:deep(.el-table__body-wrapper), |
||||
|
:deep(.el-table__cell), |
||||
|
:deep(.el-table__body td) { |
||||
|
background-color: #F3FAFE !important; // 如果想完全一致可以加这个,但有时候会影响阅读,暂保留头部颜色 |
||||
|
} |
||||
|
|
||||
|
:deep(.el-table__row:hover > .el-table__cell) { |
||||
|
background-color: #E5EBFE !important; |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue