删除多余代码

This commit is contained in:
sexygoat
2026-04-08 19:31:22 +08:00
parent b510b4539f
commit d1c6588d8f
110 changed files with 897 additions and 24613 deletions

View File

@@ -18,7 +18,7 @@
@refresh="handleRefresh"
>
<template #left>
<ElButton type="primary" @click="showCreateDialog">创建充值订单</ElButton>
<ElButton type="primary" @click="showCreateDialog" v-if="hasAuth('agent_recharge:create')">创建充值订单</ElButton>
</template>
</ArtTableHeader>
@@ -32,29 +32,16 @@
:pageSize="pagination.page_size"
:total="pagination.total"
:marginTop="10"
:row-class-name="getRowClassName"
:actions="getActions"
:actionsWidth="120"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 充值订单操作右键菜单 -->
<ArtMenuRight
ref="rechargeOperationMenuRef"
:menu-items="rechargeOperationMenuItems"
:menu-width="140"
@select="handleRechargeOperationMenuSelect"
/>
<!-- 创建充值订单对话框 -->
<ElDialog
v-model="createDialogVisible"
@@ -181,17 +168,15 @@
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useUserStore } from '@/store/modules/user'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { formatDateTime } from '@/utils/business/format'
import { RoutesAlias } from '@/router/routesAlias'
import { useAuth } from '@/composables/useAuth'
defineOptions({ name: 'AgentRechargeList' })
const router = useRouter()
const userStore = useUserStore()
const { hasAuth } = useAuth()
const loading = ref(false)
const createLoading = ref(false)
@@ -201,10 +186,6 @@
const confirmPayDialogVisible = ref(false)
const currentRecharge = ref<AgentRecharge | null>(null)
// 右键菜单
const rechargeOperationMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
const currentOperatingRecharge = ref<AgentRecharge | null>(null)
// 搜索表单初始值
const initialSearchState: AgentRechargeQueryParams = {
shop_id: undefined,
@@ -609,71 +590,25 @@
})
}
// 查看详情
const handleViewDetail = (row: AgentRecharge) => {
router.push({
path: `${RoutesAlias.AgentRecharge}/detail/${row.id}`
})
}
// 充值订单操作菜单项配置
const rechargeOperationMenuItems = computed((): MenuItemType[] => {
const items: MenuItemType[] = []
// 获取操作按钮
const getActions = (row: AgentRecharge) => {
const actions: any[] = []
// 待支付且线下转账的订单可以确认支付
if (
currentOperatingRecharge.value?.status === 1 &&
currentOperatingRecharge.value?.payment_method === 'offline'
) {
items.push({
key: 'confirm_pay',
label: '确认支付'
if (row.status === 1 && row.payment_method === 'offline' && hasAuth('agent_recharge:confirm_payment')) {
actions.push({
label: '确认支付',
handler: () => handleShowConfirmPay(row),
type: 'primary'
})
}
return items
})
// 显示充值订单操作右键菜单
const showRechargeOperationMenu = (e: MouseEvent, row: AgentRecharge) => {
e.preventDefault()
e.stopPropagation()
currentOperatingRecharge.value = row
rechargeOperationMenuRef.value?.show(e)
return actions
}
// 处理表格行右键菜单
const handleRowContextMenu = (row: AgentRecharge, column: any, event: MouseEvent) => {
showRechargeOperationMenu(event, row)
}
// 处理充值订单操作菜单选择
const handleRechargeOperationMenuSelect = (item: MenuItemType) => {
if (!currentOperatingRecharge.value) return
switch (item.key) {
case 'confirm_pay':
handleShowConfirmPay(currentOperatingRecharge.value)
break
}
}
// 使用表格右键菜单功能
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
</script>
<style scoped lang="scss">
.agent-recharge-page {
height: 100%;
}
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}
</style>

View File

@@ -1,821 +0,0 @@
<template>
<div class="agent-commission-container">
<ArtTableFullScreen>
<div class="agent-commission-page" id="table-full-screen">
<!-- 搜索栏 -->
<ElCard shadow="never" class="search-card">
<ElForm :inline="true" :model="searchForm" class="search-form">
<ElFormItem label="店铺名称">
<ElInput
v-model="searchForm.shop_name"
placeholder="请输入店铺名称"
clearable
style="width: 200px"
/>
</ElFormItem>
<ElFormItem label="店铺编码">
<ElInput
v-model="searchForm.shop_code"
placeholder="请输入店铺编码"
clearable
style="width: 200px"
/>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="handleSearch">查询</ElButton>
<ElButton @click="handleReset">重置</ElButton>
</ElFormItem>
</ElForm>
</ElCard>
<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"
:row-class-name="getRowClassName"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 右键菜单 -->
<ArtMenuRight
ref="contextMenuRef"
:menu-items="contextMenuItems"
:menu-width="120"
@select="handleContextMenuSelect"
/>
</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="10"
:height="500"
@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_type" width="120">
<template #default="scope">
<ElTag
:type="
CommissionTypeMap[scope.row.commission_type as keyof typeof CommissionTypeMap]
?.type || 'info'
"
>
{{
CommissionTypeMap[scope.row.commission_type as keyof typeof CommissionTypeMap]
?.label || scope.row.commission_type
}}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="状态" prop="status" width="100">
<template #default="scope">
<ElTag
:type="
CommissionStatusMap[scope.row.status as keyof typeof CommissionStatusMap]
?.type || 'info'
"
>
{{
CommissionStatusMap[scope.row.status as keyof typeof CommissionStatusMap]
?.label || scope.row.status
}}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="订单号" prop="order_no" min-width="180" 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>
</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>
</div>
</template>
<script setup lang="ts">
import { h, watch, onBeforeUnmount } from 'vue'
import { useRoute } from 'vue-router'
import { CommissionService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type {
ShopCommissionSummaryItem,
ShopCommissionRecordItem,
WithdrawalRequestItem,
CommissionResolveAction
} from '@/types/api/commission'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { formatDateTime, formatMoney } from '@/utils/business/format'
import {
CommissionStatusMap,
WithdrawalStatusMap,
WithdrawalMethodMap,
CommissionTypeMap
} from '@/config/constants/commission'
defineOptions({ name: 'AgentCommission' })
const { hasAuth } = useAuth()
const route = useRoute()
// 使用表格右键菜单功能
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
// 主表格状态
const loading = ref(false)
const tableRef = ref()
const summaryList = ref<ShopCommissionSummaryItem[]>([])
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
const currentRow = ref<ShopCommissionSummaryItem | null>(null)
// 搜索表单
const searchForm = reactive({
shop_name: '',
shop_code: ''
})
// 主表格分页
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 详情抽屉状态
const detailDrawerVisible = ref(false)
const activeTab = ref<'commission' | 'withdrawal'>('commission')
const currentShop = ref<ShopCommissionSummaryItem | 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 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 columnOptions = [
{ label: '店铺编码', prop: 'shop_code' },
{ label: '店铺名称', prop: 'shop_name' },
{ label: '用户名', prop: 'username' },
{ label: '手机号', prop: 'phone' },
{ 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 handleNameClick = (row: ShopCommissionSummaryItem) => {
if (hasAuth('agent_commission:detail')) {
showDetail(row)
} else {
ElMessage.warning('您没有查看详情的权限')
}
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'shop_code',
label: '店铺编码',
minWidth: 150
},
{
prop: 'shop_name',
label: '店铺名称',
minWidth: 180,
showOverflowTooltip: true,
formatter: (row: ShopCommissionSummaryItem) => {
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: 130
},
{
prop: 'total_commission',
label: '总佣金',
width: 120,
formatter: (row: ShopCommissionSummaryItem) => formatMoney(row.total_commission)
},
{
prop: 'available_commission',
label: '可提现',
width: 120,
formatter: (row: ShopCommissionSummaryItem) => {
return h(
'span',
{ style: 'color: var(--el-color-success); font-weight: 500' },
formatMoney(row.available_commission)
)
}
},
{
prop: 'frozen_commission',
label: '冻结中',
width: 120,
formatter: (row: ShopCommissionSummaryItem) => formatMoney(row.frozen_commission)
},
{
prop: 'withdrawing_commission',
label: '提现中',
width: 120,
formatter: (row: ShopCommissionSummaryItem) => {
return h(
'span',
{ style: 'color: var(--el-color-warning)' },
formatMoney(row.withdrawing_commission)
)
}
},
{
prop: 'withdrawn_commission',
label: '已提现',
width: 120,
formatter: (row: ShopCommissionSummaryItem) => formatMoney(row.withdrawn_commission)
},
{
prop: 'unwithdraw_commission',
label: '未提现',
width: 120,
formatter: (row: ShopCommissionSummaryItem) => formatMoney(row.unwithdraw_commission)
}
])
onMounted(() => {
getTableData()
})
// 获取代理商佣金汇总列表
const getTableData = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
pageSize: pagination.pageSize,
shop_name: searchForm.shop_name || undefined,
shop_code: searchForm.shop_code || undefined
}
const res = await CommissionService.getShopCommissionSummary(params)
if (res.code === 0) {
summaryList.value = res.data.items || []
pagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
ElMessage.error('获取数据失败')
} finally {
loading.value = false
}
}
// 搜索
const handleSearch = () => {
pagination.page = 1
getTableData()
}
// 重置
const handleReset = () => {
searchForm.shop_name = ''
searchForm.shop_code = ''
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: ShopCommissionSummaryItem) => {
currentShop.value = row
detailDrawerVisible.value = true
activeTab.value = 'commission'
// 重置分页
commissionPagination.page = 1
withdrawalPagination.page = 1
// 加载佣金明细
loadCommissionRecords()
}
// 监听tab切换
watch(activeTab, (newTab) => {
if (newTab === 'commission') {
loadCommissionRecords()
} else if (newTab === 'withdrawal') {
loadWithdrawalRecords()
}
})
// 监听路由变化,关闭抽屉
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,
pageSize: 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)
ElMessage.error('获取佣金明细失败')
} 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,
pageSize: 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)
ElMessage.error('获取提现记录失败')
} finally {
withdrawalLoading.value = false
}
}
// 提现记录分页
const handleWithdrawalSizeChange = (newPageSize: number) => {
withdrawalPagination.pageSize = newPageSize
loadWithdrawalRecords()
}
const handleWithdrawalCurrentChange = (newCurrentPage: number) => {
withdrawalPagination.page = newCurrentPage
loadWithdrawalRecords()
}
// 右键菜单项配置
const contextMenuItems = computed((): MenuItemType[] => {
const items: MenuItemType[] = []
// 移除详情选项,通过点击店铺名称查看详情
return items
})
// 处理表格行右键菜单
const handleRowContextMenu = (row: ShopCommissionSummaryItem, column: any, event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
currentRow.value = row
contextMenuRef.value?.show(event)
}
// 处理右键菜单选择
const handleContextMenuSelect = (item: MenuItemType) => {
if (!currentRow.value) return
// 暂无其他菜单项
}
// 处理佣金修正
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()
// 刷新佣金明细列表
loadCommissionRecords()
} catch (error: any) {
console.error(error)
ElMessage.error(error?.message || `${actionText}失败`)
} finally {
resolveSubmitLoading.value = false
}
} catch {
// 用户取消
}
}
})
}
</script>
<style lang="scss" scoped>
.agent-commission-container {
height: 100%;
}
.agent-commission-page {
.search-card {
margin-bottom: 16px;
}
.search-form {
margin-bottom: 0;
}
}
.detail-tabs {
:deep(.el-tabs__content) {
padding: 0;
}
}
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}
</style>

