解决我的账单页面报错 & 提交记录添加套餐号筛选
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {Suspense, useCallback, useEffect, useState} from 'react'
|
||||
import {PageRecord} from '@/lib/api'
|
||||
import {Bill} from '@/lib/models'
|
||||
import {useStatus} from '@/lib/states'
|
||||
@@ -141,158 +141,159 @@ export default function BillsPage(props: BillsPageProps) {
|
||||
</Form>
|
||||
</section>
|
||||
|
||||
<DataTable
|
||||
data={data.list}
|
||||
status={status}
|
||||
pagination={{
|
||||
total: data.total,
|
||||
page: data.page,
|
||||
size: data.size,
|
||||
onPageChange: async (page: number) => {
|
||||
await refresh(page, data.size)
|
||||
},
|
||||
onSizeChange: async (size: number) => {
|
||||
await refresh(data.page, size)
|
||||
},
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: 'bill_no', header: `账单编号`,
|
||||
},
|
||||
{
|
||||
accessorKey: 'info',
|
||||
header: `账单详情`,
|
||||
cell: ({row}) => {
|
||||
const bill = row.original
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 类型展示 */}
|
||||
<div className="shrink-0">
|
||||
{bill.type === 1 && (
|
||||
<div className="flex gap-2 items-center bg-orange-50 w-fit px-2 py-1 rounded-md">
|
||||
<CreditCard size={16}/>
|
||||
<span>消费</span>
|
||||
</div>
|
||||
)}
|
||||
{bill.type === 2 && (
|
||||
<div className="flex gap-2 items-center bg-green-50 w-fit px-2 py-1 rounded-md">
|
||||
<CreditCard size={16}/>
|
||||
<span>退款</span>
|
||||
</div>
|
||||
)}
|
||||
{bill.type === 3 && (
|
||||
<div className="flex gap-2 items-center bg-blue-50 w-fit px-2 py-1 rounded-md">
|
||||
<CreditCard size={16}/>
|
||||
<span>充值</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 账单详情 */}
|
||||
<div className="text-sm">
|
||||
{bill.info}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
<Suspense>
|
||||
<DataTable
|
||||
data={data.list}
|
||||
status={status}
|
||||
pagination={{
|
||||
total: data.total,
|
||||
page: data.page,
|
||||
size: data.size,
|
||||
onPageChange: async (page: number) => {
|
||||
await refresh(page, data.size)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: `状态`,
|
||||
cell: ({row}) => {
|
||||
const trade = row.original.trade
|
||||
if (![1, 2, 3, 4, 5].includes(trade?.method)) {
|
||||
onSizeChange: async (size: number) => {
|
||||
await refresh(data.page, size)
|
||||
},
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: 'bill_no', header: `账单编号`,
|
||||
},
|
||||
{
|
||||
accessorKey: 'info',
|
||||
header: `账单详情`,
|
||||
cell: ({row}) => {
|
||||
const bill = row.original
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle size={16} className="text-done"/>
|
||||
<span>已完成</span>
|
||||
{/* 类型展示 */}
|
||||
<div className="shrink-0">
|
||||
{bill.type === 1 && (
|
||||
<div className="flex gap-2 items-center bg-orange-50 w-fit px-2 py-1 rounded-md">
|
||||
<CreditCard size={16}/>
|
||||
<span>消费</span>
|
||||
</div>
|
||||
)}
|
||||
{bill.type === 2 && (
|
||||
<div className="flex gap-2 items-center bg-green-50 w-fit px-2 py-1 rounded-md">
|
||||
<CreditCard size={16}/>
|
||||
<span>退款</span>
|
||||
</div>
|
||||
)}
|
||||
{bill.type === 3 && (
|
||||
<div className="flex gap-2 items-center bg-blue-50 w-fit px-2 py-1 rounded-md">
|
||||
<CreditCard size={16}/>
|
||||
<span>充值</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 账单详情 */}
|
||||
<div className="text-sm">
|
||||
{bill.info}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (!trade) return <span>-</span>
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{trade?.status === 1 ? (
|
||||
<CheckCircle size={16} className="text-done"/>
|
||||
) : trade?.status === 2 ? (
|
||||
<AlertCircle size={16} className="text-weak"/>
|
||||
) : trade?.status === 3 ? (
|
||||
<AlertCircle size={16} className="text-fail"/>
|
||||
) : null}
|
||||
<span>
|
||||
{trade?.status === 1 ? '已完成'
|
||||
: trade?.status === 2 ? '已取消'
|
||||
: trade?.status === 3 ? '已退款' : '-'}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
header: '支付信息',
|
||||
cell: ({row}) => {
|
||||
const amount = typeof row.original.amount === 'string'
|
||||
? parseFloat(row.original.amount)
|
||||
: row.original.amount || 0
|
||||
const trade = row.original.trade
|
||||
const paymentMethodMap = {
|
||||
1: '支付宝*',
|
||||
2: '微信*',
|
||||
3: '其他',
|
||||
4: '支付宝',
|
||||
5: '微信',
|
||||
}
|
||||
const paymentMethod = trade ? paymentMethodMap[trade.method as keyof typeof paymentMethodMap] || '余额' : '余额'
|
||||
return (
|
||||
<div className="flex gap-1">
|
||||
<span className="text-sm">
|
||||
{paymentMethod}
|
||||
</span>
|
||||
<span className={amount > 0 ? 'text-green-500' : 'text-orange-500'}>
|
||||
¥{amount.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: `状态`,
|
||||
cell: ({row}) => {
|
||||
const trade = row.original.trade
|
||||
if (![1, 2, 3, 4, 5].includes(trade?.method)) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle size={16} className="text-done"/>
|
||||
<span>已完成</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (!trade) return <span>-</span>
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{trade?.status === 1 ? (
|
||||
<CheckCircle size={16} className="text-done"/>
|
||||
) : trade?.status === 2 ? (
|
||||
<AlertCircle size={16} className="text-weak"/>
|
||||
) : trade?.status === 3 ? (
|
||||
<AlertCircle size={16} className="text-fail"/>
|
||||
) : null}
|
||||
<span>
|
||||
{trade?.status === 1 ? '已完成'
|
||||
: trade?.status === 2 ? '已取消'
|
||||
: trade?.status === 3 ? '已退款' : '-'}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'platform',
|
||||
header: '支付平台',
|
||||
cell: ({row}) => {
|
||||
const trade = row.original.trade
|
||||
if (!trade) return <span>-</span>
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{trade.platform === 1 ? (
|
||||
<>
|
||||
<span>电脑网站</span>
|
||||
</>
|
||||
) : trade.platform === 2 ? (
|
||||
<>
|
||||
<span>手机网站</span>
|
||||
</>
|
||||
) : (
|
||||
<span>-</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
header: '支付信息',
|
||||
cell: ({row}) => {
|
||||
const amount = typeof row.original.amount === 'string'
|
||||
? parseFloat(row.original.amount)
|
||||
: row.original.amount || 0
|
||||
const trade = row.original.trade
|
||||
const paymentMethodMap = {
|
||||
1: '支付宝*',
|
||||
2: '微信*',
|
||||
3: '其他',
|
||||
4: '支付宝',
|
||||
5: '微信',
|
||||
}
|
||||
const paymentMethod = trade ? paymentMethodMap[trade.method as keyof typeof paymentMethodMap] || '余额' : '余额'
|
||||
return (
|
||||
<div className="flex gap-1">
|
||||
<span className="text-sm">
|
||||
{paymentMethod}
|
||||
</span>
|
||||
<span className={amount > 0 ? 'text-green-500' : 'text-orange-500'}>
|
||||
¥{amount.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'created_at', header: '创建时间', cell: ({row}) => (
|
||||
format(new Date(row.original.created_at), 'yyyy-MM-dd HH:mm:ss')
|
||||
),
|
||||
},
|
||||
// {
|
||||
// accessorKey: 'action', header: `操作`, cell: item => (
|
||||
// <div className="flex gap-2">
|
||||
// -
|
||||
// </div>
|
||||
// ),
|
||||
// },
|
||||
]}
|
||||
/>
|
||||
{
|
||||
accessorKey: 'platform',
|
||||
header: '支付平台',
|
||||
cell: ({row}) => {
|
||||
const trade = row.original.trade
|
||||
if (!trade) return <span>-</span>
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{trade.platform === 1 ? (
|
||||
<>
|
||||
<span>电脑网站</span>
|
||||
</>
|
||||
) : trade.platform === 2 ? (
|
||||
<>
|
||||
<span>手机网站</span>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'created_at',
|
||||
header: '创建时间',
|
||||
cell: ({row}) => {
|
||||
const createdAt = row.original.created_at
|
||||
if (!createdAt) return <span></span>
|
||||
const date = new Date(createdAt)
|
||||
if (isNaN(date.getTime())) return <span></span>
|
||||
return format(date, 'yyyy-MM-dd HH:mm:ss')
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Suspense>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,6 @@ export default function ChannelsPage(props: ChannelsPageProps) {
|
||||
// ======================
|
||||
// render
|
||||
// ======================
|
||||
console.log(data.list, 'data.listdata.list')
|
||||
|
||||
return (
|
||||
<Page>
|
||||
@@ -215,11 +214,35 @@ export default function ChannelsPage(props: ChannelsPageProps) {
|
||||
},
|
||||
{
|
||||
header: '提取时间',
|
||||
cell: ({row}) => format(row.original.created_at, 'yyyy-MM-dd HH:mm:ss'),
|
||||
cell: ({row}) => {
|
||||
const timeValue = row.original.created_at
|
||||
if (!timeValue) return <div>-</div>
|
||||
|
||||
try {
|
||||
const date = new Date(timeValue)
|
||||
if (isNaN(date.getTime())) return <div>-</div>
|
||||
return <div>{format(date, 'yyyy-MM-dd HH:mm:ss')}</div>
|
||||
}
|
||||
catch {
|
||||
return <div>-</div>
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
header: '过期时间',
|
||||
cell: ({row}) => format(row.original.expired_at, 'yyyy-MM-dd HH:mm:ss'),
|
||||
cell: ({row}) => {
|
||||
const timeValue = row.original.expired_at
|
||||
if (!timeValue) return <div>-</div>
|
||||
|
||||
try {
|
||||
const date = new Date(timeValue)
|
||||
if (isNaN(date.getTime())) return <div>-</div>
|
||||
return <div>{format(date, 'yyyy-MM-dd HH:mm:ss')}</div>
|
||||
}
|
||||
catch {
|
||||
return <div>-</div>
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -15,6 +15,7 @@ import DatePicker from '@/components/date-picker'
|
||||
import {Button} from '@/components/ui/button'
|
||||
import {EraserIcon, SearchIcon} from 'lucide-react'
|
||||
import {pageBatch} from '@/actions/batch'
|
||||
import {Input} from '@/components/ui/input'
|
||||
|
||||
export type RecordPageProps = {}
|
||||
|
||||
@@ -34,6 +35,7 @@ export default function RecordPage(props: RecordPageProps) {
|
||||
const filterSchema = z.object({
|
||||
time_start: z.date().optional(),
|
||||
time_end: z.date().optional(),
|
||||
resource_no: z.string().optional(),
|
||||
})
|
||||
type FilterSchema = z.infer<typeof filterSchema>
|
||||
|
||||
@@ -42,6 +44,7 @@ export default function RecordPage(props: RecordPageProps) {
|
||||
defaultValues: {
|
||||
time_start: undefined,
|
||||
time_end: undefined,
|
||||
resource_no: '',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -53,7 +56,9 @@ export default function RecordPage(props: RecordPageProps) {
|
||||
const result = await pageBatch({
|
||||
page,
|
||||
size,
|
||||
...filter,
|
||||
time_start: filter.time_start,
|
||||
time_end: filter.time_end,
|
||||
resource_no: filter.resource_no || undefined,
|
||||
})
|
||||
|
||||
if (result.success && result.data) {
|
||||
@@ -88,12 +93,22 @@ export default function RecordPage(props: RecordPageProps) {
|
||||
<section className="flex justify-between">
|
||||
<div></div>
|
||||
<Form form={filterForm} handler={filterHandler} className="flex-auto flex flex-wrap gap-4 items-end">
|
||||
<FormField name="resource_no" label={<span className="text-sm">套餐编号</span>}>
|
||||
{({id, field}) => (
|
||||
<Input
|
||||
{...field}
|
||||
id={id}
|
||||
className="h-9"
|
||||
value={field.value ?? ''}
|
||||
/>
|
||||
)}
|
||||
</FormField>
|
||||
<fieldset className="flex flex-col gap-2 items-start">
|
||||
<div>
|
||||
<legend className="block text-sm">提取时间</legend>
|
||||
<legend className="block text-sm">过期时间</legend>
|
||||
</div>
|
||||
<div className="flex gap-1 items-center">
|
||||
<FormField<FilterSchema, 'time_start'> name="time_start">
|
||||
<FormField<FilterSchema, 'time_start'> name="time_start" >
|
||||
{({field}) => (
|
||||
<DatePicker
|
||||
placeholder="选择开始时间"
|
||||
@@ -144,6 +159,10 @@ export default function RecordPage(props: RecordPageProps) {
|
||||
onSizeChange: size => fetchRecords(1, size),
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
header: '套餐编号',
|
||||
accessorKey: 'resource.resource_no',
|
||||
},
|
||||
{
|
||||
header: '批次号',
|
||||
cell: ({row}) => <div>{row.original.batch_no}</div>,
|
||||
@@ -174,11 +193,6 @@ export default function RecordPage(props: RecordPageProps) {
|
||||
cell: ({row}) => <div>{row.original.count}</div>,
|
||||
accessorKey: 'count',
|
||||
},
|
||||
// {
|
||||
// header: '资源数量',
|
||||
// cell: ({row}) => <div>{row.original.resource_id}</div>,
|
||||
// accessorKey: 'resource_id',
|
||||
// },
|
||||
{
|
||||
header: '提取时间',
|
||||
cell: ({row}) => {
|
||||
|
||||
@@ -186,7 +186,7 @@ export default function ResourceList({resourceType}: ResourceListProps) {
|
||||
const live = resourceKey === 'long'
|
||||
? (row.original as Resource<2>).long.live
|
||||
: (row.original as Resource<1>).short.live
|
||||
return <span>{isLong ? `${live}小时` : `${live}分钟`}</span>
|
||||
return <span>{isLong ? `${live}分钟` : `${live}分钟`}</span>
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user