Files
one-pipe-system/src/views/commission-management/agent-fund-overview/index.vue
luo f0a2b84e53
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m51s
feat: 产品迭代7月份
2026-07-27 16:30:29 +08:00

1400 lines
43 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="agent-commission-container">
<ArtTableFullScreen>
<div class="agent-commission-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
:gutter="16"
:el-col-span="6"
:show-expand="false"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
<ElCard shadow="never" class="art-table-card">
<!-- 表格头部 -->
<ArtTableHeader
:columnList="columnOptions"
v-model:columns="columnChecks"
@refresh="handleRefresh"
/>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="shop_id"
:loading="loading"
:data="summaryList"
:currentPage="pagination.page"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
:actions="getActions"
:actionsWidth="180"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
</ElCard>
</div>
</ArtTableFullScreen>
<!-- 详情抽屉 -->
<ElDrawer
v-model="detailDrawerVisible"
:title="`${currentShop?.shop_name || ''} - 佣金详情`"
size="60%"
destroy-on-close
>
<ElTabs v-model="activeTab" class="detail-tabs">
<!-- 佣金明细 Tab -->
<ElTabPane label="佣金明细" name="commission">
<ArtTable
ref="commissionTableRef"
row-key="id"
:loading="commissionLoading"
:data="commissionRecords"
:currentPage="commissionPagination.page"
:pageSize="commissionPagination.pageSize"
:total="commissionPagination.total"
:marginTop="0"
:height="450"
@size-change="handleCommissionSizeChange"
@current-change="handleCommissionCurrentChange"
>
<template #default>
<ElTableColumn label="佣金金额" prop="amount" width="120">
<template #default="scope">
<span style="font-weight: 500; color: var(--el-color-success)">
{{ formatMoney(scope.row.amount) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="入账后余额" prop="balance_after" width="120">
<template #default="scope">
{{ formatMoney(scope.row.balance_after) }}
</template>
</ElTableColumn>
<ElTableColumn label="佣金来源" prop="commission_source" width="120">
<template #default="scope">
<ElTag
:type="
CommissionSourceMap[
scope.row.commission_source as keyof typeof CommissionSourceMap
]?.type || 'info'
"
>
{{
CommissionSourceMap[
scope.row.commission_source as keyof typeof CommissionSourceMap
]?.label || scope.row.commission_source
}}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="状态" prop="status_name" width="100">
<template #default="scope">
<ElTag
:type="
CommissionStatusMap[scope.row.status as keyof typeof CommissionStatusMap]
?.type || 'info'
"
>
{{
scope.row.status_name ||
CommissionStatusMap[scope.row.status as keyof typeof CommissionStatusMap]
?.label ||
'-'
}}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="订单号" prop="order_no" min-width="180" show-overflow-tooltip />
<ElTableColumn
label="卖家店铺"
prop="seller_shop_name"
min-width="150"
show-overflow-tooltip
/>
<ElTableColumn label="ICCID" prop="iccid" min-width="150" show-overflow-tooltip />
<ElTableColumn
label="设备号"
prop="virtual_no"
min-width="150"
show-overflow-tooltip
/>
<ElTableColumn label="入账时间" prop="created_at" width="180">
<template #default="scope">
{{ formatDateTime(scope.row.created_at) }}
</template>
</ElTableColumn>
<ElTableColumn label="操作" width="150" fixed="right">
<template #default="scope">
<div v-if="scope.row.status === 99" style="display: flex; gap: 8px">
<ArtButtonTable
text="入账"
iconColor="#67C23A"
@click="handleResolveCommission(scope.row, 'release')"
/>
<ArtButtonTable
text="作废"
iconColor="#F56C6C"
@click="handleResolveCommission(scope.row, 'invalidate')"
/>
</div>
<span v-else>-</span>
</template>
</ElTableColumn>
</template>
</ArtTable>
</ElTabPane>
<!-- 提现记录 Tab -->
<ElTabPane label="提现记录" name="withdrawal">
<ArtTable
ref="withdrawalTableRef"
row-key="id"
:loading="withdrawalLoading"
:data="withdrawalRecords"
:currentPage="withdrawalPagination.page"
:pageSize="withdrawalPagination.pageSize"
:total="withdrawalPagination.total"
:marginTop="10"
:height="500"
@size-change="handleWithdrawalSizeChange"
@current-change="handleWithdrawalCurrentChange"
>
<template #default>
<ElTableColumn
label="提现单号"
prop="withdrawal_no"
min-width="180"
show-overflow-tooltip
/>
<ElTableColumn label="提现金额" prop="amount" width="120">
<template #default="scope">
<span style="font-weight: 500; color: var(--el-color-danger)">
{{ formatMoney(scope.row.amount) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="实际到账" prop="actual_amount" width="120">
<template #default="scope">
{{ formatMoney(scope.row.actual_amount) }}
</template>
</ElTableColumn>
<ElTableColumn label="手续费" prop="fee" width="100">
<template #default="scope">
{{ formatMoney(scope.row.fee) }}
</template>
</ElTableColumn>
<ElTableColumn label="提现方式" prop="withdrawal_method" width="100">
<template #default="scope">
{{
WithdrawalMethodMap[
scope.row.withdrawal_method as keyof typeof WithdrawalMethodMap
]?.label || scope.row.withdrawal_method
}}
</template>
</ElTableColumn>
<ElTableColumn label="状态" prop="status" width="100">
<template #default="scope">
<ElTag
:type="
WithdrawalStatusMap[scope.row.status as keyof typeof WithdrawalStatusMap]
?.type || 'info'
"
>
{{
WithdrawalStatusMap[scope.row.status as keyof typeof WithdrawalStatusMap]
?.label || scope.row.status
}}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="申请时间" prop="created_at" width="180">
<template #default="scope">
{{ formatDateTime(scope.row.created_at) }}
</template>
</ElTableColumn>
<ElTableColumn label="处理时间" prop="processed_at" width="180">
<template #default="scope">
{{ formatDateTime(scope.row.processed_at) }}
</template>
</ElTableColumn>
</template>
</ArtTable>
</ElTabPane>
<!-- 预充值钱包流水 Tab -->
<ElTabPane label="预充值钱包流水" name="mainWallet">
<ElForm :model="mainWalletSearchForm" class="main-wallet-filter" label-position="top">
<div class="main-wallet-filter__grid">
<ElFormItem label="交易类型">
<ElSelect
v-model="mainWalletSearchForm.transaction_type"
clearable
placeholder="全部类型"
>
<ElOption label="充值入账" :value="MainWalletTransactionType.RECHARGE" />
<ElOption label="套餐扣款" :value="MainWalletTransactionType.DEDUCT" />
<ElOption label="退款" :value="MainWalletTransactionType.REFUND" />
</ElSelect>
</ElFormItem>
<ElFormItem label="交易日期">
<ElDatePicker
v-model="mainWalletSearchForm.date_range"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
unlink-panels
/>
</ElFormItem>
<ElFormItem label="资产标识">
<ElInput
v-model="mainWalletSearchForm.asset_identifier"
clearable
maxlength="100"
placeholder="请输入ICCID或设备虚拟号"
@keyup.enter="handleMainWalletSearch"
/>
</ElFormItem>
<ElFormItem class="main-wallet-filter__actions">
<ElButton type="primary" @click="handleMainWalletSearch">搜索</ElButton>
<ElButton @click="handleMainWalletReset">重置</ElButton>
</ElFormItem>
</div>
</ElForm>
<ArtTable
ref="mainWalletTableRef"
row-key="id"
:loading="mainWalletLoading"
:data="mainWalletRecords"
:currentPage="mainWalletPagination.page"
:pageSize="mainWalletPagination.pageSize"
:total="mainWalletPagination.total"
:marginTop="10"
:height="500"
@size-change="handleMainWalletSizeChange"
@current-change="handleMainWalletCurrentChange"
>
<template #default>
<ElTableColumn label="交易类型" prop="transaction_type" width="120">
<template #default="scope">
<ElTag
:type="
scope.row.transaction_type === 'recharge'
? 'success'
: scope.row.transaction_type === 'deduct'
? 'warning'
: 'info'
"
>
{{
scope.row.transaction_type === 'recharge'
? '充值入账'
: scope.row.transaction_type === 'deduct'
? '套餐扣款'
: scope.row.transaction_type === 'refund'
? '退款'
: scope.row.transaction_type
}}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="交易金额" prop="amount" width="120">
<template #default="scope">
<span
:style="{
fontWeight: 500,
color:
scope.row.amount > 0 ? 'var(--el-color-success)' : 'var(--el-color-danger)'
}"
>
{{ scope.row.amount > 0 ? '+' : '' }}{{ formatMoney(scope.row.amount) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="交易前余额" prop="balance_before" width="130">
<template #default="scope">
{{ formatMoney(scope.row.balance_before) }}
</template>
</ElTableColumn>
<ElTableColumn label="交易后余额" prop="balance_after" width="130">
<template #default="scope">
{{ formatMoney(scope.row.balance_after) }}
</template>
</ElTableColumn>
<ElTableColumn
label="资产标识"
prop="asset_identifier"
min-width="160"
show-overflow-tooltip
>
<template #default="scope">
<span
v-if="scope.row.asset_identifier"
class="asset-identifier-link"
@click.stop="handleAssetIdentifierClick(scope.row.asset_identifier)"
>
{{ scope.row.asset_identifier }}
</span>
<span v-else>-</span>
</template>
</ElTableColumn>
<ElTableColumn label="备注" prop="remark" min-width="200" show-overflow-tooltip />
<ElTableColumn label="交易时间" prop="created_at" width="180">
<template #default="scope">
{{ formatDateTime(scope.row.created_at) }}
</template>
</ElTableColumn>
</template>
</ArtTable>
</ElTabPane>
</ElTabs>
</ElDrawer>
<!-- 佣金修正对话框 -->
<ElDialog
v-model="resolveDialogVisible"
:title="resolveAction === 'release' ? '佣金入账' : '佣金作废'"
width="500px"
>
<ElAlert
:title="
resolveAction === 'release' ? '确认将该笔待审佣金记录入账' : '确认将该笔待审佣金记录作废'
"
:type="resolveAction === 'release' ? 'success' : 'warning'"
style="margin-bottom: 16px"
:closable="false"
/>
<ElForm ref="resolveFormRef" :model="resolveForm" :rules="resolveRules" label-width="100px">
<ElFormItem v-if="resolveAction === 'release'" label="入账金额" prop="amount">
<ElInputNumber
v-model="resolveForm.amount"
:min="0"
:step="100"
:precision="0"
controls-position="right"
style="width: 100%"
placeholder="请输入入账金额(分)"
/>
<div style="margin-top: 4px; font-size: 12px; color: var(--el-text-color-secondary)">
金额单位为分例如100 = 10000
</div>
</ElFormItem>
<ElFormItem label="备注" prop="remark">
<ElInput
v-model="resolveForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注可选最多500字符"
maxlength="500"
show-word-limit
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="resolveDialogVisible = false">取消</ElButton>
<ElButton
:type="resolveAction === 'release' ? 'success' : 'warning'"
@click="handleResolveSubmit"
:loading="resolveSubmitLoading"
>
确认
</ElButton>
</div>
</template>
</ElDialog>
<!-- 店铺实际信用额度调整弹框 -->
<ElDialog
v-model="creditDialogVisible"
:title="`调整实际信用额度 - ${currentCreditShop?.shop_name || ''}`"
width="520px"
@closed="resetCreditDialog"
>
<ElDescriptions :column="1" border class="credit-preview">
<ElDescriptionsItem label="店铺名称">
{{ currentCreditShop?.shop_name || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="当前版本">
{{ currentCreditShop?.version ?? '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="修改前">
{{
formatCreditPreview(currentCreditShop?.credit_enabled, currentCreditShop?.credit_limit)
}}
</ElDescriptionsItem>
<ElDescriptionsItem label="修改后">
{{ formatCreditPreview(creditForm.credit_enabled, creditFormCreditLimitFen) }}
</ElDescriptionsItem>
</ElDescriptions>
<ElAlert
title="实际可用金额、欠款金额和欠款状态以后端刷新后的资金概况为准。"
type="info"
:closable="false"
show-icon
class="credit-dialog-alert"
/>
<ElForm ref="creditFormRef" :model="creditForm" :rules="creditRules" label-width="110px">
<ElFormItem label="启用信用">
<ElSwitch v-model="creditForm.credit_enabled" @change="handleCreditEnabledChange" />
</ElFormItem>
<ElFormItem label="实际信用额度" prop="credit_limit_yuan">
<ElInputNumber
v-model="creditForm.credit_limit_yuan"
:disabled="!creditForm.credit_enabled"
:min="0"
:precision="2"
:step="100"
controls-position="right"
style="width: 100%"
placeholder="请输入实际信用额度"
/>
<div class="credit-dialog-tip">单位关闭信用时额度将自动归零</div>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="creditDialogVisible = false">取消</ElButton>
<ElButton type="primary" :loading="creditSubmitting" @click="handleCreditSubmit">
确认调整
</ElButton>
</div>
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { h, watch, onBeforeUnmount, ref, reactive, onMounted, computed, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { CommissionService, ShopService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { MainWalletTransactionType } from '@/types/api/commission'
import type {
ShopFundSummaryItem,
ShopCommissionRecordItem,
WithdrawalRequestItem,
CommissionResolveAction,
MainWalletTransactionItem,
MainWalletTransactionQueryParams
} from '@/types/api/commission'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { fenToYuan, formatDateTime, formatMoney, yuanToFen } from '@/utils/business/format'
import {
CommissionStatusMap,
WithdrawalStatusMap,
WithdrawalMethodMap,
CommissionSourceMap
} from '@/config/constants/commission'
import { RoutesAlias } from '@/router/routesAlias'
import { normalizeApiError } from '@/utils/business/apiError'
defineOptions({ name: 'AgentCommission' })
const { hasAuth } = useAuth()
const route = useRoute()
const router = useRouter()
// 主表格状态
const loading = ref(false)
const tableRef = ref()
const summaryList = ref<ShopFundSummaryItem[]>([])
// 搜索表单
const searchForm = reactive({
shop_name: '',
username: ''
})
// 主表格分页
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 详情抽屉状态
const detailDrawerVisible = ref(false)
const activeTab = ref<'commission' | 'withdrawal' | 'mainWallet'>('commission')
const currentShop = ref<ShopFundSummaryItem | null>(null)
// 佣金明细状态
const commissionLoading = ref(false)
const commissionTableRef = ref()
const commissionRecords = ref<ShopCommissionRecordItem[]>([])
const commissionPagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 提现记录状态
const withdrawalLoading = ref(false)
const withdrawalTableRef = ref()
const withdrawalRecords = ref<WithdrawalRequestItem[]>([])
const withdrawalPagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 佣金修正对话框状态
const resolveDialogVisible = ref(false)
const resolveFormRef = ref<FormInstance>()
const resolveSubmitLoading = ref(false)
const resolveAction = ref<CommissionResolveAction>('release')
const currentCommissionId = ref<number>(0)
const resolveForm = reactive({
amount: undefined as number | undefined,
remark: ''
})
// 实际信用额度调整弹框状态
const creditDialogVisible = ref(false)
const creditFormRef = ref<FormInstance>()
const creditSubmitting = ref(false)
const currentCreditShop = ref<ShopFundSummaryItem | null>(null)
const creditForm = reactive({
credit_enabled: false,
credit_limit_yuan: 0
})
const creditFormCreditLimitFen = computed(() =>
creditForm.credit_enabled ? yuanToFen(creditForm.credit_limit_yuan) || 0 : 0
)
// 佣金修正表单验证规则
const resolveRules = computed<FormRules>(() => ({
amount:
resolveAction.value === 'release'
? [
{ required: true, message: '请输入入账金额', trigger: 'blur' },
{
type: 'number',
min: 1,
message: '入账金额必须大于0',
trigger: 'blur'
}
]
: [],
remark: [{ max: 500, message: '备注最多500字符', trigger: 'blur' }]
}))
const creditRules = computed<FormRules>(() => ({
credit_limit_yuan: [
{
validator: (
_rule: unknown,
value: number | undefined,
callback: (error?: Error) => void
) => {
if (!creditForm.credit_enabled) {
callback()
return
}
if (value === undefined || value === null || Number.isNaN(value)) {
callback(new Error('请输入实际信用额度'))
return
}
if (value <= 0) {
callback(new Error('实际信用额度必须大于0'))
return
}
callback()
},
trigger: 'blur'
}
]
}))
// 预充值钱包流水状态
const mainWalletLoading = ref(false)
const mainWalletTableRef = ref()
const mainWalletRecords = ref<MainWalletTransactionItem[]>([])
const mainWalletPagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
const mainWalletSearchForm = reactive<{
transaction_type: MainWalletTransactionType | undefined
date_range: [string, string] | [] | null
asset_identifier: string
}>({
transaction_type: undefined,
date_range: [],
asset_identifier: ''
})
// 列配置
const columnOptions = [
{ label: '店铺编码', prop: 'shop_code' },
{ label: '店铺名称', prop: 'shop_name' },
{ label: '用户名', prop: 'username' },
{ label: '手机号', prop: 'phone' },
{ label: '账面余额', prop: 'main_balance' },
{ label: '冻结金额', prop: 'main_frozen_balance' },
{ label: '现金可用', prop: 'cash_available_balance' },
{ label: '实际信用额度', prop: 'credit_limit' },
{ label: '总可用金额', prop: 'available_balance' },
{ label: '欠款金额', prop: 'debt_amount' },
{ label: '钱包版本', prop: 'version' },
{ label: '余额预警', prop: 'low_balance_warning' },
{ label: '总佣金', prop: 'total_commission' },
{ label: '可提现', prop: 'available_commission' },
{ label: '冻结中', prop: 'frozen_commission' },
{ label: '提现中', prop: 'withdrawing_commission' },
{ label: '已提现', prop: 'withdrawn_commission' },
{ label: '未提现', prop: 'unwithdraw_commission' }
]
// 搜索表单配置
const searchFormItems: SearchFormItem[] = [
{
label: '店铺名称',
prop: 'shop_name',
type: 'input',
config: {
clearable: true,
placeholder: '请输入店铺名称'
}
},
{
label: '用户名',
prop: 'username',
type: 'input',
config: {
clearable: true,
placeholder: '请输入主账号用户名'
}
}
]
// 处理名称点击
const handleNameClick = (row: ShopFundSummaryItem) => {
if (hasAuth('agent_commission:detail')) {
showDetail(row)
} else {
ElMessage.warning('您没有查看详情的权限')
}
}
const handleAssetIdentifierClick = (assetIdentifier: string) => {
router.push({
path: RoutesAlias.AssetInformation,
query: {
iccid: assetIdentifier
}
})
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'shop_code',
label: '店铺编码',
minWidth: 140,
showOverflowTooltip: true
},
{
prop: 'shop_name',
label: '店铺名称',
minWidth: 160,
showOverflowTooltip: true,
formatter: (row: ShopFundSummaryItem) => {
return h(
'span',
{
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
onClick: (e: MouseEvent) => {
e.stopPropagation()
handleNameClick(row)
}
},
row.shop_name
)
}
},
{
prop: 'username',
label: '用户名',
minWidth: 120
},
{
prop: 'phone',
label: '手机号',
width: 120
},
{
prop: 'main_balance',
label: '账面余额',
minWidth: 130,
formatter: (row: ShopFundSummaryItem) => {
return h(
'span',
{ style: 'color: var(--el-color-primary); font-weight: 500' },
formatMoney(row.main_balance)
)
}
},
{
prop: 'main_frozen_balance',
label: '冻结金额',
minWidth: 120,
formatter: (row: ShopFundSummaryItem) => formatMoney(row.main_frozen_balance)
},
{
prop: 'cash_available_balance',
label: '现金可用',
minWidth: 120,
formatter: (row: ShopFundSummaryItem) => formatMoney(row.cash_available_balance)
},
{
prop: 'credit_limit',
label: '实际信用额度',
minWidth: 150,
formatter: (row: ShopFundSummaryItem) => {
if (!row.credit_enabled) {
return h('span', { style: 'color: var(--el-text-color-secondary)' }, '未启用 / ¥0.00')
}
return h(
'span',
{ style: 'color: var(--el-color-warning); font-weight: 500' },
formatMoney(row.credit_limit)
)
}
},
{
prop: 'available_balance',
label: '总可用金额',
minWidth: 130,
formatter: (row: ShopFundSummaryItem) => {
return h(
'span',
{ style: 'color: var(--el-color-success); font-weight: 500' },
formatMoney(row.available_balance)
)
}
},
{
prop: 'debt_amount',
label: '欠款金额',
minWidth: 130,
formatter: (row: ShopFundSummaryItem) => {
const amountText = formatMoney(row.debt_amount)
if (!row.is_in_debt) return amountText
return h('span', { style: 'color: var(--el-color-danger); font-weight: 500' }, amountText)
}
},
{
prop: 'version',
label: '钱包版本',
minWidth: 90,
formatter: (row: ShopFundSummaryItem) => row.version ?? '-'
},
{
prop: 'low_balance_warning',
label: '余额预警',
minWidth: 170,
formatter: (row: ShopFundSummaryItem) => {
if (!row.low_balance_warning) return '-'
return h(ElTag, { type: 'danger' }, () => '现金余额不足100元')
}
},
{
prop: 'total_commission',
label: '总佣金',
minWidth: 120,
formatter: (row: ShopFundSummaryItem) => formatMoney(row.total_commission)
},
{
prop: 'available_commission',
label: '可提现',
minWidth: 120,
formatter: (row: ShopFundSummaryItem) => {
return h(
'span',
{ style: 'color: var(--el-color-success); font-weight: 500' },
formatMoney(row.available_commission)
)
}
},
{
prop: 'frozen_commission',
label: '冻结中',
minWidth: 110,
formatter: (row: ShopFundSummaryItem) => formatMoney(row.frozen_commission)
},
{
prop: 'withdrawing_commission',
label: '提现中',
minWidth: 110,
formatter: (row: ShopFundSummaryItem) => {
return h(
'span',
{ style: 'color: var(--el-color-warning)' },
formatMoney(row.withdrawing_commission)
)
}
},
{
prop: 'withdrawn_commission',
label: '已提现',
minWidth: 110,
formatter: (row: ShopFundSummaryItem) => formatMoney(row.withdrawn_commission)
},
{
prop: 'unwithdraw_commission',
label: '未提现',
minWidth: 110,
formatter: (row: ShopFundSummaryItem) => formatMoney(row.unwithdraw_commission)
}
])
onMounted(() => {
getTableData()
})
// 获取代理商资金汇总列表
const getTableData = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
page_size: pagination.pageSize,
shop_name: searchForm.shop_name || undefined,
username: searchForm.username || undefined
}
const res = await CommissionService.getShopFundSummary(params)
if (res.code === 0) {
summaryList.value = res.data.items || []
pagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
console.log('获取数据失败')
} finally {
loading.value = false
}
}
// 搜索
const handleSearch = () => {
pagination.page = 1
getTableData()
}
// 重置
const handleReset = () => {
searchForm.shop_name = ''
searchForm.username = ''
pagination.page = 1
getTableData()
}
// 刷新
const handleRefresh = () => {
getTableData()
}
// 分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getTableData()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.page = newCurrentPage
getTableData()
}
// 显示详情抽屉
const showDetail = (row: ShopFundSummaryItem) => {
currentShop.value = row
detailDrawerVisible.value = true
activeTab.value = 'commission'
// 重置分页
commissionPagination.page = 1
withdrawalPagination.page = 1
mainWalletPagination.page = 1
mainWalletPagination.total = 0
mainWalletRecords.value = []
resetMainWalletSearchForm()
// 加载佣金明细
loadCommissionRecords()
}
// 获取操作按钮
const getActions = (row: ShopFundSummaryItem) => {
const actions: any[] = []
if (hasAuth('agent_commission:detail')) {
actions.push({
label: '查看详情',
handler: () => showDetail(row),
type: 'primary'
})
}
if (hasAuth('shop:credit-limit:manage')) {
actions.push({
label: '调整额度',
handler: () => showCreditDialog(row),
type: 'primary'
})
}
return actions
}
const formatCreditPreview = (enabled?: boolean, creditLimit?: number) => {
return enabled ? `启用 / ${formatMoney(creditLimit || 0)}` : '关闭 / ¥0.00'
}
const showCreditDialog = (row: ShopFundSummaryItem) => {
currentCreditShop.value = row
creditForm.credit_enabled = Boolean(row.credit_enabled)
creditForm.credit_limit_yuan = row.credit_enabled ? fenToYuan(row.credit_limit) : 0
creditDialogVisible.value = true
nextTick(() => {
creditFormRef.value?.clearValidate()
})
}
const handleCreditEnabledChange = (enabled: boolean | string | number) => {
if (!enabled) {
creditForm.credit_limit_yuan = 0
creditFormRef.value?.clearValidate('credit_limit_yuan')
}
}
const resetCreditDialog = () => {
creditFormRef.value?.resetFields()
currentCreditShop.value = null
creditForm.credit_enabled = false
creditForm.credit_limit_yuan = 0
}
const isCreditConflictMessage = (message?: string) => {
if (!message) return false
return /版本|冲突|并发|过期|conflict/i.test(message)
}
const isCreditConflictResponse = (response: any) => {
return response?.code === 409 || isCreditConflictMessage(response?.msg)
}
const isCreditConflictError = (error: any) => {
return (
error?.response?.status === 409 ||
error?.response?.data?.code === 409 ||
isCreditConflictMessage(error?.response?.data?.msg || error?.message)
)
}
const handleCreditConflict = async () => {
const shopId = currentCreditShop.value?.shop_id
await getTableData()
if (shopId) {
const latest = summaryList.value.find((item) => item.shop_id === shopId)
if (latest) {
// 仅刷新后端版本和当前余额,保留用户刚填写的表单值,方便确认后重试。
currentCreditShop.value = latest
}
}
ElMessage.warning('资金概况已被其他操作更新,已刷新最新版本;请确认后重新提交')
}
const handleCreditSubmit = async () => {
if (!creditFormRef.value || !currentCreditShop.value) return
await creditFormRef.value.validate(async (valid) => {
if (!valid || !currentCreditShop.value) return
creditSubmitting.value = true
try {
const res = await ShopService.updateShopCreditLimit(currentCreditShop.value.shop_id, {
credit_enabled: creditForm.credit_enabled,
credit_limit: creditFormCreditLimitFen.value,
version: currentCreditShop.value.version
})
if (res.code === 0) {
ElMessage.success('实际信用额度调整成功')
creditDialogVisible.value = false
await getTableData()
return
}
if (isCreditConflictResponse(res)) {
await handleCreditConflict()
return
}
ElMessage.error(res.msg || '实际信用额度调整失败')
} catch (error: any) {
if (isCreditConflictError(error)) {
await handleCreditConflict()
} else {
console.error('实际信用额度调整失败:', error)
ElMessage.error(normalizeApiError(error).message)
}
} finally {
creditSubmitting.value = false
}
})
}
// 监听tab切换
watch(activeTab, (newTab) => {
if (newTab === 'commission') {
loadCommissionRecords()
} else if (newTab === 'withdrawal') {
loadWithdrawalRecords()
} else if (newTab === 'mainWallet') {
loadMainWalletRecords()
}
})
// 监听路由变化,关闭抽屉
watch(
() => route.path,
() => {
if (detailDrawerVisible.value) {
detailDrawerVisible.value = false
}
}
)
// 组件卸载前关闭抽屉
onBeforeUnmount(() => {
if (detailDrawerVisible.value) {
detailDrawerVisible.value = false
}
})
// 加载佣金明细
const loadCommissionRecords = async () => {
if (!currentShop.value) return
commissionLoading.value = true
try {
const params = {
page: commissionPagination.page,
page_size: commissionPagination.pageSize
}
const res = await CommissionService.getShopCommissionRecords(
currentShop.value.shop_id,
params
)
if (res.code === 0) {
commissionRecords.value = res.data.items || []
commissionPagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
console.log('获取佣金明细失败')
} finally {
commissionLoading.value = false
}
}
// 佣金明细分页
const handleCommissionSizeChange = (newPageSize: number) => {
commissionPagination.pageSize = newPageSize
loadCommissionRecords()
}
const handleCommissionCurrentChange = (newCurrentPage: number) => {
commissionPagination.page = newCurrentPage
loadCommissionRecords()
}
// 加载提现记录
const loadWithdrawalRecords = async () => {
if (!currentShop.value) return
withdrawalLoading.value = true
try {
const params = {
page: withdrawalPagination.page,
page_size: withdrawalPagination.pageSize
}
const res = await CommissionService.getShopWithdrawalRequests(
currentShop.value.shop_id,
params
)
if (res.code === 0) {
withdrawalRecords.value = res.data.items || []
withdrawalPagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
console.log('获取提现记录失败')
} finally {
withdrawalLoading.value = false
}
}
// 提现记录分页
const handleWithdrawalSizeChange = (newPageSize: number) => {
withdrawalPagination.pageSize = newPageSize
loadWithdrawalRecords()
}
const handleWithdrawalCurrentChange = (newCurrentPage: number) => {
withdrawalPagination.page = newCurrentPage
loadWithdrawalRecords()
}
// 加载预充值钱包流水
const loadMainWalletRecords = async () => {
if (!currentShop.value) return
mainWalletLoading.value = true
try {
const [startDate, endDate] = mainWalletSearchForm.date_range || []
const params: MainWalletTransactionQueryParams = {
page: mainWalletPagination.page,
page_size: mainWalletPagination.pageSize,
transaction_type: mainWalletSearchForm.transaction_type,
start_date: startDate,
end_date: endDate,
asset_identifier: mainWalletSearchForm.asset_identifier.trim() || undefined
}
const res = await CommissionService.getMainWalletTransactions(
currentShop.value.shop_id,
params
)
if (res.code === 0) {
mainWalletRecords.value = res.data.items || []
mainWalletPagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
console.log('获取预充值钱包流水失败')
} finally {
mainWalletLoading.value = false
}
}
// 预充值钱包流水分页
const handleMainWalletSizeChange = (newPageSize: number) => {
mainWalletPagination.pageSize = newPageSize
loadMainWalletRecords()
}
const handleMainWalletCurrentChange = (newCurrentPage: number) => {
mainWalletPagination.page = newCurrentPage
loadMainWalletRecords()
}
const resetMainWalletSearchForm = () => {
mainWalletSearchForm.transaction_type = undefined
mainWalletSearchForm.date_range = []
mainWalletSearchForm.asset_identifier = ''
}
const handleMainWalletSearch = () => {
mainWalletPagination.page = 1
loadMainWalletRecords()
}
const handleMainWalletReset = () => {
resetMainWalletSearchForm()
mainWalletPagination.page = 1
loadMainWalletRecords()
}
// 处理佣金修正
const handleResolveCommission = (
row: ShopCommissionRecordItem,
action: CommissionResolveAction
) => {
currentCommissionId.value = row.id
resolveAction.value = action
resolveForm.amount = undefined
resolveForm.remark = ''
resolveDialogVisible.value = true
}
// 提交佣金修正
const handleResolveSubmit = async () => {
if (!resolveFormRef.value) return
await resolveFormRef.value.validate(async (valid) => {
if (valid) {
const actionText = resolveAction.value === 'release' ? '入账' : '作废'
const confirmText =
resolveAction.value === 'release'
? `确认将该笔佣金记录入账 ${formatMoney(resolveForm.amount || 0)} 吗?`
: '确认将该笔佣金记录作废吗?'
try {
await ElMessageBox.confirm(confirmText, '确认操作', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
})
resolveSubmitLoading.value = true
try {
const params: any = {
action: resolveAction.value
}
if (resolveAction.value === 'release' && resolveForm.amount) {
params.amount = resolveForm.amount
}
if (resolveForm.remark) {
params.remark = resolveForm.remark
}
await CommissionService.resolveCommissionRecord(currentCommissionId.value, params)
ElMessage.success(`${actionText}成功`)
resolveDialogVisible.value = false
resolveFormRef.value?.resetFields()
// 刷新佣金明细列表
await loadCommissionRecords()
} catch (error: any) {
console.error(error)
console.log(error?.message || `${actionText}失败`)
} finally {
resolveSubmitLoading.value = false
}
} catch {
// 用户取消
}
}
})
}
</script>
<style lang="scss" scoped>
.agent-commission-container {
height: 100%;
}
.agent-commission-page {
:deep(.art-custom-card) {
margin-bottom: 16px;
}
}
.detail-tabs {
:deep(.el-tabs__content) {
padding: 0;
}
}
.main-wallet-filter {
padding: 16px 16px 4px;
margin-bottom: 12px;
background: var(--el-fill-color-lighter);
border: 1px solid var(--el-border-color-lighter);
border-radius: 8px;
:deep(.el-form-item) {
margin-bottom: 12px;
}
:deep(.el-select),
:deep(.el-date-editor),
:deep(.el-input) {
width: 100%;
}
}
.main-wallet-filter__grid {
display: grid;
grid-template-columns: minmax(140px, 0.8fr) minmax(260px, 1.4fr) minmax(220px, 1fr) auto;
gap: 12px 16px;
align-items: end;
}
.main-wallet-filter__actions {
:deep(.el-form-item__content) {
display: flex;
flex-wrap: nowrap;
gap: 8px;
justify-content: flex-end;
}
}
.credit-preview {
margin-bottom: 16px;
}
.credit-dialog-alert {
margin-bottom: 16px;
}
.credit-dialog-tip {
margin-top: 6px;
font-size: 12px;
line-height: 1.4;
color: var(--el-text-color-secondary);
}
@media (width <= 1200px) {
.main-wallet-filter__grid {
grid-template-columns: repeat(2, minmax(220px, 1fr));
}
.main-wallet-filter__actions {
:deep(.el-form-item__content) {
justify-content: flex-start;
}
}
}
@media (width <= 768px) {
.main-wallet-filter__grid {
grid-template-columns: 1fr;
}
}
.asset-identifier-link {
color: var(--el-color-primary);
text-decoration: underline;
cursor: pointer;
}
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}
</style>