Files
video-gen/video-gen-admin/src/components/PreResultDisplay.tsx
T
2026-07-06 18:07:45 +08:00

180 lines
6.7 KiB
TypeScript

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;