refactor(services): 重构服务文件结构,将服务按功能分类到不同目录
- 将服务文件按功能分类到core、ai、analytics、security等目录 - 修复logger导入路径问题,统一使用相对路径 - 更新相关文件的导入路径引用 - 添加新的批量操作组件导出文件 - 修复dashboard页面中的类型错误 - 添加dotenv依赖到package.json
This commit is contained in:
@@ -273,7 +273,7 @@ export const MaterialUpload: React.FC = () => {
|
||||
dataIndex: 'tags',
|
||||
key: 'tags',
|
||||
width: 150,
|
||||
render: (tags: string[]) => tags.map(t => <Tag key={t}>{t}</Tag>),
|
||||
render: (tags: string[]) => tags?.map(t => <Tag key={t}>{t}</Tag>) || '-',
|
||||
},
|
||||
{
|
||||
title: 'Actions',
|
||||
|
||||
@@ -75,14 +75,27 @@ interface Product {
|
||||
stock: number;
|
||||
status: 'DRAFT' | 'PRICED' | 'LISTED' | 'SYNCING' | 'LIVE' | 'SYNC_FAILED' | 'OFFLINE';
|
||||
platformStatus: Record<string, string>;
|
||||
shopId: string;
|
||||
shopName: string;
|
||||
platform: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface Shop {
|
||||
id: string;
|
||||
name: string;
|
||||
platform: string;
|
||||
status: 'ACTIVE' | 'INACTIVE' | 'SUSPENDED';
|
||||
region: string;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
interface FilterState {
|
||||
keyword: string;
|
||||
status: string[];
|
||||
platform: string[];
|
||||
shop: string[];
|
||||
category: string[];
|
||||
priceRange: [number, number] | null;
|
||||
stockRange: [number, number] | null;
|
||||
@@ -136,6 +149,58 @@ const PLATFORMS = [
|
||||
];
|
||||
const CATEGORIES = ['工业自动化', '电子元器件', '工具设备', '仪器仪表', '安防设备'];
|
||||
|
||||
// 店铺数据
|
||||
const SHOPS: Shop[] = [
|
||||
// TikTok平台 - 多个店铺
|
||||
{ id: 'shop-tiktok-1', name: 'TikTok旗舰店A', platform: 'TikTok', status: 'ACTIVE', region: '中国', currency: 'CNY' },
|
||||
{ id: 'shop-tiktok-2', name: 'TikTok旗舰店B', platform: 'TikTok', status: 'ACTIVE', region: '美国', currency: 'USD' },
|
||||
{ id: 'shop-tiktok-3', name: 'TikTok精品店C', platform: 'TikTok', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
|
||||
{ id: 'shop-tiktok-4', name: 'TikTok精品店D', platform: 'TikTok', status: 'INACTIVE', region: '东南亚', currency: 'SGD' },
|
||||
|
||||
// Shopee平台 - 多个店铺
|
||||
{ id: 'shop-shopee-1', name: 'Shopee官方店A', platform: 'Shopee', status: 'ACTIVE', region: '东南亚', currency: 'SGD' },
|
||||
{ id: 'shop-shopee-2', name: 'Shopee官方店B', platform: 'Shopee', status: 'ACTIVE', region: '台湾', currency: 'TWD' },
|
||||
{ id: 'shop-shopee-3', name: 'Shopee旗舰店C', platform: 'Shopee', status: 'ACTIVE', region: '马来西亚', currency: 'MYR' },
|
||||
{ id: 'shop-shopee-4', name: 'Shopee旗舰店D', platform: 'Shopee', status: 'SUSPENDED', region: '菲律宾', currency: 'PHP' },
|
||||
|
||||
// Lazada平台 - 多个店铺
|
||||
{ id: 'shop-lazada-1', name: 'Lazada官方店A', platform: 'Lazada', status: 'ACTIVE', region: '东南亚', currency: 'SGD' },
|
||||
{ id: 'shop-lazada-2', name: 'Lazada官方店B', platform: 'Lazada', status: 'ACTIVE', region: '泰国', currency: 'THB' },
|
||||
{ id: 'shop-lazada-3', name: 'Lazada旗舰店C', platform: 'Lazada', status: 'ACTIVE', region: '越南', currency: 'VND' },
|
||||
|
||||
// Amazon平台 - 多个店铺
|
||||
{ id: 'shop-amazon-1', name: 'Amazon美国店A', platform: 'Amazon', status: 'ACTIVE', region: '美国', currency: 'USD' },
|
||||
{ id: 'shop-amazon-2', name: 'Amazon美国店B', platform: 'Amazon', status: 'ACTIVE', region: '美国', currency: 'USD' },
|
||||
{ id: 'shop-amazon-3', name: 'Amazon欧洲店C', platform: 'Amazon', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
|
||||
{ id: 'shop-amazon-4', name: 'Amazon欧洲店D', platform: 'Amazon', status: 'ACTIVE', region: '英国', currency: 'GBP' },
|
||||
{ id: 'shop-amazon-5', name: 'Amazon日本店E', platform: 'Amazon', status: 'ACTIVE', region: '日本', currency: 'JPY' },
|
||||
|
||||
// eBay平台 - 多个店铺
|
||||
{ id: 'shop-ebay-1', name: 'eBay全球店A', platform: 'eBay', status: 'ACTIVE', region: '全球', currency: 'USD' },
|
||||
{ id: 'shop-ebay-2', name: 'eBay全球店B', platform: 'eBay', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
|
||||
{ id: 'shop-ebay-3', name: 'eBay美国店C', platform: 'eBay', status: 'ACTIVE', region: '美国', currency: 'USD' },
|
||||
|
||||
// Shopify平台 - 多个店铺
|
||||
{ id: 'shop-shopify-1', name: 'Shopify独立站A', platform: 'Shopify', status: 'ACTIVE', region: '全球', currency: 'USD' },
|
||||
{ id: 'shop-shopify-2', name: 'Shopify独立站B', platform: 'Shopify', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
|
||||
{ id: 'shop-shopify-3', name: 'Shopify独立站C', platform: 'Shopify', status: 'ACTIVE', region: '亚洲', currency: 'CNY' },
|
||||
|
||||
// Temu平台 - 多个店铺
|
||||
{ id: 'shop-temu-1', name: 'Temu旗舰店A', platform: 'Temu', status: 'ACTIVE', region: '美国', currency: 'USD' },
|
||||
{ id: 'shop-temu-2', name: 'Temu旗舰店B', platform: 'Temu', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
|
||||
{ id: 'shop-temu-3', name: 'Temu旗舰店C', platform: 'Temu', status: 'ACTIVE', region: '澳大利亚', currency: 'AUD' },
|
||||
|
||||
// SHEIN平台 - 多个店铺
|
||||
{ id: 'shop-shein-1', name: 'SHEIN官方店A', platform: 'Shein', status: 'ACTIVE', region: '全球', currency: 'USD' },
|
||||
{ id: 'shop-shein-2', name: 'SHEIN官方店B', platform: 'Shein', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
|
||||
{ id: 'shop-shein-3', name: 'SHEIN官方店C', platform: 'Shein', status: 'ACTIVE', region: '中东', currency: 'AED' },
|
||||
|
||||
// 其他平台店铺
|
||||
{ id: 'shop-ozon-1', name: 'Ozon旗舰店A', platform: 'Ozon', status: 'ACTIVE', region: '俄罗斯', currency: 'RUB' },
|
||||
{ id: 'shop-ali-1', name: '速卖通旗舰店A', platform: 'AliExpress', status: 'ACTIVE', region: '全球', currency: 'USD' },
|
||||
{ id: 'shop-walmart-1', name: 'Walmart旗舰店A', platform: 'Walmart', status: 'ACTIVE', region: '美国', currency: 'USD' },
|
||||
];
|
||||
|
||||
const PLATFORM_CONFIG: Record<string, { icon: React.ReactNode; color: string }> = {
|
||||
// TikTok系列
|
||||
TikTok: { icon: <GlobalOutlined />, color: '#000000' },
|
||||
@@ -204,6 +269,9 @@ const MOCK_PRODUCTS: Product[] = [
|
||||
stock: 256,
|
||||
status: 'LIVE',
|
||||
platformStatus: { Amazon: 'LIVE', eBay: 'LIVE', Shopee: 'PENDING' },
|
||||
shopId: 'shop-amazon-1',
|
||||
shopName: 'Amazon美国店',
|
||||
platform: 'Amazon',
|
||||
createdAt: '2025-12-15',
|
||||
updatedAt: '2026-03-18',
|
||||
},
|
||||
@@ -220,6 +288,9 @@ const MOCK_PRODUCTS: Product[] = [
|
||||
stock: 128,
|
||||
status: 'DRAFT',
|
||||
platformStatus: {},
|
||||
shopId: 'shop-tiktok-1',
|
||||
shopName: 'TikTok旗舰店',
|
||||
platform: 'TikTok',
|
||||
createdAt: '2026-03-10',
|
||||
updatedAt: '2026-03-10',
|
||||
},
|
||||
@@ -236,6 +307,9 @@ const MOCK_PRODUCTS: Product[] = [
|
||||
stock: 64,
|
||||
status: 'PRICED',
|
||||
platformStatus: {},
|
||||
shopId: 'shop-shopee-1',
|
||||
shopName: 'Shopee官方店',
|
||||
platform: 'Shopee',
|
||||
createdAt: '2026-03-15',
|
||||
updatedAt: '2026-03-16',
|
||||
},
|
||||
@@ -252,6 +326,9 @@ const MOCK_PRODUCTS: Product[] = [
|
||||
stock: 512,
|
||||
status: 'LISTED',
|
||||
platformStatus: { Amazon: 'LISTED' },
|
||||
shopId: 'shop-amazon-2',
|
||||
shopName: 'Amazon欧洲店',
|
||||
platform: 'Amazon',
|
||||
createdAt: '2026-03-01',
|
||||
updatedAt: '2026-03-17',
|
||||
},
|
||||
@@ -268,6 +345,9 @@ const MOCK_PRODUCTS: Product[] = [
|
||||
stock: 32,
|
||||
status: 'SYNCING',
|
||||
platformStatus: { Amazon: 'SYNCING', eBay: 'SYNCING' },
|
||||
shopId: 'shop-ebay-1',
|
||||
shopName: 'eBay全球店',
|
||||
platform: 'eBay',
|
||||
createdAt: '2026-03-18',
|
||||
updatedAt: '2026-03-18',
|
||||
},
|
||||
@@ -284,6 +364,9 @@ const MOCK_PRODUCTS: Product[] = [
|
||||
stock: 0,
|
||||
status: 'SYNC_FAILED',
|
||||
platformStatus: { Amazon: 'FAILED' },
|
||||
shopId: 'shop-temu-1',
|
||||
shopName: 'Temu旗舰店',
|
||||
platform: 'Temu',
|
||||
createdAt: '2026-03-05',
|
||||
updatedAt: '2026-03-18',
|
||||
},
|
||||
@@ -300,6 +383,9 @@ const MOCK_PRODUCTS: Product[] = [
|
||||
stock: 200,
|
||||
status: 'OFFLINE',
|
||||
platformStatus: { Amazon: 'OFFLINE', eBay: 'OFFLINE' },
|
||||
shopId: 'shop-shein-1',
|
||||
shopName: 'SHEIN官方店',
|
||||
platform: 'Shein',
|
||||
createdAt: '2025-11-20',
|
||||
updatedAt: '2026-02-28',
|
||||
},
|
||||
@@ -322,6 +408,7 @@ export const ProductList: React.FC = () => {
|
||||
keyword: '',
|
||||
status: [],
|
||||
platform: [],
|
||||
shop: [],
|
||||
category: [],
|
||||
priceRange: null,
|
||||
stockRange: null,
|
||||
@@ -388,6 +475,7 @@ export const ProductList: React.FC = () => {
|
||||
keyword: '',
|
||||
status: [],
|
||||
platform: [],
|
||||
shop: [],
|
||||
category: [],
|
||||
priceRange: null,
|
||||
stockRange: null,
|
||||
@@ -606,6 +694,9 @@ export const ProductList: React.FC = () => {
|
||||
if (filters.category.length > 0 && !filters.category.includes(product.category)) {
|
||||
return false;
|
||||
}
|
||||
if (filters.shop.length > 0 && !filters.shop.includes(product.shopId)) {
|
||||
return false;
|
||||
}
|
||||
if (filters.priceRange) {
|
||||
const [min, max] = filters.priceRange;
|
||||
if (product.price < min || product.price > max) {
|
||||
@@ -1110,6 +1201,21 @@ export const ProductList: React.FC = () => {
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="店铺">
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="选择店铺"
|
||||
value={filters.shop}
|
||||
onChange={(value) => handleFilterChange('shop', value)}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{SHOPS.map(shop => (
|
||||
<Option key={shop.id} value={shop.id}>
|
||||
{shop.name} ({shop.platform})
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="平台">
|
||||
<Select
|
||||
mode="multiple"
|
||||
|
||||
@@ -19,6 +19,9 @@ import {
|
||||
Switch,
|
||||
Tooltip,
|
||||
Alert,
|
||||
Image,
|
||||
Badge,
|
||||
Progress,
|
||||
} from 'antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
@@ -29,8 +32,20 @@ import {
|
||||
ShopOutlined,
|
||||
DollarOutlined,
|
||||
BoxPlotOutlined,
|
||||
PictureOutlined,
|
||||
VideoCameraOutlined,
|
||||
TranslationOutlined,
|
||||
ThunderboltOutlined,
|
||||
EyeOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { UploadFile } from 'antd/es/upload/interface';
|
||||
import {
|
||||
materialProcessingDataSource,
|
||||
ImageToImageParams,
|
||||
ImageToVideoParams,
|
||||
STYLE_OPTIONS,
|
||||
TRANSLATION_LANGUAGES,
|
||||
} from '@/services/materialProcessingDataSource';
|
||||
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
@@ -89,6 +104,11 @@ export const ProductPublishForm: React.FC = () => {
|
||||
const [variants, setVariants] = useState<ProductVariant[]>([]);
|
||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||
const [activeTab, setActiveTab] = useState('basic');
|
||||
const [processingJobs, setProcessingJobs] = useState<any[]>([]);
|
||||
const [materialModalVisible, setMaterialModalVisible] = useState(false);
|
||||
const [selectedImage, setSelectedImage] = useState<string>('');
|
||||
const [processingType, setProcessingType] = useState<'image-to-image' | 'image-to-video'>('image-to-image');
|
||||
const [processingLoading, setProcessingLoading] = useState(false);
|
||||
|
||||
const handleAddVariant = () => {
|
||||
const newVariant: ProductVariant = {
|
||||
@@ -112,6 +132,59 @@ export const ProductPublishForm: React.FC = () => {
|
||||
));
|
||||
};
|
||||
|
||||
const handleOpenMaterialProcessing = (imageUrl: string, type: 'image-to-image' | 'image-to-video') => {
|
||||
setSelectedImage(imageUrl);
|
||||
setProcessingType(type);
|
||||
setMaterialModalVisible(true);
|
||||
};
|
||||
|
||||
const handleProcessImage = async (params: ImageToImageParams | ImageToVideoParams) => {
|
||||
if (!selectedImage) {
|
||||
message.warning('请先选择要处理的图片');
|
||||
return;
|
||||
}
|
||||
|
||||
setProcessingLoading(true);
|
||||
try {
|
||||
let job;
|
||||
if (processingType === 'image-to-image') {
|
||||
job = await materialProcessingDataSource.imageToImage(selectedImage, params as ImageToImageParams);
|
||||
} else {
|
||||
job = await materialProcessingDataSource.imageToVideo(selectedImage, params as ImageToVideoParams);
|
||||
}
|
||||
|
||||
setProcessingJobs([...processingJobs, job]);
|
||||
message.success(`任务已创建: ${job.id}`);
|
||||
setMaterialModalVisible(false);
|
||||
|
||||
// 轮询检查任务状态
|
||||
const checkInterval = setInterval(async () => {
|
||||
const updatedJob = await materialProcessingDataSource.getJobStatus(job.id);
|
||||
if (updatedJob.status === 'completed') {
|
||||
clearInterval(checkInterval);
|
||||
message.success('素材处理完成!');
|
||||
// 将处理结果添加到文件列表
|
||||
if (processingType === 'image-to-image') {
|
||||
setFileList([...fileList, {
|
||||
uid: `processed-${Date.now()}`,
|
||||
name: `processed-${Date.now()}.jpg`,
|
||||
status: 'done',
|
||||
url: updatedJob.outputUrl,
|
||||
thumbUrl: updatedJob.outputUrl,
|
||||
} as UploadFile]);
|
||||
} else {
|
||||
message.info('视频已生成,可在素材库中查看');
|
||||
}
|
||||
setProcessingJobs(processingJobs.map(j => j.id === job.id ? updatedJob : j));
|
||||
}
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
message.error('处理失败');
|
||||
} finally {
|
||||
setProcessingLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveDraft = async () => {
|
||||
try {
|
||||
await form.validateFields();
|
||||
@@ -318,6 +391,14 @@ export const ProductPublishForm: React.FC = () => {
|
||||
|
||||
<Divider>Product Images</Divider>
|
||||
|
||||
<Alert
|
||||
message="AI素材处理"
|
||||
description="上传图片后,可使用AI进行图生图、图生视频等处理。点击已上传图片上的AI按钮开始处理。"
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
|
||||
<Form.Item label="Images">
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
@@ -325,6 +406,41 @@ export const ProductPublishForm: React.FC = () => {
|
||||
onChange={({ fileList }) => setFileList(fileList)}
|
||||
beforeUpload={() => false}
|
||||
multiple
|
||||
itemRender={(originNode, file) => (
|
||||
<div style={{ position: 'relative' }}>
|
||||
{originNode}
|
||||
{file.status === 'done' && file.url && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
background: 'rgba(0,0,0,0.5)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
gap: 4,
|
||||
padding: '2px 0',
|
||||
}}>
|
||||
<Tooltip title="图生图">
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<PictureOutlined style={{ color: '#fff' }} />}
|
||||
onClick={() => handleOpenMaterialProcessing(file.url || file.thumbUrl || '', 'image-to-image')}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="图生视频">
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<VideoCameraOutlined style={{ color: '#fff' }} />}
|
||||
onClick={() => handleOpenMaterialProcessing(file.url || file.thumbUrl || '', 'image-to-video')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{fileList.length >= 8 ? null : (
|
||||
<div>
|
||||
@@ -334,6 +450,22 @@ export const ProductPublishForm: React.FC = () => {
|
||||
)}
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
|
||||
{processingJobs.length > 0 && (
|
||||
<Card size="small" title="处理中的任务" style={{ marginTop: 16 }}>
|
||||
{processingJobs.map(job => (
|
||||
<div key={job.id} style={{ marginBottom: 8 }}>
|
||||
<Space>
|
||||
<Tag color="blue">{job.type === 'image_to_image' ? '图生图' : '图生视频'}</Tag>
|
||||
<Progress percent={job.progress} size="small" style={{ width: 200 }} />
|
||||
<Tag color={job.status === 'processing' ? 'processing' : 'success'}>
|
||||
{job.status}
|
||||
</Tag>
|
||||
</Space>
|
||||
</div>
|
||||
))}
|
||||
</Card>
|
||||
)}
|
||||
</>
|
||||
)},
|
||||
{ key: 'pricing', label: <><DollarOutlined /> Pricing</>, children: (
|
||||
@@ -495,6 +627,101 @@ export const ProductPublishForm: React.FC = () => {
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
|
||||
<Modal
|
||||
title={
|
||||
<Space>
|
||||
<ThunderboltOutlined style={{ color: '#1890ff' }} />
|
||||
{processingType === 'image-to-image' ? '图生图' : '图生视频'}
|
||||
</Space>
|
||||
}
|
||||
visible={materialModalVisible}
|
||||
onCancel={() => setMaterialModalVisible(false)}
|
||||
footer={null}
|
||||
width={600}
|
||||
>
|
||||
{selectedImage && (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Text type="secondary">原图预览:</Text>
|
||||
<Image src={selectedImage} width={200} style={{ marginTop: 8, borderRadius: 8 }} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Form layout="vertical" onFinish={handleProcessImage}>
|
||||
{processingType === 'image-to-image' ? (
|
||||
<>
|
||||
<Form.Item name="prompt" label="提示词">
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
placeholder="描述你想要生成的图片效果..."
|
||||
/>
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="style" label="风格" initialValue="realistic">
|
||||
<Select>
|
||||
{STYLE_OPTIONS.map(opt => (
|
||||
<Option key={opt.value} value={opt.value}>{opt.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="strength" label="转换强度" initialValue={0.7}>
|
||||
<InputNumber min={0} max={1} step={0.1} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Form.Item name="prompt" label="提示词">
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
placeholder="描述视频的运动效果..."
|
||||
/>
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item name="motion" label="运动类型" initialValue="zoom">
|
||||
<Select>
|
||||
<Option value="zoom">缩放</Option>
|
||||
<Option value="pan">平移</Option>
|
||||
<Option value="rotate">旋转</Option>
|
||||
<Option value="dynamic">动态</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item name="duration" label="时长(秒)" initialValue={5}>
|
||||
<InputNumber min={1} max={30} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item name="fps" label="帧率" initialValue={30}>
|
||||
<Select>
|
||||
<Option value={24}>24 FPS</Option>
|
||||
<Option value={30}>30 FPS</Option>
|
||||
<Option value={60}>60 FPS</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit" loading={processingLoading}>
|
||||
开始处理
|
||||
</Button>
|
||||
<Button onClick={() => setMaterialModalVisible(false)}>
|
||||
取消
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user