643 lines
18 KiB
Vue
643 lines
18 KiB
Vue
<template>
|
|
<ArtTableFullScreen>
|
|
<div class="agent-recharge-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"
|
|
>
|
|
<template #left>
|
|
<ElButton type="primary" @click="showCreateDialog">创建充值订单</ElButton>
|
|
</template>
|
|
</ArtTableHeader>
|
|
|
|
<!-- 表格 -->
|
|
<ArtTable
|
|
ref="tableRef"
|
|
row-key="id"
|
|
:loading="loading"
|
|
:data="rechargeList"
|
|
:currentPage="pagination.page"
|
|
:pageSize="pagination.page_size"
|
|
: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="createDialogVisible"
|
|
title="创建充值订单"
|
|
width="500px"
|
|
@closed="handleCreateDialogClosed"
|
|
>
|
|
<ElForm ref="createFormRef" :model="createForm" :rules="createRules" label-width="100px">
|
|
<ElFormItem label="充值金额" prop="amount">
|
|
<ElInputNumber
|
|
v-model="createForm.amount"
|
|
:min="100"
|
|
:max="1000000"
|
|
:precision="2"
|
|
:step="100"
|
|
style="width: 100%"
|
|
placeholder="请输入充值金额(元)"
|
|
/>
|
|
<div style="margin-top: 8px; font-size: 12px; color: var(--el-text-color-secondary)">
|
|
充值范围: ¥100 ~ ¥1,000,000
|
|
</div>
|
|
</ElFormItem>
|
|
<ElFormItem label="支付方式" prop="payment_method">
|
|
<ElSelect
|
|
v-model="createForm.payment_method"
|
|
placeholder="请选择支付方式"
|
|
style="width: 100%"
|
|
>
|
|
<ElOption label="微信在线支付" value="wechat" />
|
|
<!-- 只有平台用户才显示线下转账选项 -->
|
|
<ElOption
|
|
v-if="userStore.info.user_type === 1 || userStore.info.user_type === 2"
|
|
label="线下转账"
|
|
value="offline"
|
|
/>
|
|
</ElSelect>
|
|
</ElFormItem>
|
|
<ElFormItem label="目标店铺" prop="shop_id">
|
|
<ElTreeSelect
|
|
v-model="createForm.shop_id"
|
|
:data="shopTreeData"
|
|
placeholder="请选择店铺"
|
|
filterable
|
|
clearable
|
|
check-strictly
|
|
:render-after-expand="false"
|
|
:props="{
|
|
label: 'shop_name',
|
|
value: 'id',
|
|
children: 'children'
|
|
}"
|
|
style="width: 100%"
|
|
/>
|
|
</ElFormItem>
|
|
</ElForm>
|
|
<template #footer>
|
|
<div class="dialog-footer">
|
|
<ElButton @click="createDialogVisible = false">取消</ElButton>
|
|
<ElButton type="primary" @click="handleCreateRecharge" :loading="createLoading">
|
|
确认创建
|
|
</ElButton>
|
|
</div>
|
|
</template>
|
|
</ElDialog>
|
|
|
|
<!-- 确认线下支付对话框 -->
|
|
<ElDialog
|
|
v-model="confirmPayDialogVisible"
|
|
title="确认线下充值"
|
|
width="400px"
|
|
@closed="handleConfirmPayDialogClosed"
|
|
>
|
|
<ElForm
|
|
ref="confirmPayFormRef"
|
|
:model="confirmPayForm"
|
|
:rules="confirmPayRules"
|
|
label-width="100px"
|
|
>
|
|
<ElFormItem label="充值单号">
|
|
<span>{{ currentRecharge?.recharge_no }}</span>
|
|
</ElFormItem>
|
|
<ElFormItem label="充值金额">
|
|
<span>{{ formatCurrency(currentRecharge?.amount || 0) }}</span>
|
|
</ElFormItem>
|
|
<ElFormItem label="操作密码" prop="operation_password">
|
|
<ElInput
|
|
v-model="confirmPayForm.operation_password"
|
|
type="password"
|
|
placeholder="请输入操作密码"
|
|
show-password
|
|
/>
|
|
</ElFormItem>
|
|
</ElForm>
|
|
<template #footer>
|
|
<div class="dialog-footer">
|
|
<ElButton @click="confirmPayDialogVisible = false">取消</ElButton>
|
|
<ElButton type="primary" @click="handleConfirmPay" :loading="confirmPayLoading">
|
|
确认支付
|
|
</ElButton>
|
|
</div>
|
|
</template>
|
|
</ElDialog>
|
|
</ElCard>
|
|
</div>
|
|
</ArtTableFullScreen>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { h } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { AgentRechargeService, ShopService } from '@/api/modules'
|
|
import { ElMessage, ElTag, ElTreeSelect } from 'element-plus'
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
import type {
|
|
AgentRecharge,
|
|
AgentRechargeQueryParams,
|
|
AgentRechargeStatus,
|
|
AgentRechargePaymentMethod,
|
|
CreateAgentRechargeRequest,
|
|
ConfirmOfflinePaymentRequest,
|
|
ShopResponse
|
|
} from '@/types/api'
|
|
import type { SearchFormItem } from '@/types'
|
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
|
import { useUserStore } from '@/store/modules/user'
|
|
import { formatDateTime } from '@/utils/business/format'
|
|
import { RoutesAlias } from '@/router/routesAlias'
|
|
|
|
defineOptions({ name: 'AgentRechargeList' })
|
|
|
|
const router = useRouter()
|
|
const userStore = useUserStore()
|
|
|
|
const loading = ref(false)
|
|
const createLoading = ref(false)
|
|
const confirmPayLoading = ref(false)
|
|
const tableRef = ref()
|
|
const createDialogVisible = ref(false)
|
|
const confirmPayDialogVisible = ref(false)
|
|
const currentRecharge = ref<AgentRecharge | null>(null)
|
|
|
|
// 搜索表单初始值
|
|
const initialSearchState: AgentRechargeQueryParams = {
|
|
shop_id: undefined,
|
|
status: undefined,
|
|
start_date: '',
|
|
end_date: ''
|
|
}
|
|
|
|
// 搜索表单
|
|
const searchForm = reactive<AgentRechargeQueryParams>({ ...initialSearchState })
|
|
|
|
// 店铺选项
|
|
const shopOptions = ref<any[]>([])
|
|
const shopTreeData = ref<ShopResponse[]>([])
|
|
|
|
// 搜索表单配置
|
|
const searchFormItems: SearchFormItem[] = [
|
|
{
|
|
label: '店铺',
|
|
prop: 'shop_id',
|
|
type: 'select',
|
|
placeholder: '请选择店铺',
|
|
options: () =>
|
|
shopOptions.value.map((shop) => ({
|
|
label: shop.shop_name,
|
|
value: shop.id
|
|
})),
|
|
config: {
|
|
clearable: true,
|
|
filterable: true
|
|
}
|
|
},
|
|
{
|
|
label: '状态',
|
|
prop: 'status',
|
|
type: 'select',
|
|
placeholder: '请选择状态',
|
|
options: [
|
|
{ label: '待支付', value: 1 },
|
|
{ label: '已完成', value: 2 },
|
|
{ label: '已取消', value: 3 }
|
|
],
|
|
config: {
|
|
clearable: true
|
|
}
|
|
},
|
|
{
|
|
label: '创建时间',
|
|
prop: 'dateRange',
|
|
type: 'daterange',
|
|
config: {
|
|
clearable: true,
|
|
startPlaceholder: '开始日期',
|
|
endPlaceholder: '结束日期',
|
|
valueFormat: 'YYYY-MM-DD'
|
|
}
|
|
}
|
|
]
|
|
|
|
// 分页
|
|
const pagination = reactive({
|
|
page: 1,
|
|
page_size: 20,
|
|
total: 0
|
|
})
|
|
|
|
// 列配置
|
|
const columnOptions = [
|
|
{ label: 'ID', prop: 'id' },
|
|
{ label: '充值单号', prop: 'recharge_no' },
|
|
{ label: '店铺名称', prop: 'shop_name' },
|
|
{ label: '充值金额', prop: 'amount' },
|
|
{ label: '状态', prop: 'status' },
|
|
{ label: '支付方式', prop: 'payment_method' },
|
|
{ label: '支付通道', prop: 'payment_channel' },
|
|
{ label: '创建时间', prop: 'created_at' },
|
|
{ label: '支付时间', prop: 'paid_at' },
|
|
{ label: '完成时间', prop: 'completed_at' },
|
|
{ label: '操作', prop: 'actions' }
|
|
]
|
|
|
|
const createFormRef = ref<FormInstance>()
|
|
const confirmPayFormRef = ref<FormInstance>()
|
|
|
|
const createRules = reactive<FormRules>({
|
|
amount: [{ required: true, message: '请输入充值金额', trigger: 'blur' }],
|
|
payment_method: [{ required: true, message: '请选择支付方式', trigger: 'change' }],
|
|
shop_id: [{ required: true, message: '请选择目标店铺', trigger: 'change' }]
|
|
})
|
|
|
|
const confirmPayRules = reactive<FormRules>({
|
|
operation_password: [{ required: true, message: '请输入操作密码', trigger: 'blur' }]
|
|
})
|
|
|
|
const createForm = reactive<{ amount: number; payment_method: string; shop_id: number | null }>({
|
|
amount: 100,
|
|
payment_method: 'wechat',
|
|
shop_id: null
|
|
})
|
|
|
|
const confirmPayForm = reactive<ConfirmOfflinePaymentRequest>({
|
|
operation_password: ''
|
|
})
|
|
|
|
const rechargeList = ref<AgentRecharge[]>([])
|
|
|
|
// 格式化货币 - 将分转换为元
|
|
const formatCurrency = (amount: number): string => {
|
|
return `¥${(amount / 100).toFixed(2)}`
|
|
}
|
|
|
|
// 获取状态标签类型
|
|
const getStatusType = (status: AgentRechargeStatus): 'warning' | 'success' | 'info' => {
|
|
const statusMap: Record<AgentRechargeStatus, 'warning' | 'success' | 'info'> = {
|
|
1: 'warning', // 待支付
|
|
2: 'success', // 已完成
|
|
3: 'info' // 已取消
|
|
}
|
|
return statusMap[status] || 'info'
|
|
}
|
|
|
|
// 获取状态文本
|
|
const getStatusText = (status: AgentRechargeStatus): string => {
|
|
const statusMap: Record<AgentRechargeStatus, string> = {
|
|
1: '待支付',
|
|
2: '已完成',
|
|
3: '已取消'
|
|
}
|
|
return statusMap[status] || '-'
|
|
}
|
|
|
|
// 获取支付方式文本
|
|
const getPaymentMethodText = (method: AgentRechargePaymentMethod): string => {
|
|
const methodMap: Record<AgentRechargePaymentMethod, string> = {
|
|
wechat: '微信在线支付',
|
|
offline: '线下转账'
|
|
}
|
|
return methodMap[method] || method
|
|
}
|
|
|
|
// 动态列配置
|
|
const { columnChecks, columns } = useCheckedColumns(() => [
|
|
{
|
|
prop: 'id',
|
|
label: 'ID',
|
|
width: 80
|
|
},
|
|
{
|
|
prop: 'recharge_no',
|
|
label: '充值单号',
|
|
minWidth: 200,
|
|
formatter: (row: AgentRecharge) => {
|
|
return h(
|
|
'span',
|
|
{
|
|
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
|
onClick: (e: MouseEvent) => {
|
|
e.stopPropagation()
|
|
handleViewDetail(row)
|
|
}
|
|
},
|
|
row.recharge_no
|
|
)
|
|
}
|
|
},
|
|
{
|
|
prop: 'shop_name',
|
|
label: '店铺名称',
|
|
minWidth: 150
|
|
},
|
|
{
|
|
prop: 'amount',
|
|
label: '充值金额',
|
|
width: 120,
|
|
formatter: (row: AgentRecharge) => formatCurrency(row.amount)
|
|
},
|
|
{
|
|
prop: 'status',
|
|
label: '状态',
|
|
width: 100,
|
|
formatter: (row: AgentRecharge) => {
|
|
return h(ElTag, { type: getStatusType(row.status) }, () => getStatusText(row.status))
|
|
}
|
|
},
|
|
{
|
|
prop: 'payment_method',
|
|
label: '支付方式',
|
|
width: 120,
|
|
formatter: (row: AgentRecharge) => getPaymentMethodText(row.payment_method)
|
|
},
|
|
{
|
|
prop: 'payment_channel',
|
|
label: '支付通道',
|
|
width: 120
|
|
},
|
|
{
|
|
prop: 'created_at',
|
|
label: '创建时间',
|
|
width: 180,
|
|
formatter: (row: AgentRecharge) => formatDateTime(row.created_at)
|
|
},
|
|
{
|
|
prop: 'paid_at',
|
|
label: '支付时间',
|
|
width: 180,
|
|
formatter: (row: AgentRecharge) => (row.paid_at ? formatDateTime(row.paid_at) : '-')
|
|
},
|
|
{
|
|
prop: 'completed_at',
|
|
label: '完成时间',
|
|
width: 180,
|
|
formatter: (row: AgentRecharge) => (row.completed_at ? formatDateTime(row.completed_at) : '-')
|
|
},
|
|
{
|
|
prop: 'actions',
|
|
label: '操作',
|
|
width: 150,
|
|
fixed: 'right',
|
|
formatter: (row: AgentRecharge) => {
|
|
const buttons: any[] = []
|
|
|
|
// 待支付且线下转账的订单可以确认支付
|
|
if (row.status === 1 && row.payment_method === 'offline') {
|
|
buttons.push(
|
|
h(
|
|
ElButton,
|
|
{
|
|
type: 'primary',
|
|
link: true,
|
|
size: 'small',
|
|
onClick: () => handleShowConfirmPay(row)
|
|
},
|
|
() => '确认支付'
|
|
)
|
|
)
|
|
}
|
|
|
|
buttons.push(
|
|
h(
|
|
ElButton,
|
|
{
|
|
type: 'primary',
|
|
link: true,
|
|
size: 'small',
|
|
onClick: () => handleViewDetail(row)
|
|
},
|
|
() => '查看详情'
|
|
)
|
|
)
|
|
|
|
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
|
}
|
|
}
|
|
])
|
|
|
|
onMounted(() => {
|
|
getTableData()
|
|
loadShops()
|
|
})
|
|
|
|
// 构建树形数据
|
|
const buildTreeData = (items: ShopResponse[]) => {
|
|
const map = new Map<number, ShopResponse & { children?: ShopResponse[] }>()
|
|
const tree: ShopResponse[] = []
|
|
|
|
// 先将所有项放入 map
|
|
items.forEach((item) => {
|
|
map.set(item.id, { ...item, children: [] })
|
|
})
|
|
|
|
// 构建树形结构
|
|
items.forEach((item) => {
|
|
const node = map.get(item.id)!
|
|
if (item.parent_id && map.has(item.parent_id)) {
|
|
// 有父节点,添加到父节点的 children 中
|
|
const parent = map.get(item.parent_id)!
|
|
if (!parent.children) parent.children = []
|
|
parent.children.push(node)
|
|
} else {
|
|
// 没有父节点或父节点不存在,作为根节点
|
|
tree.push(node)
|
|
}
|
|
})
|
|
|
|
return tree
|
|
}
|
|
|
|
// 加载店铺列表
|
|
const loadShops = async () => {
|
|
try {
|
|
const params: any = {
|
|
page: 1,
|
|
page_size: 9999 // 获取所有数据用于构建树形结构
|
|
}
|
|
const res = await ShopService.getShops(params)
|
|
if (res.code === 0) {
|
|
const items = res.data.items || []
|
|
// 保留平铺列表用于搜索
|
|
shopOptions.value = items
|
|
// 构建树形数据用于创建对话框
|
|
shopTreeData.value = buildTreeData(items)
|
|
}
|
|
} catch (error) {
|
|
console.error('Load shops failed:', error)
|
|
}
|
|
}
|
|
|
|
// 获取充值订单列表
|
|
const getTableData = async () => {
|
|
loading.value = true
|
|
try {
|
|
const params: AgentRechargeQueryParams = {
|
|
page: pagination.page,
|
|
page_size: pagination.page_size,
|
|
shop_id: searchForm.shop_id,
|
|
status: searchForm.status,
|
|
start_date: searchForm.start_date || undefined,
|
|
end_date: searchForm.end_date || undefined
|
|
}
|
|
const res = await AgentRechargeService.getAgentRecharges(params)
|
|
if (res.code === 0) {
|
|
rechargeList.value = res.data.list || []
|
|
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 = () => {
|
|
// 处理日期范围
|
|
if (searchForm.dateRange && Array.isArray(searchForm.dateRange)) {
|
|
searchForm.start_date = searchForm.dateRange[0]
|
|
searchForm.end_date = searchForm.dateRange[1]
|
|
} else {
|
|
searchForm.start_date = ''
|
|
searchForm.end_date = ''
|
|
}
|
|
pagination.page = 1
|
|
getTableData()
|
|
}
|
|
|
|
// 刷新表格
|
|
const handleRefresh = () => {
|
|
getTableData()
|
|
}
|
|
|
|
// 处理表格分页变化
|
|
const handleSizeChange = (newPageSize: number) => {
|
|
pagination.page_size = newPageSize
|
|
getTableData()
|
|
}
|
|
|
|
const handleCurrentChange = (newCurrentPage: number) => {
|
|
pagination.page = newCurrentPage
|
|
getTableData()
|
|
}
|
|
|
|
// 显示创建订单对话框
|
|
const showCreateDialog = async () => {
|
|
createDialogVisible.value = true
|
|
}
|
|
|
|
// 对话框关闭后的清理
|
|
const handleCreateDialogClosed = () => {
|
|
createFormRef.value?.resetFields()
|
|
createForm.amount = 100
|
|
createForm.payment_method = 'wechat'
|
|
createForm.shop_id = null
|
|
}
|
|
|
|
// 创建充值订单
|
|
const handleCreateRecharge = async () => {
|
|
if (!createFormRef.value) return
|
|
|
|
await createFormRef.value.validate(async (valid) => {
|
|
if (valid) {
|
|
createLoading.value = true
|
|
try {
|
|
const data: CreateAgentRechargeRequest = {
|
|
amount: createForm.amount * 100, // 元转分
|
|
payment_method: createForm.payment_method as AgentRechargePaymentMethod,
|
|
shop_id: createForm.shop_id!
|
|
}
|
|
|
|
await AgentRechargeService.createAgentRecharge(data)
|
|
ElMessage.success('充值订单创建成功')
|
|
createDialogVisible.value = false
|
|
createFormRef.value.resetFields()
|
|
await getTableData()
|
|
} catch (error) {
|
|
console.error(error)
|
|
} finally {
|
|
createLoading.value = false
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 显示确认支付对话框
|
|
const handleShowConfirmPay = (row: AgentRecharge) => {
|
|
currentRecharge.value = row
|
|
confirmPayDialogVisible.value = true
|
|
}
|
|
|
|
// 确认支付对话框关闭后的清理
|
|
const handleConfirmPayDialogClosed = () => {
|
|
confirmPayFormRef.value?.resetFields()
|
|
confirmPayForm.operation_password = ''
|
|
currentRecharge.value = null
|
|
}
|
|
|
|
// 确认线下支付
|
|
const handleConfirmPay = async () => {
|
|
if (!confirmPayFormRef.value || !currentRecharge.value) return
|
|
|
|
await confirmPayFormRef.value.validate(async (valid) => {
|
|
if (valid) {
|
|
confirmPayLoading.value = true
|
|
try {
|
|
await AgentRechargeService.confirmOfflinePayment(currentRecharge.value.id, {
|
|
operation_password: confirmPayForm.operation_password
|
|
})
|
|
ElMessage.success('确认支付成功')
|
|
confirmPayDialogVisible.value = false
|
|
confirmPayFormRef.value.resetFields()
|
|
await getTableData()
|
|
} catch (error) {
|
|
console.error(error)
|
|
} finally {
|
|
confirmPayLoading.value = false
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 查看详情
|
|
const handleViewDetail = (row: AgentRecharge) => {
|
|
router.push({
|
|
path: `${RoutesAlias.AgentRecharge}/detail/${row.id}`
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.agent-recharge-page {
|
|
height: 100%;
|
|
}
|
|
</style>
|