feat:账单管理界面
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
110
src/views/user-center/bill/modules/bill-search.vue
Normal file
110
src/views/user-center/bill/modules/bill-search.vue
Normal 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>
|
||||||
Reference in New Issue
Block a user