plan pages
This commit is contained in:
377
Cunkebao/app/scenarios/payment/[id]/page.tsx
Normal file
377
Cunkebao/app/scenarios/payment/[id]/page.tsx
Normal file
@@ -0,0 +1,377 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
import { ChevronLeft, Download, Share2, QrCode, Copy, BarChart4, Users } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
|
||||
export default function PaymentCodeDetailPage({ params }: { params: { id: string } }) {
|
||||
const router = useRouter()
|
||||
const [activeTab, setActiveTab] = useState("detail")
|
||||
|
||||
// 模拟数据
|
||||
const paymentCode = {
|
||||
id: params.id,
|
||||
name: "社群会员付款码",
|
||||
amount: "19.9",
|
||||
description: "加入VIP社群会员",
|
||||
status: "active",
|
||||
qrCodeUrl: "/placeholder.svg?height=300&width=300&query=QR%20Code",
|
||||
paymentMethod: "wechat",
|
||||
createTime: "2023-10-26 11:00:00",
|
||||
totalPayments: 156,
|
||||
totalAmount: 3104.4,
|
||||
distribution: {
|
||||
enabled: true,
|
||||
type: "percentage",
|
||||
value: "10%",
|
||||
totalDistributed: 310.44,
|
||||
},
|
||||
redPacket: {
|
||||
enabled: true,
|
||||
amount: "5",
|
||||
totalSent: 156,
|
||||
totalAmount: 780,
|
||||
},
|
||||
autoWelcome: {
|
||||
enabled: true,
|
||||
message: "感谢您的支付,欢迎加入我们的VIP社群!",
|
||||
},
|
||||
tags: ["付款客户", "社群会员", "已成交"],
|
||||
}
|
||||
|
||||
const recentPayments = [
|
||||
{ id: 1, user: "用户1", amount: "19.9", time: "2023-10-26 15:30:00", status: "success" },
|
||||
{ id: 2, user: "用户2", amount: "19.9", time: "2023-10-26 14:45:00", status: "success" },
|
||||
{ id: 3, user: "用户3", amount: "19.9", time: "2023-10-26 13:20:00", status: "success" },
|
||||
{ id: 4, user: "用户4", amount: "19.9", time: "2023-10-26 12:10:00", status: "success" },
|
||||
{ id: 5, user: "用户5", amount: "19.9", time: "2023-10-26 11:05:00", status: "success" },
|
||||
]
|
||||
|
||||
const handleStatusChange = (checked: boolean) => {
|
||||
toast({
|
||||
title: checked ? "付款码已启用" : "付款码已停用",
|
||||
description: checked ? "客户现在可以通过此付款码支付" : "客户将无法通过此付款码支付",
|
||||
})
|
||||
}
|
||||
|
||||
const handleCopy = (text: string) => {
|
||||
navigator.clipboard.writeText(text)
|
||||
toast({
|
||||
title: "复制成功",
|
||||
description: "内容已复制到剪贴板",
|
||||
})
|
||||
}
|
||||
|
||||
const handleDownload = () => {
|
||||
toast({
|
||||
title: "下载成功",
|
||||
description: "付款码已保存到相册",
|
||||
})
|
||||
}
|
||||
|
||||
const handleShare = () => {
|
||||
toast({
|
||||
title: "分享成功",
|
||||
description: "付款码已分享",
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-gray-50">
|
||||
{/* 头部 */}
|
||||
<header className="sticky top-0 z-10 bg-white border-b">
|
||||
<div className="flex items-center justify-between h-14 px-4">
|
||||
<div className="flex items-center">
|
||||
<Button variant="ghost" size="icon" onClick={() => router.back()}>
|
||||
<ChevronLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<h1 className="ml-2 text-lg font-medium">付款码详情</h1>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Switch checked={paymentCode.status === "active"} onCheckedChange={handleStatusChange} />
|
||||
<span className="ml-2 text-sm">{paymentCode.status === "active" ? "启用中" : "已停用"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* 标签页 */}
|
||||
<div className="flex-1 px-4 pb-20">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
||||
<TabsList className="grid grid-cols-3 mb-4">
|
||||
<TabsTrigger value="detail" className="text-xs">
|
||||
<QrCode className="h-4 w-4 mr-1" />
|
||||
付款码
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="payments" className="text-xs">
|
||||
<BarChart4 className="h-4 w-4 mr-1" />
|
||||
支付记录
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="settings" className="text-xs">
|
||||
<Users className="h-4 w-4 mr-1" />
|
||||
高级设置
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* 付款码详情 */}
|
||||
<TabsContent value="detail" className="space-y-4">
|
||||
<Card>
|
||||
<CardContent className="p-4 flex flex-col items-center">
|
||||
<h2 className="font-medium text-center mb-2">{paymentCode.name}</h2>
|
||||
<p className="text-sm text-gray-500 mb-4">{paymentCode.description}</p>
|
||||
|
||||
<div className="relative w-64 h-64 mb-4">
|
||||
<Image
|
||||
src={paymentCode.qrCodeUrl || "/placeholder.svg"}
|
||||
alt="付款码"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="text-center mb-4">
|
||||
<Badge variant="outline" className="text-green-500 bg-green-50">
|
||||
¥{paymentCode.amount}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center space-x-4 w-full">
|
||||
<Button variant="outline" size="sm" onClick={handleDownload}>
|
||||
<Download className="h-4 w-4 mr-1" />
|
||||
下载
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={handleShare}>
|
||||
<Share2 className="h-4 w-4 mr-1" />
|
||||
分享
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleCopy(`付款码:${paymentCode.name},金额:${paymentCode.amount}元`)}
|
||||
>
|
||||
<Copy className="h-4 w-4 mr-1" />
|
||||
复制
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">付款码信息</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">付款码ID</span>
|
||||
<span className="font-medium">{paymentCode.id}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">支付方式</span>
|
||||
<span className="font-medium">
|
||||
{paymentCode.paymentMethod === "wechat"
|
||||
? "微信支付"
|
||||
: paymentCode.paymentMethod === "alipay"
|
||||
? "支付宝"
|
||||
: "微信和支付宝"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">创建时间</span>
|
||||
<span className="font-medium">{paymentCode.createTime}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">支付笔数</span>
|
||||
<span className="font-medium">{paymentCode.totalPayments}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">支付金额</span>
|
||||
<span className="font-medium">¥{paymentCode.totalAmount}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* 支付记录 */}
|
||||
<TabsContent value="payments" className="space-y-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<h3 className="font-medium">最近支付记录</h3>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-blue-500"
|
||||
onClick={() => router.push(`/scenarios/payment/${params.id}/payments`)}
|
||||
>
|
||||
查看全部
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{recentPayments.map((payment) => (
|
||||
<div key={payment.id} className="flex justify-between items-center py-2 border-b last:border-0">
|
||||
<div>
|
||||
<p className="font-medium">{payment.user}</p>
|
||||
<p className="text-xs text-gray-500">{payment.time}</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="font-medium">¥{payment.amount}</p>
|
||||
<Badge variant="outline" className="text-green-500 bg-green-50 text-xs">
|
||||
支付成功
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">支付统计</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">总支付笔数</span>
|
||||
<span className="font-medium">{paymentCode.totalPayments}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">总支付金额</span>
|
||||
<span className="font-medium">¥{paymentCode.totalAmount}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">平均客单价</span>
|
||||
<span className="font-medium">
|
||||
¥{(paymentCode.totalAmount / paymentCode.totalPayments).toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">新增客户数</span>
|
||||
<span className="font-medium">{Math.floor(paymentCode.totalPayments * 0.9)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* 高级设置 */}
|
||||
<TabsContent value="settings" className="space-y-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">分销返利设置</h3>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-gray-500">启用分销返利</span>
|
||||
<Switch checked={paymentCode.distribution.enabled} />
|
||||
</div>
|
||||
|
||||
{paymentCode.distribution.enabled && (
|
||||
<div className="space-y-3 pl-4 border-l-2 border-blue-100">
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">返利类型</span>
|
||||
<span className="font-medium">
|
||||
{paymentCode.distribution.type === "percentage" ? "比例返利" : "固定金额"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">返利值</span>
|
||||
<span className="font-medium">{paymentCode.distribution.value}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">已发放金额</span>
|
||||
<span className="font-medium">¥{paymentCode.distribution.totalDistributed}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">红包奖励设置</h3>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-gray-500">启用红包奖励</span>
|
||||
<Switch checked={paymentCode.redPacket.enabled} />
|
||||
</div>
|
||||
|
||||
{paymentCode.redPacket.enabled && (
|
||||
<div className="space-y-3 pl-4 border-l-2 border-blue-100">
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">红包金额</span>
|
||||
<span className="font-medium">¥{paymentCode.redPacket.amount}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">已发放数量</span>
|
||||
<span className="font-medium">{paymentCode.redPacket.totalSent}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">已发放金额</span>
|
||||
<span className="font-medium">¥{paymentCode.redPacket.totalAmount}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">自动欢迎语设置</h3>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-gray-500">启用自动欢迎语</span>
|
||||
<Switch checked={paymentCode.autoWelcome.enabled} />
|
||||
</div>
|
||||
|
||||
{paymentCode.autoWelcome.enabled && (
|
||||
<div className="pl-4 border-l-2 border-blue-100">
|
||||
<div className="py-2">
|
||||
<span className="text-gray-500 block mb-2">欢迎语内容</span>
|
||||
<div className="bg-gray-50 p-3 rounded-md text-sm">{paymentCode.autoWelcome.message}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">客户标签设置</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{paymentCode.tags.map((tag, index) => (
|
||||
<Badge key={index} variant="outline" className="rounded-full">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
<Button variant="outline" size="sm" className="rounded-full text-blue-500">
|
||||
+ 添加标签
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="flex justify-center mt-6">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="text-red-500 border-red-200 hover:bg-red-50"
|
||||
onClick={() => {
|
||||
if (confirm("确定要删除此付款码吗?删除后将无法恢复。")) {
|
||||
toast({
|
||||
title: "付款码已删除",
|
||||
description: "付款码已成功删除",
|
||||
})
|
||||
router.push("/scenarios/payment")
|
||||
}
|
||||
}}
|
||||
>
|
||||
删除付款码
|
||||
</Button>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
3
Cunkebao/app/scenarios/payment/[id]/payments/loading.tsx
Normal file
3
Cunkebao/app/scenarios/payment/[id]/payments/loading.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Loading() {
|
||||
return null
|
||||
}
|
||||
263
Cunkebao/app/scenarios/payment/[id]/payments/page.tsx
Normal file
263
Cunkebao/app/scenarios/payment/[id]/payments/page.tsx
Normal file
@@ -0,0 +1,263 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ChevronLeft, Download, Filter, Search } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { DatePickerWithRange } from "@/components/ui/date-picker"
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
|
||||
export default function PaymentRecordsPage({ params }: { params: { id: string } }) {
|
||||
const router = useRouter()
|
||||
const [searchTerm, setSearchTerm] = useState("")
|
||||
const [statusFilter, setStatusFilter] = useState("all")
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [showFilters, setShowFilters] = useState(false)
|
||||
|
||||
// 模拟数据
|
||||
const paymentRecords = Array.from({ length: 50 }, (_, i) => ({
|
||||
id: i + 1,
|
||||
transactionId: `WX${Date.now()}${i}`,
|
||||
user: `用户${i + 1}`,
|
||||
platformUserId: `oq_${Math.random().toString(36).substr(2, 9)}`,
|
||||
amount: "19.9",
|
||||
paymentTime: new Date(Date.now() - i * 3600000).toLocaleString(),
|
||||
status: i % 10 === 0 ? "refunded" : "success",
|
||||
paymentMethod: "wechat",
|
||||
customerTags: ["付款客户", "社群会员"],
|
||||
}))
|
||||
|
||||
const itemsPerPage = 10
|
||||
const totalPages = Math.ceil(paymentRecords.length / itemsPerPage)
|
||||
const startIndex = (currentPage - 1) * itemsPerPage
|
||||
const endIndex = startIndex + itemsPerPage
|
||||
const currentRecords = paymentRecords.slice(startIndex, endIndex)
|
||||
|
||||
const handleExport = () => {
|
||||
toast({
|
||||
title: "导出成功",
|
||||
description: "支付记录已导出为Excel文件",
|
||||
})
|
||||
}
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
switch (status) {
|
||||
case "success":
|
||||
return (
|
||||
<Badge variant="outline" className="text-green-500 bg-green-50">
|
||||
支付成功
|
||||
</Badge>
|
||||
)
|
||||
case "refunded":
|
||||
return (
|
||||
<Badge variant="outline" className="text-orange-500 bg-orange-50">
|
||||
已退款
|
||||
</Badge>
|
||||
)
|
||||
case "failed":
|
||||
return (
|
||||
<Badge variant="outline" className="text-red-500 bg-red-50">
|
||||
支付失败
|
||||
</Badge>
|
||||
)
|
||||
default:
|
||||
return <Badge variant="outline">{status}</Badge>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-gray-50">
|
||||
{/* 头部 */}
|
||||
<header className="sticky top-0 z-10 bg-white border-b">
|
||||
<div className="flex items-center justify-between h-14 px-4">
|
||||
<div className="flex items-center">
|
||||
<Button variant="ghost" size="icon" onClick={() => router.back()}>
|
||||
<ChevronLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<h1 className="ml-2 text-lg font-medium">支付记录</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" size="icon" onClick={() => setShowFilters(!showFilters)}>
|
||||
<Filter className="h-5 w-5" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" onClick={handleExport}>
|
||||
<Download className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* 搜索和筛选 */}
|
||||
<div className="p-4 space-y-4">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
placeholder="搜索用户名、交易ID"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showFilters && (
|
||||
<Card>
|
||||
<CardContent className="p-4 space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-1.5 block">支付状态</label>
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择状态" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">全部</SelectItem>
|
||||
<SelectItem value="success">支付成功</SelectItem>
|
||||
<SelectItem value="refunded">已退款</SelectItem>
|
||||
<SelectItem value="failed">支付失败</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-1.5 block">支付时间</label>
|
||||
<DatePickerWithRange />
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1"
|
||||
onClick={() => {
|
||||
setStatusFilter("all")
|
||||
setSearchTerm("")
|
||||
}}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
<Button className="flex-1">应用筛选</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 统计信息 */}
|
||||
<div className="px-4 pb-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="grid grid-cols-3 gap-4 text-center">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">总支付笔数</p>
|
||||
<p className="text-xl font-semibold">{paymentRecords.length}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">总支付金额</p>
|
||||
<p className="text-xl font-semibold">¥{(paymentRecords.length * 19.9).toFixed(2)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">成功率</p>
|
||||
<p className="text-xl font-semibold">90%</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 记录列表 - 移动端优化 */}
|
||||
<div className="flex-1 px-4 pb-20">
|
||||
<div className="space-y-3">
|
||||
{currentRecords.map((record) => (
|
||||
<Card key={record.id}>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<div>
|
||||
<p className="font-medium">{record.user}</p>
|
||||
<p className="text-xs text-gray-500">交易ID: {record.transactionId}</p>
|
||||
</div>
|
||||
{getStatusBadge(record.status)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-1 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-500">支付金额</span>
|
||||
<span className="font-medium">¥{record.amount}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-500">支付时间</span>
|
||||
<span>{record.paymentTime}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-500">支付方式</span>
|
||||
<span>{record.paymentMethod === "wechat" ? "微信支付" : "支付宝"}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex flex-wrap gap-1">
|
||||
{record.customerTags.map((tag, index) => (
|
||||
<Badge key={index} variant="outline" className="text-xs">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-3 pt-3 border-t">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full text-blue-500"
|
||||
onClick={() => router.push(`/wechat-accounts/${record.platformUserId}`)}
|
||||
>
|
||||
查看客户详情
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 分页 */}
|
||||
<div className="mt-6">
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}
|
||||
className={currentPage === 1 ? "pointer-events-none opacity-50" : ""}
|
||||
/>
|
||||
</PaginationItem>
|
||||
|
||||
{[...Array(Math.min(5, totalPages))].map((_, i) => {
|
||||
const pageNumber = i + 1
|
||||
return (
|
||||
<PaginationItem key={pageNumber}>
|
||||
<PaginationLink onClick={() => setCurrentPage(pageNumber)} isActive={currentPage === pageNumber}>
|
||||
{pageNumber}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
})}
|
||||
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
|
||||
className={currentPage === totalPages ? "pointer-events-none opacity-50" : ""}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
307
Cunkebao/app/scenarios/payment/new/page.tsx
Normal file
307
Cunkebao/app/scenarios/payment/new/page.tsx
Normal file
@@ -0,0 +1,307 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ChevronLeft, Info } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
|
||||
export default function NewPaymentCodePage() {
|
||||
const router = useRouter()
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
amount: "",
|
||||
description: "",
|
||||
paymentMethod: "wechat",
|
||||
amountType: "fixed",
|
||||
enableDistribution: false,
|
||||
distributionType: "percentage",
|
||||
distributionValue: "",
|
||||
enableRedPacket: false,
|
||||
redPacketAmount: "",
|
||||
enableAutoWelcome: false,
|
||||
welcomeMessage: "",
|
||||
tags: [],
|
||||
})
|
||||
|
||||
const handleChange = (field: string, value: any) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }))
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
// 表单验证
|
||||
if (!formData.name) {
|
||||
toast({
|
||||
title: "请输入付款码名称",
|
||||
variant: "destructive",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (formData.amountType === "fixed" && !formData.amount) {
|
||||
toast({
|
||||
title: "请输入付款金额",
|
||||
variant: "destructive",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
toast({
|
||||
title: "付款码创建成功",
|
||||
description: "您可以在付款码列表中查看和管理",
|
||||
})
|
||||
router.push("/scenarios/payment")
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-gray-50">
|
||||
{/* 头部 */}
|
||||
<header className="sticky top-0 z-10 bg-white border-b">
|
||||
<div className="flex items-center justify-between h-14 px-4">
|
||||
<div className="flex items-center">
|
||||
<Button variant="ghost" size="icon" onClick={() => router.back()}>
|
||||
<ChevronLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<h1 className="ml-2 text-lg font-medium">新建付款码</h1>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* 表单内容 */}
|
||||
<div className="flex-1 p-4 pb-20">
|
||||
<Card className="mb-4">
|
||||
<CardContent className="p-4 space-y-4">
|
||||
<h2 className="font-medium">基本信息</h2>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="name">付款码名称</Label>
|
||||
<Input
|
||||
id="name"
|
||||
placeholder="请输入付款码名称"
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange("name", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="paymentMethod">支付方式</Label>
|
||||
<Select value={formData.paymentMethod} onValueChange={(value) => handleChange("paymentMethod", value)}>
|
||||
<SelectTrigger id="paymentMethod">
|
||||
<SelectValue placeholder="选择支付方式" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="wechat">微信支付</SelectItem>
|
||||
<SelectItem value="alipay">支付宝</SelectItem>
|
||||
<SelectItem value="both">微信和支付宝</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>金额设置</Label>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="h-4 w-4 text-gray-400" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p className="w-[200px] text-xs">
|
||||
固定金额:生成固定金额的付款码
|
||||
<br />
|
||||
自由金额:用户可自行输入支付金额
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<RadioGroup
|
||||
value={formData.amountType}
|
||||
onValueChange={(value) => handleChange("amountType", value)}
|
||||
className="flex flex-col space-y-1"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="fixed" id="fixed" />
|
||||
<Label htmlFor="fixed">固定金额</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="free" id="free" />
|
||||
<Label htmlFor="free">自由金额</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
{formData.amountType === "fixed" && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="amount">付款金额 (元)</Label>
|
||||
<Input
|
||||
id="amount"
|
||||
type="number"
|
||||
step="0.01"
|
||||
placeholder="请输入付款金额"
|
||||
value={formData.amount}
|
||||
onChange={(e) => handleChange("amount", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="description">付款描述</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
placeholder="请输入付款描述,用户支付时可见"
|
||||
value={formData.description}
|
||||
onChange={(e) => handleChange("description", e.target.value)}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="mb-4">
|
||||
<CardContent className="p-4 space-y-4">
|
||||
<h2 className="font-medium">高级设置</h2>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="enableDistribution">启用分销返利</Label>
|
||||
<p className="text-xs text-gray-500">开启后,用户付款可触发分销返利</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="enableDistribution"
|
||||
checked={formData.enableDistribution}
|
||||
onCheckedChange={(checked) => handleChange("enableDistribution", checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{formData.enableDistribution && (
|
||||
<div className="space-y-4 pl-4 border-l-2 border-blue-100">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="distributionType">返利类型</Label>
|
||||
<Select
|
||||
value={formData.distributionType}
|
||||
onValueChange={(value) => handleChange("distributionType", value)}
|
||||
>
|
||||
<SelectTrigger id="distributionType">
|
||||
<SelectValue placeholder="选择返利类型" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="percentage">比例返利</SelectItem>
|
||||
<SelectItem value="fixed">固定金额</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="distributionValue">
|
||||
{formData.distributionType === "percentage" ? "返利比例 (%)" : "返利金额 (元)"}
|
||||
</Label>
|
||||
<Input
|
||||
id="distributionValue"
|
||||
type="number"
|
||||
step={formData.distributionType === "percentage" ? "1" : "0.01"}
|
||||
placeholder={formData.distributionType === "percentage" ? "例如:10" : "例如:5"}
|
||||
value={formData.distributionValue}
|
||||
onChange={(e) => handleChange("distributionValue", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="enableRedPacket">启用红包奖励</Label>
|
||||
<p className="text-xs text-gray-500">开启后,用户付款可获得红包奖励</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="enableRedPacket"
|
||||
checked={formData.enableRedPacket}
|
||||
onCheckedChange={(checked) => handleChange("enableRedPacket", checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{formData.enableRedPacket && (
|
||||
<div className="space-y-2 pl-4 border-l-2 border-blue-100">
|
||||
<Label htmlFor="redPacketAmount">红包金额 (元)</Label>
|
||||
<Input
|
||||
id="redPacketAmount"
|
||||
type="number"
|
||||
step="0.01"
|
||||
placeholder="例如:5"
|
||||
value={formData.redPacketAmount}
|
||||
onChange={(e) => handleChange("redPacketAmount", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="enableAutoWelcome">自动欢迎语</Label>
|
||||
<p className="text-xs text-gray-500">开启后,用户付款成功后自动发送欢迎语</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="enableAutoWelcome"
|
||||
checked={formData.enableAutoWelcome}
|
||||
onCheckedChange={(checked) => handleChange("enableAutoWelcome", checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{formData.enableAutoWelcome && (
|
||||
<div className="space-y-2 pl-4 border-l-2 border-blue-100">
|
||||
<Label htmlFor="welcomeMessage">欢迎语内容</Label>
|
||||
<Textarea
|
||||
id="welcomeMessage"
|
||||
placeholder="请输入欢迎语内容"
|
||||
value={formData.welcomeMessage}
|
||||
onChange={(e) => handleChange("welcomeMessage", e.target.value)}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4 space-y-4">
|
||||
<h2 className="font-medium">客户标签设置</h2>
|
||||
<p className="text-sm text-gray-500">设置通过此付款码添加的客户自动打上的标签</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="outline" size="sm" className="rounded-full">
|
||||
付款客户
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="rounded-full">
|
||||
高价值
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="rounded-full">
|
||||
已成交
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="rounded-full text-blue-500">
|
||||
+ 添加标签
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 底部操作栏 */}
|
||||
<div className="fixed bottom-0 left-0 right-0 bg-white border-t p-4 flex justify-between">
|
||||
<Button variant="outline" onClick={() => router.back()}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSubmit}>创建付款码</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
278
Cunkebao/app/scenarios/payment/page.tsx
Normal file
278
Cunkebao/app/scenarios/payment/page.tsx
Normal file
@@ -0,0 +1,278 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ChevronLeft, Plus, Settings, QrCode, BarChart4, CreditCard, Users, Gift } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
|
||||
export default function PaymentCodePage() {
|
||||
const router = useRouter()
|
||||
const [activeTab, setActiveTab] = useState("codes")
|
||||
|
||||
// 模拟数据
|
||||
const paymentCodes = [
|
||||
{ id: 1, name: "社群会员付款码", amount: "19.9", status: "active", totalPayments: 156, totalAmount: 3104.4 },
|
||||
{ id: 2, name: "课程付款码", amount: "99", status: "active", totalPayments: 78, totalAmount: 7722 },
|
||||
{ id: 3, name: "咨询服务付款码", amount: "199", status: "active", totalPayments: 45, totalAmount: 8955 },
|
||||
{ id: 4, name: "产品购买付款码", amount: "299", status: "active", totalPayments: 32, totalAmount: 9568 },
|
||||
]
|
||||
|
||||
const statistics = {
|
||||
today: { payments: 24, amount: 2568, customers: 22 },
|
||||
week: { payments: 156, amount: 15420, customers: 142 },
|
||||
month: { payments: 311, amount: 29845, customers: 289 },
|
||||
}
|
||||
|
||||
const distributions = [
|
||||
{ id: 1, name: "分销返利规则1", type: "percentage", value: "10%", totalDistributed: 1245.6 },
|
||||
{ id: 2, name: "分销返利规则2", type: "fixed", value: "5元", totalDistributed: 890 },
|
||||
]
|
||||
|
||||
const redPackets = [
|
||||
{ id: 1, name: "新客红包", amount: "5", totalSent: 156, totalAmount: 780 },
|
||||
{ id: 2, name: "推广红包", amount: "10", totalSent: 89, totalAmount: 890 },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-gray-50">
|
||||
{/* 头部 */}
|
||||
<header className="sticky top-0 z-10 bg-white border-b">
|
||||
<div className="flex items-center justify-between h-14 px-4">
|
||||
<div className="flex items-center">
|
||||
<Button variant="ghost" size="icon" onClick={() => router.back()}>
|
||||
<ChevronLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<h1 className="ml-2 text-lg font-medium">付款码获客</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" size="icon" onClick={() => router.push("/scenarios/payment/settings")}>
|
||||
<Settings className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<div className="p-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-lg font-medium">数据概览</h2>
|
||||
<Badge variant="outline" className="text-blue-500 bg-blue-50">
|
||||
今日
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-gray-500">支付笔数</p>
|
||||
<p className="text-xl font-semibold">{statistics.today.payments}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-gray-500">支付金额</p>
|
||||
<p className="text-xl font-semibold">¥{statistics.today.amount}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-gray-500">新增客户</p>
|
||||
<p className="text-xl font-semibold">{statistics.today.customers}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 标签页 */}
|
||||
<div className="flex-1 px-4 pb-20">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
||||
<TabsList className="grid grid-cols-4 mb-4">
|
||||
<TabsTrigger value="codes" className="text-xs">
|
||||
<QrCode className="h-4 w-4 mr-1" />
|
||||
付款码
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="stats" className="text-xs">
|
||||
<BarChart4 className="h-4 w-4 mr-1" />
|
||||
数据统计
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="distribution" className="text-xs">
|
||||
<Users className="h-4 w-4 mr-1" />
|
||||
分销返利
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="redpacket" className="text-xs">
|
||||
<Gift className="h-4 w-4 mr-1" />
|
||||
红包池
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* 付款码列表 */}
|
||||
<TabsContent value="codes" className="space-y-4">
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={() => router.push("/scenarios/payment/new")} size="sm">
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
新建付款码
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{paymentCodes.map((code) => (
|
||||
<Card key={code.id} className="overflow-hidden">
|
||||
<CardContent className="p-0">
|
||||
<div className="flex items-center p-4 border-b">
|
||||
<div className="bg-blue-100 rounded-full p-2 mr-3">
|
||||
<CreditCard className="h-5 w-5 text-blue-500" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-medium">{code.name}</h3>
|
||||
<p className="text-sm text-gray-500">金额: ¥{code.amount}</p>
|
||||
</div>
|
||||
<Badge variant={code.status === "active" ? "success" : "secondary"}>
|
||||
{code.status === "active" ? "启用中" : "已停用"}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 divide-x">
|
||||
<div className="p-3 text-center">
|
||||
<p className="text-sm text-gray-500">支付笔数</p>
|
||||
<p className="font-semibold">{code.totalPayments}</p>
|
||||
</div>
|
||||
<div className="p-3 text-center">
|
||||
<p className="text-sm text-gray-500">支付金额</p>
|
||||
<p className="font-semibold">¥{code.totalAmount}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 border-t">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full text-blue-500"
|
||||
onClick={() => router.push(`/scenarios/payment/${code.id}`)}
|
||||
>
|
||||
查看详情
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</TabsContent>
|
||||
|
||||
{/* 数据统计 */}
|
||||
<TabsContent value="stats" className="space-y-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">支付数据统计</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">今日支付笔数</span>
|
||||
<span className="font-medium">{statistics.today.payments}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">今日支付金额</span>
|
||||
<span className="font-medium">¥{statistics.today.amount}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">今日新增客户</span>
|
||||
<span className="font-medium">{statistics.today.customers}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">本周支付笔数</span>
|
||||
<span className="font-medium">{statistics.week.payments}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">本周支付金额</span>
|
||||
<span className="font-medium">¥{statistics.week.amount}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">本月支付金额</span>
|
||||
<span className="font-medium">¥{statistics.month.amount}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="flex justify-center">
|
||||
<Button variant="outline" onClick={() => router.push("/scenarios/payment/stats")}>
|
||||
查看详细统计
|
||||
</Button>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
{/* 分销返利 */}
|
||||
<TabsContent value="distribution" className="space-y-4">
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={() => router.push("/scenarios/payment/distribution/new")} size="sm">
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
新建分销规则
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{distributions.map((rule) => (
|
||||
<Card key={rule.id}>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<h3 className="font-medium">{rule.name}</h3>
|
||||
<Badge variant="outline">{rule.type === "percentage" ? "比例" : "固定金额"}</Badge>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">返利值</span>
|
||||
<span className="font-medium">{rule.value}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">已发放金额</span>
|
||||
<span className="font-medium">¥{rule.totalDistributed}</span>
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full text-blue-500"
|
||||
onClick={() => router.push(`/scenarios/payment/distribution/${rule.id}`)}
|
||||
>
|
||||
查看详情
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</TabsContent>
|
||||
|
||||
{/* 红包池 */}
|
||||
<TabsContent value="redpacket" className="space-y-4">
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={() => router.push("/scenarios/payment/redpacket/new")} size="sm">
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
新建红包
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{redPackets.map((packet) => (
|
||||
<Card key={packet.id}>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<h3 className="font-medium">{packet.name}</h3>
|
||||
<Badge variant="outline" className="text-red-500 bg-red-50">
|
||||
¥{packet.amount}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">已发放数量</span>
|
||||
<span className="font-medium">{packet.totalSent}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">已发放金额</span>
|
||||
<span className="font-medium">¥{packet.totalAmount}</span>
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full text-blue-500"
|
||||
onClick={() => router.push(`/scenarios/payment/redpacket/${packet.id}`)}
|
||||
>
|
||||
查看详情
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
260
Cunkebao/app/scenarios/payment/stats/page.tsx
Normal file
260
Cunkebao/app/scenarios/payment/stats/page.tsx
Normal file
@@ -0,0 +1,260 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ChevronLeft, Download, Calendar, TrendingUp, TrendingDown } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
|
||||
import { LineChartComponent, BarChartComponent, PieChartComponent } from "@/app/components/common/Charts"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
|
||||
export default function PaymentStatsPage() {
|
||||
const router = useRouter()
|
||||
const [dateRange, setDateRange] = useState("week")
|
||||
const [activeTab, setActiveTab] = useState("overview")
|
||||
|
||||
// 模拟图表数据
|
||||
const lineChartData = [
|
||||
{ name: "周一", 支付笔数: 24, 支付金额: 478.8 },
|
||||
{ name: "周二", 支付笔数: 32, 支付金额: 636.8 },
|
||||
{ name: "周三", 支付笔数: 28, 支付金额: 557.2 },
|
||||
{ name: "周四", 支付笔数: 36, 支付金额: 716.4 },
|
||||
{ name: "周五", 支付笔数: 42, 支付金额: 835.8 },
|
||||
{ name: "周六", 支付笔数: 38, 支付金额: 756.2 },
|
||||
{ name: "周日", 支付笔数: 35, 支付金额: 696.5 },
|
||||
]
|
||||
|
||||
const barChartData = [
|
||||
{ name: "社群会员付款码", 支付笔数: 156, 支付金额: 3104.4 },
|
||||
{ name: "课程付款码", 支付笔数: 78, 支付金额: 7722 },
|
||||
{ name: "咨询服务付款码", 支付笔数: 45, 支付金额: 8955 },
|
||||
{ name: "产品购买付款码", 支付笔数: 32, 支付金额: 9568 },
|
||||
]
|
||||
|
||||
const pieChartData = [
|
||||
{ name: "微信支付", value: 75 },
|
||||
{ name: "支付宝", value: 25 },
|
||||
]
|
||||
|
||||
const lineChartConfig = {
|
||||
支付笔数: { label: "支付笔数", color: "#3b82f6" },
|
||||
支付金额: { label: "支付金额", color: "#10b981" },
|
||||
}
|
||||
|
||||
const barChartConfig = {
|
||||
支付笔数: { label: "支付笔数", color: "#3b82f6" },
|
||||
支付金额: { label: "支付金额", color: "#10b981" },
|
||||
}
|
||||
|
||||
const pieChartConfig = {
|
||||
微信支付: { label: "微信支付", color: "#10b981" },
|
||||
支付宝: { label: "支付宝", color: "#3b82f6" },
|
||||
}
|
||||
|
||||
const handleExport = () => {
|
||||
toast({
|
||||
title: "导出成功",
|
||||
description: "数据统计报表已导出为Excel文件",
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-gray-50">
|
||||
{/* 头部 */}
|
||||
<header className="sticky top-0 z-10 bg-white border-b">
|
||||
<div className="flex items-center justify-between h-14 px-4">
|
||||
<div className="flex items-center">
|
||||
<Button variant="ghost" size="icon" onClick={() => router.back()}>
|
||||
<ChevronLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<h1 className="ml-2 text-lg font-medium">数据统计</h1>
|
||||
</div>
|
||||
<Button variant="ghost" size="icon" onClick={handleExport}>
|
||||
<Download className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* 日期范围选择 */}
|
||||
<div className="p-4">
|
||||
<Select value={dateRange} onValueChange={setDateRange}>
|
||||
<SelectTrigger>
|
||||
<Calendar className="h-4 w-4 mr-2" />
|
||||
<SelectValue placeholder="选择时间范围" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="today">今日</SelectItem>
|
||||
<SelectItem value="week">本周</SelectItem>
|
||||
<SelectItem value="month">本月</SelectItem>
|
||||
<SelectItem value="quarter">本季度</SelectItem>
|
||||
<SelectItem value="year">本年</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<div className="px-4 pb-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-sm text-gray-500">总支付笔数</p>
|
||||
<TrendingUp className="h-4 w-4 text-green-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-semibold">311</p>
|
||||
<p className="text-xs text-green-500 mt-1">+12.5% 较上期</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-sm text-gray-500">总支付金额</p>
|
||||
<TrendingUp className="h-4 w-4 text-green-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-semibold">¥29,845</p>
|
||||
<p className="text-xs text-green-500 mt-1">+8.3% 较上期</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-sm text-gray-500">新增客户</p>
|
||||
<TrendingDown className="h-4 w-4 text-red-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-semibold">289</p>
|
||||
<p className="text-xs text-red-500 mt-1">-3.2% 较上期</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-sm text-gray-500">平均客单价</p>
|
||||
<TrendingUp className="h-4 w-4 text-green-500" />
|
||||
</div>
|
||||
<p className="text-2xl font-semibold">¥95.98</p>
|
||||
<p className="text-xs text-green-500 mt-1">+5.6% 较上期</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 图表标签页 */}
|
||||
<div className="flex-1 px-4 pb-20">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
<TabsList className="grid grid-cols-3 mb-4">
|
||||
<TabsTrigger value="overview">概览</TabsTrigger>
|
||||
<TabsTrigger value="trend">趋势</TabsTrigger>
|
||||
<TabsTrigger value="distribution">分布</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="overview" className="space-y-4">
|
||||
<LineChartComponent
|
||||
title="支付趋势"
|
||||
description="本周支付笔数和金额趋势"
|
||||
data={lineChartData}
|
||||
config={lineChartConfig}
|
||||
height={250}
|
||||
/>
|
||||
|
||||
<BarChartComponent
|
||||
title="付款码对比"
|
||||
description="各付款码支付情况对比"
|
||||
data={barChartData}
|
||||
config={barChartConfig}
|
||||
height={250}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="trend" className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>支付趋势分析</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<LineChartComponent data={lineChartData} config={lineChartConfig} height={300} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">趋势说明</h3>
|
||||
<div className="space-y-2 text-sm">
|
||||
<p>• 本周支付笔数较上周增长 12.5%</p>
|
||||
<p>• 周五为支付高峰期,建议加强营销推广</p>
|
||||
<p>• 周末支付量保持稳定,用户活跃度良好</p>
|
||||
<p>• 平均每日支付笔数为 33.6 笔</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="distribution" className="space-y-4">
|
||||
<PieChartComponent
|
||||
title="支付方式分布"
|
||||
description="各支付方式占比"
|
||||
data={pieChartData}
|
||||
config={pieChartConfig}
|
||||
height={250}
|
||||
/>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">客户分布</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">新客户</span>
|
||||
<div className="flex items-center">
|
||||
<span className="font-medium mr-2">289</span>
|
||||
<span className="text-sm text-gray-500">(92.9%)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 border-b">
|
||||
<span className="text-gray-500">老客户</span>
|
||||
<div className="flex items-center">
|
||||
<span className="font-medium mr-2">22</span>
|
||||
<span className="text-sm text-gray-500">(7.1%)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">复购率</span>
|
||||
<span className="font-medium">7.1%</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<h3 className="font-medium mb-3">地域分布 TOP5</h3>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ city: "广州", count: 89, percent: 28.6 },
|
||||
{ city: "深圳", count: 67, percent: 21.5 },
|
||||
{ city: "上海", count: 45, percent: 14.5 },
|
||||
{ city: "北京", count: 38, percent: 12.2 },
|
||||
{ city: "杭州", count: 32, percent: 10.3 },
|
||||
].map((item, index) => (
|
||||
<div key={index} className="flex items-center justify-between">
|
||||
<span className="text-gray-600">{item.city}</span>
|
||||
<div className="flex items-center">
|
||||
<div className="w-24 bg-gray-200 rounded-full h-2 mr-2">
|
||||
<div className="bg-blue-500 h-2 rounded-full" style={{ width: `${item.percent}%` }} />
|
||||
</div>
|
||||
<span className="text-sm font-medium w-12 text-right">{item.count}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user