Files
soul-yongping/app/admin/referral-settings/page.tsx

273 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useEffect, useState } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import { Switch } from "@/components/ui/switch"
import { Slider } from "@/components/ui/slider"
import { Badge } from "@/components/ui/badge"
import { Save, Percent, Users, Wallet, Info } from "lucide-react"
type ReferralConfig = {
distributorShare: number
minWithdrawAmount: number
bindingDays: number
userDiscount: number
enableAutoWithdraw: boolean
}
const DEFAULT_REFERRAL_CONFIG: ReferralConfig = {
distributorShare: 90,
minWithdrawAmount: 10,
bindingDays: 30,
userDiscount: 5,
enableAutoWithdraw: false,
}
export default function ReferralSettingsPage() {
const [config, setConfig] = useState<ReferralConfig>(DEFAULT_REFERRAL_CONFIG)
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
useEffect(() => {
const loadConfig = async () => {
try {
const res = await fetch("/api/db/config?key=referral_config")
if (res.ok) {
const data = await res.json()
if (data?.success && data.config) {
setConfig({
distributorShare: data.config.distributorShare ?? 90,
minWithdrawAmount: data.config.minWithdrawAmount ?? 10,
bindingDays: data.config.bindingDays ?? 30,
userDiscount: data.config.userDiscount ?? 5,
enableAutoWithdraw: data.config.enableAutoWithdraw ?? false,
})
}
}
} catch (e) {
console.error("加载 referral_config 失败:", e)
} finally {
setLoading(false)
}
}
loadConfig()
}, [])
const handleSave = async () => {
setSaving(true)
try {
// 确保所有字段都是正确类型(防止字符串导致计算错误)
const safeConfig = {
distributorShare: Number(config.distributorShare) || 0,
minWithdrawAmount: Number(config.minWithdrawAmount) || 0,
bindingDays: Number(config.bindingDays) || 0,
userDiscount: Number(config.userDiscount) || 0,
enableAutoWithdraw: Boolean(config.enableAutoWithdraw),
}
const body = {
key: "referral_config",
config: safeConfig,
description: "分销 / 推广规则配置",
}
const res = await fetch("/api/db/config", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
})
const data = await res.json()
if (!res.ok || !data?.success) {
alert("保存失败: " + (data?.error || res.statusText))
return
}
alert("✅ 分销配置已保存成功!\n\n• 小程序与网站的推广规则会一起生效\n• 绑定关系会使用新的天数配置\n• 佣金比例会立即应用到新订单\n\n如有缓存请刷新前台/小程序页面。")
} catch (e: any) {
console.error("保存 referral_config 失败:", e)
alert("保存失败: " + (e?.message || String(e)))
} finally {
setSaving(false)
}
}
const handleNumberChange = (field: keyof ReferralConfig) => (e: React.ChangeEvent<HTMLInputElement>) => {
const value = parseFloat(e.target.value || "0")
setConfig((prev) => ({ ...prev, [field]: isNaN(value) ? 0 : value }))
}
return (
<div className="p-8 max-w-4xl mx-auto">
<div className="flex justify-between items-center mb-8">
<div>
<h2 className="text-2xl font-bold text-white flex items-center gap-2">
<Wallet className="w-5 h-5 text-[#38bdac]" />
广 /
</h2>
<p className="text-gray-400 mt-1">
90% 30 Web
</p>
</div>
<Button
onClick={handleSave}
disabled={saving || loading}
className="bg-[#38bdac] hover:bg-[#2da396] text-white"
>
<Save className="w-4 h-4 mr-2" />
{saving ? "保存中..." : "保存配置"}
</Button>
</div>
<div className="space-y-6">
{/* 核心规则卡片 */}
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-white">
<Percent className="w-4 h-4 text-[#38bdac]" />
广
</CardTitle>
<CardDescription className="text-gray-400">
广
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid grid-cols-3 gap-6">
<div className="space-y-2">
<Label className="text-gray-300 flex items-center gap-2">
<Info className="w-3 h-3 text-[#38bdac]" />
%
</Label>
<Input
type="number"
min={0}
max={100}
className="bg-[#0a1628] border-gray-700 text-white"
value={config.userDiscount}
onChange={handleNumberChange("userDiscount")}
/>
<p className="text-xs text-gray-500"> 5 5%</p>
</div>
<div className="space-y-2">
<Label className="text-gray-300 flex items-center gap-2">
<Users className="w-3 h-3 text-[#38bdac]" />
广%
</Label>
<div className="flex items-center gap-4">
<Slider
className="flex-1"
min={10}
max={100}
step={1}
value={[config.distributorShare]}
onValueChange={([val]) => setConfig((prev) => ({ ...prev, distributorShare: val }))}
/>
<Input
type="number"
min={0}
max={100}
className="w-20 bg-[#0a1628] border-gray-700 text-white text-center"
value={config.distributorShare}
onChange={handleNumberChange("distributorShare")}
/>
</div>
<p className="text-xs text-gray-500">
= × {" "}
<span className="text-[#38bdac] font-mono">{config.distributorShare}%</span>
</p>
</div>
<div className="space-y-2">
<Label className="text-gray-300 flex items-center gap-2">
<Users className="w-3 h-3 text-[#38bdac]" />
</Label>
<Input
type="number"
min={1}
max={365}
className="bg-[#0a1628] border-gray-700 text-white"
value={config.bindingDays}
onChange={handleNumberChange("bindingDays")}
/>
<p className="text-xs text-gray-500"></p>
</div>
</div>
</CardContent>
</Card>
{/* 提现与自动提现 */}
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-white">
<Wallet className="w-4 h-4 text-[#38bdac]" />
</CardTitle>
<CardDescription className="text-gray-400">
广
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid grid-cols-2 gap-6">
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<Input
type="number"
min={0}
step={1}
className="bg-[#0a1628] border-gray-700 text-white"
value={config.minWithdrawAmount}
onChange={handleNumberChange("minWithdrawAmount")}
/>
<p className="text-xs text-gray-500"> X </p>
</div>
<div className="space-y-2">
<Label className="text-gray-300 flex items-center gap-2">
<Badge variant="outline" className="border-[#38bdac]/40 text-[#38bdac] text-[10px]">
</Badge>
</Label>
<div className="flex items-center gap-3 mt-1">
<Switch
checked={config.enableAutoWithdraw}
onCheckedChange={(checked) => setConfig((prev) => ({ ...prev, enableAutoWithdraw: checked }))}
/>
<span className="text-sm text-gray-400">
</span>
</div>
</div>
</div>
</CardContent>
</Card>
{/* 提示卡片 */}
<Card className="bg-[#0f2137] border-gray-700/50">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-gray-200 text-sm">
<Info className="w-4 h-4 text-[#38bdac]" />
使
</CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-xs text-gray-400 leading-relaxed">
<p>
1. <code className="font-mono text-[11px] text-[#38bdac]">system_config.referral_config</code>广
Web 广
</p>
<p>
2.
</p>
<p>
3.
</p>
</CardContent>
</Card>
</div>
</div>
)
}