View File

@@ -1,767 +0,0 @@
<template>
<div class="my-commission-page">
<!-- 标签页 -->
<ElCard shadow="never">
<ElTabs v-model="activeTab">
<!-- 佣金明细 -->
<ElTabPane label="佣金明细" name="commission">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="commissionSearchForm"
:items="commissionSearchItems"
:show-expand="false"
@reset="handleCommissionReset"
@search="handleCommissionSearch"
/>
<!-- 表格头部 -->
<ArtTableHeader
:columnList="commissionColumnOptions"
v-model:columns="commissionColumnChecks"
@refresh="handleCommissionRefresh"
style="margin-top: 20px"
>
<template #left>
<ElButton
type="primary"
@click="showWithdrawalDialog"
v-permission="'my_commission:add'"
>发起提现</ElButton
>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="commissionTableRef"
row-key="id"
:loading="commissionLoading"
:data="commissionList"
:currentPage="commissionPagination.page"
:pageSize="commissionPagination.pageSize"
:total="commissionPagination.total"
:marginTop="10"
:height="420"
@size-change="handleCommissionSizeChange"
@current-change="handleCommissionCurrentChange"
>
<template #default>
<ElTableColumn
v-for="col in commissionColumns"
:key="col.prop || col.type"
v-bind="col"
/>
</template>
</ArtTable>
</ElTabPane>
<!-- 提现记录 -->
<ElTabPane label="提现记录" name="withdrawal">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="withdrawalSearchForm"
:items="withdrawalSearchItems"
:show-expand="false"
@reset="handleWithdrawalReset"
@search="handleWithdrawalSearch"
/>
<!-- 表格头部 -->
<ArtTableHeader
:columnList="withdrawalColumnOptions"
v-model:columns="withdrawalColumnChecks"
@refresh="handleWithdrawalRefresh"
style="margin-top: 20px"
/>
<!-- 表格 -->
<ArtTable
ref="withdrawalTableRef"
row-key="id"
:loading="withdrawalLoading"
:data="withdrawalList"
:currentPage="withdrawalPagination.page"
:pageSize="withdrawalPagination.pageSize"
:total="withdrawalPagination.total"
:marginTop="10"
:height="500"
@size-change="handleWithdrawalSizeChange"
@current-change="handleWithdrawalCurrentChange"
>
<template #default>
<ElTableColumn
v-for="col in withdrawalColumns"
:key="col.prop || col.type"
v-bind="col"
/>
</template>
</ArtTable>
</ElTabPane>
</ElTabs>
</ElCard>
<!-- 发起提现对话框 -->
<ElDialog
v-model="withdrawalDialogVisible"
title="发起提现"
width="600px"
@close="handleDialogClose"
>
<ElForm
ref="withdrawalFormRef"
:model="withdrawalForm"
:rules="withdrawalRules"
label-width="120px"
>
<ElFormItem label="可提现金额">
<span style="font-size: 20px; font-weight: 500; color: var(--el-color-success)">
{{ formatMoney(summary.available_commission) }}
</span>
</ElFormItem>
<ElFormItem label="提现金额" prop="amount">
<ElInputNumber
v-model="withdrawalForm.amount"
:min="100"
:max="summary.available_commission"
:precision="0"
:step="100"
style="width: 100%"
placeholder="请输入提现金额(分)"
/>
<div style="margin-top: 4px; font-size: 12px; color: var(--el-text-color-secondary)">
金额单位为分如1元=100
</div>
</ElFormItem>
<ElFormItem label="提现方式" prop="withdrawal_method">
<ElRadioGroup v-model="withdrawalForm.withdrawal_method">
<ElRadio :label="WithdrawalMethod.ALIPAY">支付宝</ElRadio>
<ElRadio :label="WithdrawalMethod.WECHAT">微信</ElRadio>
<ElRadio :label="WithdrawalMethod.BANK">银行卡</ElRadio>
</ElRadioGroup>
</ElFormItem>
<!-- 支付宝/微信 -->
<template v-if="withdrawalForm.withdrawal_method !== WithdrawalMethod.BANK">
<ElFormItem label="账户名" prop="account_name">
<ElInput v-model="withdrawalForm.account_name" placeholder="请输入账户名" />
</ElFormItem>
<ElFormItem label="账号" prop="account_number">
<ElInput v-model="withdrawalForm.account_number" placeholder="请输入账号" />
</ElFormItem>
</template>
<!-- 银行卡 -->
<template v-if="withdrawalForm.withdrawal_method === WithdrawalMethod.BANK">
<ElFormItem label="银行名称" prop="bank_name">
<ElSelect
v-model="withdrawalForm.bank_name"
placeholder="请选择银行"
style="width: 100%"
>
<ElOption label="中国工商银行" value="中国工商银行" />
<ElOption label="中国建设银行" value="中国建设银行" />
<ElOption label="中国农业银行" value="中国农业银行" />
<ElOption label="中国银行" value="中国银行" />
<ElOption label="招商银行" value="招商银行" />
<ElOption label="交通银行" value="交通银行" />
<ElOption label="中国邮政储蓄银行" value="中国邮政储蓄银行" />
<ElOption label="其他银行" value="其他银行" />
</ElSelect>
</ElFormItem>
<ElFormItem label="账户名" prop="account_name">
<ElInput v-model="withdrawalForm.account_name" placeholder="请输入账户名" />
</ElFormItem>
<ElFormItem label="卡号" prop="account_number">
<ElInput v-model="withdrawalForm.account_number" placeholder="请输入银行卡号" />
</ElFormItem>
</template>
<ElFormItem label="备注" prop="remark">
<ElInput v-model="withdrawalForm.remark" type="textarea" :rows="3" placeholder="选填" />
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="withdrawalDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmitWithdrawal" :loading="submitLoading">
提交
</ElButton>
</div>
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { CommissionService } from '@/api/modules'
import { ElMessage, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type {
MyCommissionSummary,
MyCommissionRecordItem,
WithdrawalRequestItem,
CommissionRecordQueryParams,
WithdrawalRequestQueryParams,
SubmitWithdrawalParams,
CommissionType,
CommissionStatus,
WithdrawalStatus
} from '@/types/api/commission'
import { WithdrawalMethod } from '@/types/api/commission'
import {
CommissionStatusMap,
CommissionTypeMap,
WithdrawalStatusMap,
WithdrawalMethodMap
} from '@/config/constants/commission'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { formatDateTime, formatMoney } from '@/utils/business/format'
defineOptions({ name: 'MyCommission' })
const { hasAuth } = useAuth()
// 标签页
const activeTab = ref('commission')
// 佣金概览
const summary = ref<MyCommissionSummary>({
total_commission: 0,
available_commission: 0,
frozen_commission: 0,
withdrawing_commission: 0,
withdrawn_commission: 0
})
// ==================== 佣金明细 ====================
const commissionLoading = ref(false)
const commissionTableRef = ref()
// 搜索表单初始值
const initialCommissionSearchState = {
commission_type: undefined as CommissionType | undefined,
status: undefined as CommissionStatus | undefined,
start_time: '',
end_time: ''
}
// 搜索表单
const commissionSearchForm = reactive({ ...initialCommissionSearchState })
// 搜索表单配置
const commissionSearchItems = computed<SearchFormItem[]>(() => [
{
label: '佣金类型',
prop: 'commission_type',
type: 'select',
options: [
{ label: '一次性佣金', value: 'one_time' },
{ label: '长期佣金', value: 'long_term' }
],
config: {
clearable: true,
placeholder: '请选择佣金类型'
}
},
{
label: '状态',
prop: 'status',
type: 'select',
options: [
{ label: '已冻结', value: 1 },
{ label: '解冻中', value: 2 },
{ label: '已发放', value: 3 },
{ label: '已失效', value: 4 }
],
config: {
clearable: true,
placeholder: '请选择状态'
}
},
{
label: '时间范围',
prop: 'dateRange',
type: 'date',
config: {
type: 'daterange',
clearable: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
startPlaceholder: '开始日期',
endPlaceholder: '结束日期'
},
onChange: ({ val }: { prop: string; val: any }) => {
if (val && val.length === 2) {
commissionSearchForm.start_time = val[0]
commissionSearchForm.end_time = val[1]
} else {
commissionSearchForm.start_time = ''
commissionSearchForm.end_time = ''
}
}
}
])
// 分页
const commissionPagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 列配置
const commissionColumnOptions = [
{ label: 'ID', prop: 'id' },
{ label: '佣金金额', prop: 'amount' },
{ label: '佣金类型', prop: 'commission_type' },
{ label: '状态', prop: 'status' },
{ label: '订单ID', prop: 'order_id' },
{ label: '创建时间', prop: 'created_at' }
]
const commissionList = ref<MyCommissionRecordItem[]>([])
// 动态列配置
const { columnChecks: commissionColumnChecks, columns: commissionColumns } = useCheckedColumns(
() => [
{
prop: 'id',
label: 'ID',
width: 80
},
{
prop: 'amount',
label: '佣金金额',
width: 140,
formatter: (row: MyCommissionRecordItem) => formatMoney(row.amount)
},
{
prop: 'commission_type',
label: '佣金类型',
width: 120,
formatter: (row: MyCommissionRecordItem) => {
const config = CommissionTypeMap[row.commission_type as keyof typeof CommissionTypeMap]
return h(ElTag, { type: config.type }, () => config.label)
}
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row: MyCommissionRecordItem) => {
const config = CommissionStatusMap[row.status as keyof typeof CommissionStatusMap]
return h(ElTag, { type: config.type }, () => config.label)
}
},
{
prop: 'order_id',
label: '订单ID',
width: 100,
formatter: (row: MyCommissionRecordItem) => row.order_id || '-'
},
{
prop: 'shop_id',
label: '店铺ID',
width: 100
},
{
prop: 'created_at',
label: '创建时间',
width: 180,
formatter: (row: MyCommissionRecordItem) => formatDateTime(row.created_at)
}
]
)
// 获取佣金明细
const getCommissionList = async () => {
commissionLoading.value = true
try {
const params = {
pageSize: commissionPagination.pageSize,
current: commissionPagination.page,
commission_type: commissionSearchForm.commission_type,
status: commissionSearchForm.status,
start_time: commissionSearchForm.start_time || undefined,
end_time: commissionSearchForm.end_time || undefined
} as CommissionRecordQueryParams
const res = await CommissionService.getMyCommissionRecords(params)
if (res.code === 0) {
commissionList.value = res.data.items || []
commissionPagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
} finally {
commissionLoading.value = false
}
}
// 重置搜索
const handleCommissionReset = () => {
Object.assign(commissionSearchForm, { ...initialCommissionSearchState })
commissionPagination.page = 1
getCommissionList()
}
// 搜索
const handleCommissionSearch = () => {
commissionPagination.page = 1
getCommissionList()
}
// 刷新表格
const handleCommissionRefresh = () => {
getCommissionList()
}
// 处理表格分页变化
const handleCommissionSizeChange = (newPageSize: number) => {
commissionPagination.pageSize = newPageSize
getCommissionList()
}
const handleCommissionCurrentChange = (newCurrentPage: number) => {
commissionPagination.page = newCurrentPage
getCommissionList()
}
// ==================== 提现记录 ====================
const withdrawalLoading = ref(false)
const withdrawalTableRef = ref()
// 搜索表单初始值
const initialWithdrawalSearchState = {
status: undefined as WithdrawalStatus | undefined,
start_time: '',
end_time: ''
}
// 搜索表单
const withdrawalSearchForm = reactive({ ...initialWithdrawalSearchState })
// 搜索表单配置
const withdrawalSearchItems = computed<SearchFormItem[]>(() => [
{
label: '状态',
prop: 'status',
type: 'select',
options: [
{ label: '待审核', value: 1 },
{ label: '已通过', value: 2 },
{ label: '已拒绝', value: 3 },
{ label: '已到账', value: 4 }
],
config: {
clearable: true,
placeholder: '请选择状态'
}
},
{
label: '时间范围',
prop: 'dateRange',
type: 'date',
config: {
type: 'daterange',
clearable: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
startPlaceholder: '开始日期',
endPlaceholder: '结束日期'
},
onChange: ({ val }: { prop: string; val: any }) => {
if (val && val.length === 2) {
withdrawalSearchForm.start_time = val[0]
withdrawalSearchForm.end_time = val[1]
} else {
withdrawalSearchForm.start_time = ''
withdrawalSearchForm.end_time = ''
}
}
}
])
// 分页
const withdrawalPagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 列配置
const withdrawalColumnOptions = [
{ label: 'ID', prop: 'id' },
{ label: '提现单号', prop: 'withdrawal_no' },
{ label: '金额', prop: 'amount' },
{ label: '手续费', prop: 'fee' },
{ label: '实际到账', prop: 'actual_amount' },
{ label: '提现方式', prop: 'withdrawal_method' },
{ label: '状态', prop: 'status' },
{ label: '申请时间', prop: 'created_at' }
]
const withdrawalList = ref<WithdrawalRequestItem[]>([])
// 动态列配置
const { columnChecks: withdrawalColumnChecks, columns: withdrawalColumns } = useCheckedColumns(
() => [
{
prop: 'id',
label: 'ID',
width: 80
},
{
prop: 'withdrawal_no',
label: '提现单号',
minWidth: 160
},
{
prop: 'amount',
label: '金额',
width: 120,
formatter: (row: WithdrawalRequestItem) => formatMoney(row.amount)
},
{
prop: 'fee',
label: '手续费',
width: 100,
formatter: (row: WithdrawalRequestItem) => formatMoney(row.fee)
},
{
prop: 'actual_amount',
label: '实际到账',
width: 120,
formatter: (row: WithdrawalRequestItem) => formatMoney(row.actual_amount)
},
{
prop: 'withdrawal_method',
label: '提现方式',
width: 100,
formatter: (row: WithdrawalRequestItem) => {
const config =
WithdrawalMethodMap[row.withdrawal_method as keyof typeof WithdrawalMethodMap]
return config ? config.label : row.withdrawal_method
}
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row: WithdrawalRequestItem) => {
const config = WithdrawalStatusMap[row.status as keyof typeof WithdrawalStatusMap]
return h(ElTag, { type: config.type }, () => config.label)
}
},
{
prop: 'created_at',
label: '申请时间',
width: 180,
formatter: (row: WithdrawalRequestItem) => formatDateTime(row.created_at)
}
]
)
// 获取提现记录
const getWithdrawalList = async () => {
withdrawalLoading.value = true
try {
const params = {
pageSize: withdrawalPagination.pageSize,
current: withdrawalPagination.page,
status: withdrawalSearchForm.status,
start_time: withdrawalSearchForm.start_time || undefined,
end_time: withdrawalSearchForm.end_time || undefined
} as WithdrawalRequestQueryParams
const res = await CommissionService.getMyWithdrawalRequests(params)
if (res.code === 0) {
withdrawalList.value = res.data.items || []
withdrawalPagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
} finally {
withdrawalLoading.value = false
}
}
// 重置搜索
const handleWithdrawalReset = () => {
Object.assign(withdrawalSearchForm, { ...initialWithdrawalSearchState })
withdrawalPagination.page = 1
getWithdrawalList()
}
// 搜索
const handleWithdrawalSearch = () => {
withdrawalPagination.page = 1
getWithdrawalList()
}
// 刷新表格
const handleWithdrawalRefresh = () => {
getWithdrawalList()
}
// 处理表格分页变化
const handleWithdrawalSizeChange = (newPageSize: number) => {
withdrawalPagination.pageSize = newPageSize
getWithdrawalList()
}
const handleWithdrawalCurrentChange = (newCurrentPage: number) => {
withdrawalPagination.page = newCurrentPage
getWithdrawalList()
}
// ==================== 发起提现 ====================
const withdrawalDialogVisible = ref(false)
const submitLoading = ref(false)
const withdrawalFormRef = ref<FormInstance>()
// 提现表单
const withdrawalForm = reactive<SubmitWithdrawalParams>({
amount: 0,
withdrawal_method: WithdrawalMethod.ALIPAY,
account_name: '',
account_number: '',
bank_name: '',
remark: ''
})
// 提现表单验证规则
const withdrawalRules = reactive<FormRules>({
amount: [
{ required: true, message: '请输入提现金额', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value < 100) {
callback(new Error('提现金额不能小于100分1元'))
} else if (value > summary.value.available_commission) {
callback(new Error('提现金额不能大于可提现佣金'))
} else {
callback()
}
},
trigger: 'blur'
}
],
withdrawal_method: [{ required: true, message: '请选择提现方式', trigger: 'change' }],
account_name: [
{ required: true, message: '请输入账户名', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
account_number: [
{ required: true, message: '请输入账号', trigger: 'blur' },
{ min: 5, max: 50, message: '长度在 5 到 50 个字符', trigger: 'blur' }
],
bank_name: [
{
validator: (rule, value, callback) => {
if (withdrawalForm.withdrawal_method === WithdrawalMethod.BANK && !value) {
callback(new Error('请选择银行名称'))
} else {
callback()
}
},
trigger: 'change'
}
]
})
// 显示提现对话框
const showWithdrawalDialog = () => {
if (summary.value.available_commission < 100) {
ElMessage.warning('可提现佣金不足100分1元无法发起提现')
return
}
withdrawalDialogVisible.value = true
}
// 关闭对话框
const handleDialogClose = () => {
withdrawalFormRef.value?.resetFields()
withdrawalForm.amount = 0
withdrawalForm.withdrawal_method = WithdrawalMethod.ALIPAY
withdrawalForm.account_name = ''
withdrawalForm.account_number = ''
withdrawalForm.bank_name = ''
withdrawalForm.remark = ''
}
// 提交提现申请
const handleSubmitWithdrawal = async () => {
if (!withdrawalFormRef.value) return
await withdrawalFormRef.value.validate(async (valid) => {
if (valid) {
submitLoading.value = true
try {
const params: SubmitWithdrawalParams = {
amount: withdrawalForm.amount,
withdrawal_method: withdrawalForm.withdrawal_method,
account_name: withdrawalForm.account_name,
account_number: withdrawalForm.account_number,
remark: withdrawalForm.remark
}
// 如果是银行卡提现,添加银行名称
if (withdrawalForm.withdrawal_method === WithdrawalMethod.BANK) {
params.bank_name = withdrawalForm.bank_name
}
await CommissionService.submitWithdrawalRequest(params)
ElMessage.success('提现申请提交成功')
withdrawalDialogVisible.value = false
// 刷新数据
loadSummary()
if (activeTab.value === 'withdrawal') {
getWithdrawalList()
}
} catch (error) {
console.error(error)
} finally {
submitLoading.value = false
}
}
})
}
// ==================== 初始化 ====================
// 加载佣金概览
const loadSummary = async () => {
try {
const res = await CommissionService.getMyCommissionSummary()
if (res.code === 0) {
summary.value = res.data
}
} catch (error) {
console.error(error)
}
}
// 监听标签页切换
watch(activeTab, (newTab) => {
if (newTab === 'commission') {
getCommissionList()
} else if (newTab === 'withdrawal') {
getWithdrawalList()
}
})
onMounted(() => {
loadSummary()
getCommissionList()
})
</script>
<style lang="scss" scoped>
.my-commission-page {
// 样式已移动到分析页的佣金概览组件
}
</style>

View File

@@ -1,428 +0,0 @@
<template>
<ArtTableFullScreen>
<div class="withdrawal-approval-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
:show-expand="false"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
<ElCard shadow="never" class="art-table-card">
<!-- 表格头部 -->
<ArtTableHeader
:columnList="columnOptions"
v-model:columns="columnChecks"
@refresh="handleRefresh"
>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="withdrawalList"
:currentPage="pagination.page"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 拒绝提现对话框 -->
<ElDialog
v-model="rejectDialogVisible"
:title="$t('commission.dialog.reject')"
width="500px"
>
<ElForm ref="rejectFormRef" :model="rejectForm" :rules="rejectRules" label-width="100px">
<ElFormItem :label="$t('commission.form.rejectReason')" prop="reject_reason">
<ElInput
v-model="rejectForm.reject_reason"
type="textarea"
:rows="4"
:placeholder="$t('commission.form.rejectReasonPlaceholder')"
/>
</ElFormItem>
<ElFormItem :label="$t('commission.form.remark')" prop="remark">
<ElInput
v-model="rejectForm.remark"
type="textarea"
:rows="3"
:placeholder="$t('commission.form.remarkPlaceholder')"
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="rejectDialogVisible = false">{{ $t('common.cancel') }}</ElButton>
<ElButton type="primary" @click="handleRejectSubmit" :loading="rejectSubmitLoading">
{{ $t('common.confirm') }}
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { CommissionService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type {
WithdrawalRequestItem,
WithdrawalStatus,
WithdrawalMethod
} from '@/types/api/commission'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime, formatMoney } from '@/utils/business/format'
import { WithdrawalStatusMap, WithdrawalMethodMap } from '@/config/constants'
import { useI18n } from 'vue-i18n'
defineOptions({ name: 'WithdrawalApproval' })
const { t } = useI18n()
const rejectDialogVisible = ref(false)
const loading = ref(false)
const rejectSubmitLoading = ref(false)
const tableRef = ref()
const currentWithdrawalId = ref<number>(0)
// 搜索表单初始值
const initialSearchState = {
withdrawal_no: '',
shop_name: '',
status: undefined as WithdrawalStatus | undefined,
start_time: '',
end_time: ''
}
// 搜索表单
const searchForm = reactive({ ...initialSearchState })
// 提现状态选项
const withdrawalStatusOptions = [
{ label: t('commission.status.pending'), value: 1 },
{ label: t('commission.status.approved'), value: 2 },
{ label: t('commission.status.rejected'), value: 3 },
{ label: t('commission.status.completed'), value: 4 }
]
// 搜索表单配置
const searchFormItems: SearchFormItem[] = [
{
label: t('commission.searchForm.status'),
prop: 'status',
type: 'select',
options: withdrawalStatusOptions,
config: {
clearable: true,
placeholder: t('commission.searchForm.statusPlaceholder')
}
},
{
label: t('commission.searchForm.withdrawalNo'),
prop: 'withdrawal_no',
type: 'input',
config: {
clearable: true,
placeholder: t('commission.searchForm.withdrawalNoPlaceholder')
}
},
{
label: t('commission.searchForm.shopName'),
prop: 'shop_name',
type: 'input',
config: {
clearable: true,
placeholder: t('commission.searchForm.shopNamePlaceholder')
}
},
{
label: t('commission.searchForm.dateRange'),
prop: 'dateRange',
type: 'daterange',
config: {
clearable: true,
startPlaceholder: t('commission.searchForm.dateRangePlaceholder.0'),
endPlaceholder: t('commission.searchForm.dateRangePlaceholder.1'),
valueFormat: 'YYYY-MM-DD HH:mm:ss',
onChange: (val: [string, string] | null) => {
if (val && val.length === 2) {
searchForm.start_time = val[0]
searchForm.end_time = val[1]
} else {
searchForm.start_time = ''
searchForm.end_time = ''
}
}
}
}
]
// 分页
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 列配置
const columnOptions = [
{ label: t('commission.table.withdrawalNo'), prop: 'withdrawal_no' },
{ label: t('commission.table.shopName'), prop: 'shop_name' },
{ label: t('commission.table.applicant'), prop: 'applicant_name' },
{ label: t('commission.table.withdrawalAmount'), prop: 'amount' },
{ label: t('commission.table.fee'), prop: 'fee' },
{ label: t('commission.table.actualAmount'), prop: 'actual_amount' },
{ label: t('commission.table.withdrawalMethod'), prop: 'withdrawal_method' },
{ label: t('commission.table.status'), prop: 'status' },
{ label: t('commission.table.applyTime'), prop: 'created_at' },
{ label: t('commission.table.approveTime'), prop: 'processed_at' },
{ label: t('commission.table.actions'), prop: 'operation' }
]
const rejectFormRef = ref<FormInstance>()
const rejectRules = reactive<FormRules>({
reject_reason: [
{ required: true, message: t('commission.validation.rejectReasonRequired'), trigger: 'blur' }
],
remark: [
{ required: true, message: t('commission.validation.remarkRequired'), trigger: 'blur' }
]
})
const rejectForm = reactive({
reject_reason: '',
remark: ''
})
const withdrawalList = ref<WithdrawalRequestItem[]>([])
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'withdrawal_no',
label: t('commission.table.withdrawalNo'),
minWidth: 180
},
{
prop: 'shop_name',
label: t('commission.table.shopName'),
minWidth: 150
},
{
prop: 'applicant_name',
label: t('commission.table.applicant'),
width: 120
},
{
prop: 'amount',
label: t('commission.table.withdrawalAmount'),
width: 120,
align: 'right',
formatter: (row: WithdrawalRequestItem) => formatMoney(row.amount)
},
{
prop: 'fee',
label: t('commission.table.fee'),
width: 100,
align: 'right',
formatter: (row: WithdrawalRequestItem) => formatMoney(row.fee)
},
{
prop: 'actual_amount',
label: t('commission.table.actualAmount'),
width: 120,
align: 'right',
formatter: (row: WithdrawalRequestItem) => formatMoney(row.actual_amount)
},
{
prop: 'withdrawal_method',
label: t('commission.table.withdrawalMethod'),
width: 120,
formatter: (row: WithdrawalRequestItem) => {
const method = WithdrawalMethodMap[row.withdrawal_method as WithdrawalMethod]
return method?.label || row.withdrawal_method
}
},
{
prop: 'status',
label: t('commission.table.status'),
width: 100,
formatter: (row: WithdrawalRequestItem) => {
const statusInfo = WithdrawalStatusMap[row.status as keyof typeof WithdrawalStatusMap]
return h(ElTag, { type: statusInfo?.type || 'info' }, () => statusInfo?.label || '未知')
}
},
{
prop: 'created_at',
label: t('commission.table.applyTime'),
width: 180,
formatter: (row: WithdrawalRequestItem) => formatDateTime(row.created_at)
},
{
prop: 'processed_at',
label: t('commission.table.approveTime'),
width: 180,
formatter: (row: WithdrawalRequestItem) => formatDateTime(row.processed_at)
},
{
prop: 'operation',
label: t('commission.table.actions'),
width: 150,
fixed: 'right',
formatter: (row: WithdrawalRequestItem) => {
// 只有待审核状态才显示操作按钮
if (row.status === 1) {
return h('div', { style: 'display: flex; gap: 8px;' }, [
h(ArtButtonTable, {
text: t('commission.buttons.approve'),
iconColor: '#67C23A',
onClick: () => handleApprove(row)
}),
h(ArtButtonTable, {
text: t('commission.buttons.reject'),
iconColor: '#F56C6C',
onClick: () => showRejectDialog(row)
})
])
}
return '-'
}
}
])
onMounted(() => {
getTableData()
})
// 获取提现申请列表
const getTableData = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
pageSize: pagination.pageSize,
withdrawal_no: searchForm.withdrawal_no || undefined,
shop_name: searchForm.shop_name || undefined,
status: searchForm.status,
start_time: searchForm.start_time || undefined,
end_time: searchForm.end_time || undefined
}
const res = await CommissionService.getWithdrawalRequests(params)
if (res.code === 0) {
withdrawalList.value = res.data.items || []
pagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
} finally {
loading.value = false
}
}
// 重置搜索
const handleReset = () => {
Object.assign(searchForm, { ...initialSearchState })
pagination.page = 1
getTableData()
}
// 搜索
const handleSearch = () => {
pagination.page = 1
getTableData()
}
// 刷新表格
const handleRefresh = () => {
getTableData()
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getTableData()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.page = newCurrentPage
getTableData()
}
// 审批通过
const handleApprove = (row: WithdrawalRequestItem) => {
ElMessageBox.confirm(t('commission.messages.approveConfirm'), t('common.tips'), {
confirmButtonText: t('common.confirm'),
cancelButtonText: t('common.cancel'),
type: 'warning'
})
.then(async () => {
try {
await CommissionService.approveWithdrawal(row.id)
ElMessage.success(t('commission.messages.approveSuccess'))
getTableData()
} catch (error) {
console.error(error)
}
})
.catch(() => {
// 用户取消
})
}
// 显示拒绝对话框
const showRejectDialog = (row: WithdrawalRequestItem) => {
currentWithdrawalId.value = row.id
rejectForm.reject_reason = ''
rejectForm.remark = ''
rejectDialogVisible.value = true
}
// 提交拒绝
const handleRejectSubmit = async () => {
if (!rejectFormRef.value) return
await rejectFormRef.value.validate(async (valid) => {
if (valid) {
rejectSubmitLoading.value = true
try {
await CommissionService.rejectWithdrawal(currentWithdrawalId.value, {
reject_reason: rejectForm.reject_reason,
remark: rejectForm.remark
})
ElMessage.success(t('commission.messages.rejectSuccess'))
rejectDialogVisible.value = false
rejectFormRef.value?.resetFields()
getTableData()
} catch (error) {
console.error(error)
} finally {
rejectSubmitLoading.value = false
}
}
})
}
</script>
<style lang="scss" scoped>
.withdrawal-approval-page {
// 可以在这里添加提现审批页面特定样式
}
</style>

