180 lines
6.7 KiB
TypeScript
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; |