Files
one-pipe-system/src/views/commission-management/my-commission/index.vue
luo ebf9739f06
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m0s
fix:sort
2026-06-22 11:09:50 +08:00

881 lines
27 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="my-commission-page">
<!-- 标签页 -->
<ElCard shadow="never">
<ElTabs v-model="activeTab">
<!-- 佣金明细 -->
<ElTabPane label="佣金明细" name="commission">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="commissionSearchForm"
:items="commissionSearchItems"
show-expand
@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,
ShopCommissionRecordItem,
WithdrawalRequestItem,
CommissionRecordQueryParams,
WithdrawalRequestQueryParams,
SubmitWithdrawalParams,
WithdrawalStatus
} from '@/types/api/commission'
import { WithdrawalMethod } from '@/types/api/commission'
import { WithdrawalStatusMap, WithdrawalMethodMap } from '@/config/constants/commission'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { useUserStore } from '@/store/modules/user'
import { formatDateTime, formatMoney } from '@/utils/business/format'
defineOptions({ name: 'MyCommission' })
const { hasAuth } = useAuth()
const userStore = useUserStore()
// 获取当前用户的 shop_id
const currentShopId = computed(() => userStore.info?.shop_id)
// 如果没有 shop_id显示提示
const hasShopId = computed(() => !!currentShopId.value)
// 标签页
const activeTab = ref('commission')
// 佣金概览
const summary = ref<MyCommissionSummary>({
main_balance: 0,
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_source: undefined as string | undefined,
status: undefined as number | undefined,
start_time: '',
end_time: '',
iccid: '',
virtual_no: '',
order_no: ''
}
// 搜索表单
const commissionSearchForm = reactive({ ...initialCommissionSearchState })
// 搜索表单配置
const commissionSearchItems = computed<SearchFormItem[]>(() => [
{
label: '佣金来源',
prop: 'commission_source',
type: 'select',
options: [
{ label: '成本差价', value: 'cost_diff' },
{ label: '一次性佣金', value: 'one_time' }
],
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: 'ICCID',
prop: 'iccid',
type: 'input',
config: {
clearable: true,
placeholder: '请输入ICCID'
}
},
{
label: '虚拟号',
prop: 'virtual_no',
type: 'input',
config: {
clearable: true,
placeholder: '请输入虚拟号'
}
},
{
label: '订单号',
prop: 'order_no',
type: 'input',
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: '佣金金额', prop: 'amount' },
{ label: '佣金来源', prop: 'commission_source' },
{ label: '状态', prop: 'status_name' },
{ label: 'ICCID', prop: 'iccid' },
{ label: '虚拟号', prop: 'virtual_no' },
{ label: '订单号', prop: 'order_no' },
{ label: '卖家店铺', prop: 'seller_shop_name' },
{ label: '入账后余额', prop: 'balance_after' },
{ label: '创建时间', prop: 'created_at' }
]
const commissionList = ref<ShopCommissionRecordItem[]>([])
// 佣金来源映射
const CommissionSourceMap = {
cost_diff: { label: '成本差价', type: 'primary' as const },
one_time: { label: '一次性佣金', type: 'success' as const }
}
// 状态映射
const CommissionRecordStatusMap = {
1: { label: '已冻结', type: 'info' as const },
2: { label: '解冻中', type: 'warning' as const },
3: { label: '已发放', type: 'success' as const },
4: { label: '已失效', type: 'danger' as const }
}
// 动态列配置
const { columnChecks: commissionColumnChecks, columns: commissionColumns } = useCheckedColumns(
() => [
{
prop: 'amount',
label: '佣金金额',
width: 120,
formatter: (row: ShopCommissionRecordItem) => formatMoney(row.amount)
},
{
prop: 'commission_source',
label: '佣金来源',
width: 120,
formatter: (row: ShopCommissionRecordItem) => {
const config =
CommissionSourceMap[row.commission_source as keyof typeof CommissionSourceMap]
if (!config) return String(row.commission_source ?? '-')
return h(ElTag, { type: config.type }, () => config.label)
}
},
{
prop: 'status_name',
label: '状态',
width: 100,
formatter: (row: ShopCommissionRecordItem) => {
return String(row.status_name ?? '-')
}
},
{
prop: 'iccid',
label: 'ICCID',
formatter: (row: ShopCommissionRecordItem) => row.iccid || '-'
},
{
prop: 'virtual_no',
label: '虚拟号',
width: 130,
formatter: (row: ShopCommissionRecordItem) => row.virtual_no || '-'
},
{
prop: 'order_no',
label: '订单号',
width: 160,
formatter: (row: ShopCommissionRecordItem) => row.order_no || '-'
},
{
prop: 'seller_shop_name',
label: '卖家店铺',
width: 150,
formatter: (row: ShopCommissionRecordItem) => row.seller_shop_name || '-'
},
{
prop: 'balance_after',
label: '入账后余额',
width: 120,
formatter: (row: ShopCommissionRecordItem) => formatMoney(row.balance_after)
},
{
prop: 'created_at',
label: '创建时间',
width: 180,
formatter: (row: ShopCommissionRecordItem) => formatDateTime(row.created_at)
}
]
)
// 获取佣金明细
const getCommissionList = async () => {
if (!currentShopId.value) {
ElMessage.warning('未找到店铺信息')
return
}
commissionLoading.value = true
try {
const params: CommissionRecordQueryParams = {
page_size: commissionPagination.pageSize,
page: commissionPagination.page,
commission_source: commissionSearchForm.commission_source,
status: commissionSearchForm.status,
start_time: commissionSearchForm.start_time || undefined,
end_time: commissionSearchForm.end_time || undefined,
iccid: commissionSearchForm.iccid || undefined,
virtual_no: commissionSearchForm.virtual_no || undefined,
order_no: commissionSearchForm.order_no || undefined
}
const res = await CommissionService.getShopCommissionRecords(currentShopId.value, 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_name' },
{ label: '拒绝原因', prop: 'reject_reason' },
{ label: '申请时间', prop: 'created_at' },
{ label: '到账时间', prop: 'paid_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_name',
label: '状态',
width: 100,
formatter: (row: WithdrawalRequestItem) => {
const config = WithdrawalStatusMap[row.status as keyof typeof WithdrawalStatusMap]
const tagType = config?.type || 'info'
const label = row.status_name || config?.label || String(row.status)
return h(ElTag, { type: tagType }, () => label)
}
},
{
prop: 'reject_reason',
label: '拒绝原因',
minWidth: 120,
formatter: (row: WithdrawalRequestItem) => row.reject_reason || '-'
},
{
prop: 'created_at',
label: '申请时间',
width: 180,
formatter: (row: WithdrawalRequestItem) => formatDateTime(row.created_at)
},
{
prop: 'paid_at',
label: '到账时间',
width: 180,
formatter: (row: WithdrawalRequestItem) => (row.paid_at ? formatDateTime(row.paid_at) : '-')
}
]
)
// 获取提现记录
const getWithdrawalList = async () => {
if (!currentShopId.value) {
ElMessage.warning('未找到店铺信息')
return
}
withdrawalLoading.value = true
try {
const params: WithdrawalRequestQueryParams = {
page_size: withdrawalPagination.pageSize,
page: withdrawalPagination.page,
status: withdrawalSearchForm.status,
start_time: withdrawalSearchForm.start_time || undefined,
end_time: withdrawalSearchForm.end_time || undefined
}
const res = await CommissionService.getShopWithdrawalRequests(currentShopId.value, 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
if (!currentShopId.value) {
ElMessage.warning('未找到店铺信息')
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.submitShopWithdrawalRequest(currentShopId.value, params)
ElMessage.success('提现申请提交成功')
withdrawalDialogVisible.value = false
// 刷新数据
await loadSummary()
if (activeTab.value === 'withdrawal') {
await getWithdrawalList()
}
} catch (error) {
console.error(error)
} finally {
submitLoading.value = false
}
}
})
}
// ==================== 初始化 ====================
// 加载佣金概览
const loadSummary = async () => {
if (!currentShopId.value) {
return
}
try {
// 调用 fund-summary 接口,通过 shop_id 过滤获取自己的数据
const res = await CommissionService.getShopFundSummary({
page: 1,
page_size: 1
})
if (res.code === 0 && res.data.items && res.data.items.length > 0) {
// 从结果中找到当前用户的店铺数据
const myShopData = res.data.items.find((item) => item.shop_id === currentShopId.value)
if (myShopData) {
summary.value = {
main_balance: myShopData.main_balance || 0,
total_commission: myShopData.total_commission,
available_commission: myShopData.available_commission,
frozen_commission: myShopData.frozen_commission,
withdrawing_commission: myShopData.withdrawing_commission,
withdrawn_commission: myShopData.withdrawn_commission
}
}
}
} catch (error) {
console.error(error)
}
}
// 监听标签页切换
watch(activeTab, (newTab) => {
if (newTab === 'commission') {
getCommissionList()
} else if (newTab === 'withdrawal') {
getWithdrawalList()
}
})
onMounted(() => {
if (!hasShopId.value) {
ElMessage.warning('当前账号未关联店铺,无法查看佣金信息')
return
}
loadSummary()
getCommissionList()
})
</script>