View File

@@ -1,257 +0,0 @@
<template>
<ArtTableFullScreen>
<div class="withdrawal-settings-page" id="table-full-screen">
<!-- 配置列表 -->
<ElCard shadow="never" class="art-table-card">
<!-- 表格头部 -->
<ArtTableHeader
:columnList="columnOptions"
v-model:columns="columnChecks"
@refresh="handleRefresh"
>
<template #left>
<ElButton type="primary" @click="showDialog" v-permission="'withdrawal_settings:add'"
>新增配置</ElButton
>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="settingsList"
:marginTop="10"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
</ElCard>
<!-- 新增配置对话框 -->
<ElDialog v-model="dialogVisible" title="新增提现配置" width="500px">
<ElForm ref="formRef" :model="form" :rules="rules" label-width="110px">
<ElFormItem label="最低提现金额" prop="min_withdrawal_amount">
<ElInputNumber
v-model="form.min_withdrawal_amount"
:min="1"
:precision="2"
:step="10"
style="width: 100%"
/>
<div class="form-tip">单位</div>
</ElFormItem>
<ElFormItem label="手续费率" prop="fee_rate">
<ElInputNumber
v-model="form.fee_rate"
:min="0"
:max="100"
:precision="2"
:step="0.1"
style="width: 100%"
/>
<div class="form-tip">单位%百分比</div>
</ElFormItem>
<ElFormItem label="每日提现次数" prop="daily_withdrawal_limit">
<ElInputNumber
v-model="form.daily_withdrawal_limit"
:min="1"
:max="100"
:step="1"
style="width: 100%"
/>
<div class="form-tip">单位</div>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit(formRef)" :loading="submitLoading">
提交
</ElButton>
</div>
</template>
</ElDialog>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { CommissionService } from '@/api/modules'
import { ElMessage, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type { WithdrawalSettingItem } from '@/types/api/commission'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { formatDateTime, formatMoney, formatFeeRate } from '@/utils/business/format'
defineOptions({ name: 'CommissionWithdrawalSettings' })
const { hasAuth } = useAuth()
const dialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
const tableRef = ref()
const formRef = ref<FormInstance>()
// 配置列表
const settingsList = ref<WithdrawalSettingItem[]>([])
// 表单数据(使用元和百分比)
const form = reactive({
min_withdrawal_amount: 100,
fee_rate: 0.2,
daily_withdrawal_limit: 3
})
// 表单验证规则
const rules = reactive<FormRules>({
min_withdrawal_amount: [
{ required: true, message: '请输入最低提现金额', trigger: 'blur' },
{ type: 'number', min: 1, message: '最低提现金额必须大于0', trigger: 'blur' }
],
fee_rate: [
{ required: true, message: '请输入手续费率', trigger: 'blur' },
{ type: 'number', min: 0, max: 100, message: '手续费率必须在0-100之间', trigger: 'blur' }
],
daily_withdrawal_limit: [
{ required: true, message: '请输入每日提现次数', trigger: 'blur' },
{ type: 'number', min: 1, message: '每日提现次数必须大于0', trigger: 'blur' }
]
})
// 列配置
const columnOptions = [
{ label: '最低提现金额', prop: 'min_withdrawal_amount' },
{ label: '手续费率', prop: 'fee_rate' },
{ label: '每日提现次数', prop: 'daily_withdrawal_limit' },
{ label: '到账天数', prop: 'arrival_days' },
{ label: '是否生效', prop: 'is_active' },
{ label: '创建人', prop: 'creator_name' },
{ label: '创建时间', prop: 'created_at' }
]
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'min_withdrawal_amount',
label: '最低提现金额',
formatter: (row: WithdrawalSettingItem) => formatMoney(row.min_withdrawal_amount)
},
{
prop: 'fee_rate',
label: '手续费率',
formatter: (row: WithdrawalSettingItem) => formatFeeRate(row.fee_rate)
},
{
prop: 'daily_withdrawal_limit',
label: '每日提现次数',
formatter: (row: WithdrawalSettingItem) => `${row.daily_withdrawal_limit}`
},
{
prop: 'arrival_days',
label: '到账天数',
formatter: (row: WithdrawalSettingItem) => {
return row.arrival_days === 0 ? '实时到账' : `${row.arrival_days}`
}
},
{
prop: 'is_active',
label: '是否生效',
formatter: (row: WithdrawalSettingItem) => {
return h(ElTag, { type: row.is_active ? 'success' : 'info' }, () =>
row.is_active ? '生效中' : '已失效'
)
}
},
{
prop: 'creator_name',
label: '创建人',
formatter: (row: WithdrawalSettingItem) => row.creator_name || '-'
},
{
prop: 'created_at',
label: '创建时间',
formatter: (row: WithdrawalSettingItem) => formatDateTime(row.created_at)
}
])
onMounted(() => {
loadSettingsList()
})
// 加载配置列表
const loadSettingsList = async () => {
loading.value = true
try {
const res = await CommissionService.getWithdrawalSettings()
if (res.code === 0) {
settingsList.value = res.data.items || []
}
} catch (error) {
console.error('获取配置列表失败:', error)
} finally {
loading.value = false
}
}
// 刷新数据
const handleRefresh = () => {
loadSettingsList()
}
// 显示新增对话框
const showDialog = () => {
dialogVisible.value = true
// 重置表单
form.min_withdrawal_amount = 100
form.fee_rate = 0.2
form.daily_withdrawal_limit = 3
formRef.value?.clearValidate()
}
// 提交表单
const handleSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
submitLoading.value = true
try {
// 转换单位:元 -> 分,百分比 -> 基点
const params = {
min_withdrawal_amount: Math.round(form.min_withdrawal_amount * 100), // 元转分
fee_rate: Math.round(form.fee_rate * 100), // 百分比转基点
daily_withdrawal_limit: form.daily_withdrawal_limit
}
await CommissionService.createWithdrawalSetting(params)
ElMessage.success('新增配置成功')
dialogVisible.value = false
formEl.resetFields()
loadSettingsList()
} catch (error) {
console.error(error)
} finally {
submitLoading.value = false
}
}
})
}
</script>
<style lang="scss" scoped>
.withdrawal-settings-page {
.form-tip {
margin-top: 4px;
font-size: 12px;
color: var(--el-text-color-secondary);
}
}
</style>

