更新服务器信息为新的 IP 地址,调整相关文档和代码中的默认配置,确保部署和连接的一致性。同时,优化订单管理界面,增强商品信息的格式化逻辑,提升用户体验。

This commit is contained in:
乘风
2026-02-05 21:08:28 +08:00
parent 1a95aee112
commit 3ccf331e12
61 changed files with 11231 additions and 311 deletions

View File

@@ -24,23 +24,41 @@ interface Purchase {
function OrdersContent() {
const { getAllPurchases, getAllUsers } = useStore()
const [purchases, setPurchases] = useState<Purchase[]>([])
const [purchases, setPurchases] = useState<any[]>([]) // 改为 any[] 以支持新字段
const [users, setUsers] = useState<any[]>([])
const [searchTerm, setSearchTerm] = useState("")
const [statusFilter, setStatusFilter] = useState<string>("all")
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
// 从API获取订单包含用户昵称
async function loadOrders() {
setIsLoading(true)
setPurchases(getAllPurchases())
setUsers(getAllUsers())
setIsLoading(false)
}, [getAllPurchases, getAllUsers])
try {
const ordersRes = await fetch('/api/orders')
const ordersData = await ordersRes.json()
if (ordersData.success && ordersData.orders) {
setPurchases(ordersData.orders)
}
const usersRes = await fetch('/api/db/users')
const usersData = await usersRes.json()
if (usersData.success && usersData.users) {
setUsers(usersData.users)
}
} catch (e) {
console.error('加载订单失败', e)
} finally {
setIsLoading(false)
}
}
// 获取用户昵称
const getUserNickname = (userId: string) => {
const user = users.find(u => u.id === userId)
return user?.nickname || "未知用户"
useEffect(() => {
loadOrders()
}, [])
// 获取用户昵称(优先使用 order.userNickname
const getUserNickname = (order: any) => {
return order.userNickname || users.find((u: any) => u.id === order.userId)?.nickname || "匿名用户"
}
// 获取用户手机号
@@ -48,28 +66,64 @@ function OrdersContent() {
const user = users.find(u => u.id === userId)
return user?.phone || "-"
}
// 格式化商品信息
const formatProduct = (order: any) => {
const type = order.productType || ""
const desc = order.description || ""
if (desc) {
if (type === "section" && desc.includes("章节")) {
if (desc.includes("-")) {
const parts = desc.split("-")
if (parts.length >= 3) {
return {
name: `${parts[1]}章 第${parts[2]}`,
type: "《一场Soul的创业实验》"
}
}
}
return { name: desc, type: "章节购买" }
}
if (type === "fullbook" || desc.includes("全书")) {
return { name: "《一场Soul的创业实验》", type: "全书购买" }
}
if (type === "match" || desc.includes("伙伴")) {
return { name: "找伙伴匹配", type: "功能服务" }
}
return { name: desc, type: "其他" }
}
if (type === "section") return { name: `章节 ${order.productId || ""}`, type: "单章" }
if (type === "fullbook") return { name: "《一场Soul的创业实验》", type: "全书" }
if (type === "match") return { name: "找伙伴匹配", type: "功能" }
return { name: "未知商品", type: type || "其他" }
}
// 过滤订单
const filteredPurchases = purchases.filter((p) => {
const product = formatProduct(p)
const matchSearch =
getUserNickname(p.userId).includes(searchTerm) ||
getUserNickname(p).includes(searchTerm) ||
getUserPhone(p.userId).includes(searchTerm) ||
p.sectionTitle?.includes(searchTerm) ||
p.id.includes(searchTerm)
product.name.includes(searchTerm) ||
(p.orderSn && p.orderSn.includes(searchTerm)) ||
(p.id && p.id.includes(searchTerm))
const matchStatus = statusFilter === "all" || p.status === statusFilter
const matchStatus = statusFilter === "all" || p.status === statusFilter ||
(statusFilter === "completed" && p.status === "paid")
return matchSearch && matchStatus
})
// 统计数据
const totalRevenue = purchases.filter(p => p.status === "completed").reduce((sum, p) => sum + p.amount, 0)
// 统计数据status 可能是 'paid' 或 'completed'
const totalRevenue = purchases.filter(p => p.status === "paid" || p.status === "completed").reduce((sum, p) => sum + Number(p.amount || 0), 0)
const todayRevenue = purchases
.filter(p => {
const today = new Date().toDateString()
return p.status === "completed" && new Date(p.createdAt).toDateString() === today
return (p.status === "paid" || p.status === "completed") && new Date(p.createdAt).toDateString() === today
})
.reduce((sum, p) => sum + p.amount, 0)
.reduce((sum, p) => sum + Number(p.amount || 0), 0)
return (
<div className="p-8 max-w-7xl mx-auto">
@@ -110,6 +164,7 @@ function OrdersContent() {
<option value="all"></option>
<option value="completed"></option>
<option value="pending"></option>
<option value="created"></option>
<option value="failed"></option>
</select>
</div>
@@ -144,61 +199,57 @@ function OrdersContent() {
</TableRow>
</TableHeader>
<TableBody>
{filteredPurchases.map((purchase) => (
<TableRow key={purchase.id} className="hover:bg-[#0a1628] border-gray-700/50">
<TableCell className="font-mono text-xs text-gray-400">
{purchase.id.slice(0, 12)}...
</TableCell>
<TableCell>
<div>
<p className="text-white text-sm">{getUserNickname(purchase.userId)}</p>
<p className="text-gray-500 text-xs">{getUserPhone(purchase.userId)}</p>
</div>
</TableCell>
<TableCell>
<div>
<p className="text-white text-sm">
{purchase.type === "fullbook" ? "整本购买" :
purchase.type === "match" ? "匹配次数" :
purchase.sectionTitle || `章节${purchase.sectionId}`}
</p>
<p className="text-gray-500 text-xs">
{purchase.type === "fullbook" ? "全书" :
purchase.type === "match" ? "功能" : "单章"}
</p>
</div>
</TableCell>
<TableCell className="text-[#38bdac] font-bold">
¥{purchase.amount.toFixed(2)}
</TableCell>
<TableCell className="text-gray-300">
{purchase.paymentMethod === "wechat" ? "微信支付" :
purchase.paymentMethod === "alipay" ? "支付宝" :
purchase.paymentMethod || "微信支付"}
</TableCell>
<TableCell>
{purchase.status === "completed" ? (
<Badge className="bg-green-500/20 text-green-400 hover:bg-green-500/20 border-0">
</Badge>
) : purchase.status === "pending" ? (
<Badge className="bg-yellow-500/20 text-yellow-400 hover:bg-yellow-500/20 border-0">
</Badge>
) : (
<Badge className="bg-red-500/20 text-red-400 hover:bg-red-500/20 border-0">
</Badge>
)}
</TableCell>
<TableCell className="text-[#FFD700]">
{purchase.referrerEarnings ? `¥${purchase.referrerEarnings.toFixed(2)}` : "-"}
</TableCell>
<TableCell className="text-gray-400 text-sm">
{new Date(purchase.createdAt).toLocaleString()}
</TableCell>
</TableRow>
))}
{filteredPurchases.map((purchase) => {
const product = formatProduct(purchase)
return (
<TableRow key={purchase.id} className="hover:bg-[#0a1628] border-gray-700/50">
<TableCell className="font-mono text-xs text-gray-400">
{(purchase.orderSn || purchase.id || "").slice(0, 12)}...
</TableCell>
<TableCell>
<div>
<p className="text-white text-sm">{getUserNickname(purchase)}</p>
<p className="text-gray-500 text-xs">{getUserPhone(purchase.userId)}</p>
</div>
</TableCell>
<TableCell>
<div>
<p className="text-white text-sm">{product.name}</p>
<p className="text-gray-500 text-xs">{product.type}</p>
</div>
</TableCell>
<TableCell className="text-[#38bdac] font-bold">
¥{Number(purchase.amount || 0).toFixed(2)}
</TableCell>
<TableCell className="text-gray-300">
{purchase.paymentMethod === "wechat" ? "微信支付" :
purchase.paymentMethod === "alipay" ? "支付宝" :
purchase.paymentMethod || "微信支付"}
</TableCell>
<TableCell>
{purchase.status === "paid" || purchase.status === "completed" ? (
<Badge className="bg-green-500/20 text-green-400 hover:bg-green-500/20 border-0">
</Badge>
) : purchase.status === "pending" || purchase.status === "created" ? (
<Badge className="bg-yellow-500/20 text-yellow-400 hover:bg-yellow-500/20 border-0">
</Badge>
) : (
<Badge className="bg-red-500/20 text-red-400 hover:bg-red-500/20 border-0">
</Badge>
)}
</TableCell>
<TableCell className="text-[#FFD700]">
{purchase.referrerEarnings ? `¥${Number(purchase.referrerEarnings).toFixed(2)}` : "-"}
</TableCell>
<TableCell className="text-gray-400 text-sm">
{new Date(purchase.createdAt).toLocaleString('zh-CN')}
</TableCell>
</TableRow>
)
})}
{filteredPurchases.length === 0 && (
<TableRow>
<TableCell colSpan={8} className="text-center py-12 text-gray-500">