素材列表添加多选功能
This commit is contained in:
+442
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -28,7 +28,7 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-BhNIfJ-Y.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-BvgCzUzu.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BhPFzWLH.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -449,53 +449,45 @@ export async function getOAuthList(params: OAuthListParams): Promise<any> {
|
||||
}
|
||||
|
||||
// 前测模板列表
|
||||
// /api/pre-test-template/list
|
||||
export interface PreTestListParams {
|
||||
platform?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export async function getPreTestList(params?: PreTestListParams): Promise<any> {
|
||||
if (USE_MOCK) return mock.mockGetPreTestList(params);
|
||||
const page = params?.page || 1;
|
||||
const pageSize = Math.min(params?.pageSize || 10, 100);
|
||||
return api.get(`/pre-test-template/list?page=${page}&page_size=${pageSize}`);
|
||||
const query = new URLSearchParams();
|
||||
if (params.platform) query.set('platform', params.platform);
|
||||
if (params.page !== undefined) query.set('page', String(params.page));
|
||||
if (params.pageSize !== undefined) query.set('page_size', String(params.pageSize));
|
||||
return api.get(`/pre-test-template/list?${query.toString()}`);
|
||||
}
|
||||
|
||||
// 获取前测字段列表
|
||||
// /api/pre-test-template/fields
|
||||
export async function getPreTestFields(): Promise<any> {
|
||||
if (USE_MOCK) return mock.mockGetPreTestFields();
|
||||
return api.get(`/pre-test-template/fields`);
|
||||
}
|
||||
|
||||
// 创建前测模板
|
||||
// /api/pre-test-template/create
|
||||
export async function createPreTest(params: any): Promise<any> {
|
||||
if (USE_MOCK) return mock.mockCreatePreTest(params);
|
||||
return api.post(`/pre-test-template/create`, params);
|
||||
}
|
||||
|
||||
// 前测模板详情
|
||||
// /api/pre-test-template/select/{template_id}
|
||||
export async function getPreTestDetail(templateId: string): Promise<any> {
|
||||
return api.get(`/pre-test-template/select/${templateId}`);
|
||||
}
|
||||
|
||||
// 更新前测模板
|
||||
// /api/pre-test-template/update/{template_id}
|
||||
export async function updatePreTest(templateId: string, params: any): Promise<any> {
|
||||
return api.post(`/pre-test-template/update/${templateId}`, params);
|
||||
}
|
||||
|
||||
// 删除前测模板
|
||||
// /api/pre-test-template/delete/{template_id}
|
||||
export async function deletePreTest(templateId: string): Promise<any> {
|
||||
return api.get(`/pre-test-template/delete/${templateId}`);
|
||||
}
|
||||
|
||||
// 获取默认前测模板
|
||||
// /api/pre-test-template/default
|
||||
export async function getDefaultPreTest(): Promise<any> {
|
||||
return api.get(`/pre-test-template/default`);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,6 @@ const AuthorizationPage: React.FC = () => {
|
||||
const list = async () => {
|
||||
try {
|
||||
const res = await getDefaultPreTest();
|
||||
console.log(res);
|
||||
} catch (error) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@ interface PreTestRecord {
|
||||
selectedFields: string[];
|
||||
status: string;
|
||||
createdAt: string;
|
||||
name: string;
|
||||
platform: string;
|
||||
}
|
||||
|
||||
interface PreTestField {
|
||||
@@ -147,9 +149,16 @@ const PreTest: React.FC = () => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [platform, setPlatform] = useState<string>('AD');
|
||||
const [searchPlatform, setSearchPlatform] = useState<string | undefined>(undefined);
|
||||
const [areaOptions, setAreaOptions] = useState<any[]>([]);
|
||||
const [cityCodeMap, setCityCodeMap] = useState<Record<string, string>>({});
|
||||
const [provinceCityMap, setProvinceCityMap] = useState<Record<string, string[]>>({});
|
||||
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
|
||||
const [pushModalOpen, setPushModalOpen] = useState(false);
|
||||
const [pushIsPreTest, setPushIsPreTest] = useState<'1' | '2'>('2');
|
||||
const [pushPreTestTemplate, setPushPreTestTemplate] = useState<string>('');
|
||||
const [pushTemplates, setPushTemplates] = useState<any[]>([]);
|
||||
const [pushTemplatesLoading, setPushTemplatesLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadRecords();
|
||||
@@ -162,7 +171,7 @@ const PreTest: React.FC = () => {
|
||||
const loadRecords = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await getPreTestList({ page: currentPage, pageSize });
|
||||
const res = await getPreTestList({ page: currentPage, pageSize, platform: searchPlatform });
|
||||
setRecords(res.data || []);
|
||||
setTotal(res.pagination?.total || 0);
|
||||
setCurrentPage(res.pagination?.page || 1);
|
||||
@@ -615,29 +624,111 @@ const PreTest: React.FC = () => {
|
||||
setPageSize(size);
|
||||
};
|
||||
|
||||
const handleOpenPushModal = async () => {
|
||||
if (selectedRows.size === 0) {
|
||||
message.warning('请先选择要推送的前测模板');
|
||||
return;
|
||||
}
|
||||
setPushTemplatesLoading(true);
|
||||
try {
|
||||
const res = await getPreTestList({ page: 1, pageSize: 100 });
|
||||
setPushTemplates(res.data || []);
|
||||
} catch (error) {
|
||||
message.error('获取前测模板列表失败');
|
||||
} finally {
|
||||
setPushTemplatesLoading(false);
|
||||
}
|
||||
setPushIsPreTest('2');
|
||||
setPushPreTestTemplate('');
|
||||
setPushModalOpen(true);
|
||||
};
|
||||
|
||||
const handlePushSubmit = async () => {
|
||||
if (selectedRows.size === 0) {
|
||||
message.warning('请先选择要推送的前测模板');
|
||||
return;
|
||||
}
|
||||
if (pushIsPreTest === '1' && !pushPreTestTemplate) {
|
||||
message.warning('请选择前测模板');
|
||||
return;
|
||||
}
|
||||
message.success('推送前测成功');
|
||||
setPushModalOpen(false);
|
||||
setSelectedRows(new Set());
|
||||
};
|
||||
|
||||
const selectedRecords = records.filter(record => selectedRows.has(record.id));
|
||||
|
||||
return (
|
||||
<div style={{ minHeight: 'calc(100vh - 80px)' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
|
||||
<FileTextOutlined style={{ color: '#6366f1', fontSize: 16 }} />
|
||||
<Typography.Text strong style={{ fontSize: 16 }}>素材前测</Typography.Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12, marginBottom: 16 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="medium"
|
||||
icon={<PlusOutlined />}
|
||||
loading={loading}
|
||||
onClick={handleOpenModal}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
fontSize: 14,
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||
border: 'none',
|
||||
boxShadow: '0 4px 14px rgba(99,102,241,0.3)',
|
||||
}}
|
||||
>
|
||||
新增前测
|
||||
</Button>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Select
|
||||
placeholder="投放平台"
|
||||
value={searchPlatform}
|
||||
onChange={(value) => setSearchPlatform(value)}
|
||||
style={{ width: 140 }}
|
||||
allowClear
|
||||
options={[
|
||||
{ value: 'AD', label: 'AD' },
|
||||
{ value: 'QIANCHUAN', label: '千川' },
|
||||
{ value: 'LOCAL', label: '本地推' },
|
||||
]}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => { setCurrentPage(1); loadRecords(); }}
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setSearchPlatform(undefined);
|
||||
setCurrentPage(1);
|
||||
loadRecords();
|
||||
}}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="medium"
|
||||
loading={pushTemplatesLoading}
|
||||
onClick={handleOpenPushModal}
|
||||
disabled={selectedRows.size === 0}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
fontSize: 14,
|
||||
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
|
||||
border: 'none',
|
||||
boxShadow: '0 4px 14px rgba(16,185,129,0.3)',
|
||||
}}
|
||||
>
|
||||
推送前测 ({selectedRows.size})
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
size="medium"
|
||||
icon={<PlusOutlined />}
|
||||
loading={loading}
|
||||
onClick={handleOpenModal}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
fontSize: 14,
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||
border: 'none',
|
||||
boxShadow: '0 4px 14px rgba(99,102,241,0.3)',
|
||||
}}
|
||||
>
|
||||
新增前测
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{
|
||||
background: '#fff',
|
||||
@@ -653,6 +744,13 @@ const PreTest: React.FC = () => {
|
||||
rowKey="id"
|
||||
bordered={false}
|
||||
scroll={{ x: 'max-content' }}
|
||||
rowSelection={{
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: Array.from(selectedRows),
|
||||
onChange: (keys) => {
|
||||
setSelectedRows(new Set(keys as string[]));
|
||||
},
|
||||
}}
|
||||
onRow={() => ({
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
@@ -985,6 +1083,77 @@ const PreTest: React.FC = () => {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="推送前测"
|
||||
open={pushModalOpen}
|
||||
onCancel={() => { setPushModalOpen(false); setSelectedRows(new Set()); }}
|
||||
onOk={handlePushSubmit}
|
||||
okText="推送"
|
||||
cancelText="取消"
|
||||
width={600}
|
||||
style={{ borderRadius: 16 }}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: 16, marginBottom: 24 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<Typography.Text style={{ fontSize: 14, color: '#475569' }}>是否开启前测:</Typography.Text>
|
||||
<Select
|
||||
value={pushIsPreTest}
|
||||
onChange={(value) => {
|
||||
setPushIsPreTest(value);
|
||||
if (value !== '1') {
|
||||
setPushPreTestTemplate('');
|
||||
}
|
||||
}}
|
||||
style={{ width: 120 }}
|
||||
options={[
|
||||
{ value: '1', label: '是' },
|
||||
{ value: '2', label: '否' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<Typography.Text style={{ fontSize: 14, color: '#475569' }}>前测模板:</Typography.Text>
|
||||
<Select
|
||||
value={pushPreTestTemplate}
|
||||
onChange={(value) => setPushPreTestTemplate(value)}
|
||||
style={{ width: 200 }}
|
||||
disabled={pushIsPreTest !== '1'}
|
||||
loading={pushTemplatesLoading}
|
||||
placeholder={pushIsPreTest !== '1' ? '请先开启前测' : '选择前测模板'}
|
||||
options={pushTemplates.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 12 }}>
|
||||
<Typography.Text strong style={{ fontSize: 14, color: '#1e293b', display: 'block', marginBottom: 12 }}>
|
||||
已选前测模板 ({selectedRows.size})
|
||||
</Typography.Text>
|
||||
<div style={{ maxHeight: 200, overflowY: 'auto' }}>
|
||||
{selectedRecords.length > 0 ? (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{selectedRecords.map(record => (
|
||||
<div key={record.id} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 12, background: '#f8fafc', borderRadius: 8 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography.Text strong style={{ fontSize: 14, color: '#1e293b' }}>{record.name}</Typography.Text>
|
||||
<Typography.Text style={{ fontSize: 12, color: '#94a3b8' }}>ID: {record.id}</Typography.Text>
|
||||
</div>
|
||||
<Tag color={record.platform === 'AD' ? 'blue' : record.platform === 'QIANCHUAN' ? 'green' : 'orange'} style={{ borderRadius: 6, fontSize: 12 }}>
|
||||
{record.platform === 'AD' ? 'AD' : record.platform === 'QIANCHUAN' ? '千川' : '本地推'}
|
||||
</Tag>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Typography.Text style={{ color: '#94a3b8' }}>暂无选中的前测模板</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user