View File

@@ -1,241 +0,0 @@
<template>
<div class="page-content">
<ElCard shadow="never">
<!-- 搜索栏 -->
<ElForm :inline="true" :model="searchForm" class="search-form">
<ElFormItem label="客户账号">
<ElInput
v-model="searchForm.accountNo"
placeholder="请输入客户账号"
clearable
style="width: 200px"
/>
</ElFormItem>
<ElFormItem label="客户名称">
<ElInput
v-model="searchForm.customerName"
placeholder="请输入客户名称"
clearable
style="width: 200px"
/>
</ElFormItem>
<ElFormItem label="客户类型">
<ElSelect
v-model="searchForm.customerType"
placeholder="请选择"
clearable
style="width: 150px"
>
<ElOption label="代理商" value="agent" />
<ElOption label="企业客户" value="enterprise" />
</ElSelect>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="handleSearch">查询</ElButton>
<ElButton @click="handleReset">重置</ElButton>
</ElFormItem>
</ElForm>
<!-- 数据表格 -->
<ArtTable :data="tableData" index stripe>
<template #default>
<ElTableColumn label="客户账号" prop="accountNo" min-width="150" />
<ElTableColumn label="客户名称" prop="customerName" min-width="150" />
<ElTableColumn label="客户类型" prop="customerType" width="120">
<template #default="scope">
<ElTag :type="scope.row.customerType === 'agent' ? 'success' : 'primary'">
{{ scope.row.customerType === 'agent' ? '代理商' : '企业客户' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="佣金总额" prop="totalCommission" width="150">
<template #default="scope"> ¥{{ scope.row.totalCommission.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="可提现金额" prop="availableAmount" width="150">
<template #default="scope">
<span style="color: var(--el-color-success)">
¥{{ scope.row.availableAmount.toFixed(2) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="待入账金额" prop="pendingAmount" width="150">
<template #default="scope">
<span style="color: var(--el-color-warning)">
¥{{ scope.row.pendingAmount.toFixed(2) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="已提现金额" prop="withdrawnAmount" width="150">
<template #default="scope"> ¥{{ scope.row.withdrawnAmount.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="提现次数" prop="withdrawCount" width="100" />
<ElTableColumn label="最后提现时间" prop="lastWithdrawTime" width="180" />
<ElTableColumn label="操作" width="180" fixed="right">
<template #default="scope">
<ElButton link type="primary" @click="handleViewDetail(scope.row)">查看详情</ElButton>
<ElButton link type="primary" @click="handleViewFlow(scope.row)">流水记录</ElButton>
</template>
</ElTableColumn>
</template>
</ArtTable>
<!-- 分页 -->
<ElPagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
style="justify-content: flex-end; margin-top: 20px"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</ElCard>
<!-- 详情对话框 -->
<ElDialog v-model="detailDialogVisible" title="客户账号详情" width="800px" align-center>
<ElDescriptions :column="2" border>
<ElDescriptionsItem label="客户账号">{{ currentRow?.accountNo }}</ElDescriptionsItem>
<ElDescriptionsItem label="客户名称">{{ currentRow?.customerName }}</ElDescriptionsItem>
<ElDescriptionsItem label="客户类型">
<ElTag :type="currentRow?.customerType === 'agent' ? 'success' : 'primary'">
{{ currentRow?.customerType === 'agent' ? '代理商' : '企业客户' }}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem label="联系电话">{{ currentRow?.phone }}</ElDescriptionsItem>
<ElDescriptionsItem label="佣金总额"
>¥{{ currentRow?.totalCommission.toFixed(2) }}</ElDescriptionsItem
>
<ElDescriptionsItem label="可提现金额">
<span style="color: var(--el-color-success)">
¥{{ currentRow?.availableAmount.toFixed(2) }}
</span>
</ElDescriptionsItem>
<ElDescriptionsItem label="待入账金额">
<span style="color: var(--el-color-warning)">
¥{{ currentRow?.pendingAmount.toFixed(2) }}
</span>
</ElDescriptionsItem>
<ElDescriptionsItem label="已提现金额"
>¥{{ currentRow?.withdrawnAmount.toFixed(2) }}</ElDescriptionsItem
>
<ElDescriptionsItem label="提现次数">{{ currentRow?.withdrawCount }}</ElDescriptionsItem>
<ElDescriptionsItem label="最后提现时间">{{
currentRow?.lastWithdrawTime
}}</ElDescriptionsItem>
<ElDescriptionsItem label="注册时间">{{ currentRow?.createTime }}</ElDescriptionsItem>
<ElDescriptionsItem label="备注" :span="2">{{
currentRow?.remark || '无'
}}</ElDescriptionsItem>
</ElDescriptions>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
defineOptions({ name: 'CustomerAccount' })
const searchForm = reactive({
accountNo: '',
customerName: '',
customerType: ''
})
const pagination = reactive({
page: 1,
pageSize: 20,
total: 100
})
const detailDialogVisible = ref(false)
const currentRow = ref<any>(null)
// 模拟表格数据
const tableData = ref([
{
id: '1',
accountNo: 'ACC20260001',
customerName: '深圳市科技有限公司',
customerType: 'agent',
phone: '13800138000',
totalCommission: 158900.5,
availableAmount: 58900.5,
pendingAmount: 50000.0,
withdrawnAmount: 50000.0,
withdrawCount: 12,
lastWithdrawTime: '2026-01-08 15:00:00',
createTime: '2025-06-01 10:00:00',
remark: '优质代理商'
},
{
id: '2',
accountNo: 'ACC20260002',
customerName: '广州智能设备公司',
customerType: 'enterprise',
phone: '13900139000',
totalCommission: 89600.0,
availableAmount: 35600.0,
pendingAmount: 24000.0,
withdrawnAmount: 30000.0,
withdrawCount: 8,
lastWithdrawTime: '2026-01-05 10:30:00',
createTime: '2025-07-15 14:20:00',
remark: ''
},
{
id: '3',
accountNo: 'ACC20260003',
customerName: '北京物联网代理',
customerType: 'agent',
phone: '13700137000',
totalCommission: 256700.0,
availableAmount: 106700.0,
pendingAmount: 80000.0,
withdrawnAmount: 70000.0,
withdrawCount: 15,
lastWithdrawTime: '2026-01-09 09:15:00',
createTime: '2025-05-10 09:00:00',
remark: '金牌代理商'
}
])
const handleSearch = () => {
ElMessage.success('查询成功')
}
const handleReset = () => {
searchForm.accountNo = ''
searchForm.customerName = ''
searchForm.customerType = ''
ElMessage.info('已重置')
}
const handleViewDetail = (row: any) => {
currentRow.value = row
detailDialogVisible.value = true
}
const handleViewFlow = (row: any) => {
ElMessage.info(`查看 ${row.customerName} 的流水记录`)
}
const handleSizeChange = (size: number) => {
pagination.pageSize = size
ElMessage.info(`每页显示 ${size}`)
}
const handleCurrentChange = (page: number) => {
pagination.page = page
ElMessage.info(`当前第 ${page}`)
}
</script>
<style lang="scss" scoped>
.page-content {
.search-form {
margin-bottom: 20px;
}
}
</style>

View File

@@ -1,333 +0,0 @@
<template>
<div class="page-content">
<!-- 统计卡片 -->
<ElRow :gutter="20" style="margin-bottom: 20px">
<ElCol :xs="24" :sm="12" :lg="6">
<ElCard shadow="hover">
<div class="stat-card">
<div
class="stat-icon"
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
>
<i class="iconfont-sys">&#xe71d;</i>
</div>
<div class="stat-content">
<div class="stat-label">佣金总额</div>
<div class="stat-value">¥{{ accountInfo.totalCommission.toFixed(2) }}</div>
</div>
</div>
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6">
<ElCard shadow="hover">
<div class="stat-card">
<div
class="stat-icon"
style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)"
>
<i class="iconfont-sys">&#xe71e;</i>
</div>
<div class="stat-content">
<div class="stat-label">可提现金额</div>
<div class="stat-value">¥{{ accountInfo.availableAmount.toFixed(2) }}</div>
</div>
</div>
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6">
<ElCard shadow="hover">
<div class="stat-card">
<div
class="stat-icon"
style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)"
>
<i class="iconfont-sys">&#xe720;</i>
</div>
<div class="stat-content">
<div class="stat-label">待入账金额</div>
<div class="stat-value">¥{{ accountInfo.pendingAmount.toFixed(2) }}</div>
</div>
</div>
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6">
<ElCard shadow="hover">
<div class="stat-card">
<div
class="stat-icon"
style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%)"
>
<i class="iconfont-sys">&#xe71f;</i>
</div>
<div class="stat-content">
<div class="stat-label">已提现金额</div>
<div class="stat-value">¥{{ accountInfo.withdrawnAmount.toFixed(2) }}</div>
</div>
</div>
</ElCard>
</ElCol>
</ElRow>
<!-- 操作按钮 -->
<ElRow style="margin-bottom: 20px">
<ElButton type="primary" @click="showWithdrawDialog">申请提现</ElButton>
<ElButton @click="viewWithdrawHistory">提现记录</ElButton>
</ElRow>
<!-- 收支流水 -->
<ElCard shadow="never">
<template #header>
<div style="display: flex; align-items: center; justify-content: space-between">
<span style="font-weight: 500">收支流水</span>
<ElRadioGroup v-model="flowType" size="small">
<ElRadioButton value="all">全部</ElRadioButton>
<ElRadioButton value="income">收入</ElRadioButton>
<ElRadioButton value="withdraw">提现</ElRadioButton>
</ElRadioGroup>
</div>
</template>
<ArtTable :data="filteredFlowData" index max-height="500">
<template #default>
<ElTableColumn label="流水号" prop="flowNo" min-width="180" />
<ElTableColumn label="类型" prop="type">
<template #default="scope">
<ElTag :type="scope.row.type === 'income' ? 'success' : 'warning'">
{{ scope.row.type === 'income' ? '收入' : '提现' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="金额" prop="amount">
<template #default="scope">
<span
:style="{
color:
scope.row.type === 'income'
? 'var(--el-color-success)'
: 'var(--el-color-danger)'
}"
>
{{ scope.row.type === 'income' ? '+' : '-' }}¥{{ scope.row.amount.toFixed(2) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="余额" prop="balance">
<template #default="scope"> ¥{{ scope.row.balance.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="说明" prop="description" show-overflow-tooltip />
<ElTableColumn label="时间" prop="createTime" width="180" />
</template>
</ArtTable>
</ElCard>
<!-- 提现申请对话框 -->
<ElDialog v-model="withdrawDialogVisible" title="申请提现" width="600px" align-center>
<ElForm
ref="withdrawFormRef"
:model="withdrawForm"
:rules="withdrawRules"
label-width="120px"
>
<ElFormItem label="可提现金额">
<span style="font-size: 20px; font-weight: 500; color: var(--el-color-success)">
¥{{ accountInfo.availableAmount.toFixed(2) }}
</span>
</ElFormItem>
<ElFormItem label="提现金额" prop="amount">
<ElInputNumber
v-model="withdrawForm.amount"
:min="1"
:max="accountInfo.availableAmount"
:precision="2"
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="手续费">
<span>¥{{ calculatedFee.toFixed(2) }}</span>
<span style="margin-left: 8px; color: var(--el-text-color-secondary)">
(费率: {{ feeRate * 100 }}%)
</span>
</ElFormItem>
<ElFormItem label="实际到账">
<span style="font-size: 18px; font-weight: 500; color: var(--el-color-success)">
¥{{ actualAmount.toFixed(2) }}
</span>
</ElFormItem>
<ElFormItem label="收款银行" prop="bankName">
<ElSelect
v-model="withdrawForm.bankName"
placeholder="请选择收款银行"
style="width: 100%"
>
<ElOption label="中国工商银行" value="工商银行" />
<ElOption label="中国建设银行" value="建设银行" />
<ElOption label="中国农业银行" value="农业银行" />
<ElOption label="中国银行" value="中国银行" />
<ElOption label="招商银行" value="招商银行" />
</ElSelect>
</ElFormItem>
<ElFormItem label="银行账户" prop="bankAccount">
<ElInput v-model="withdrawForm.bankAccount" placeholder="请输入银行账户" />
</ElFormItem>
<ElFormItem label="开户姓名" prop="accountName">
<ElInput v-model="withdrawForm.accountName" placeholder="请输入开户姓名" />
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="withdrawDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="submitWithdraw">提交申请</ElButton>
</div>
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
defineOptions({ name: 'MyAccount' })
const accountInfo = reactive({
totalCommission: 158900.5,
availableAmount: 58900.5,
pendingAmount: 50000.0,
withdrawnAmount: 50000.0
})
const flowType = ref('all')
const withdrawDialogVisible = ref(false)
const withdrawFormRef = ref<FormInstance>()
const feeRate = 0.002 // 手续费率 0.2%
const withdrawForm = reactive({
amount: 0,
bankName: '',
bankAccount: '',
accountName: ''
})
const withdrawRules = reactive<FormRules>({
amount: [
{ required: true, message: '请输入提现金额', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value > accountInfo.availableAmount) {
callback(new Error('提现金额不能大于可提现金额'))
} else if (value < 1) {
callback(new Error('提现金额不能小于1元'))
} else {
callback()
}
},
trigger: 'blur'
}
],
bankName: [{ required: true, message: '请选择收款银行', trigger: 'change' }],
bankAccount: [{ required: true, message: '请输入银行账户', trigger: 'blur' }],
accountName: [{ required: true, message: '请输入开户姓名', trigger: 'blur' }]
})
const flowData = ref([
{
id: '1',
flowNo: 'FL202601090001',
type: 'income',
amount: 1580.0,
balance: 58900.5,
description: '套餐销售佣金',
createTime: '2026-01-09 10:30:00'
},
{
id: '2',
flowNo: 'FL202601080001',
type: 'withdraw',
amount: 5000.0,
balance: 57320.5,
description: '提现到账',
createTime: '2026-01-08 15:00:00'
},
{
id: '3',
flowNo: 'FL202601070001',
type: 'income',
amount: 2360.0,
balance: 62320.5,
description: '号卡分配佣金',
createTime: '2026-01-07 14:20:00'
}
])
const calculatedFee = computed(() => {
return withdrawForm.amount * feeRate
})
const actualAmount = computed(() => {
return withdrawForm.amount - calculatedFee.value
})
const filteredFlowData = computed(() => {
if (flowType.value === 'all') return flowData.value
return flowData.value.filter((item) => item.type === flowType.value)
})
const showWithdrawDialog = () => {
withdrawForm.amount = 0
withdrawForm.bankName = ''
withdrawForm.bankAccount = ''
withdrawForm.accountName = ''
withdrawDialogVisible.value = true
}
const submitWithdraw = async () => {
if (!withdrawFormRef.value) return
await withdrawFormRef.value.validate((valid) => {
if (valid) {
ElMessage.success('提现申请提交成功,请等待审核')
withdrawDialogVisible.value = false
}
})
}
const viewWithdrawHistory = () => {
ElMessage.info('查看提现记录')
}
</script>
<style lang="scss" scoped>
.page-content {
.stat-card {
display: flex;
gap: 16px;
align-items: center;
.stat-icon {
display: flex;
align-items: center;
justify-content: center;
width: 60px;
height: 60px;
font-size: 28px;
color: white;
border-radius: 12px;
}
.stat-content {
flex: 1;
.stat-label {
margin-bottom: 8px;
font-size: 14px;
color: var(--el-text-color-secondary);
}
.stat-value {
font-size: 24px;
font-weight: 600;
color: var(--el-text-color-primary);
}
}
}
}
</style>

View File

@@ -18,7 +18,9 @@
@refresh="handleRefresh"
>
<template #left>
<ElButton type="primary" @click="showCreateDialog">创建退款申请</ElButton>
<ElButton type="primary" @click="showCreateDialog" v-if="hasAuth('refund:create')"
>创建退款申请</ElButton
>
</template>
</ArtTableHeader>
@@ -32,34 +34,21 @@
:pageSize="pagination.page_size"
:total="pagination.total"
:marginTop="10"
:row-class-name="getRowClassName"
:actions="getActions"
:actionsWidth="160"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 退款操作右键菜单 -->
<ArtMenuRight
ref="refundOperationMenuRef"
:menu-items="refundOperationMenuItems"
:menu-width="140"
@select="handleRefundOperationMenuSelect"
/>
<!-- 创建退款申请对话框 -->
<ElDialog
v-model="createDialogVisible"
title="创建退款申请"
width="500px"
width="30%"
@closed="handleCreateDialogClosed"
>
<ElForm ref="createFormRef" :model="createForm" :rules="createRules" label-width="120px">
@@ -321,17 +310,15 @@
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useUserStore } from '@/store/modules/user'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { formatDateTime } from '@/utils/business/format'
import { RoutesAlias } from '@/router/routesAlias'
import { useAuth } from '@/composables/useAuth'
defineOptions({ name: 'RefundList' })
const router = useRouter()
const userStore = useUserStore()
const { hasAuth } = useAuth()
const loading = ref(false)
const createLoading = ref(false)
@@ -348,10 +335,6 @@
const resubmitDialogVisible = ref(false)
const currentRefund = ref<Refund | null>(null)
// 右键菜单
const refundOperationMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
const currentOperatingRefund = ref<Refund | null>(null)
// 搜索表单初始值
const initialSearchState: RefundQueryParams = {
status: undefined,
@@ -971,88 +954,52 @@
})
}
// 退款操作菜单项配置
const refundOperationMenuItems = computed((): MenuItemType[] => {
const items: MenuItemType[] = []
if (!currentOperatingRefund.value) return items
// 获取操作按钮
const getActions = (row: Refund) => {
const actions: any[] = []
// 待审批状态可以审批通过、拒绝或退回
if (currentOperatingRefund.value.status === 1) {
items.push({
key: 'approve',
label: '审批通过'
})
items.push({
key: 'reject',
label: '审批拒绝'
})
items.push({
key: 'return',
label: '退回'
})
if (row.status === 1) {
// 退回按钮直接显示
if (hasAuth('refund:return')) {
actions.push({
label: '退回',
handler: () => handleShowReturn(row),
type: 'primary'
})
}
// 审批通过和审批拒绝放到更多里
if (hasAuth('refund:approve')) {
actions.push({
label: '审批通过',
handler: () => handleShowApprove(row),
type: 'primary'
})
}
if (hasAuth('refund:reject')) {
actions.push({
label: '审批拒绝',
handler: () => handleShowReject(row),
type: 'danger'
})
}
}
// 已拒绝或已退回状态可以重新提交
if (currentOperatingRefund.value.status === 3 || currentOperatingRefund.value.status === 4) {
items.push({
key: 'resubmit',
label: '重新提交'
if (row.status === 3 || row.status === 4) {
actions.push({
label: '重新提交',
handler: () => handleShowResubmit(row),
type: 'primary'
})
}
return items
})
// 显示退款操作右键菜单
const showRefundOperationMenu = (e: MouseEvent, row: Refund) => {
e.preventDefault()
e.stopPropagation()
currentOperatingRefund.value = row
refundOperationMenuRef.value?.show(e)
return actions
}
// 处理表格行右键菜单
const handleRowContextMenu = (row: Refund, column: any, event: MouseEvent) => {
showRefundOperationMenu(event, row)
}
// 处理退款操作菜单选择
const handleRefundOperationMenuSelect = (item: MenuItemType) => {
if (!currentOperatingRefund.value) return
switch (item.key) {
case 'approve':
handleShowApprove(currentOperatingRefund.value)
break
case 'reject':
handleShowReject(currentOperatingRefund.value)
break
case 'return':
handleShowReturn(currentOperatingRefund.value)
break
case 'resubmit':
handleShowResubmit(currentOperatingRefund.value)
break
}
}
// 使用表格右键菜单功能
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
</script>
<style scoped lang="scss">
.refund-page {
height: 100%;
}
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}
</style>