129 lines
5.1 KiB
JavaScript
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.

import AddOutlined from "@mui/icons-material/AddOutlined";
import { useMemo, useState } from "react";
import { AdminFilterSelect, AdminListToolbar } from "@/shared/ui/AdminListLayout.jsx";
import { Button } from "@/shared/ui/Button.jsx";
import { DataTable } from "@/shared/ui/DataTable.jsx";
import { TimeText } from "@/shared/ui/TimeText.jsx";
import { formatNumber, formatPercent } from "../format.js";
import { OpsStatusBadge } from "./OpsStatusBadge.jsx";
export function ConfigsView({ apps, luckyGifts, onCreate, onEdit, saving }) {
const [appFilter, setAppFilter] = useState("");
const appOptions = useMemo(
() =>
apps.map((item) => [
item.app_code ?? item.appCode,
`${item.app_name ?? item.appName ?? ""} (${item.app_code ?? item.appCode})`,
]),
[apps],
);
// 配置列表已在 dashboard 阶段跨应用拉全,应用筛选只做前端过滤,避免重复扇出请求。
const items = useMemo(
() => (appFilter ? luckyGifts.filter((config) => config.app_code === appFilter) : luckyGifts),
[appFilter, luckyGifts],
);
const columns = useMemo(() => buildColumns({ onEdit, saving }), [onEdit, saving]);
return (
<div className="ops-view">
<AdminListToolbar
actions={
<Button
disabled={saving}
startIcon={<AddOutlined fontSize="small" />}
variant="primary"
onClick={onCreate}
>
添加配置
</Button>
}
filters={
<AdminFilterSelect
label="应用"
options={[["", "全部应用"], ...appOptions]}
value={appFilter}
onChange={setAppFilter}
/>
}
/>
<DataTable
columns={columns}
emptyLabel="暂无幸运礼物配置"
items={items}
minWidth="1260px"
rowKey={(item) => `${item.app_code}:${item.pool_id}:${item.rule_version}`}
title="幸运礼物配置"
total={items.length}
/>
</div>
);
}
function buildColumns({ onEdit, saving }) {
return [
{ key: "app_code", label: "应用", width: "minmax(88px, 0.7fr)" },
{ key: "pool_id", label: "奖池", width: "minmax(110px, 0.9fr)" },
{
key: "rule_version",
label: "版本",
render: (item) => (item.is_default ? "-" : `v${item.rule_version}`),
width: "minmax(72px, 0.5fr)",
},
{
key: "strategy_version",
label: "策略",
// 升级前的不可变版本可能没有该字段owner 会按 fixed_v2 解释;列表保持同一兼容口径,
// 不能把空值展示成 dynamic_v3避免运营误判已完成策略切换。
render: (item) => item.strategy_version || "fixed_v2",
width: "minmax(112px, 0.8fr)",
},
{
key: "enabled",
label: "状态",
render: (item) => {
// 默认草稿是服务端为未配置奖池生成的占位,不参与线上抽奖,必须与"停用"区分开。
if (item.is_default) {
return <OpsStatusBadge label="默认草稿" tone="warning" />;
}
return item.enabled ? (
<OpsStatusBadge label="已启用" tone="succeeded" />
) : (
<OpsStatusBadge label="已停用" />
);
},
width: "minmax(128px, 0.8fr)",
},
{ key: "target_rtp_percent", label: "目标 RTP", render: (item) => formatPercent(item.target_rtp_percent) },
{ key: "pool_rate_percent", label: "公共奖池", render: (item) => formatPercent(item.pool_rate_percent) },
{
key: "profit_anchor_rate",
label: "平台 / 主播",
render: (item) => `${formatPercent(item.profit_rate_percent)} / ${formatPercent(item.anchor_rate_percent)}`,
width: "minmax(144px, 1fr)",
},
{ key: "control_band_percent", label: "控制带宽", render: (item) => formatPercent(item.control_band_percent) },
{
key: "settlement_window_wager",
label: "结算窗口流水",
render: (item) => formatNumber(item.settlement_window_wager),
},
{
key: "created_at_ms",
label: "发布时间",
render: (item) => (item.is_default ? "-" : <TimeText value={item.created_at_ms} />),
width: "minmax(176px, 1.1fr)",
},
{
key: "actions",
label: "操作",
render: (item) => (
<Button disabled={saving} size="small" onClick={() => onEdit(item)}>
{item.is_default ? "发布配置" : "新增版本"}
</Button>
),
},
];
}