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 (
}
onClick={() => navigate('/templates')}
>
{t('templateAdd.back') || t('common.back') || '返回'}
{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 (
}
loading={loading}
disabled={hasErrors}
>
{t('templateAdd.create') || '创建模板'}
)
}}
)
}
export default TemplateAdd