完成前测素材,模板列表
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Input, Select, Table, Pagination, Tag, Typography } from 'antd';
|
||||
import { FileTextOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
import { Button, Input, Select, Table, Pagination, Tag, Typography, Tooltip } from 'antd';
|
||||
import { FileTextOutlined } from '@ant-design/icons';
|
||||
import { getMaterialList } from '../api';
|
||||
import PreResultDisplay from '../components/PreResultDisplay';
|
||||
const formatDateTime = (dateStr: string) => {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
@@ -48,6 +49,16 @@ const AdminMaterialList: React.FC = () => {
|
||||
title: '备注',
|
||||
dataIndex: 'note',
|
||||
key: 'note',
|
||||
width: 200,
|
||||
render: (text: string) => (
|
||||
<Input.TextArea
|
||||
value={text || ''}
|
||||
readOnly
|
||||
autoSize={{ minRows: 1, maxRows: 4 }}
|
||||
style={{ color: '#94a3b8', resize: 'none', border: 'none', background: 'transparent', padding: 0 }}
|
||||
placeholder="-"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '授权ID',
|
||||
@@ -58,6 +69,8 @@ const AdminMaterialList: React.FC = () => {
|
||||
title: '预测试结果',
|
||||
dataIndex: 'preResult',
|
||||
key: 'preResult',
|
||||
width: 120,
|
||||
render: (text: string) => <PreResultDisplay preResult={text} />,
|
||||
},
|
||||
{
|
||||
title: '预测试模板ID',
|
||||
@@ -94,7 +107,15 @@ const AdminMaterialList: React.FC = () => {
|
||||
title: '上传ID',
|
||||
dataIndex: 'uploadId',
|
||||
key: 'uploadId',
|
||||
width: 160,
|
||||
render: (v: string) => {
|
||||
if (!v) return '-';
|
||||
const short = v.length > 12 ? `${v.slice(0, 6)}...${v.slice(-4)}` : v;
|
||||
return (
|
||||
<Tooltip title={v} placement="topLeft">
|
||||
<Typography.Text style={{ fontSize: 12 }}>{short}</Typography.Text>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '资源类型',
|
||||
@@ -145,13 +166,13 @@ const AdminMaterialList: React.FC = () => {
|
||||
page: currentPage,
|
||||
page_size: pageSize,
|
||||
});
|
||||
const data = res.items || [];
|
||||
const data = res.data || [];
|
||||
const tableData = data.map((item: any, index: number) => ({
|
||||
...item,
|
||||
index: (currentPage - 1) * pageSize + index + 1,
|
||||
}));
|
||||
setTableData(tableData);
|
||||
setTotal(res.total || 0);
|
||||
setTotal(res.pagination?.total || 0);
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error);
|
||||
} finally {
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Input, Select, Table, Pagination, Tag, Typography } from 'antd';
|
||||
import { FileTextOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
|
||||
import { Button, Input, Table, Pagination, Tag, Typography, DatePicker } from 'antd';
|
||||
import { FileTextOutlined } from '@ant-design/icons';
|
||||
import { getPreTestTemplateList } from '../api';
|
||||
import dayjs from 'dayjs';
|
||||
const formatDateTime = (dateStr: string) => {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
const { Text } = Typography;
|
||||
|
||||
const AdminPreTestTemplates: React.FC = () => {
|
||||
const [tableData, setTableData] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [searchPlatform, setSearchPlatform] = useState('');
|
||||
const [searchName, setSearchName] = useState('');
|
||||
|
||||
const [searchId, setSearchId] = useState('');
|
||||
const [searchPhone, setSearchPhone] = useState('');
|
||||
const [searchCreatedAt, setSearchCreatedAt] = useState<[dayjs.Dayjs, dayjs.Dayjs] | undefined>();
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
@@ -22,92 +33,180 @@ const AdminPreTestTemplates: React.FC = () => {
|
||||
render: (text: number) => <span style={{ color: '#64748b' }}>{text}</span>,
|
||||
},
|
||||
{
|
||||
title: '模板名称',
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phone',
|
||||
key: 'phone',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '受众年龄',
|
||||
dataIndex: 'audienceAge',
|
||||
key: 'audienceAge',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '受众性别',
|
||||
dataIndex: 'audienceGender',
|
||||
key: 'audienceGender',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '受众网络',
|
||||
dataIndex: 'audienceNetwork',
|
||||
key: 'audienceNetwork',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '受众区域',
|
||||
dataIndex: 'audienceRegion',
|
||||
key: 'audienceRegion',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '预算',
|
||||
dataIndex: 'budget',
|
||||
key: 'budget',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '成本上限',
|
||||
dataIndex: 'costCap',
|
||||
key: 'costCap',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'CPA预算',
|
||||
dataIndex: 'cpaBid',
|
||||
key: 'cpaBid',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'CPC预算',
|
||||
dataIndex: 'cpcBid',
|
||||
key: 'cpcBid',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '客户名称',
|
||||
dataIndex: 'cusName',
|
||||
key: 'cusName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '外部操作',
|
||||
dataIndex: 'externalAction',
|
||||
key: 'externalAction',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 200,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '投放平台',
|
||||
title: '名称',
|
||||
dataIndex: 'nobid',
|
||||
key: 'nobid',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'note',
|
||||
key: 'note',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '平台',
|
||||
dataIndex: 'platform',
|
||||
key: 'platform',
|
||||
width: 120,
|
||||
render: (text: string) => (
|
||||
<Tag color={text === 'AD' ? 'blue' : text === 'QIANCHUAN' ? 'green' : 'orange'}>
|
||||
{text === 'AD' ? 'AD' : text === 'QIANCHUAN' ? '千川' : '本地推'}
|
||||
</Tag>
|
||||
),
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
title: '定价类型',
|
||||
dataIndex: 'pricingType',
|
||||
key: 'pricingType',
|
||||
width: 100,
|
||||
render: (text: string) => (
|
||||
<Tag color={text === 'active' ? 'green' : 'red'}>
|
||||
{text === 'active' ? '启用' : '禁用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '目标成本',
|
||||
dataIndex: 'targetCost',
|
||||
key: 'targetCost',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '用户ID',
|
||||
dataIndex: 'userId',
|
||||
key: 'userId',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '用户手机号',
|
||||
dataIndex: 'userPhone',
|
||||
key: 'userPhone',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '是否默认',
|
||||
dataIndex: 'isDefault',
|
||||
key: 'isDefault',
|
||||
width: 100,
|
||||
render: (text: boolean) => <Tag color={text ? 'green' : 'red'}>{text ? '是' : '否'}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
key: 'createdAt',
|
||||
width: 180,
|
||||
render: (text: string) => <span style={{ color: '#64748b' }}>{text}</span>,
|
||||
render: (text: string) => <span style={{ color: '#64748b' }}>{formatDateTime(text)}</span>,
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updatedAt',
|
||||
key: 'updatedAt',
|
||||
width: 180,
|
||||
render: (text: string) => <span style={{ color: '#64748b' }}>{text}</span>,
|
||||
render: (text: string) => <span style={{ color: '#64748b' }}>{formatDateTime(text)}</span>,
|
||||
},
|
||||
];
|
||||
|
||||
const loadRecords = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
params.set('page', String(currentPage));
|
||||
params.set('page_size', String(pageSize));
|
||||
if (searchPlatform) params.set('platform', searchPlatform);
|
||||
if (searchName) params.set('name', searchName);
|
||||
const mockData = {
|
||||
items: Array.from({ length: pageSize }, (_, i) => ({
|
||||
id: `${currentPage}-${i}`,
|
||||
index: (currentPage - 1) * pageSize + i + 1,
|
||||
name: `前测模板${(currentPage - 1) * pageSize + i + 1}`,
|
||||
platform: ['AD', 'QIANCHUAN', 'LOCAL'][i % 3],
|
||||
status: i % 5 === 0 ? 'inactive' : 'active',
|
||||
createdAt: '2026-07-01 10:00:00',
|
||||
updatedAt: '2026-07-02 14:30:00',
|
||||
})),
|
||||
total: 50,
|
||||
};
|
||||
setTableData(mockData.items);
|
||||
setTotal(mockData.total);
|
||||
const res = await getPreTestTemplateList({
|
||||
id: searchId || undefined,
|
||||
phone: searchPhone || undefined,
|
||||
created_at: searchCreatedAt ? [searchCreatedAt[0].format('YYYY-MM-DD'), searchCreatedAt[1].format('YYYY-MM-DD')] : undefined,
|
||||
page: currentPage,
|
||||
page_size: pageSize,
|
||||
});
|
||||
const data = res.data || [];
|
||||
const tableData = data.map((item: any, index: number) => ({
|
||||
...item,
|
||||
index: (currentPage - 1) * pageSize + index + 1,
|
||||
}));
|
||||
setTableData(tableData);
|
||||
setTotal(res.pagination.total || 0);
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadRecords();
|
||||
}, [currentPage, pageSize]);
|
||||
|
||||
const handlePageChange = (page: number, size: number) => {
|
||||
setCurrentPage(page);
|
||||
setPageSize(size);
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
setCurrentPage(1);
|
||||
loadRecords();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
|
||||
@@ -115,26 +214,34 @@ const AdminPreTestTemplates: React.FC = () => {
|
||||
<Text strong style={{ fontSize: 16 }}>素材前测模板</Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
|
||||
<Input
|
||||
placeholder="模板名称"
|
||||
value={searchName}
|
||||
onChange={(e) => setSearchName(e.target.value)}
|
||||
style={{ width: 180 }}
|
||||
placeholder="ID"
|
||||
value={searchId}
|
||||
onChange={(e) => setSearchId(e.target.value)}
|
||||
style={{ width: 160 }}
|
||||
allowClear
|
||||
onPressEnter={handleSearch}
|
||||
/>
|
||||
<Select
|
||||
placeholder="投放平台"
|
||||
value={searchPlatform}
|
||||
onChange={(value) => setSearchPlatform(value)}
|
||||
style={{ width: 140 }}
|
||||
<Input
|
||||
placeholder="手机号"
|
||||
value={searchPhone}
|
||||
onChange={(e) => setSearchPhone(e.target.value)}
|
||||
style={{ width: 160 }}
|
||||
allowClear
|
||||
options={[
|
||||
{ value: 'AD', label: 'AD' },
|
||||
{ value: 'QIANCHUAN', label: '千川' },
|
||||
{ value: 'LOCAL', label: '本地推' },
|
||||
]}
|
||||
onPressEnter={handleSearch}
|
||||
/>
|
||||
<DatePicker.RangePicker
|
||||
placeholder={['开始日期', '结束日期']}
|
||||
value={searchCreatedAt}
|
||||
onChange={(dates) => {
|
||||
if (dates && dates[0] && dates[1]) {
|
||||
setSearchCreatedAt([dates[0], dates[1]]);
|
||||
} else {
|
||||
setSearchCreatedAt(undefined);
|
||||
}
|
||||
}}
|
||||
style={{ width: 260 }}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
@@ -187,5 +294,4 @@ const AdminPreTestTemplates: React.FC = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminPreTestTemplates;
|
||||
Reference in New Issue
Block a user