"use client"; import { useMemo, useState } from "react"; import { CalendarCheck, Flame, MessageSquarePlus, PenLine, Star } from "lucide-react"; import { AdminSettingsGroup, AdminSettingsRow, AdminStepper } from "@/components/admin"; import ConfirmDialog from "@/components/ConfirmDialog"; import useUnsavedGuard from "@/hooks/useUnsavedGuard"; import { apiUpdateSiteSettings, type PublicSettings } from "@/lib/api"; import { toast } from "@/lib/toast"; /** 积分规则 8 键(0–100,后端按 0–100 裁剪) */ type PointsKeys = Pick< PublicSettings, | "points_checkin_base" | "points_streak_every_days" | "points_streak_bonus" | "points_post_reward" | "points_post_daily_cap" | "points_reply_reward" | "points_reply_daily_cap" | "points_recommend_reward" >; const POINTS_KEYS: (keyof PointsKeys)[] = [ "points_checkin_base", "points_streak_every_days", "points_streak_bonus", "points_post_reward", "points_post_daily_cap", "points_reply_reward", "points_reply_daily_cap", "points_recommend_reward", ]; /** 管理员可配置的积分增长规则:签到基础分 / 连续签到加成 / 发帖奖励 / 回复奖励(各带每日上限)/ 推荐奖励 */ export default function EconomySettings({ initial }: { initial: PublicSettings }) { const pickInitial = (): PointsKeys => Object.fromEntries(POINTS_KEYS.map((k) => [k, initial[k]])) as PointsKeys; const [form, setForm] = useState(pickInitial); const [saved, setSaved] = useState(pickInitial); const [saving, setSaving] = useState(false); const dirty = useMemo(() => POINTS_KEYS.some((k) => form[k] !== saved[k]), [form, saved]); const leave = useUnsavedGuard(dirty); const set = (k: keyof PointsKeys, v: number) => setForm((f) => ({ ...f, [k]: v })); const save = async () => { if (saving || !dirty) return; setSaving(true); try { const payload = Object.fromEntries(POINTS_KEYS.map((k) => [k, form[k]])); const res = await apiUpdateSiteSettings(payload); const next = Object.fromEntries(POINTS_KEYS.map((k) => [k, res[k]])) as PointsKeys; setForm(next); setSaved(next); toast("积分规则已保存", "ok"); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setSaving(false); } }; return (
签到基础分 } hintBelow="每日首次签到固定获得的积分(0=关闭,上限 100)。" > set("points_checkin_base", v)} min={0} max={100} unit="分" ariaLabel="签到基础分" disabled={saving} /> 连续签到触发天数 } hintBelow="连续签到每满 N 天额外发放一次加成(0=关闭,上限 100)。" > set("points_streak_every_days", v)} min={0} max={100} unit="天" ariaLabel="连续签到触发天数" disabled={saving} /> 连续签到加成 } hintBelow="触发加成时额外获得的积分(0=关闭,上限 100);与触发天数配合使用。" > set("points_streak_bonus", v)} min={0} max={100} unit="分" ariaLabel="连续签到加成分值" disabled={saving} /> 发帖奖励 } hintBelow="帖子发布成功后发放(0=关闭,上限 100);仅公开可见的帖子计奖,删除不回追。" > set("points_post_reward", v)} min={0} max={100} unit="分" ariaLabel="发帖奖励分值" disabled={saving} /> 发帖奖励每日上限} hintBelow="每人每天最多计奖的帖子数(0=不限,上限 100),防止刷分。" > set("points_post_daily_cap", v)} min={0} max={100} unit="帖" ariaLabel="发帖奖励每日上限" disabled={saving} /> 回复奖励 } hintBelow="评论发布成功后发放(0=关闭,上限 100);仅公开可见的评论计奖。" > set("points_reply_reward", v)} min={0} max={100} unit="分" ariaLabel="回复奖励分值" disabled={saving} /> 回复奖励每日上限} hintBelow="每人每天最多计奖的评论数(0=不限,上限 100),防止刷分。" > set("points_reply_daily_cap", v)} min={0} max={100} unit="条" ariaLabel="回复奖励每日上限" disabled={saving} /> 推荐奖励 } hintBelow="帖子被管理员推荐(加精)时,作者一次性获得该积分(0=关闭,上限 100);推荐人为作者本人时不发放,同一帖子只发放一次。" > set("points_recommend_reward", v)} min={0} max={100} unit="分" ariaLabel="推荐奖励分值" disabled={saving} />
{dirty ? "有未保存的修改" : "已保存的规则对后续签到/发帖/回复/推荐生效"}
); }