feat():learning后台管理前端页面初始化
This commit is contained in:
732
src/pages/VmPricing.tsx
Normal file
732
src/pages/VmPricing.tsx
Normal file
@@ -0,0 +1,732 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Card,
|
||||
Table,
|
||||
Button,
|
||||
Input,
|
||||
Select,
|
||||
Space,
|
||||
Form,
|
||||
message,
|
||||
Switch,
|
||||
Typography,
|
||||
InputNumber,
|
||||
Tabs,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
} from 'antd';
|
||||
import { SearchOutlined, ReloadOutlined, EditOutlined, PlusOutlined, DeleteOutlined, CheckOutlined } from '@ant-design/icons';
|
||||
import { VmPricingApi } from '../services/api';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Title } = Typography;
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
interface VmSpecItem {
|
||||
id: number;
|
||||
spec_type: string;
|
||||
cpu_cores: number;
|
||||
memory_gb: number;
|
||||
description?: string;
|
||||
cost_price_per_minute: number;
|
||||
markup_rate: number;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
interface VmTemplateItem {
|
||||
id: number;
|
||||
spec_type: string;
|
||||
template_id: string;
|
||||
is_default: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
const VmPricing: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [specsData, setSpecsData] = useState<VmSpecItem[]>([]);
|
||||
const [templatesData, setTemplatesData] = useState<VmTemplateItem[]>([]);
|
||||
const [specsTotal, setSpecsTotal] = useState(0);
|
||||
const [templatesTotal, setTemplatesTotal] = useState(0);
|
||||
const [specsPage, setSpecsPage] = useState(1);
|
||||
const [templatesPage, setTemplatesPage] = useState(1);
|
||||
const [specsPageSize, setSpecsPageSize] = useState(10);
|
||||
const [templatesPageSize, setTemplatesPageSize] = useState(10);
|
||||
const [activeTab, setActiveTab] = useState('specs');
|
||||
const [editingCell, setEditingCell] = useState<{ rowId: number; field: string } | null>(null);
|
||||
const [editingValue, setEditingValue] = useState<number>(0);
|
||||
const [specModalVisible, setSpecModalVisible] = useState(false);
|
||||
const [templateModalVisible, setTemplateModalVisible] = useState(false);
|
||||
const [specsForm] = Form.useForm();
|
||||
const [templatesForm] = Form.useForm();
|
||||
const [templateForm] = Form.useForm();
|
||||
const [specForm] = Form.useForm();
|
||||
const [specTypes, setSpecTypes] = useState<string[]>([]);
|
||||
|
||||
// 获取规格类型列表(用于模板表单)
|
||||
const fetchSpecTypes = async () => {
|
||||
try {
|
||||
const response = await VmPricingApi.getSpecs({ size: 1000 });
|
||||
const types = [...new Set((response.data || []).map((item: VmSpecItem) => item.spec_type))];
|
||||
setSpecTypes(types);
|
||||
} catch (error) {
|
||||
console.error('获取规格类型失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取规格数据
|
||||
const fetchSpecs = async (searchParams: any = {}) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await VmPricingApi.getSpecs({
|
||||
...searchParams,
|
||||
page: specsPage,
|
||||
size: specsPageSize,
|
||||
});
|
||||
setSpecsData(response.data || []);
|
||||
setSpecsTotal(response.total || 0);
|
||||
} catch (error) {
|
||||
console.error('获取规格数据失败:', error);
|
||||
message.error('获取规格数据失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取模板数据
|
||||
const fetchTemplates = async (searchParams: any = {}) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await VmPricingApi.getTemplates({
|
||||
...searchParams,
|
||||
page: templatesPage,
|
||||
size: templatesPageSize,
|
||||
});
|
||||
setTemplatesData(response.data || []);
|
||||
setTemplatesTotal(response.total || 0);
|
||||
} catch (error) {
|
||||
console.error('获取模板数据失败:', error);
|
||||
message.error('获取模板数据失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSpecTypes(); // 获取规格类型列表
|
||||
if (activeTab === 'specs') {
|
||||
fetchSpecs();
|
||||
} else {
|
||||
fetchTemplates();
|
||||
}
|
||||
}, [specsPage, specsPageSize, templatesPage, templatesPageSize, activeTab]);
|
||||
|
||||
useEffect(() => {
|
||||
// 当规格数据更新时,更新规格类型列表
|
||||
if (specsData.length > 0) {
|
||||
const types = [...new Set(specsData.map(item => item.spec_type))];
|
||||
setSpecTypes(types);
|
||||
}
|
||||
}, [specsData]);
|
||||
|
||||
// 更新规格
|
||||
const handleUpdateSpec = async (id: number, field: string, value: number | boolean) => {
|
||||
try {
|
||||
await VmPricingApi.updateSpec(id, { [field]: value });
|
||||
message.success('更新成功');
|
||||
setEditingCell(null);
|
||||
fetchSpecs();
|
||||
} catch (error) {
|
||||
message.error('更新失败');
|
||||
console.error('更新规格失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 更新状态
|
||||
const handleUpdateStatus = async (id: number, isActive: boolean) => {
|
||||
handleUpdateSpec(id, 'is_active', isActive);
|
||||
};
|
||||
|
||||
// 搜索规格
|
||||
const handleSpecsSearch = (values: any) => {
|
||||
setSpecsPage(1);
|
||||
fetchSpecs(values);
|
||||
};
|
||||
|
||||
// 重置规格搜索
|
||||
const handleSpecsReset = () => {
|
||||
specsForm.resetFields();
|
||||
setSpecsPage(1);
|
||||
fetchSpecs();
|
||||
};
|
||||
|
||||
// 搜索模板
|
||||
const handleTemplatesSearch = (values: any) => {
|
||||
setTemplatesPage(1);
|
||||
fetchTemplates(values);
|
||||
};
|
||||
|
||||
// 重置模板搜索
|
||||
const handleTemplatesReset = () => {
|
||||
templatesForm.resetFields();
|
||||
setTemplatesPage(1);
|
||||
fetchTemplates();
|
||||
};
|
||||
|
||||
// 开始编辑
|
||||
const handleEdit = (rowId: number, field: string, value: number) => {
|
||||
setEditingCell({ rowId, field });
|
||||
setEditingValue(value);
|
||||
};
|
||||
|
||||
// 保存编辑
|
||||
const handleSaveEdit = (rowId: number, field: string) => {
|
||||
let valueToSave = editingValue;
|
||||
// 加价率需要转换为小数(前端显示为百分比,后端存储为小数)
|
||||
if (field === 'markup_rate') {
|
||||
valueToSave = editingValue / 100;
|
||||
}
|
||||
handleUpdateSpec(rowId, field, valueToSave);
|
||||
};
|
||||
|
||||
// 取消编辑
|
||||
const handleCancelEdit = () => {
|
||||
setEditingCell(null);
|
||||
};
|
||||
|
||||
// 创建规格
|
||||
const handleCreateSpec = async (values: any) => {
|
||||
try {
|
||||
// 转换加价率:前端输入百分比,后端存储小数
|
||||
const data = {
|
||||
...values,
|
||||
markup_rate: values.markup_rate ? values.markup_rate / 100 : undefined,
|
||||
};
|
||||
await VmPricingApi.createSpec(data);
|
||||
message.success('规格创建成功');
|
||||
setSpecModalVisible(false);
|
||||
specForm.resetFields();
|
||||
fetchSpecs();
|
||||
fetchSpecTypes(); // 刷新规格类型列表
|
||||
} catch (error: any) {
|
||||
message.error(error?.response?.data?.message || '规格创建失败');
|
||||
console.error('创建规格失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 创建模板
|
||||
const handleCreateTemplate = async (values: any) => {
|
||||
try {
|
||||
await VmPricingApi.createTemplate(values);
|
||||
message.success('模板创建成功');
|
||||
setTemplateModalVisible(false);
|
||||
templateForm.resetFields();
|
||||
fetchTemplates();
|
||||
} catch (error: any) {
|
||||
message.error(error?.response?.data?.message || '模板创建失败');
|
||||
console.error('创建模板失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除规格
|
||||
const handleDeleteSpec = async (id: number) => {
|
||||
try {
|
||||
await VmPricingApi.deleteSpec(id);
|
||||
message.success('规格删除成功');
|
||||
fetchSpecs();
|
||||
fetchSpecTypes(); // 刷新规格类型列表
|
||||
} catch (error) {
|
||||
message.error('规格删除失败');
|
||||
console.error('删除规格失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除模板
|
||||
const handleDeleteTemplate = async (id: number) => {
|
||||
try {
|
||||
await VmPricingApi.deleteTemplate(id);
|
||||
message.success('模板删除成功');
|
||||
fetchTemplates();
|
||||
} catch (error) {
|
||||
message.error('模板删除失败');
|
||||
console.error('删除模板失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 设置默认模板
|
||||
const handleSetDefaultTemplate = async (id: number) => {
|
||||
try {
|
||||
await VmPricingApi.setDefaultTemplate(id);
|
||||
message.success('默认模板设置成功');
|
||||
fetchTemplates();
|
||||
} catch (error) {
|
||||
message.error('设置默认模板失败');
|
||||
console.error('设置默认模板失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 规格表格列
|
||||
const specsColumns = [
|
||||
{
|
||||
title: '配置类型',
|
||||
dataIndex: 'spec_type',
|
||||
key: 'spec_type',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: 'CPU核心',
|
||||
dataIndex: 'cpu_cores',
|
||||
key: 'cpu_cores',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '内存(GB)',
|
||||
dataIndex: 'memory_gb',
|
||||
key: 'memory_gb',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
width: 150,
|
||||
render: (text: string) => text || '-',
|
||||
},
|
||||
{
|
||||
title: '成本价($/分钟)',
|
||||
dataIndex: 'cost_price_per_minute',
|
||||
key: 'cost_price_per_minute',
|
||||
width: 160,
|
||||
render: (value: number, record: VmSpecItem) => {
|
||||
if (editingCell?.rowId === record.id && editingCell?.field === 'cost_price_per_minute') {
|
||||
return (
|
||||
<Space.Compact>
|
||||
<InputNumber
|
||||
value={editingValue}
|
||||
onChange={(val) => setEditingValue(val || 0)}
|
||||
precision={8}
|
||||
min={0}
|
||||
style={{ width: 120 }}
|
||||
autoFocus
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={() => handleSaveEdit(record.id, 'cost_price_per_minute')}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
<Button size="small" onClick={handleCancelEdit}>
|
||||
取消
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}
|
||||
onClick={() => handleEdit(record.id, 'cost_price_per_minute', value)}
|
||||
>
|
||||
<span>${value.toFixed(8)}</span>
|
||||
<EditOutlined style={{ marginLeft: 8, fontSize: 12, color: '#1890ff' }} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '加价率(%)',
|
||||
dataIndex: 'markup_rate',
|
||||
key: 'markup_rate',
|
||||
width: 140,
|
||||
render: (value: number, record: VmSpecItem) => {
|
||||
if (editingCell?.rowId === record.id && editingCell?.field === 'markup_rate') {
|
||||
return (
|
||||
<Space.Compact>
|
||||
<InputNumber
|
||||
value={editingValue}
|
||||
onChange={(val) => setEditingValue(val || 0)}
|
||||
precision={4}
|
||||
min={0}
|
||||
max={100}
|
||||
style={{ width: 100 }}
|
||||
autoFocus
|
||||
formatter={(value) => `${value}%`}
|
||||
parser={(value) => value!.replace('%', '')}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={() => handleSaveEdit(record.id, 'markup_rate')}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
<Button size="small" onClick={handleCancelEdit}>
|
||||
取消
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}
|
||||
onClick={() => handleEdit(record.id, 'markup_rate', value * 100)}
|
||||
>
|
||||
<span>{(value * 100).toFixed(2)}%</span>
|
||||
<EditOutlined style={{ marginLeft: 8, fontSize: 12, color: '#1890ff' }} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'is_active',
|
||||
key: 'is_active',
|
||||
width: 80,
|
||||
render: (value: boolean, record: VmSpecItem) => (
|
||||
<Switch
|
||||
checked={value}
|
||||
onChange={(checked) => handleUpdateStatus(record.id, checked)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
render: (_: any, record: VmSpecItem) => (
|
||||
<Popconfirm
|
||||
title="确定要删除这个规格吗?"
|
||||
description="删除后该规格的所有配置将无法使用"
|
||||
onConfirm={() => handleDeleteSpec(record.id)}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
size="small"
|
||||
icon={<DeleteOutlined />}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// 模板表格列
|
||||
const templatesColumns = [
|
||||
{
|
||||
title: '配置类型',
|
||||
dataIndex: 'spec_type',
|
||||
key: 'spec_type',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '模板ID',
|
||||
dataIndex: 'template_id',
|
||||
key: 'template_id',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '默认模板',
|
||||
dataIndex: 'is_default',
|
||||
key: 'is_default',
|
||||
width: 100,
|
||||
render: (value: boolean, record: VmTemplateItem) => (
|
||||
value ? (
|
||||
<span style={{ color: '#52c41a' }}>
|
||||
<CheckOutlined /> 是
|
||||
</span>
|
||||
) : (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => handleSetDefaultTemplate(record.id)}
|
||||
>
|
||||
设为默认
|
||||
</Button>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 120,
|
||||
render: (_: any, record: VmTemplateItem) => (
|
||||
<Popconfirm
|
||||
title="确定要删除这个模板吗?"
|
||||
onConfirm={() => handleDeleteTemplate(record.id)}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
size="small"
|
||||
icon={<DeleteOutlined />}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card title={<Title level={4}>虚拟机价格配置</Title>}>
|
||||
<Tabs activeKey={activeTab} onChange={setActiveTab}>
|
||||
<TabPane tab="规格价格" key="specs">
|
||||
<Space style={{ marginBottom: 16 }}>
|
||||
<Form form={specsForm} onFinish={handleSpecsSearch} layout="inline">
|
||||
<Form.Item name="spec_type">
|
||||
<Input placeholder="配置类型" style={{ width: 150 }} />
|
||||
</Form.Item>
|
||||
<Form.Item name="status">
|
||||
<Select placeholder="状态" allowClear style={{ width: 120 }}>
|
||||
<Option value="active">启用</Option>
|
||||
<Option value="inactive">禁用</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit" icon={<SearchOutlined />}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button onClick={handleSpecsReset} icon={<ReloadOutlined />}>
|
||||
重置
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => setSpecModalVisible(true)}
|
||||
>
|
||||
添加规格
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
<Table
|
||||
dataSource={specsData}
|
||||
columns={specsColumns}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
pagination={{
|
||||
current: specsPage,
|
||||
pageSize: specsPageSize,
|
||||
total: specsTotal,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
onChange: (page, pageSize) => {
|
||||
setSpecsPage(page);
|
||||
setSpecsPageSize(pageSize || 10);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="模板配置" key="templates">
|
||||
<Space style={{ marginBottom: 16 }}>
|
||||
<Form form={templatesForm} onFinish={handleTemplatesSearch} layout="inline">
|
||||
<Form.Item name="spec_type">
|
||||
<Input placeholder="配置类型" style={{ width: 150 }} />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit" icon={<SearchOutlined />}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button onClick={handleTemplatesReset} icon={<ReloadOutlined />}>
|
||||
重置
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => setTemplateModalVisible(true)}
|
||||
>
|
||||
添加模板
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
<Table
|
||||
dataSource={templatesData}
|
||||
columns={templatesColumns}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
pagination={{
|
||||
current: templatesPage,
|
||||
pageSize: templatesPageSize,
|
||||
total: templatesTotal,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
onChange: (page, pageSize) => {
|
||||
setTemplatesPage(page);
|
||||
setTemplatesPageSize(pageSize || 10);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
||||
{/* 添加规格模态框 */}
|
||||
<Modal
|
||||
title="添加规格"
|
||||
open={specModalVisible}
|
||||
onCancel={() => {
|
||||
setSpecModalVisible(false);
|
||||
specForm.resetFields();
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<Form
|
||||
form={specForm}
|
||||
layout="vertical"
|
||||
onFinish={handleCreateSpec}
|
||||
>
|
||||
<Form.Item
|
||||
name="spec_type"
|
||||
label="配置类型"
|
||||
rules={[{ required: true, message: '请输入配置类型' }]}
|
||||
>
|
||||
<Input placeholder="如:2c4g, 4c8g, 6c12g" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="cpu_cores"
|
||||
label="CPU核心数"
|
||||
rules={[{ required: true, message: '请输入CPU核心数' }]}
|
||||
>
|
||||
<InputNumber min={1} style={{ width: '100%' }} placeholder="请输入CPU核心数" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="memory_gb"
|
||||
label="内存(GB)"
|
||||
rules={[{ required: true, message: '请输入内存大小' }]}
|
||||
>
|
||||
<InputNumber min={1} style={{ width: '100%' }} placeholder="请输入内存大小(GB)" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="description"
|
||||
label="描述"
|
||||
>
|
||||
<Input placeholder="配置描述(可选)" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="cost_price_per_minute"
|
||||
label="成本价($/分钟)"
|
||||
rules={[{ required: true, message: '请输入成本价' }]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
precision={8}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="请输入成本价(美元/分钟)"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="markup_rate"
|
||||
label="加价率(%)"
|
||||
initialValue={30}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={2}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="默认30%"
|
||||
formatter={(value) => `${value}%`}
|
||||
parser={(value) => value!.replace('%', '')}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="is_active"
|
||||
label="启用"
|
||||
valuePropName="checked"
|
||||
initialValue={true}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit">
|
||||
创建
|
||||
</Button>
|
||||
<Button onClick={() => {
|
||||
setSpecModalVisible(false);
|
||||
specForm.resetFields();
|
||||
}}>
|
||||
取消
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
{/* 添加模板模态框 */}
|
||||
<Modal
|
||||
title="添加模板"
|
||||
open={templateModalVisible}
|
||||
onCancel={() => {
|
||||
setTemplateModalVisible(false);
|
||||
templateForm.resetFields();
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<Form
|
||||
form={templateForm}
|
||||
layout="vertical"
|
||||
onFinish={handleCreateTemplate}
|
||||
>
|
||||
<Form.Item
|
||||
name="spec_type"
|
||||
label="配置类型"
|
||||
rules={[{ required: true, message: '请选择配置类型' }]}
|
||||
>
|
||||
<Select placeholder="请选择配置类型" showSearch>
|
||||
{specTypes.map(type => (
|
||||
<Option key={type} value={type}>{type}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="template_id"
|
||||
label="模板ID"
|
||||
rules={[{ required: true, message: '请输入模板ID' }]}
|
||||
>
|
||||
<Input placeholder="请输入E2B模板ID" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="is_default"
|
||||
label="设为默认模板"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit">
|
||||
创建
|
||||
</Button>
|
||||
<Button onClick={() => {
|
||||
setTemplateModalVisible(false);
|
||||
templateForm.resetFields();
|
||||
}}>
|
||||
取消
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default VmPricing;
|
||||
|
||||
Reference in New Issue
Block a user