优化数据归属问题
This commit is contained in:
277
web/src/views/backend/order/adminWithdrawOrder/index.vue
Normal file
277
web/src/views/backend/order/adminWithdrawOrder/index.vue
Normal file
@@ -0,0 +1,277 @@
|
||||
<template>
|
||||
<div class="default-main ba-table-box">
|
||||
<el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
|
||||
<TableHeader
|
||||
:buttons="['refresh', 'comSearch', 'quickSearch', 'columnDisplay']"
|
||||
:quick-search-placeholder="t('Quick search placeholder', { fields: t('order.adminWithdrawOrder.quick Search Fields') })"
|
||||
/>
|
||||
<div class="withdraw-stats-cards">
|
||||
<el-card shadow="never" class="withdraw-stat-card">
|
||||
<div class="label">{{ t('order.adminWithdrawOrder.stat_total_count') }}</div>
|
||||
<div class="value">{{ stats.total_count }}</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="withdraw-stat-card">
|
||||
<div class="label">{{ t('order.adminWithdrawOrder.stat_pending_count') }}</div>
|
||||
<div class="value">{{ stats.pending_count }}</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="withdraw-stat-card">
|
||||
<div class="label">{{ t('order.adminWithdrawOrder.stat_pending_amount') }}</div>
|
||||
<div class="value">{{ stats.pending_amount }}</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="withdraw-stat-card">
|
||||
<div class="label">{{ t('order.adminWithdrawOrder.stat_approved_amount') }}</div>
|
||||
<div class="value">{{ stats.approved_amount }}</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="withdraw-filter-row">
|
||||
<el-radio-group v-model="statusFilterMode" size="small" @change="onStatusFilterChange">
|
||||
<el-radio-button label="all">{{ t('order.adminWithdrawOrder.filter_all') }}</el-radio-button>
|
||||
<el-radio-button label="pending">{{ t('order.adminWithdrawOrder.filter_pending') }}</el-radio-button>
|
||||
<el-radio-button label="approved">{{ t('order.adminWithdrawOrder.filter_approved') }}</el-radio-button>
|
||||
<el-radio-button label="rejected">{{ t('order.adminWithdrawOrder.filter_rejected') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-model="receiveTypeFilterMode" class="receive-type-filter" @change="onStatusFilterChange">
|
||||
<el-option :label="t('order.adminWithdrawOrder.filter_receive_type_all')" value="all" />
|
||||
<el-option :label="t('order.adminWithdrawOrder.receive_type_bank')" value="bank" />
|
||||
<el-option :label="t('order.adminWithdrawOrder.receive_type_ewallet')" value="ewallet" />
|
||||
<el-option :label="t('order.adminWithdrawOrder.receive_type_crypto')" value="crypto" />
|
||||
</el-select>
|
||||
</div>
|
||||
<Table ref="tableRef"></Table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, provide, reactive, ref, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { baTableApi } from '/@/api/common'
|
||||
import TableHeader from '/@/components/table/header/index.vue'
|
||||
import Table from '/@/components/table/index.vue'
|
||||
import baTableClass from '/@/utils/baTable'
|
||||
import createAxios from '/@/utils/axios'
|
||||
|
||||
defineOptions({
|
||||
name: 'order/adminWithdrawOrder',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const tableRef = useTemplateRef('tableRef')
|
||||
const statusFilterMode = ref<'all' | 'pending' | 'approved' | 'rejected'>('all')
|
||||
const receiveTypeFilterMode = ref<'all' | 'bank' | 'ewallet' | 'crypto'>('all')
|
||||
const stats = reactive({
|
||||
total_count: 0,
|
||||
pending_count: 0,
|
||||
approved_count: 0,
|
||||
rejected_count: 0,
|
||||
total_amount: '0.00',
|
||||
pending_amount: '0.00',
|
||||
approved_amount: '0.00',
|
||||
})
|
||||
|
||||
const onReview = async (row: anyObj, action: 'approve' | 'reject') => {
|
||||
const needReason = action === 'reject'
|
||||
const { value } = await ElMessageBox.prompt(
|
||||
needReason ? t('order.adminWithdrawOrder.reject_reason_required') : t('order.adminWithdrawOrder.review_remark_optional'),
|
||||
action === 'approve' ? t('order.adminWithdrawOrder.review_approve_title') : t('order.adminWithdrawOrder.review_reject_title'),
|
||||
{
|
||||
confirmButtonText: t('Confirm'),
|
||||
cancelButtonText: t('Cancel'),
|
||||
inputPlaceholder: needReason ? t('order.adminWithdrawOrder.reject_reason_required') : t('order.adminWithdrawOrder.review_remark_optional'),
|
||||
inputPattern: needReason ? /.+/ : undefined,
|
||||
inputErrorMessage: needReason ? t('order.adminWithdrawOrder.reject_reason_required') : '',
|
||||
})
|
||||
await createAxios(
|
||||
{
|
||||
url: `/admin/order.AdminWithdrawOrder/${action}`,
|
||||
method: 'post',
|
||||
data: { id: row.id, remark: value || '' },
|
||||
},
|
||||
{ showSuccessMessage: true }
|
||||
)
|
||||
await loadStats()
|
||||
baTable.getData()
|
||||
}
|
||||
|
||||
const optButtons: OptButton[] = [
|
||||
{
|
||||
render: 'tipButton',
|
||||
name: 'approve',
|
||||
title: 'order.adminWithdrawOrder.review_btn_approve',
|
||||
text: '',
|
||||
type: 'success',
|
||||
icon: 'el-icon-Check',
|
||||
display: (row: TableRow) => Number(row.status) === 0,
|
||||
click: (row: TableRow) => void onReview(row as anyObj, 'approve'),
|
||||
},
|
||||
{
|
||||
render: 'tipButton',
|
||||
name: 'reject',
|
||||
title: 'order.adminWithdrawOrder.review_btn_reject',
|
||||
text: '',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-Close',
|
||||
display: (row: TableRow) => Number(row.status) === 0,
|
||||
click: (row: TableRow) => void onReview(row as anyObj, 'reject'),
|
||||
},
|
||||
]
|
||||
|
||||
const baTable = new baTableClass(
|
||||
new baTableApi('/admin/order.AdminWithdrawOrder/'),
|
||||
{
|
||||
pk: 'id',
|
||||
column: [
|
||||
{ type: 'selection', align: 'center', operator: false },
|
||||
{ label: t('order.adminWithdrawOrder.id'), prop: 'id', align: 'center', width: 70, operator: 'RANGE', sortable: 'custom' },
|
||||
{ label: t('order.adminWithdrawOrder.order_no'), prop: 'order_no', align: 'center', minWidth: 170, operator: 'LIKE' },
|
||||
{ label: t('order.adminWithdrawOrder.admin_username'), prop: 'admin.username', align: 'center', minWidth: 120, operator: 'LIKE' },
|
||||
{ label: t('order.adminWithdrawOrder.channel_name'), prop: 'channel.name', align: 'center', minWidth: 120, operator: 'LIKE' },
|
||||
{ label: t('order.adminWithdrawOrder.amount'), prop: 'amount', align: 'center', minWidth: 100, operator: 'RANGE' },
|
||||
{ label: t('order.adminWithdrawOrder.actual_amount'), prop: 'actual_amount', align: 'center', minWidth: 100, operator: 'RANGE' },
|
||||
{
|
||||
label: t('order.adminWithdrawOrder.status'),
|
||||
prop: 'status',
|
||||
align: 'center',
|
||||
operator: 'eq',
|
||||
render: 'tag',
|
||||
custom: { 0: 'warning', 1: 'success', 2: 'danger' },
|
||||
replaceValue: {
|
||||
0: t('order.adminWithdrawOrder.status 0'),
|
||||
1: t('order.adminWithdrawOrder.status 1'),
|
||||
2: t('order.adminWithdrawOrder.status 2'),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('order.adminWithdrawOrder.receive_type'),
|
||||
prop: 'receive_type',
|
||||
align: 'center',
|
||||
minWidth: 110,
|
||||
operator: 'eq',
|
||||
render: 'tag',
|
||||
custom: { bank: 'primary', ewallet: 'success', crypto: 'warning' },
|
||||
replaceValue: {
|
||||
bank: t('order.adminWithdrawOrder.receive_type_bank'),
|
||||
ewallet: t('order.adminWithdrawOrder.receive_type_ewallet'),
|
||||
crypto: t('order.adminWithdrawOrder.receive_type_crypto'),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('order.adminWithdrawOrder.receive_account'),
|
||||
prop: 'receive_account',
|
||||
align: 'center',
|
||||
minWidth: 160,
|
||||
operator: 'LIKE',
|
||||
showOverflowTooltip: true,
|
||||
},
|
||||
{ label: t('order.adminWithdrawOrder.review_admin_username'), prop: 'reviewAdmin.username', align: 'center', minWidth: 120, operator: 'LIKE' },
|
||||
{ label: t('order.adminWithdrawOrder.remark'), prop: 'remark', align: 'center', minWidth: 180, operator: 'LIKE', showOverflowTooltip: true },
|
||||
{
|
||||
label: t('order.adminWithdrawOrder.create_time'),
|
||||
prop: 'create_time',
|
||||
align: 'center',
|
||||
render: 'datetime',
|
||||
operator: 'RANGE',
|
||||
comSearchRender: 'datetime',
|
||||
sortable: 'custom',
|
||||
width: 160,
|
||||
timeFormat: 'yyyy-mm-dd hh:MM:ss',
|
||||
},
|
||||
{ label: t('Operate'), align: 'center', width: 90, render: 'buttons', buttons: optButtons, operator: false, fixed: 'right' },
|
||||
],
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
const onStatusFilterChange = () => {
|
||||
baTable.getData()
|
||||
}
|
||||
|
||||
const loadStats = async () => {
|
||||
const res = await createAxios({ url: '/admin/order.AdminWithdrawOrder/stats', method: 'get' })
|
||||
if (res.code !== 1 || !res.data) {
|
||||
return
|
||||
}
|
||||
stats.total_count = Number(res.data.total_count ?? 0)
|
||||
stats.pending_count = Number(res.data.pending_count ?? 0)
|
||||
stats.approved_count = Number(res.data.approved_count ?? 0)
|
||||
stats.rejected_count = Number(res.data.rejected_count ?? 0)
|
||||
stats.total_amount = String(res.data.total_amount ?? '0.00')
|
||||
stats.pending_amount = String(res.data.pending_amount ?? '0.00')
|
||||
stats.approved_amount = String(res.data.approved_amount ?? '0.00')
|
||||
}
|
||||
|
||||
baTable.before.getData = () => {
|
||||
const filter = baTable.table.filter || {}
|
||||
const searchRaw = filter.search
|
||||
const search = Array.isArray(searchRaw)
|
||||
? searchRaw.filter((item: any) => item && item.field !== 'status' && item.field !== 'receive_type')
|
||||
: []
|
||||
if (statusFilterMode.value === 'pending') {
|
||||
search.push({ field: 'status', operator: 'eq', val: 0 })
|
||||
} else if (statusFilterMode.value === 'approved') {
|
||||
search.push({ field: 'status', operator: 'eq', val: 1 })
|
||||
} else if (statusFilterMode.value === 'rejected') {
|
||||
search.push({ field: 'status', operator: 'eq', val: 2 })
|
||||
}
|
||||
if (receiveTypeFilterMode.value !== 'all') {
|
||||
search.push({ field: 'receive_type', operator: 'eq', val: receiveTypeFilterMode.value })
|
||||
}
|
||||
filter.search = search
|
||||
baTable.table.filter = filter
|
||||
}
|
||||
|
||||
baTable.after.getData = () => {
|
||||
void loadStats()
|
||||
}
|
||||
|
||||
provide('baTable', baTable)
|
||||
|
||||
onMounted(() => {
|
||||
baTable.table.ref = tableRef.value
|
||||
baTable.mount()
|
||||
baTable.getData()?.then(() => {
|
||||
baTable.initSort()
|
||||
baTable.dragSort()
|
||||
})
|
||||
void loadStats()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.withdraw-stats-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.withdraw-stat-card .label {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.withdraw-stat-card .value {
|
||||
margin-top: 6px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.withdraw-filter-row {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.receive-type-filter {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.withdraw-stats-cards {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user