2
0

feat:账单管理界面

This commit is contained in:
zhongzm
2025-01-03 18:01:37 +08:00
parent 61c62f48b0
commit 8b2edce4e1
2 changed files with 186 additions and 66 deletions

View File

@@ -3,47 +3,17 @@ import { useTable } from '@/hooks/common/table';
import { SimpleScrollbar } from '~/packages/materials/src';
import { computed, shallowRef } from 'vue';
import { useElementSize } from '@vueuse/core';
interface BillInfo {
billId: string;
username: string;
amount: number;
paymentMethod: string;
}
// 模拟API调用函数
const doGetBillInfo = async (params: any) => {
// TODO: 替换为实际的API调用
return {
data: {
rows: [
{
billId: 'BILL001',
username: '张三',
amount: 199.99,
paymentMethod: 'alipay',
},
{
billId: 'BILL002',
username: '李四',
amount: 299.50,
paymentMethod: 'wechat',
},
],
total: 2
}
};
};
import { fetchBillList } from '@/service/api/auth';
import { Tag as ATag } from 'ant-design-vue';
import BillSearch from '@/views/user-center/bill/modules/bill-search.vue';
const wrapperEl = shallowRef<HTMLElement | null>(null);
const { height: wrapperElHeight } = useElementSize(wrapperEl);
const scrollConfig = computed(() => {
return {
y: wrapperElHeight.value - 72,
x: 800
};
});
const scrollConfig = computed(() => ({
y: wrapperElHeight.value - 72,
x: 1200
}));
const {
columns,
@@ -52,49 +22,73 @@ const {
loading,
getData,
mobilePagination,
searchParams,
resetSearchParams
} = useTable({
apiFn: doGetBillInfo,
apiFn: fetchBillList,
immediate: true,
apiParams: {
pageNum: 1,
pageSize: 10,
},
rowKey: 'billId',
columns: (): AntDesign.TableColumn<AntDesign.TableDataWithIndex<BillInfo>>[] => [
userName: '',
type: undefined,
status: undefined
} as Api.Auth.BillParams,
rowKey: 'id',
pagination: true,
columns: (): AntDesign.TableColumn<Api.Auth.BillInfo>[] => [
{
key: 'billId',
dataIndex: 'billId',
title: '账单ID',
align: 'center',
},
{
key: 'username',
dataIndex: 'username',
key: 'userName',
dataIndex: 'userName',
title: '用户名',
align: 'center',
width: 120
},
{
key: 'amount',
dataIndex: 'amount',
key: 'type',
dataIndex: 'type',
title: '订单类型',
align: 'center',
width: 120,
customRender: ({ text }) => {
const typeMap: Record<number, string> = {
0: '套餐订单',
1: '充值订单'
};
return typeMap[text] || text;
}
},
{
key: 'packageName',
dataIndex: 'packageName',
title: '套餐名称',
align: 'center',
width: 150,
customRender: ({ text }) => text || '-'
},
{
key: 'orderAmount',
dataIndex: 'orderAmount',
title: '金额',
align: 'center',
customRender: ({ record }: { record: BillInfo }) => {
return `¥${record.amount.toFixed(2)}`;
},
width: 120,
customRender: ({ text }) => `¥${Number(text).toFixed(2)}`
},
{
key: 'paymentMethod',
dataIndex: 'paymentMethod',
title: '支付方式',
key: 'status',
dataIndex: 'status',
title: '状态',
align: 'center',
customRender: ({ record }: { record: BillInfo }) => {
const methodMap: Record<string, string> = {
'alipay': '支付宝',
'wechat': '微信支付',
'card': '银行卡',
width: 100,
customRender: ({ text }) => {
const statusMap: Record<number, { text: string; color: string }> = {
0: { text: '支付', color: 'warning' },
1: { text: '已支付', color: 'success' },
2: { text: '已取消', color: 'error' }
};
return methodMap[record.paymentMethod] || record.paymentMethod;
},
const status = statusMap[text] || { text, color: 'default' };
return <ATag color={status.color}>{status.text}</ATag>;
}
},
]
});
@@ -103,6 +97,11 @@ const {
<template>
<SimpleScrollbar>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<BillSearch
v-model:model="searchParams"
@reset="resetSearchParams"
@search="getData"
/>
<ACard
title="账单信息"
:bordered="false"
@@ -123,11 +122,22 @@ const {
:columns="columns"
:data-source="data"
:loading="loading"
row-key="billId"
row-key="id"
size="small"
:pagination="mobilePagination"
:pagination="{
...mobilePagination,
total: mobilePagination.total,
current: searchParams.pageNum,
pageSize: searchParams.pageSize,
showTotal: (total: number) => `共 ${total} 条`
}"
:scroll="scrollConfig"
class="h-full"
@change="(pagination) => {
searchParams.pageNum = pagination.current;
searchParams.pageSize = pagination.pageSize;
getData();
}"
/>
</ACard>
</div>

View File

@@ -0,0 +1,110 @@
<script setup lang="ts">
import { ref, computed } from 'vue';
import { Form } from 'ant-design-vue';
import type { FormInstance } from 'ant-design-vue';
interface SearchModel {
pageNum: number;
pageSize: number;
userName?: string;
type?: number;
status?: number;
}
const props = withDefaults(defineProps<{
model: SearchModel;
}>(), {
model: () => ({
pageNum: 1,
pageSize: 10,
userName: '',
type: undefined,
status: undefined
})
});
const emit = defineEmits<{
'update:model': [value: SearchModel];
'search': [];
'reset': [];
}>();
const formRef = ref<FormInstance>();
const { resetFields } = Form.useForm(props.model);
const formModel = computed({
get: () => props.model,
set: (val: SearchModel) => emit('update:model', val)
});
function handleReset() {
resetFields();
emit('reset');
}
function handleSearch() {
emit('search');
}
const orderTypeOptions = [
{ label: '套餐订单', value: 0 },
{ label: '充值订单', value: 1 }
];
const orderStatusOptions = [
{ label: '待支付', value: 0 },
{ label: '已支付', value: 1 },
{ label: '已取消', value: 2 }
];
</script>
<template>
<ACard :bordered="false" class="search-card">
<AForm
ref="formRef"
:model="formModel"
layout="inline"
class="flex flex-wrap gap-16px items-center"
>
<AFormItem label="用户名">
<AInput
v-model:value="formModel.userName"
placeholder="请输入用户名"
allow-clear
/>
</AFormItem>
<AFormItem label="订单类型">
<ASelect
v-model:value="formModel.type"
placeholder="请选择订单类型"
:options="orderTypeOptions"
allow-clear
style="width: 200px"
/>
</AFormItem>
<AFormItem label="订单状态">
<ASelect
v-model:value="formModel.status"
placeholder="请选择订单状态"
:options="orderStatusOptions"
allow-clear
style="width: 200px"
/>
</AFormItem>
<AFormItem class="flex-1 justify-end">
<ASpace>
<AButton @click="handleReset">重置</AButton>
<AButton type="primary" @click="handleSearch">查询</AButton>
</ASpace>
</AFormItem>
</AForm>
</ACard>
</template>
<style scoped>
.search-card {
:deep(.ant-card-body) {
padding: 16px;
}
}
</style>