完成前测素材,模板列表
This commit is contained in:
Vendored
+35
-35
@@ -1,37 +1,37 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<title>后台管理</title>
|
||||
<script>
|
||||
(function() {
|
||||
var cached = localStorage.getItem('siteInfo');
|
||||
if (cached) {
|
||||
try {
|
||||
var info = JSON.parse(cached);
|
||||
if (info.siteName) {
|
||||
document.title = info.siteName + ' - 管理后台';
|
||||
}
|
||||
if (info.siteLogo) {
|
||||
var link = document.querySelector('link[rel="icon"]');
|
||||
if (link) {
|
||||
link.href = info.siteLogo;
|
||||
link.type = 'image/png';
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<title>后台管理</title>
|
||||
<script>
|
||||
(function() {
|
||||
var cached = localStorage.getItem('siteInfo');
|
||||
if (cached) {
|
||||
try {
|
||||
var info = JSON.parse(cached);
|
||||
if (info.siteName) {
|
||||
document.title = info.siteName + ' - 管理后台';
|
||||
}
|
||||
if (info.siteLogo) {
|
||||
var link = document.querySelector('link[rel="icon"]');
|
||||
if (link) {
|
||||
link.href = info.siteLogo;
|
||||
link.type = 'image/png';
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-Dx-bvo36.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>␍
|
||||
</body>
|
||||
</html>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -999,6 +999,23 @@ export async function getMaterialList(params: MaterialListParams): Promise<any>
|
||||
return api.get(`/material-admin/material-list?${query.toString()}`);
|
||||
}
|
||||
|
||||
export interface PreTestTemplateListParams {
|
||||
id?: string;
|
||||
phone?: string;
|
||||
created_at?: [string, string];
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}
|
||||
export async function getPreTestTemplateList(params: PreTestTemplateListParams): Promise<any> {
|
||||
const query = new URLSearchParams();
|
||||
if (params.id) query.set('id', params.id);
|
||||
if (params.phone) query.set('phone', params.phone);
|
||||
if (params.created_at !== undefined) query.set('created_at', params.created_at[0] + ',' + params.created_at[1]);
|
||||
if (params.page !== undefined) query.set('page', String(params.page));
|
||||
if (params.page_size !== undefined) query.set('page_size', String(params.page_size));
|
||||
return api.get(`/material-admin/pre-test-template-list?${query.toString()}`);
|
||||
}
|
||||
|
||||
// ── Private Portrait Admin ────────────────────────────────
|
||||
export async function adminGetPrivatePortraitProjects(params: { userId?: string; keyword?: string; status?: string; page?: number; pageSize?: number } = {}): Promise<PrivatePortraitProjectListOut> {
|
||||
const query = new URLSearchParams();
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Popover, Tag, List, Descriptions, Typography } from 'antd';
|
||||
|
||||
interface PreResultData {
|
||||
video_id: string;
|
||||
advertiser_id: number;
|
||||
material_id: string;
|
||||
is_ad_high_quality_material: string;
|
||||
is_ecp_high_quality_material: string;
|
||||
is_inefficient_material: string;
|
||||
is_first_publish_material: string;
|
||||
is_local_high_quality_material: string;
|
||||
not_ad_high_quality_reason: string[] | null;
|
||||
not_ecp_high_quality_reason: string[] | null;
|
||||
}
|
||||
|
||||
interface PreResultDisplayProps {
|
||||
preResult: string;
|
||||
}
|
||||
|
||||
const fieldConfig = [
|
||||
{ key: 'is_ad_high_quality_material', label: 'AD优质', noLabel: 'AD非优质素材', unknownLabel: 'AD未知', yesColor: 'green', noColor: 'red', unknownColor: 'default' },
|
||||
{ key: 'is_ecp_high_quality_material', label: '千川优质', noLabel: '千川非优质素材', unknownLabel: '千川未知', yesColor: 'green', noColor: 'red', unknownColor: 'default' },
|
||||
{ key: 'is_local_high_quality_material', label: '本地推优质', noLabel: '本地推非优质', unknownLabel: '本地推未知', yesColor: 'green', noColor: 'red', unknownColor: 'default' },
|
||||
{ key: 'is_inefficient_material', label: '低效', noLabel: '非低效', unknownLabel: '是否低效未知', yesColor: 'red', noColor: 'green', unknownColor: 'default' },
|
||||
{ key: 'is_first_publish_material', label: '首发', noLabel: '非首发', unknownLabel: '是否首发未知', yesColor: 'blue', noColor: 'default', unknownColor: 'default' },
|
||||
];
|
||||
|
||||
const PreResultDisplay: React.FC<PreResultDisplayProps> = ({ preResult }) => {
|
||||
const [parsedData, setParsedData] = useState<PreResultData | null>(null);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!preResult) {
|
||||
setParsedData(null);
|
||||
setHasError(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = JSON.parse(preResult);
|
||||
setParsedData(data);
|
||||
setHasError(false);
|
||||
} catch {
|
||||
setParsedData(null);
|
||||
setHasError(true);
|
||||
}
|
||||
}, [preResult]);
|
||||
|
||||
if (!preResult || hasError || !parsedData) {
|
||||
return <span style={{ color: '#64748b' }}>-</span>;
|
||||
}
|
||||
|
||||
const getIndicatorDisplay = (key: string) => {
|
||||
const config = fieldConfig.find(c => c.key === key);
|
||||
if (!config) return null;
|
||||
const value = parsedData[key as keyof PreResultData];
|
||||
const isYes = value === 'YES';
|
||||
const isNo = value === 'NO';
|
||||
const isUnknown = value === 'UNKNOWN';
|
||||
if (isUnknown) return null;
|
||||
let displayLabel, displayColor;
|
||||
if (isYes) {
|
||||
displayLabel = config.label;
|
||||
displayColor = config.yesColor;
|
||||
} else if (isNo) {
|
||||
displayLabel = config.noLabel;
|
||||
displayColor = config.noColor;
|
||||
}
|
||||
return {
|
||||
tag: (
|
||||
<Tag
|
||||
key={key}
|
||||
color={displayColor}
|
||||
style={{ marginRight: 6, fontSize: 12, borderRadius: 4 }}
|
||||
>
|
||||
{displayLabel}
|
||||
</Tag>
|
||||
),
|
||||
detail: {
|
||||
label: config.label.replace('优质', '优质素材'),
|
||||
value: displayLabel,
|
||||
color: displayColor,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const content = (
|
||||
<div style={{ maxWidth: 500, padding: '8px 0' }}>
|
||||
<Typography.Text strong style={{ fontSize: 14, display: 'block', marginBottom: 12, color: '#1e293b' }}>前测结果详情</Typography.Text>
|
||||
|
||||
<Descriptions column={1} size="small" style={{ marginBottom: 12 }}>
|
||||
<Descriptions.Item label="视频ID" labelStyle={{ fontWeight: 500, color: '#64748b' }} contentStyle={{ color: '#1e293b' }}>{parsedData.video_id}</Descriptions.Item>
|
||||
<Descriptions.Item label="广告主ID" labelStyle={{ fontWeight: 500, color: '#64748b' }} contentStyle={{ color: '#1e293b' }}>{parsedData.advertiser_id}</Descriptions.Item>
|
||||
<Descriptions.Item label="素材ID" labelStyle={{ fontWeight: 500, color: '#64748b' }} contentStyle={{ color: '#1e293b' }}>{parsedData.material_id}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<Typography.Text strong style={{ fontSize: 12, color: '#64748b', display: 'block', marginBottom: 8 }}>质量评估</Typography.Text>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 12 }}>
|
||||
{fieldConfig.map(config => {
|
||||
const value = parsedData[config.key as keyof PreResultData];
|
||||
const isYes = value === 'YES';
|
||||
const isNo = value === 'NO';
|
||||
const isUnknown = value === 'UNKNOWN';
|
||||
if (isUnknown) return null;
|
||||
let displayLabel, displayColor;
|
||||
if (isYes) {
|
||||
displayLabel = config.label;
|
||||
displayColor = config.yesColor;
|
||||
} else if (isNo) {
|
||||
displayLabel = config.noLabel;
|
||||
displayColor = config.noColor;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tag
|
||||
key={config.key}
|
||||
color={displayColor}
|
||||
style={{ fontSize: 12, borderRadius: 4 }}
|
||||
>
|
||||
{displayLabel}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{parsedData.not_ad_high_quality_reason && parsedData.not_ad_high_quality_reason.length > 0 && (
|
||||
<div style={{ marginBottom: 12 }}>
|
||||
<Typography.Text strong style={{ fontSize: 12, color: '#ef4444', display: 'block', marginBottom: 8 }}>
|
||||
AD非优质原因
|
||||
</Typography.Text>
|
||||
<List
|
||||
dataSource={parsedData.not_ad_high_quality_reason}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item key={index} style={{ padding: '4px 0', fontSize: 12, color: '#64748b' }}>
|
||||
{index + 1}. {item}
|
||||
</List.Item>
|
||||
)}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{parsedData.not_ecp_high_quality_reason && parsedData.not_ecp_high_quality_reason.length > 0 && (
|
||||
<div>
|
||||
<Typography.Text strong style={{ fontSize: 12, color: '#ef4444', display: 'block', marginBottom: 8 }}>
|
||||
千川非优质原因
|
||||
</Typography.Text>
|
||||
<List
|
||||
dataSource={parsedData.not_ecp_high_quality_reason}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item key={index} style={{ padding: '4px 0', fontSize: 12, color: '#64748b' }}>
|
||||
{index + 1}. {item}
|
||||
</List.Item>
|
||||
)}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={content}
|
||||
title={null}
|
||||
trigger="hover"
|
||||
placement="topLeft"
|
||||
overlayStyle={{ borderRadius: 12, boxShadow: '0 8px 24px rgba(0,0,0,0.12)' }}
|
||||
>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 4}}>
|
||||
{fieldConfig.map(config => {
|
||||
const display = getIndicatorDisplay(config.key);
|
||||
return display?.tag;
|
||||
})}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default PreResultDisplay;
|
||||
@@ -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;
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/app.tsx","./src/env.d.ts","./src/main.tsx","./src/api/client.ts","./src/api/crypto.ts","./src/api/index.ts","./src/pages/adminauthoriz.tsx","./src/pages/adminconsume.tsx","./src/pages/admincontactrequests.tsx","./src/pages/admincreditratios.tsx","./src/pages/admincreditrecords.tsx","./src/pages/admindashboard.tsx","./src/pages/admingenerationairecords.tsx","./src/pages/admingenerationrecords.tsx","./src/pages/adminhomematerials.tsx","./src/pages/adminhotopeningreplicationdetail.tsx","./src/pages/adminhotopeningreplications.tsx","./src/pages/adminimageengines.tsx","./src/pages/adminindustries.tsx","./src/pages/adminlayout.tsx","./src/pages/adminloginpage.tsx","./src/pages/adminmateriallist.tsx","./src/pages/adminmenuconfig.tsx","./src/pages/adminmodels.tsx","./src/pages/adminnotificationmanager.tsx","./src/pages/adminoauthlist.tsx","./src/pages/adminoauthapplist.tsx","./src/pages/adminoperationlogs.tsx","./src/pages/adminpaymentconfig.tsx","./src/pages/adminpaymentstats.tsx","./src/pages/adminplatform.tsx","./src/pages/adminpretesttemplates.tsx","./src/pages/adminprivateportraitprojects.tsx","./src/pages/adminrechargepackages.tsx","./src/pages/adminreplicationprojectdetail.tsx","./src/pages/adminsettings.tsx","./src/pages/adminshotreplications.tsx","./src/pages/adminshottasksetdetail.tsx","./src/pages/adminteams.tsx","./src/pages/adminusers.tsx","./src/pages/adminvideoengines.tsx","./src/pages/adminvideopromptschemaconfig.tsx","./src/pages/adminreplication/components/jsoncollapse.tsx","./src/pages/adminreplication/components/mediapreview.tsx","./src/pages/adminreplication/components/statustag.tsx","./src/pages/adminreplication/components/videopromptschemaviewer.tsx","./src/pages/homematerials/homematerialassettable.tsx","./src/pages/homematerials/homematerialcategorypanel.tsx","./src/pages/homematerials/homematerialuploadmodal.tsx","./src/pages/homematerials/watermarkeditor.tsx","./src/pages/homematerials/watermarklibrarymodal.tsx","./src/pages/homematerials/watermarkpreview.tsx","./src/store/index.ts","./src/types/index.ts","./src/types/xlsx-js-style.d.ts","./src/utils/excelexport.ts","./src/utils/formatdate.ts","./src/utils/resourceurl.ts","./src/utils/videopromptschema.ts"],"version":"6.0.3"}
|
||||
{"root":["./src/app.tsx","./src/env.d.ts","./src/main.tsx","./src/api/client.ts","./src/api/crypto.ts","./src/api/index.ts","./src/components/preresultdisplay.tsx","./src/pages/adminauthoriz.tsx","./src/pages/adminconsume.tsx","./src/pages/admincontactrequests.tsx","./src/pages/admincreditratios.tsx","./src/pages/admincreditrecords.tsx","./src/pages/admindashboard.tsx","./src/pages/admingenerationairecords.tsx","./src/pages/admingenerationrecords.tsx","./src/pages/adminhomematerials.tsx","./src/pages/adminhotopeningreplicationdetail.tsx","./src/pages/adminhotopeningreplications.tsx","./src/pages/adminimageengines.tsx","./src/pages/adminindustries.tsx","./src/pages/adminlayout.tsx","./src/pages/adminloginpage.tsx","./src/pages/adminmateriallist.tsx","./src/pages/adminmenuconfig.tsx","./src/pages/adminmodels.tsx","./src/pages/adminnotificationmanager.tsx","./src/pages/adminoauthlist.tsx","./src/pages/adminoauthapplist.tsx","./src/pages/adminoperationlogs.tsx","./src/pages/adminpaymentconfig.tsx","./src/pages/adminpaymentstats.tsx","./src/pages/adminplatform.tsx","./src/pages/adminpretesttemplates.tsx","./src/pages/adminprivateportraitprojects.tsx","./src/pages/adminrechargepackages.tsx","./src/pages/adminreplicationprojectdetail.tsx","./src/pages/adminsettings.tsx","./src/pages/adminshotreplications.tsx","./src/pages/adminshottasksetdetail.tsx","./src/pages/adminteams.tsx","./src/pages/adminusers.tsx","./src/pages/adminvideoengines.tsx","./src/pages/adminvideopromptschemaconfig.tsx","./src/pages/adminreplication/components/jsoncollapse.tsx","./src/pages/adminreplication/components/mediapreview.tsx","./src/pages/adminreplication/components/statustag.tsx","./src/pages/adminreplication/components/videopromptschemaviewer.tsx","./src/pages/homematerials/homematerialassettable.tsx","./src/pages/homematerials/homematerialcategorypanel.tsx","./src/pages/homematerials/homematerialuploadmodal.tsx","./src/pages/homematerials/watermarkeditor.tsx","./src/pages/homematerials/watermarklibrarymodal.tsx","./src/pages/homematerials/watermarkpreview.tsx","./src/store/index.ts","./src/types/index.ts","./src/types/xlsx-js-style.d.ts","./src/utils/excelexport.ts","./src/utils/formatdate.ts","./src/utils/resourceurl.ts","./src/utils/videopromptschema.ts"],"version":"6.0.3"}
|
||||
Reference in New Issue
Block a user