Browse Source
Merge branch 'zhangyong/feature-20260113094820-现金重构' into milestone-20251215-多语言二期2
zhangyong/feature-20260113094820-现金重构
Merge branch 'zhangyong/feature-20260113094820-现金重构' into milestone-20251215-多语言二期2
zhangyong/feature-20260113094820-现金重构
7 changed files with 1116 additions and 106 deletions
-
17package-lock.json
-
28src/api/cash/financialAccount.js
-
1src/assets/SvgIcons/consume.svg
-
98src/views/consume/gold/coinConsumeDetail.vue
-
564src/views/moneyManage/financialAccount/cashFlow.vue
-
510src/views/moneyManage/financialAccount/performanceAttribution.vue
-
2src/views/workspace/index.vue
@ -0,0 +1,28 @@ |
|||
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 |
|||
}) |
|||
} |
|||
|
|||
// 查询业绩归属
|
|||
export const performanceSelect = (data) => { |
|||
return http({ |
|||
method: 'POST', |
|||
url: '/cashCollection/performanceSelect', |
|||
data |
|||
}) |
|||
} |
|||
@ -0,0 +1 @@ |
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1768291260270" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1605" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M463.99957 784.352211c0 26.509985 21.490445 48.00043 48.00043 48.00043s48.00043-21.490445 48.00043-48.00043c0-26.509985-21.490445-48.00043-48.00043-48.00043S463.99957 757.842226 463.99957 784.352211z" fill="#1296db" p-id="1606"></path><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#1296db" p-id="1607"></path><path d="M512 673.695256c-17.664722 0-32.00086-14.336138-32.00086-31.99914l0-54.112297c0-52.352533 39.999785-92.352318 75.32751-127.647359 25.887273-25.919957 52.67249-52.67249 52.67249-74.016718 0-53.343368-43.07206-96.735385-95.99914-96.735385-53.823303 0-95.99914 41.535923-95.99914 94.559333 0 17.664722-14.336138 31.99914-32.00086 31.99914s-32.00086-14.336138-32.00086-31.99914c0-87.423948 71.775299-158.559333 160.00086-158.559333s160.00086 72.095256 160.00086 160.735385c0 47.904099-36.32028 84.191695-71.424378 119.295794-27.839699 27.776052-56.575622 56.511974-56.575622 82.3356l0 54.112297C544.00086 659.328155 529.664722 673.695256 512 673.695256z" fill="#1296db" p-id="1608"></path></svg> |
|||
@ -1,103 +1,509 @@ |
|||
<template> |
|||
<div class="cash-flow-page"> |
|||
<div class="cash-flow-header"> |
|||
<span class="cash-flow-title">资金流水账</span> |
|||
</div> |
|||
<div class="cash-flow-filters"> |
|||
<el-form inline> |
|||
<el-form-item label="地区"> |
|||
<el-select |
|||
v-model="region" |
|||
clearable |
|||
placeholder="请选择地区" |
|||
style="width: 220px" |
|||
> |
|||
<el-option |
|||
v-for="item in regionOptions" |
|||
:key="item" |
|||
:label="item" |
|||
:value="item" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-form> |
|||
</div> |
|||
<el-table |
|||
:data="filteredData" |
|||
border |
|||
stripe |
|||
style="width: 100%" |
|||
class="cash-flow-table" |
|||
> |
|||
<el-table-column prop="date" label="日期" width="160" /> |
|||
<el-table-column prop="region" label="地区" width="160" /> |
|||
<el-table-column prop="type" label="类型" width="160" /> |
|||
<el-table-column prop="amount" label="金额" /> |
|||
<el-table-column prop="remark" label="备注" /> |
|||
</el-table> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, ref } from 'vue' |
|||
import { useRoute } from 'vue-router' |
|||
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 route = useRoute() |
|||
|
|||
const defaultRegions = [ |
|||
t('cash.markets.Singapore'), |
|||
t('cash.markets.Malaysia'), |
|||
t('cash.markets.HongKong'), |
|||
t('cash.markets.Thailand'), |
|||
t('cash.markets.VietnamHCM'), |
|||
t('cash.markets.Canada') |
|||
|
|||
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 regionOptions = ref([...defaultRegions]) |
|||
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) |
|||
|
|||
const initialRegion = typeof route.query.region === 'string' ? route.query.region : '' |
|||
if (initialRegion && !regionOptions.value.includes(initialRegion)) { |
|||
regionOptions.value.unshift(initialRegion) |
|||
// 转换树形结构(参考 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 region = ref(initialRegion) |
|||
// 查询列表 |
|||
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, |
|||
} |
|||
} |
|||
|
|||
const tableData = ref([ |
|||
{ |
|||
id: 1, |
|||
date: '2025-01-01 10:00:00', |
|||
region: region.value || defaultRegions[0], |
|||
type: '收款', |
|||
amount: 10000, |
|||
remark: '示例数据' |
|||
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 filteredData = computed(() => tableData.value) |
|||
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> |
|||
|
|||
<style scoped> |
|||
.cash-flow-page { |
|||
padding: 16px; |
|||
background-color: #ffffff; |
|||
<template> |
|||
<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> |
|||
|
|||
<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; |
|||
} |
|||
|
|||
.cash-flow-header { |
|||
margin-bottom: 16px; |
|||
.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; |
|||
} |
|||
} |
|||
|
|||
.cash-flow-title { |
|||
font-size: 18px; |
|||
font-weight: 600; |
|||
.pagination-container { |
|||
margin-top: 15px; |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
} |
|||
|
|||
.cash-flow-filters { |
|||
margin-bottom: 16px; |
|||
// 表格样式覆盖 (参考 coinConsumeDetail) |
|||
:deep(.el-table__header-wrapper), |
|||
:deep(.el-table__body-wrapper), |
|||
:deep(.el-table__cell), |
|||
:deep(.el-table__body td) { |
|||
background-color: #F3FAFE !important; // 如果想完全一致可以加这个,但有时候会影响阅读,暂保留头部颜色 |
|||
} |
|||
|
|||
.cash-flow-table { |
|||
margin-top: 8px; |
|||
: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,performanceSelect } 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, |
|||
performanceDTO:{ |
|||
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 performanceSelect(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, |
|||
performanceDTO:{ |
|||
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> |
|||
<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> |
|||
|
|||
<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