Files
makemd/dashboard/src/pages/Ad/AutoAdjustment/index.tsx

369 lines
10 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { Card, Layout, Typography, Row, Col, Form, Input, Select, Button, Table, Switch, DatePicker, TimePicker, message, Alert, Modal } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, SaveOutlined, PlayCircleOutlined, PauseCircleOutlined, HistoryOutlined } from '@ant-design/icons';
import { Link } from 'umi';
const { Content } = Layout;
const { Title, Text, Paragraph } = Typography;
const { Option } = Select;
const { Item } = Form;
const { RangePicker } = DatePicker;
interface Strategy {
id: string;
name: string;
adId: string;
adName: string;
type: string;
condition: string;
action: string;
status: 'active' | 'inactive' | 'paused';
createdAt: string;
lastExecuted: string;
executionCount: number;
}
interface ExecutionHistory {
id: string;
strategyId: string;
strategyName: string;
executionTime: string;
status: 'success' | 'failed' | 'pending';
result: string;
details: string;
}
const AutoAdjustment: React.FC = () => {
const [strategies, setStrategies] = useState<Strategy[]>([]);
const [executionHistory, setExecutionHistory] = useState<ExecutionHistory[]>([]);
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
const [editingStrategy, setEditingStrategy] = useState<Strategy | null>(null);
const [form] = Form.useForm();
const mockStrategies: Strategy[] = [
{
id: '1',
name: 'ROI自动调整',
adId: 'AD001',
adName: '智能手表推广',
type: 'ROI',
condition: 'ROI < 3.0',
action: '降低CPC 10%',
status: 'active',
createdAt: '2026-03-10',
lastExecuted: '2026-03-18 10:30',
executionCount: 15,
},
{
id: '2',
name: '点击率优化',
adId: 'AD002',
adName: '无线耳机促销',
type: 'CTR',
condition: 'CTR < 2.0%',
action: '调整创意',
status: 'paused',
createdAt: '2026-03-08',
lastExecuted: '2026-03-15 14:20',
executionCount: 8,
},
{
id: '3',
name: '预算自动分配',
adId: 'AD003',
adName: '智能音箱新品',
type: 'Budget',
condition: '日花费 < 预算的80%',
action: '增加预算10%',
status: 'active',
createdAt: '2026-03-05',
lastExecuted: '2026-03-18 09:15',
executionCount: 20,
},
];
const mockExecutionHistory: ExecutionHistory[] = [
{
id: '1',
strategyId: '1',
strategyName: 'ROI自动调整',
executionTime: '2026-03-18 10:30',
status: 'success',
result: 'CPC降低10%',
details: 'ROI为2.8低于阈值3.0执行降低CPC操作',
},
{
id: '2',
strategyId: '3',
strategyName: '预算自动分配',
executionTime: '2026-03-18 09:15',
status: 'success',
result: '预算增加10%',
details: '日花费为预算的75%低于阈值80%,执行增加预算操作',
},
{
id: '3',
strategyId: '1',
strategyName: 'ROI自动调整',
executionTime: '2026-03-17 16:45',
status: 'failed',
result: '操作失败',
details: 'API调用失败无法执行调整操作',
},
];
const handleAddStrategy = () => {
setEditingStrategy(null);
form.resetFields();
setIsModalVisible(true);
};
const handleEditStrategy = (strategy: Strategy) => {
setEditingStrategy(strategy);
form.setFieldsValue(strategy);
setIsModalVisible(true);
};
const handleDeleteStrategy = (id: string) => {
const updatedStrategies = strategies.filter(item => item.id !== id);
setStrategies(updatedStrategies);
message.success('策略已删除');
};
const handleToggleStrategy = (id: string) => {
const updatedStrategies = strategies.map(item =>
item.id === id
? { ...item, status: item.status === 'active' ? 'paused' : 'active' }
: item
);
setStrategies(updatedStrategies);
message.success('策略状态已更新');
};
const handleSaveStrategy = () => {
form.validateFields().then(values => {
if (editingStrategy) {
const updatedStrategies = strategies.map(item =>
item.id === editingStrategy.id ? { ...item, ...values } : item
);
setStrategies(updatedStrategies);
message.success('策略已更新');
} else {
const newStrategy: Strategy = {
id: (strategies.length + 1).toString(),
...values,
status: 'inactive' as const,
createdAt: new Date().toISOString().split('T')[0],
lastExecuted: '-',
executionCount: 0,
};
setStrategies([...strategies, newStrategy]);
message.success('策略已创建');
}
setIsModalVisible(false);
});
};
const columns = [
{
title: '策略名称',
dataIndex: 'name',
key: 'name',
},
{
title: '广告',
dataIndex: 'adName',
key: 'adName',
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
},
{
title: '条件',
dataIndex: 'condition',
key: 'condition',
},
{
title: '动作',
dataIndex: 'action',
key: 'action',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => (
<span style={{
color: status === 'active' ? '#52c41a' :
status === 'paused' ? '#faad14' : '#d9d9d9'
}}>
{status === 'active' ? '活跃' :
status === 'paused' ? '暂停' : '未激活'}
</span>
),
},
{
title: '执行次数',
dataIndex: 'executionCount',
key: 'executionCount',
},
{
title: '上次执行',
dataIndex: 'lastExecuted',
key: 'lastExecuted',
},
{
title: '操作',
key: 'action',
render: (_: any, record: Strategy) => (
<div style={{ display: 'flex', gap: 8 }}>
<Button
icon={record.status === 'active' ? <PauseCircleOutlined /> : <PlayCircleOutlined />}
size="small"
onClick={() => handleToggleStrategy(record.id)}
>
{record.status === 'active' ? '暂停' : '激活'}
</Button>
<Button
icon={<EditOutlined />}
size="small"
onClick={() => handleEditStrategy(record)}
>
</Button>
<Button
icon={<DeleteOutlined />}
size="small"
danger
onClick={() => handleDeleteStrategy(record.id)}
>
</Button>
</div>
),
},
];
const historyColumns = [
{
title: '策略名称',
dataIndex: 'strategyName',
key: 'strategyName',
},
{
title: '执行时间',
dataIndex: 'executionTime',
key: 'executionTime',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => (
<span style={{
color: status === 'success' ? '#52c41a' :
status === 'failed' ? '#ff4d4f' : '#faad14'
}}>
{status === 'success' ? '成功' :
status === 'failed' ? '失败' : '待执行'}
</span>
),
},
{
title: '结果',
dataIndex: 'result',
key: 'result',
},
{
title: '详情',
dataIndex: 'details',
key: 'details',
},
];
return (
<Content style={{ padding: 24, margin: 0, minHeight: 280, background: '#fff' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
<Title level={4}></Title>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAddStrategy}>
</Button>
</div>
{/* 策略列表 */}
<Card title="策略列表" style={{ marginBottom: 24 }}>
<Table
columns={columns}
dataSource={mockStrategies}
rowKey="id"
pagination={{ pageSize: 10 }}
/>
</Card>
{/* 执行历史 */}
<Card title="执行历史">
<Table
columns={historyColumns}
dataSource={mockExecutionHistory}
rowKey="id"
pagination={{ pageSize: 10 }}
/>
</Card>
{/* 策略编辑模态框 */}
<Modal
title={editingStrategy ? "编辑策略" : "添加策略"}
open={isModalVisible}
onCancel={() => setIsModalVisible(false)}
footer={null}
>
<Form
form={form}
layout="vertical"
initialValues={{
name: '',
adId: 'AD001',
type: 'ROI',
condition: '',
action: '',
}}
>
<Item label="策略名称" name="name" rules={[{ required: true, message: '请输入策略名称' }]}>
<Input placeholder="输入策略名称" />
</Item>
<Item label="广告" name="adId">
<Select>
<Option value="AD001">广</Option>
<Option value="AD002">线</Option>
<Option value="AD003"></Option>
</Select>
</Item>
<Item label="策略类型" name="type">
<Select>
<Option value="ROI">ROI优化</Option>
<Option value="CTR"></Option>
<Option value="Budget"></Option>
<Option value="Conversion"></Option>
</Select>
</Item>
<Item label="触发条件" name="condition" rules={[{ required: true, message: '请输入触发条件' }]}>
<Input placeholder="例如ROI < 3.0" />
</Item>
<Item label="执行动作" name="action" rules={[{ required: true, message: '请输入执行动作' }]}>
<Input placeholder="例如降低CPC 10%" />
</Item>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12, marginTop: 24 }}>
<Button onClick={() => setIsModalVisible(false)}></Button>
<Button type="primary" icon={<SaveOutlined />} onClick={handleSaveStrategy}>
</Button>
</div>
</Form>
</Modal>
</Content>
);
};
export default AutoAdjustment;