From 72de65d6703965d71db2ac490781bafefdb38370 Mon Sep 17 00:00:00 2001 From: WrBug Date: Sat, 14 Feb 2026 01:23:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E4=BC=98=E5=8C=96=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E8=B4=A6=E6=88=B7=E5=BC=B9=E7=AA=97=E4=B8=8E=E6=96=87?= =?UTF-8?q?=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 导入弹窗:增加步骤条、导入方式改为按钮、代理选项卡片紧凑展示 - 代理选项:标题改为「请选择账户类型」,地址完整显示,有资产标绿,小白说明放入卡片内 - 私钥/助记词:输入框默认两行、禁止换行(换行自动转空格/去除) - 多语言:新增 proxyAddressHelp,更新 selectProxyOption 文案 - Modal 宽度与内边距微调;PositionList 移除未使用 useRef Co-authored-by: Cursor --- frontend/src/components/AccountImportForm.tsx | 169 ++++++++++-------- frontend/src/locales/en/common.json | 3 +- frontend/src/locales/zh-CN/common.json | 3 +- frontend/src/locales/zh-TW/common.json | 3 +- frontend/src/pages/AccountList.tsx | 6 +- .../src/pages/CopyTradingOrders/AddModal.tsx | 4 +- frontend/src/pages/PositionList.tsx | 2 +- 7 files changed, 111 insertions(+), 79 deletions(-) diff --git a/frontend/src/components/AccountImportForm.tsx b/frontend/src/components/AccountImportForm.tsx index 9bb1e4a..4b08c4b 100644 --- a/frontend/src/components/AccountImportForm.tsx +++ b/frontend/src/components/AccountImportForm.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' -import { Form, Input, Button, Radio, Space, Card, Spin, message, Alert } from 'antd' -import { CheckCircleOutlined, ExclamationCircleOutlined } from '@ant-design/icons' +import { Form, Input, Button, Radio, Space, Card, Spin, message, Alert, Steps, Tag } from 'antd' +import { KeyOutlined, WalletOutlined, UserOutlined, CheckCircleOutlined, ExclamationCircleOutlined } from '@ant-design/icons' import { useTranslation } from 'react-i18next' import { useAccountStore } from '../store/accountStore' import { @@ -42,9 +42,14 @@ const AccountImportForm: React.FC = ({ const [loadingProxyOptions, setLoadingProxyOptions] = useState(false) const [step, setStep] = useState<'input' | 'select'>('input') // 步骤:输入 -> 选择代理地址 - // 当私钥输入时,自动推导地址 + // 当私钥输入时,自动推导地址(不支持换行,自动去除换行符) const handlePrivateKeyChange = (e: React.ChangeEvent) => { - const privateKey = e.target.value.trim() + const raw = e.target.value + const normalized = raw.replace(/\r?\n/g, '') + if (normalized !== raw) { + form.setFieldsValue({ privateKey: normalized }) + } + const privateKey = normalized.trim() if (!privateKey) { setDerivedAddress('') setAddressError('') @@ -85,9 +90,14 @@ const AccountImportForm: React.FC = ({ } } - // 当助记词输入时,自动推导地址 + // 当助记词输入时,自动推导地址(不支持换行,换行符转为空格) const handleMnemonicChange = (e: React.ChangeEvent) => { - const mnemonic = e.target.value.trim() + const raw = e.target.value + const normalized = raw.replace(/\r?\n/g, ' ').replace(/\s+/g, ' ').trimStart() + if (/\r?\n/.test(raw)) { + form.setFieldsValue({ mnemonic: normalized }) + } + const mnemonic = normalized.trim() if (!mnemonic) { setDerivedAddress('') setAddressError('') @@ -150,7 +160,7 @@ const AccountImportForm: React.FC = ({ if (options.length > 0) { setStep('select') // 如果有资产,默认选择第一个有资产的选项 - const hasAssetsOption = options.find(opt => opt.hasAssets) + const hasAssetsOption = options.find((opt: ProxyOption) => opt.hasAssets) if (hasAssetsOption) { setSelectedProxyType(hasAssetsOption.walletType) } else { @@ -262,23 +272,38 @@ const AccountImportForm: React.FC = ({ } } + const currentStep = step === 'input' ? 0 : 1 + return ( - <> +
+ }, + { title: t('accountImport.selectProxyOption'), icon: }, + { title: t('accountImport.accountName'), icon: } + ]} + />
- + { setImportType(e.target.value) }} + optionType="button" + buttonStyle="solid" + size={isMobile ? 'middle' : 'large'} > - {t('accountImport.privateKey')} - {t('accountImport.mnemonic')} + {t('accountImport.privateKey')} + {t('accountImport.mnemonic')} @@ -303,9 +328,10 @@ const AccountImportForm: React.FC = ({ validateStatus={addressError ? 'error' : derivedAddress ? 'success' : ''} > e.key === 'Enter' && e.preventDefault()} disabled={loadingProxyOptions} /> @@ -357,9 +383,10 @@ const AccountImportForm: React.FC = ({ validateStatus={addressError ? 'error' : derivedAddress ? 'success' : ''} > e.key === 'Enter' && e.preventDefault()} disabled={loadingProxyOptions} /> @@ -397,14 +424,14 @@ const AccountImportForm: React.FC = ({ +
{t('accountImport.loadingProxyOptions')}
} type="info" showIcon={false} - style={{ marginBottom: '16px' }} + style={{ marginBottom: 16 }} />
)} @@ -424,70 +451,70 @@ const AccountImportForm: React.FC = ({ } } ]} + style={{ marginBottom: 20 }} > {loadingProxyOptions ? ( - +
+ +
) : ( - - {proxyOptions.map((option) => ( - setSelectedProxyType(option.walletType)} - style={{ - cursor: 'pointer', - border: selectedProxyType === option.walletType ? '2px solid #1890ff' : '1px solid #d9d9d9', - backgroundColor: selectedProxyType === option.walletType ? '#e6f7ff' : '#fff' - }} - > -
-
-
- - - {t(`accountImport.proxyOption.${option.walletType}.title`)} - + + {proxyOptions.map((option) => { + const isSelected = selectedProxyType === option.walletType + const typeLabel = option.walletType.toLowerCase() === 'magic' ? 'Magic' : 'Safe' + return ( + setSelectedProxyType(option.walletType)} + size="small" + style={{ + cursor: 'pointer', + borderColor: isSelected ? 'var(--ant-color-primary)' : undefined, + borderWidth: isSelected ? 2 : 1, + backgroundColor: isSelected ? 'var(--ant-color-primary-bg)' : undefined, + transition: 'border-color 0.2s, background-color 0.2s' + }} + > +
+ + + + {typeLabel} + {option.hasAssets && ( - + {t('accountImport.proxyOption.hasAssets')} )} {option.error && ( - + {t('accountImport.proxyOption.error')} )} -
-
- {t(`accountImport.proxyOption.${option.walletType}.description`)} -
-
- {t('accountImport.proxyOption.proxyAddress')}: {option.proxyAddress || '-'} -
- {option.error ? ( -
- {option.error} -
- ) : ( -
- - {t('accountImport.proxyOption.availableBalance')}: {formatUSDC(option.availableBalance)} USDC - - - {t('accountImport.proxyOption.positionBalance')}: {formatUSDC(option.positionBalance)} USDC - - - {t('accountImport.proxyOption.totalBalance')}: {formatUSDC(option.totalBalance)} USDC - - - {t('accountImport.proxyOption.positionCount')}: {option.positionCount} - -
+
+ {!option.error && ( + + {formatUSDC(option.totalBalance)} USDC + )}
-
- - ))} +
+ {option.proxyAddress ? ( + {option.proxyAddress} + ) : ( + '-' + )} + {option.error && ( + {option.error} + )} +
+
+ {t('accountImport.proxyOption.proxyAddressHelp')} +
+ + ) + })} )} @@ -496,30 +523,32 @@ const AccountImportForm: React.FC = ({ - - + + {showCancelButton && onCancel && ( - )} - +
) } diff --git a/frontend/src/locales/en/common.json b/frontend/src/locales/en/common.json index c9c0222..1d2654e 100644 --- a/frontend/src/locales/en/common.json +++ b/frontend/src/locales/en/common.json @@ -214,7 +214,7 @@ "walletType": "Wallet Type", "walletTypeHelp": "Web3 Wallet: Polymarket accounts connected via browser wallets like MetaMask\nMagic: Polymarket accounts logged in via email or social accounts (Google, Twitter, etc.)", "loadingProxyOptions": "Loading proxy addresses and asset information...", - "selectProxyOption": "Please select a proxy address", + "selectProxyOption": "Please select account type", "proxyOptionRequired": "Please select a proxy address", "proxyOption": { "magic": { @@ -226,6 +226,7 @@ "title": "Safe Proxy Address" }, "proxyAddress": "Proxy Address", + "proxyAddressHelp": "The proxy address is the wallet address you actually use on Polymarket: Magic for email/social login accounts, Safe for browser wallet (e.g. MetaMask) accounts. Please select the one that matches how you log in to Polymarket.", "availableBalance": "Available Balance", "positionBalance": "Position Balance", "totalBalance": "Total Balance", diff --git a/frontend/src/locales/zh-CN/common.json b/frontend/src/locales/zh-CN/common.json index 2b3a232..5c44ff3 100644 --- a/frontend/src/locales/zh-CN/common.json +++ b/frontend/src/locales/zh-CN/common.json @@ -213,7 +213,7 @@ "walletType": "钱包类型", "walletTypeHelp": "Web3钱包:使用 MetaMask 等浏览器钱包连接的 Polymarket 账户\nMagic:通过邮箱或社交账号(如 Google、Twitter)登录的 Polymarket 账户", "loadingProxyOptions": "正在获取代理地址和资产信息...", - "selectProxyOption": "请选择代理地址", + "selectProxyOption": "请选择账户类型", "proxyOptionRequired": "请选择一个代理地址", "proxyOption": { "magic": { @@ -225,6 +225,7 @@ "title": "Safe 代理地址" }, "proxyAddress": "代理地址", + "proxyAddressHelp": "代理地址是您在 Polymarket 上实际使用的钱包地址:Magic 为邮箱/社交登录账户,Safe 为浏览器钱包(如 MetaMask)账户。请选择与您 Polymarket 登录方式一致的一项。", "availableBalance": "可用余额", "positionBalance": "仓位余额", "totalBalance": "总余额", diff --git a/frontend/src/locales/zh-TW/common.json b/frontend/src/locales/zh-TW/common.json index 8228776..e6f203c 100644 --- a/frontend/src/locales/zh-TW/common.json +++ b/frontend/src/locales/zh-TW/common.json @@ -214,7 +214,7 @@ "walletType": "錢包類型", "walletTypeHelp": "Web3錢包:使用 MetaMask 等瀏覽器錢包連接的 Polymarket 帳戶\nMagic:透過郵箱或社群帳號(如 Google、Twitter)登入的 Polymarket 帳戶", "loadingProxyOptions": "正在獲取代理地址和資產信息...", - "selectProxyOption": "請選擇代理地址", + "selectProxyOption": "請選擇帳戶類型", "proxyOptionRequired": "請選擇一個代理地址", "proxyOption": { "magic": { @@ -226,6 +226,7 @@ "title": "Safe 代理地址" }, "proxyAddress": "代理地址", + "proxyAddressHelp": "代理地址是您在 Polymarket 上實際使用的錢包地址:Magic 為郵箱/社群登入帳戶,Safe 為瀏覽器錢包(如 MetaMask)帳戶。請選擇與您 Polymarket 登入方式一致的一項。", "availableBalance": "可用餘額", "positionBalance": "倉位餘額", "totalBalance": "總餘額", diff --git a/frontend/src/pages/AccountList.tsx b/frontend/src/pages/AccountList.tsx index 8d93a80..8d2e880 100644 --- a/frontend/src/pages/AccountList.tsx +++ b/frontend/src/pages/AccountList.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react' -import { Card, Table, Button, Space, Tag, Popconfirm, message, Typography, Spin, Modal, Descriptions, Divider, Form, Input } from 'antd' +import { Card, Table, Button, Space, Tag, Popconfirm, message, Typography, Spin, Modal, Descriptions, Divider, Form, Input, Alert } from 'antd' import { PlusOutlined, ReloadOutlined, EditOutlined, CopyOutlined } from '@ant-design/icons' import { useTranslation } from 'react-i18next' import { useAccountStore } from '../store/accountStore' @@ -883,9 +883,9 @@ const AccountList: React.FC = () => { accountImportForm.resetFields() }} footer={null} - width={isMobile ? '95%' : 600} + width={isMobile ? '95%' : 640} style={{ top: isMobile ? 20 : 50 }} - bodyStyle={{ padding: '24px', maxHeight: 'calc(100vh - 150px)', overflow: 'auto' }} + bodyStyle={{ padding: isMobile ? '16px 20px' : '24px 28px', maxHeight: 'calc(100vh - 140px)', overflow: 'auto' }} destroyOnClose maskClosable closable diff --git a/frontend/src/pages/CopyTradingOrders/AddModal.tsx b/frontend/src/pages/CopyTradingOrders/AddModal.tsx index b581c9b..78b3b89 100644 --- a/frontend/src/pages/CopyTradingOrders/AddModal.tsx +++ b/frontend/src/pages/CopyTradingOrders/AddModal.tsx @@ -1106,9 +1106,9 @@ const AddModal: React.FC = ({ accountImportForm.resetFields() }} footer={null} - width={isMobile ? '95%' : 600} + width={isMobile ? '95%' : 640} style={{ top: isMobile ? 20 : 50 }} - bodyStyle={{ padding: '24px', maxHeight: 'calc(100vh - 150px)', overflow: 'auto' }} + bodyStyle={{ padding: isMobile ? '16px 20px' : '24px 28px', maxHeight: 'calc(100vh - 140px)', overflow: 'auto' }} destroyOnClose maskClosable closable diff --git a/frontend/src/pages/PositionList.tsx b/frontend/src/pages/PositionList.tsx index ad722ea..4d31ac5 100644 --- a/frontend/src/pages/PositionList.tsx +++ b/frontend/src/pages/PositionList.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useMemo, useRef } from 'react' +import { useEffect, useState, useMemo } from 'react' import { Card, Table, Tag, message, Space, Input, Radio, Select, Button, Row, Col, Empty, Modal, Form, Descriptions } from 'antd' import { SearchOutlined, AppstoreOutlined, UnorderedListOutlined, UpOutlined, DownOutlined } from '@ant-design/icons' import { useNavigate } from 'react-router-dom'