import React, { useEffect, useState } from 'react' import { Card, Steps, Button, Space, Tag, Spin, Typography, message } from 'antd' import { CheckCircleOutlined, CloseCircleOutlined, WalletOutlined, KeyOutlined, SafetyOutlined, LinkOutlined, ReloadOutlined } from '@ant-design/icons' import { useTranslation } from 'react-i18next' import { apiService } from '../services/api' const { Paragraph, Text } = Typography export interface SetupStatus { proxyDeployed: boolean tradingEnabled: boolean tokensApproved: boolean approvalDetails?: Record error?: string } interface AccountSetupStatusBlockProps { accountId: number onRefresh?: () => void onAllCompleted?: () => void size?: 'small' | 'default' showApprovalDetails?: boolean /** 嵌入模式:不渲染 Card,仅渲染步骤与授权详情(供弹窗等复用) */ embedded?: boolean } /** 步骤 key 与步骤编号对应 */ const STEP_KEYS = ['step1', 'step2', 'step3'] as const const stepKeyToNumber = (key: string): number => STEP_KEYS.indexOf(key as typeof STEP_KEYS[number]) + 1 const AccountSetupStatusBlock: React.FC = ({ accountId, onRefresh, onAllCompleted, size = 'default', showApprovalDetails = true, embedded = false }) => { const { t } = useTranslation() const [setupStatus, setSetupStatus] = useState(null) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [actionLoading, setActionLoading] = useState(null) const fetchStatus = async () => { if (accountId <= 0) return try { const response = await apiService.accounts.checkSetupStatus(accountId) if (response.data.code === 0 && response.data.data) { setSetupStatus(response.data.data) } else { setSetupStatus(null) } } catch (error) { console.error('获取账户设置状态失败:', error) setSetupStatus(null) } finally { setLoading(false) setRefreshing(false) } } useEffect(() => { setLoading(true) fetchStatus() }, [accountId]) // 每 5 秒轮询最新状态(首次加载完成后且存在未完成步骤时轮询,全部完成后停止) useEffect(() => { if (accountId <= 0 || setupStatus == null) return const allCompleted = setupStatus.proxyDeployed && setupStatus.tradingEnabled && setupStatus.tokensApproved if (allCompleted) return const timer = setInterval(() => { fetchStatus() }, 5000) return () => clearInterval(timer) }, [accountId, setupStatus?.proxyDeployed, setupStatus?.tradingEnabled, setupStatus?.tokensApproved]) // 全部完成时通知父组件(供弹窗等关闭或更新用) const allCompleted = setupStatus != null && setupStatus.proxyDeployed && setupStatus.tradingEnabled && setupStatus.tokensApproved useEffect(() => { if (allCompleted) onAllCompleted?.() }, [allCompleted, onAllCompleted]) const handleRefresh = async () => { setRefreshing(true) await fetchStatus() onRefresh?.() } const handleStepAction = async (key: string) => { const stepNum = stepKeyToNumber(key) if (stepNum < 1) return setActionLoading(key) try { const response = await apiService.accounts.executeSetupStep(accountId, stepNum) const res = response.data if (res.code !== 0) { message.error(res.msg || t('accountSetup.actionFailed')) return } const data = res.data if (data?.redirectUrl) { window.open(data.redirectUrl, '_blank') } if (data?.success !== false) { await fetchStatus() onRefresh?.() if (data?.transactionHash) { message.success(t('accountSetup.actionSuccess')) } } } catch (err) { message.error(t('accountSetup.actionFailed')) } finally { setActionLoading(null) } } if (loading && !setupStatus) { const loadingContent = (
) return embedded ?
{loadingContent}
: ( {loadingContent} ) } if (!setupStatus) { const errorContent = ( <> {t('accountSetup.error.description')}
) return embedded ?
{errorContent}
: ( {errorContent} ) } const steps = [ { key: 'step1', title: t('accountSetup.step1.title'), description: t('accountSetup.step1.description'), icon: , completed: setupStatus.proxyDeployed, actionLabel: t('accountSetup.step1.action') }, { key: 'step2', title: t('accountSetup.step2.title'), description: t('accountSetup.step2.description'), icon: , completed: setupStatus.tradingEnabled, actionLabel: t('accountSetup.step2.action') }, { key: 'step3', title: t('accountSetup.step3.title'), description: t('accountSetup.step3.description'), icon: , completed: setupStatus.tokensApproved, actionLabel: t('accountSetup.step3.action') } ] const stepsContent = ( <> !s.completed)} size="small" style={{ marginBottom: 16 }} > {steps.map((step) => ( {step.title} {step.completed ? ( }> {t('accountSetup.completed')} ) : ( }> {t('accountSetup.pending')} )} } description={
{step.description} {!step.completed && ( )}
} icon={step.icon} status={step.completed ? 'finish' : 'process'} /> ))}
{showApprovalDetails && setupStatus.approvalDetails && Object.keys(setupStatus.approvalDetails).length > 0 && (
{t('accountSetup.approvalDetails.title')} {Object.entries(setupStatus.approvalDetails).map(([contract, allowance]) => { const isUnlimited = allowance === 'unlimited' const isApproved = isUnlimited || parseFloat(allowance) > 0 const displayText = isUnlimited ? t('accountSetup.approvalDetails.unlimited') : isApproved ? `${parseFloat(allowance).toFixed(2)} USDC` : t('accountSetup.approvalDetails.notApproved') return (
{t(`accountSetup.approvalDetails.${contract}`) || contract} {displayText}
) })}
)} {setupStatus.error && (
{setupStatus.error}
)} ) if (embedded) { return
{stepsContent}
} return ( } onClick={handleRefresh} loading={refreshing} > {t('accountSetup.refresh')} } > {stepsContent} ) } export default AccountSetupStatusBlock