"use client" import * as React from "react" import { ManagePage, ManageDetailPanel } from "@/components/common/general/manage-pannel" import { Input } from "@/components/ui/input" import { formatDateTime } from "@/lib/utils" import type { UserPayConfig } from "@/lib/services" import { useAdmin } from "@/contexts/admin-context" /** * 积分配置详情面板组件 * 显示积分配置的详细信息和编辑面板 * * @example * ```tsx * * ``` * @param {UserPayConfig} config - 积分配置 * @param {Partial} editData - 编辑数据 * @param {function} onEditDataChange - 编辑数据改变回调 * @param {function} onSave - 保存回调 * @param {boolean} saving - 是否正在保存 * @returns {React.ReactNode} 积分配置详情面板组件 */ function PayConfigDetailPanel({ config, editData, onEditDataChange, onSave, saving }: { config: UserPayConfig | null editData: Partial onEditDataChange: (field: keyof UserPayConfig, value: UserPayConfig[keyof UserPayConfig]) => void onSave: () => void saving: boolean }) { return ( 配置等级 Level {config?.level} 配置ID {config?.id} 创建时间 {config ? formatDateTime(config.created_at) : ''} 更新时间 {config ? formatDateTime(config.updated_at) : ''} 最低分数 { const value = e.target.value if (value === '') { onEditDataChange('min_score', 0) return } const numValue = parseInt(value) if (isNaN(numValue)) { return } if (numValue >= 0) { onEditDataChange('min_score', numValue) } }} className="text-xs text-right h-4 w-16 px-0 rounded-none border-none shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-[12px]" /> LDC 最高分数 { const value = e.target.value if (value === '') { onEditDataChange('max_score', null) return } const numValue = parseInt(value) if (isNaN(numValue)) { return } if (numValue >= 0) { onEditDataChange('max_score', numValue) } }} className="text-xs text-right h-4 w-16 px-0 rounded-none border-none shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-[12px]" /> LDC 手续费率 { const value = e.target.value if (value === '') { onEditDataChange('fee_rate', '0') return } const numValue = parseInt(value) if (isNaN(numValue)) { return } if (numValue >= 0 && numValue <= 100) { onEditDataChange('fee_rate', (numValue / 100).toString()) } }} className="text-xs text-right h-4 w-16 px-0 mr-3 rounded-none border-none shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-[12px]" /> % 分数转化率 { const value = e.target.value if (value === '') { onEditDataChange('score_rate', '0') return } const numValue = parseInt(value) if (isNaN(numValue)) { return } if (numValue >= 0 && numValue <= 100) { onEditDataChange('score_rate', (numValue / 100).toString()) } }} className="text-xs text-right h-4 w-16 px-0 mr-3 rounded-none border-none shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-[12px]" /> % 分发费率 { const value = e.target.value if (value === '') { onEditDataChange('distribute_rate', '0') return } const numValue = parseInt(value) if (isNaN(numValue)) { return } if (numValue >= 0 && numValue <= 100) { onEditDataChange('distribute_rate', (numValue / 100).toString()) } }} className="text-xs text-right h-4 w-16 px-0 mr-3 rounded-none border-none shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-[12px]" /> % 每日支付上限 { const value = e.target.value if (value === '') { onEditDataChange('daily_limit', null) return } const numValue = parseInt(value) if (isNaN(numValue)) { return } if (numValue >= 0) { onEditDataChange('daily_limit', numValue) } }} className="text-xs text-right h-4 w-16 px-0 rounded-none border-none shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-[12px]" /> LDC ) } /** * 用户积分配置管理组件 * * @example * ```tsx * * ``` * @returns {React.ReactNode} 用户积分配置管理组件 */ export function UserPayConfigs() { const { userPayConfigs: configs, userPayConfigsLoading: loading, userPayConfigsError: error, refetchUserPayConfigs, updateUserPayConfig, deleteUserPayConfig } = useAdmin() const getInitialEditData = (config: UserPayConfig) => ({ min_score: config.min_score, max_score: config.max_score, daily_limit: config.daily_limit, fee_rate: config.fee_rate.toString(), score_rate: config.score_rate.toString(), distribute_rate: config.distribute_rate.toString(), }) const handleSave = async (config: UserPayConfig, editData: Partial) => { if (!config) return await updateUserPayConfig(config.id, { min_score: editData.min_score ?? config.min_score, max_score: editData.max_score, daily_limit: editData.daily_limit, fee_rate: editData.fee_rate?.toString() ?? config.fee_rate.toString(), score_rate: editData.score_rate?.toString() ?? config.score_rate.toString(), distribute_rate: editData.distribute_rate?.toString() ?? config.distribute_rate.toString(), }) await refetchUserPayConfigs() } const handleDelete = async (config: UserPayConfig) => { await deleteUserPayConfig(config.id) } return ( title="积分配置" data={configs} loading={loading} error={error} onReload={refetchUserPayConfigs} getInitialEditData={getInitialEditData} onSave={handleSave} onDelete={handleDelete} getId={(config) => config.id} emptyDescription="未发现积分配置" loadingDescription="配置加载中" columns={[ { header: "等级", cell: (item) => Level {item.level}, width: "min-w-[80px]", align: "left" }, { header: "最低分", cell: (item) => item.min_score, width: "min-w-[200px]", align: "left" }, { header: "最高分", cell: (item) => item.max_score || "无限制", width: "min-w-[200px]", align: "left" }, { header: "每日限额", cell: (item) => item.daily_limit ? `LDC ${ item.daily_limit.toLocaleString() }` : "无限制", width: "min-w-[200px]", align: "left" }, { header: "费率", cell: (item) => `${ (Number(item.fee_rate) * 100).toFixed(2) }%`, width: "min-w-[200px]", align: "left" }, { header: "分数转化率", cell: (item) => `${ (Number(item.score_rate) * 100).toFixed(2) }%`, width: "min-w-[200px]", align: "left" }, { header: "分发费率", cell: (item) => `${ (Number(item.distribute_rate) * 100).toFixed(2) }%`, width: "min-w-[200px]", align: "left" }, { header: "更新时间", cell: (item) => {formatDateTime(item.updated_at)}, width: "min-w-[200px]", align: "left" }, ]} renderDetail={({ selected, hovered, editData, onEditDataChange, onSave, saving }) => ( )} /> ) }
Level {config?.level}
{config?.id}
{config ? formatDateTime(config.created_at) : ''}
{config ? formatDateTime(config.updated_at) : ''}
LDC
%