"use client"; import { PlusOutlined } from "@ant-design/icons"; import { useGetIdentity } from "@refinedev/core"; import { useTable } from "@refinedev/antd"; import { List } from "@refinedev/antd"; import { App, Button, Form, Input, InputNumber, Modal, Select, Space, Switch, Table, Typography, type TableColumnsType } from "antd"; import { useState } from "react"; import { adminRequestJson, type AdminIdentity } from "@/lib/admin/providers"; import { formatAdminDate } from "./resource-table"; const { Text } = Typography; type FeatureFlag = { id: string; flagKey: string; version: number; enabled: boolean; rolloutPercentage: number; config: Record; status: string; createdAt: string; publishedAt: string | null; }; type FlagFilters = { q?: string; status?: string }; type FlagForm = { flagKey: string; enabled: boolean; rolloutPercentage: number; configJson: string; }; export default function FeatureFlagsManagement() { const { message } = App.useApp(); const { data: identity } = useGetIdentity(); const table = useTable({ resource: "feature-flags", syncWithLocation: true, pagination: { pageSize: 20 }, onSearch: ({ q, status }) => [ { field: "q", operator: "contains", value: q }, { field: "status", operator: "eq", value: status }, ], }); const [form] = Form.useForm(); const [editing, setEditing] = useState(null); const [open, setOpen] = useState(false); const [saving, setSaving] = useState(false); const [publishingId, setPublishingId] = useState(null); const canWrite = Boolean(identity?.permissions.includes("ops.flags.write")); function edit(item?: FeatureFlag) { setEditing(item ?? null); form.setFieldsValue(item ? { flagKey: item.flagKey, enabled: item.enabled, rolloutPercentage: item.rolloutPercentage, configJson: JSON.stringify(item.config, null, 2), } : { flagKey: "", enabled: false, rolloutPercentage: 0, configJson: "{}", }); setOpen(true); } async function save(values: FlagForm) { setSaving(true); try { let config: unknown; try { config = JSON.parse(values.configJson); } catch { throw new Error("配置 JSON 格式不正确"); } await adminRequestJson("/api/admin/feature-flags", { method: "POST", body: JSON.stringify({ action: "save", id: editing?.id ?? null, flagKey: values.flagKey.trim(), enabled: values.enabled, rolloutPercentage: values.rolloutPercentage, config, expectedVersion: editing?.version ?? null, }), }); message.success("功能开关草稿已保存"); setOpen(false); await table.tableQuery.refetch(); } catch (error) { message.error(error instanceof Error ? error.message : "保存失败"); } finally { setSaving(false); } } async function publish(item: FeatureFlag) { setPublishingId(item.id); try { await adminRequestJson("/api/admin/feature-flags", { method: "POST", body: JSON.stringify({ action: "publish", id: item.id, expectedVersion: item.version }), }); message.success("功能开关已发布"); await table.tableQuery.refetch(); } catch (error) { message.error(error instanceof Error ? error.message : "发布失败"); } finally { setPublishingId(null); } } const columns: TableColumnsType = [ { title: "开关", render: (_, item) => {item.flagKey}v{item.version} }, { title: "状态", render: (_, item) => {item.status === "published" ? "已发布" : item.status === "draft" ? "草稿" : item.status} · {item.enabled ? "已开启" : "已关闭"} }, { title: "灰度", dataIndex: "rolloutPercentage", render: (value) => `${value}%` }, { title: "配置", dataIndex: "config", render: (value) => {JSON.stringify(value)} }, { title: "发布时间", dataIndex: "publishedAt", render: formatAdminDate }, { title: "操作", fixed: "right", render: (_, item) => {canWrite && }{canWrite && item.status !== "published" && }, }, ]; return } onClick={() => edit()}>新增开关 : null}>
; }