- 新增徽章系统:badge 服务与 handler、管理端配置页、BadgeChip/LevelBadge 展示组件 - 新增等级模型:model/level 及前端 levels 元数据 - 管理端经济模块拆分为 points/badges/levels 独立页面,EconomySettings 迁入设置 - 积分、签到、评论等服务适配等级与徽章逻辑
94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
"use client";
|
||
|
||
import { useCallback, useState } from "react";
|
||
import { useRouter, useSearchParams } from "next/navigation";
|
||
import { AdminSegmented } from "@/components/admin";
|
||
import type { EconomyStats, PublicSettings } from "@/lib/api";
|
||
import EconomyOverview from "./EconomyOverview";
|
||
import EconomySettings from "../settings/EconomySettings";
|
||
import UserPointsTab from "./UserPointsTab";
|
||
|
||
type TabKey = "overview" | "rules" | "users";
|
||
|
||
const TAB_OPTIONS = [
|
||
{ key: "overview", label: "经济总览" },
|
||
{ key: "rules", label: "积分规则" },
|
||
{ key: "users", label: "用户积分" },
|
||
];
|
||
|
||
/**
|
||
* 积分管理页标签壳:经济总览(管理员及以上)/ 积分规则 + 用户积分(超管/站长)。
|
||
* tab 记在 URL query(?tab=)便于刷新/分享保持;三个面板保持挂载、用 display 切换,
|
||
* 避免切标签丢失规则的未保存修改与用户列表状态。
|
||
*/
|
||
export default function PointsTabs({
|
||
stats,
|
||
settings,
|
||
canManage,
|
||
}: {
|
||
stats: EconomyStats | null;
|
||
settings: PublicSettings | null;
|
||
canManage: boolean;
|
||
}) {
|
||
const router = useRouter();
|
||
const searchParams = useSearchParams();
|
||
const queryTab = searchParams.get("tab");
|
||
const [tab, setTab] = useState<TabKey>(() => {
|
||
const t = queryTab;
|
||
if (canManage && (t === "rules" || t === "users")) return t;
|
||
return "overview";
|
||
});
|
||
|
||
const switchTab = useCallback(
|
||
(next: string) => {
|
||
setTab(next as TabKey);
|
||
const params = new URLSearchParams(searchParams.toString());
|
||
if (next === "overview") params.delete("tab");
|
||
else params.set("tab", next);
|
||
const qs = params.toString();
|
||
router.replace(qs ? `?${qs}` : "?", { scroll: false });
|
||
},
|
||
[router, searchParams]
|
||
);
|
||
|
||
const options = canManage
|
||
? TAB_OPTIONS
|
||
: TAB_OPTIONS.filter((o) => o.key === "overview");
|
||
|
||
return (
|
||
<div>
|
||
{canManage ? (
|
||
<div className="mb-4">
|
||
<AdminSegmented
|
||
options={options}
|
||
value={tab}
|
||
onChange={(v) => switchTab(String(v))}
|
||
ariaLabel="积分管理标签"
|
||
/>
|
||
</div>
|
||
) : null}
|
||
|
||
{/* 各面板保持挂载(display 切换):规则未保存修改与用户列表状态不因切标签丢失 */}
|
||
<div style={tab === "overview" ? undefined : { display: "none" }}>
|
||
{stats ? (
|
||
<EconomyOverview stats={stats} />
|
||
) : (
|
||
<div className="panel p-8 text-center">
|
||
<p className="meta">暂时无法加载积分数据</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
{canManage ? (
|
||
<>
|
||
<div style={tab === "rules" ? undefined : { display: "none" }}>
|
||
{settings ? <EconomySettings initial={settings} /> : null}
|
||
</div>
|
||
<div style={tab === "users" ? undefined : { display: "none" }}>
|
||
<UserPointsTab />
|
||
</div>
|
||
</>
|
||
) : null}
|
||
</div>
|
||
);
|
||
}
|