import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { Card, Form, Input, Button, Radio, InputNumber, Switch, message, Typography, Space, Divider } from 'antd' import { ArrowLeftOutlined, SaveOutlined } from '@ant-design/icons' import { apiService } from '../services/api' import { useTranslation } from 'react-i18next' const { Title } = Typography const TemplateAdd: React.FC = () => { const { t } = useTranslation() const navigate = useNavigate() const [form] = Form.useForm() const [loading, setLoading] = useState(false) const [copyMode, setCopyMode] = useState<'RATIO' | 'FIXED'>('RATIO') const handleSubmit = async (values: any) => { // 前端校验:如果填写了 minOrderSize,必须 >= 1 if (values.copyMode === 'RATIO' && values.minOrderSize !== undefined && values.minOrderSize !== null && values.minOrderSize !== '' && Number(values.minOrderSize) < 1) { message.error(t('templateAdd.minOrderSizeError') || '最小金额必须 >= 1') return } // 前端校验:固定金额模式下,fixedAmount 必填且必须 >= 1 if (values.copyMode === 'FIXED') { const fixedAmount = values.fixedAmount if (fixedAmount === undefined || fixedAmount === null || fixedAmount === '') { message.error(t('templateAdd.fixedAmountRequired') || '请输入固定跟单金额') return } const amount = Number(fixedAmount) if (isNaN(amount)) { message.error(t('templateAdd.invalidNumber') || '请输入有效的数字') return } if (amount < 1) { message.error(t('templateAdd.fixedAmountError') || '固定金额必须 >= 1,请重新输入') return } } setLoading(true) try { const response = await apiService.templates.create({ templateName: values.templateName, copyMode: values.copyMode || 'RATIO', // 将百分比转换为小数:100% -> 1.0 copyRatio: values.copyMode === 'RATIO' && values.copyRatio ? (values.copyRatio / 100).toString() : undefined, fixedAmount: values.copyMode === 'FIXED' ? values.fixedAmount?.toString() : undefined, maxOrderSize: values.copyMode === 'RATIO' ? values.maxOrderSize?.toString() : undefined, minOrderSize: values.copyMode === 'RATIO' ? values.minOrderSize?.toString() : undefined, maxDailyOrders: values.maxDailyOrders, priceTolerance: values.priceTolerance?.toString(), supportSell: values.supportSell !== false, minOrderDepth: values.minOrderDepth?.toString(), maxSpread: values.maxSpread?.toString(), minPrice: values.minPrice?.toString(), maxPrice: values.maxPrice?.toString(), pushFilteredOrders: values.pushFilteredOrders ?? false }) if (response.data.code === 0) { message.success(t('templateAdd.createSuccess') || '创建模板成功') navigate('/templates') } else { message.error(response.data.msg || t('templateAdd.createFailed') || '创建模板失败') } } catch (error: any) { message.error(error.message || t('templateAdd.createFailed') || '创建模板失败') } finally { setLoading(false) } } return (
{t('templateAdd.title') || '创建跟单模板'}
setCopyMode(e.target.value)}> {t('templateAdd.ratioMode') || '比例模式'} {t('templateAdd.fixedAmountMode') || '固定金额模式'} {copyMode === 'RATIO' && ( { const parsed = parseFloat(value || '0') if (parsed > 10000) return 10000 return parsed }} formatter={(value) => { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' if (num > 10000) return '10000' return num.toString().replace(/\.0+$/, '') }} /> )} {copyMode === 'FIXED' && ( { // required 已经处理了空值情况,这里只处理非空值的校验 if (value !== undefined && value !== null && value !== '') { const amount = Number(value) if (isNaN(amount)) { return Promise.reject(new Error(t('templateAdd.invalidNumber') || '请输入有效的数字')) } if (amount < 1) { return Promise.reject(new Error(t('templateAdd.fixedAmountError') || '固定金额必须 >= 1,请重新输入')) } } return Promise.resolve() } } ]} > = 1'} formatter={(value) => { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' return num.toString().replace(/\.0+$/, '') }} /> )} {copyMode === 'RATIO' && ( <> { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' return num.toString().replace(/\.0+$/, '') }} /> = $1。例如:设置为 10,如果计算出的跟单金额小于 10,则跳过该订单。'} rules={[ { validator: (_, value) => { if (value === undefined || value === null || value === '') { return Promise.resolve() // 可选字段,允许为空 } if (typeof value === 'number' && value < 1) { return Promise.reject(new Error(t('templateAdd.minOrderSizeError') || '最小金额必须 >= 1')) } return Promise.resolve() } } ]} > = 1(可选)'} formatter={(value) => { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' return num.toString().replace(/\.0+$/, '') }} /> )} { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' return num.toString().replace(/\.0+$/, '') }} /> { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' return num.toString().replace(/\.0+$/, '') }} /> { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' return num.toString().replace(/\.0+$/, '') }} /> {t('templateAdd.priceRangeFilter') || '价格区间过滤'} { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' return num.toString().replace(/\.0+$/, '') }} /> - { if (!value && value !== 0) return '' const num = parseFloat(value.toString()) if (isNaN(num)) return '' return num.toString().replace(/\.0+$/, '') }} /> {/* 跟单卖出 - 表单最底部 */} {({ getFieldsError }) => { const errors = getFieldsError() const hasErrors = errors.some(({ errors }) => errors && errors.length > 0) return ( ) }}
) } export default TemplateAdd