<route lang="yaml">
meta:
enabled: false
</route>
<script setup lang="ts">
// import { VxeUI, VxeToolbarPropTypes, VxeToolbarEvents } from 'vxe-table'
import type { VxePagerEvents,VxeGridListeners, VxeGridProps,VxeColumnPropTypes,VxeToolbarPropTypes, VxeToolbarEvents,VxeToolbarInstance,VxeTableInstance,VxeTablePropTypes} from 'vxe-table'
import apiTransaction from '@/api/modules/transaction'
import { timeShortcuts } from '@/utils/timeShortcuts'
import VxeUI from 'vxe-pc-ui'
import useUserStore from '@/store/modules/user'
import defaultAvatar from '@/assets/images/default-avatar.png'
import useSettingsStore from '@/store/modules/settings'
import VxeUITable from 'vxe-table'
import type { VxeFormProps, VxeFormListeners } from 'vxe-pc-ui'
defineOptions({
name: 'TransactionList',
})
const userStore = useUserStore()
const tableData = shallowRef<RowVO[]>([])
const transactionTypeOptions = [
{ value: '', label: '全部' },
{ value: 0, label: '支付' },
{ value: 1, label: '充值' },
{ value: 2, label: '退款' },
{ value: 3, label: '补缴' },
]
const statusOptions = [
{ value: '', label: '全部' },
{ value: 0, label: '待处理' },
{ value: 1, label: '处理中' },
{ value: 2, label: '成功' },
{ value: 3, label: '失败' },
{ value: 4, label: '已过期' },
{ value: 5, label: '已关闭' }
];
// 退款类型
const refundTypesMap :Record<number, { name: string; color: string }> = {
1: { name: '本金退款', color: '#3B82F6' }, // 橙色
2: { name: '赠金扣款', color: '#2A9D8F' }, // 青绿色
}
// 退款方式
const refundModeMap :Record<number, { name: string; color: string }> = {
1: { name: '原路退回', color: '#3B82F6' }, // 橙色
2: { name: '人工处理', color: '#2A9D8F' }, // 青绿色
}
// 交易类型映射
const transactionTypeMap: Record<number, { name: string; color: string }> = {
0: { name: '支付', color: '#3B82F6' }, // 蓝色 - 充电订单支付
1: { name: '充值', color: '#10B981' }, // 绿色 - 用户/系统充值
2: { name: '退款', color: '#F59E0B' }, // 橙色 - 充电订单退款
3: { name: '补缴', color: '#EF4444' } // 红色 - 充电订单补缴
};
// 交易状态映射
const statusMap: Record<number, { name: string; color: string }> = {
0: { name: '待处理', color: '#9CA3AF' }, // 灰色 - 交易已创建
1: { name: '处理中', color: '#F59E0B' }, // 橙色 - 交易进行中
2: { name: '成功', color: '#10B981' }, // 绿色 - 交易成功
3: { name: '失败', color: '#EF4444' }, // 红色 - 交易失败
4: { name: '已过期', color: '#6B7280' }, // 深灰 - 交易超时
5: { name: '已关闭', color: '#6B7280' } // 深灰 - 交易关闭
};
// 支付方式映射
// const payModeMap: Record<number, { name: string; color: string }> = {
// 0: { name: '未知支付', color: '#9CA3AF' }, // 灰色
// 1: { name: '在线支付', color: '#3B82F6' }, // 蓝色
// 2: { name: '集团支付', color: '#8B5CF6' }, // 紫色
// 3: { name: '鉴权卡', color: '#EC4899' }, // 粉色
// 4: { name: '用户余额', color: '#10B981' }, // 绿色
// 5: { name: '钱包卡', color: '#F59E0B' }, // 橙色
// 6: { name: '后台', color: '#6366F1' }, // 靛蓝
// 7: { name: 'VIN', color: '#14B8A6' }, // 蓝绿色
// 1000: { name: '第三方平台', color: '#F97316' } // 深橙色
// };
// 支付渠道映射
const payChannelMap: Record<number, { name: string; color: string }> = {
0: { name: '未知渠道', color: '#9CA3AF' }, // 灰色
1: { name: '余额', color: '#10B981' }, // 绿色
2: { name: '微信', color: '#22C55E' } // 亮绿色(微信品牌色)
};
const settingsStore = useSettingsStore()
watch(() => settingsStore.currentColorScheme, () => {
VxeUITable.setTheme(settingsStore.currentColorScheme!)
}, {
immediate: true,
})
// 类型定义
interface RowVO {
id: number; //id
customer_id: string; // 客户ID
avatar: string; // 头像
customer_name: string; // 客户
account_name: string; // 账户名称
operator_id: string; // 运营商ID
operator_name: string; // 运营商名称
type: number; // 交易类型
pay_channel: number; // 支付渠道
pay_mode: number; // 支付方式
status: number; // 交易状态
platform_id: string; // 平台ID
platform_name: string; // 平台名称
out_order_id: string; // 订单号
out_refund_order_id: string; // 退款订单号
platform_transaction_id: string; // 平台单号
amount: number; // 退款金额
principal_amount: number;
gift_amount: number; // 赠金
before_balance: number; // 交易前余额
after_balance: number; // 交易后余额
refund_type: number; // 退款类型(1: 原路退回, 2: 人工退款)
refund_mode: number; // 退款方式(1: 本金退款, 2: 赠金扣款)
remark: string; // 备注
finish_time: string; // 完成时间
created_at: string;// 创建时间
loading?: boolean;
}
const data = ref({
loading:true,
keyword:'',
pagerConfig:{
total: 0,
currentPage: 1,
pageSize: 10
},
})
// 刷新流水
async function onRefresh(row: RowVO) {
try {
row.loading = true;
await apiTransaction.refreshTransaction({
id: row.id.toString(),
});
} catch (error) {
console.error('刷新交易失败:', error);
} finally {
setTimeout(() => {
row.loading = false
}, 3000)
}
}
// 加载表格数据
function getData() {
data.value.loading = false
const formData = formOptions.data
const params = {
page: data.value.pagerConfig.currentPage,
pageSize: data.value.pagerConfig.pageSize,
...(formData?.type && { type: Number(formData.type) }),
...(formData?.status && { status: Number(formData.status) }),
...(formData?.phone?.trim() && { phone: formData.phone.trim() }),
...(formData?.id?.trim() && { id: formData.id.trim() }),
...(formData?.operator_id?.trim() && { operator_id: formData.operator_id.trim() }),
...(formData?.platform_id?.trim() && { platform_id: formData.platform_id.trim() }),
...(data.value.keyword && { keyword: data.value.keyword }),
...(formData?.created_at && {
start_time: formData?.created_at[0],
end_time: formData?.created_at[1],
}),
};
apiTransaction.transactionList(params).then((res) => {
// 使用新数组替换,触发shallowRef更新
tableData.value = [...res.data.list];
data.value.pagerConfig.total = res.data.total;
// data.tableData = tableData.value;
}).catch((error) => {
console.error('加载数据失败:', error)
}).finally(() => {
data.value.loading = false
})
}
// 初始化加载
onMounted(() => {
for (let i = 0; i < 10; i++) {
tableData.value.push({
id: 10002122121213111 + i,
customer_id: '10002122121213111',
avatar: 'https://p3-flow-imagex-sign.byteimg.com/ocean-cloud-tos/image_skill/55e1ac2e-7b0d-439c-a368-ae574eccb5b6_1749951146378770644~tplv-a9rns2rl98-web-thumb-watermark-v2.jpeg?rk3s=b14c611d&x-expires=1781487146&x-signature=rnQnWXNqI0XnI5ODJyBfNqTV0rM%3D',
customer_name: '客户' + (i + 1),
account_name: '账户' + (i + 1),
operator_id: 'OP100013213232342' + (i % 3 + 1),
operator_name: ['移动', '联通', '电信'][i % 3],
type: [1, 2, 3][i % 3], // 假设1:充值 2:消费 3:退款
pay_channel: [1, 2, 3][i % 3], // 假设1:支付宝 2:微信 3:银联
pay_mode: [1, 2][i % 2], // 假设1:在线 2:线下
status: [0, 1, 2, 3][i % 4], // 0-3状态
platform_id: 'PL' + (100 + i),
platform_name: ['支付宝', '微信支付', '银联'][i % 3],
out_order_id: 'ORD' + Date.now().toString().slice(-12) + i,
out_refund_order_id: 'REF' + Date.now().toString().slice(-12) + i,
platform_transaction_id: 'TRX' + Date.now().toString().slice(-18) + i,
amount: 100.50 + i * 10,
principal_amount: 95.00 + i * 10,
gift_amount: 5.50 + i,
before_balance: 500.00 + i * 50,
after_balance: 400.00 + i * 50,
refund_type: [1, 2][i % 2], // 1:原路退回 2:人工退款
refund_mode: [1, 2][i % 2], // 1:本金退款 2:赠金扣款
remark: `测试交易数据${i + 1}` + (i % 3 === 0 ? '(加急处理)' : ''),
finish_time:'2023-05-15 14:30:' + (22 + i).toString().padStart(2, '0'),
created_at: '2023-05-15 14:30:' + (22 + i).toString().padStart(2, '0')
});
}
// data.tableData=tableData.value
// getData()
const $table = tableRef.value
const $toolbar = toolbarRef.value
if ($table && $toolbar) {
$table.connect($toolbar)
}
})
// 清理工作
onUnmounted(() => {
tableData.value = []
})
function formatLocal(date: Date): string {
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(date)
.replace(/\//g, '-')
.replace(/(\d{2}):(\d{2}):(\d{2})/, '$1:$2:$3');
}
// 个性化列
const toolbarRef = ref<VxeToolbarInstance>()
const tableRef = ref<VxeTableInstance>()
// 实时更新
const customConfig = reactive<VxeTablePropTypes.CustomConfig>({
immediate: true,
})
// 搜索表单
interface FormDataVO {
id: string
type: string | number
operator_id: string
platform_id: string
phone: string
status: string | number
created_at: [string, string] // 新增时间范围字段
}
// 搜索配置
const formOptions = reactive<VxeFormProps<FormDataVO>>({
data: {
id: '',
type: '',
operator_id: '', // 运营商 id
platform_id: '', // 平台 id
phone: '',
status: '',
created_at: [
formatLocal(new Date(new Date().setHours(0, 0, 0, 0))), // 当天开始时间 00:00:00
formatLocal(new Date(new Date().setHours(23, 59, 59, 0))), // 当天结束时间 23:59:59
], // 新增时间范围
},
size:'small',
items: [
{ field: 'id', title: '单号', span: 4, folding: true,itemRender: { name: 'VxeInput' } },
{ field: 'operator_id', title: '运营商ID', span: 4,folding: true, itemRender: { name: 'VxeInput' } },
{ field: 'platform_id', title: '平台ID', span: 4,folding: true, itemRender: { name: 'VxeInput' } },
{ field: 'phone', title: '手机号', span: 4, folding: true, itemRender: { name: 'VxeInput' } },
{
field: 'type',
title: '交易类型',
span: 4,
folding: false,
itemRender: {
name: 'VxeSelect',
options: transactionTypeOptions,
},
},
{
field: 'status',
title: '交易状态',
span: 4,
folding: false,
itemRender: {
name: 'VxeSelect',
options: statusOptions,
},
},
{
field: 'created_at', // 新增时间筛选字段
title: '交易时间',
span: 8,
folding: false,
slots: { default: 'default_created_at' }, // 使用插槽渲染
},
{
span: 5,
collapseNode: true,
align: 'center',
itemRender: {
name: 'VxeButtonGroup',
options: [
{ type: 'submit',size:'small', content: '搜索',icon: 'i-ep:search', status: 'primary' },
// { type: 'reset', content: '重置' }
]
}
}
]
})
// 表单搜索按钮操作
const formEvents: VxeFormListeners = {
submit () {
getData()
},
reset () {
getData()
}
}
// 左侧按钮
const toolbarButtons = ref<VxeToolbarPropTypes.Buttons>([
{ name: '刷新', code: 'refresh', status: 'info', icon: 'i-ep:refresh' },
])
// 左侧按钮事件
const buttonClickEvent: VxeToolbarEvents.ButtonClick = ({ code }) => {
if (code ==='refresh') {
getData()
}
}
// 分页查询事件
const onPageChange: VxePagerEvents.PageChange = ({ pageSize, currentPage }) => {
data.value.pagerConfig.currentPage = currentPage
data.value.pagerConfig.pageSize = pageSize
getData()
}
</script>
<template>
<div>
<FaPageMain>
<vxe-form v-bind="formOptions" v-on="formEvents">
<template #default_created_at="{ data }">
<el-date-picker v-model="data.created_at" style="width: 100%;" type="datetimerange"
start-placeholder="开始时间" end-placeholder="结束时间" range-separator="至" :shortcuts="timeShortcuts"
:default-time="[
new Date(2000, 1, 1, 0, 0, 0), // 开始时间默认 00:00:00
new Date(2000, 1, 1, 23, 59, 59), // 结束时间默认 23:59:59
]" />
</template>
</vxe-form>
<vxe-toolbar :buttons="toolbarButtons" @button-click="buttonClickEvent" ref="toolbarRef" size="mini" custom>
<template #tools>
<vxe-input v-model="data.keyword" type="search" clearable placeholder="单号/订单号/退款订单号/平台单号"
style="width: 180px;margin-right: 10px;" @keyup.enter="data.pagerConfig.currentPage=1,getData()" @clear="data.pagerConfig.currentPage=1,getData()" />
</template>
</vxe-toolbar>
<vxe-table
id="table_toolbar_custom"
ref="tableRef"
size="small"
round
stripe
:loading="data.loading"
:custom-config="customConfig"
:column-config="{useKey: true}"
:row-config="{useKey: true}"
:data="tableData">
<vxe-column field="id" title="单号" width="185" align="center"></vxe-column>
<vxe-column field="customer_id" title="客户ID" width="185" align="center">
<template #default="{ row }">
<vxe-button mode="text" status="primary" @click="onRefresh(row)">{{ row.customer_id }}</vxe-button>
</template>
</vxe-column>
<vxe-column field="avatar" title="头像" :visible="false" width="185" align="center">
<template #default="{ row }">
<vxe-button mode="text" status="primary" @click="onRefresh(row)">刷新</vxe-button>
</template>
</vxe-column>
<vxe-column field="customer_name" title="账户名称" width="185" align="center"></vxe-column>
<vxe-column field="operator_name" title="运营商" width="185" align="center">
<template #default="{ row }">
<vxe-button mode="text" status="primary" @click="onRefresh(row)">{{row.operator_name}}</vxe-button>
</template>
</vxe-column>
<vxe-column field="type" title="交易类型" :visible="false" width="185" align="center"></vxe-column>
<vxe-column field="pay_channel" title="支付渠道" width="185" :visible="false" align="center"></vxe-column>
<vxe-column field="platform" title="支付平台" width="185" :visible="false" align="center"></vxe-column>
<vxe-column field="out_order_id" title="订单号" :visible="false" width="185" align="center"></vxe-column>
<vxe-column field="out_refund_order_id" title="退款订单号" :visible="false" width="185" align="center"></vxe-column>
<vxe-column field="refund_info" title="退款信息" width="185" :visible="false" align="center"></vxe-column>
<vxe-column field="platform_transaction_id" title="平台支付单号" :visible="false" width="185" align="center"></vxe-column>
<vxe-column field="amount" title="交易金额" width="185" align="center"></vxe-column>
<vxe-column field="pay_amount" title="金额来源" width="185" align="center"></vxe-column>
<vxe-column field="before_balance" title="交易前后余额" :visible="false" width="185" align="center"></vxe-column>
<vxe-column
field="status"
title="状态"
width="185"
align="center"
type="html"
:formatter="({cellValue}) => {
return `<span style='color: ${statusMap[cellValue].color}'>${statusMap[cellValue].name}</span>`
}"
></vxe-column>
<vxe-column field="remark" title="备注" :visible="false" width="180">
<template #default="{ row }">
<vxe-text-ellipsis line-clamp="2" :content="row.remark"></vxe-text-ellipsis>
</template>
</vxe-column>
<vxe-column field="time" title="交易时间" width="200" :formatter="({row}) => {
return `创建:${row.created_at || '--'}\n完成:${row.finish_time || '--'}`;
}"></vxe-column>
<vxe-column title="操作" width="90" fixed="right" align="center">
<template #default="{ row }">
<vxe-button mode="text" status="primary" :loading="row.loading" @click="onRefresh(row)">刷新</vxe-button>
</template>
</vxe-column>
<!-- <vxe-column field="操作" title="操作" width="185" fixed="right" align="center"></vxe-column> -->
</vxe-table>
<!-- 分页 -->
<vxe-pager
v-model:currentPage="data.pagerConfig.currentPage"
v-model:pageSize="data.pagerConfig.pageSize"
:total="100"
:pageSizes="[10,20]"
@page-change="onPageChange">
</vxe-pager>
</FaPageMain>
</div>
</template>
<style scoped>
.loading-placeholder {
min-height: 200px;
padding: 16px;
background-color: var(--el-bg-color);
border-radius: 4px;
}
:deep(.transition-height) {
/* 设置面板外边距 */
padding: 10px 20px !important;
}
/* 调整表格中搜索内容form和表格的间距 */
:deep(.vxe-grid--layout-header-wrapper) {
margin-bottom: 10px;
}
</style>
设置骨架屏
最新发布