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 { SimpleScrollbar } from '~/packages/materials/src';
import { computed, shallowRef } from 'vue'; import { computed, shallowRef } from 'vue';
import { useElementSize } from '@vueuse/core'; import { useElementSize } from '@vueuse/core';
import { fetchBillList } from '@/service/api/auth';
interface BillInfo { import { Tag as ATag } from 'ant-design-vue';
billId: string; import BillSearch from '@/views/user-center/bill/modules/bill-search.vue';
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
}
};
};
const wrapperEl = shallowRef<HTMLElement | null>(null); const wrapperEl = shallowRef<HTMLElement | null>(null);
const { height: wrapperElHeight } = useElementSize(wrapperEl); const { height: wrapperElHeight } = useElementSize(wrapperEl);
const scrollConfig = computed(() => { const scrollConfig = computed(() => ({
return {
y: wrapperElHeight.value - 72, y: wrapperElHeight.value - 72,
x: 800 x: 1200
}; }));
});
const { const {
columns, columns,
@@ -52,49 +22,73 @@ const {
loading, loading,
getData, getData,
mobilePagination, mobilePagination,
searchParams,
resetSearchParams
} = useTable({ } = useTable({
apiFn: doGetBillInfo, apiFn: fetchBillList,
immediate: true, immediate: true,
apiParams: { apiParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
}, userName: '',
rowKey: 'billId', type: undefined,
columns: (): AntDesign.TableColumn<AntDesign.TableDataWithIndex<BillInfo>>[] => [ status: undefined
} as Api.Auth.BillParams,
rowKey: 'id',
pagination: true,
columns: (): AntDesign.TableColumn<Api.Auth.BillInfo>[] => [
{ {
key: 'billId', key: 'userName',
dataIndex: 'billId', dataIndex: 'userName',
title: '账单ID',
align: 'center',
},
{
key: 'username',
dataIndex: 'username',
title: '用户名', title: '用户名',
align: 'center', align: 'center',
width: 120
}, },
{ {
key: 'amount', key: 'type',
dataIndex: 'amount', 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: '金额', title: '金额',
align: 'center', align: 'center',
customRender: ({ record }: { record: BillInfo }) => { width: 120,
return `¥${record.amount.toFixed(2)}`; customRender: ({ text }) => `¥${Number(text).toFixed(2)}`
},
}, },
{ {
key: 'paymentMethod', key: 'status',
dataIndex: 'paymentMethod', dataIndex: 'status',
title: '支付方式', title: '状态',
align: 'center', align: 'center',
customRender: ({ record }: { record: BillInfo }) => { width: 100,
const methodMap: Record<string, string> = { customRender: ({ text }) => {
'alipay': '支付宝', const statusMap: Record<number, { text: string; color: string }> = {
'wechat': '微信支付', 0: { text: '支付', color: 'warning' },
'card': '银行卡', 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> <template>
<SimpleScrollbar> <SimpleScrollbar>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto"> <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 <ACard
title="账单信息" title="账单信息"
:bordered="false" :bordered="false"
@@ -123,11 +122,22 @@ const {
:columns="columns" :columns="columns"
:data-source="data" :data-source="data"
:loading="loading" :loading="loading"
row-key="billId" row-key="id"
size="small" size="small"
:pagination="mobilePagination" :pagination="{
...mobilePagination,
total: mobilePagination.total,
current: searchParams.pageNum,
pageSize: searchParams.pageSize,
showTotal: (total: number) => `共 ${total} 条`
}"
:scroll="scrollConfig" :scroll="scrollConfig"
class="h-full" class="h-full"
@change="(pagination) => {
searchParams.pageNum = pagination.current;
searchParams.pageSize = pagination.pageSize;
getData();
}"
/> />
</ACard> </ACard>
</div> </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>