添加城市地区缓存

This commit is contained in:
Lrd
2026-06-30 17:19:42 +08:00
parent a3e70b5d49
commit ecb0f512c3
8 changed files with 2409 additions and 74 deletions
@@ -2,27 +2,29 @@ import React, { useState } from 'react';
import { Popover, Tag, List, Descriptions, Typography } from 'antd';
interface PreResultData {
video_id: string; //视频id
advertiser_id: number; //广告主id
material_id: string; //素材id
is_ad_high_quality_material: string; //是否优质素材
is_ecp_high_quality_material: string; //是否千川优质素材
is_inefficient_material: string; //是否低效素材
is_first_publish_material: string; //是否首发素材
not_ad_high_quality_reason: string[] | null; //AD非优质原因
not_ecp_high_quality_reason: string[] | null; //千川非优质原因
is_local_high_quality_material: string; //是否本地推优质素材
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 qualityConfig: Record<string, { color: string; label: string }> = {
YES: { color: 'green', label: '' },
NO: { color: 'red', label: '' },
UNKNOWN: { color: 'default', label: '未知' },
};
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);
@@ -48,51 +50,79 @@ const PreResultDisplay: React.FC<PreResultDisplayProps> = ({ preResult }) => {
return <span style={{ color: '#64748b' }}>-</span>;
}
const allQualityFields = [
parsedData.is_ad_high_quality_material,
parsedData.is_ecp_high_quality_material,
parsedData.is_local_high_quality_material,
];
const hasNoQuality = allQualityFields.some((val) => val === 'NO');
const allYes = allQualityFields.every((val) => val === 'YES');
let statusTag;
if (hasNoQuality) {
statusTag = <Tag color="red"></Tag>;
} else if (allYes) {
statusTag = <Tag color="green"></Tag>;
} else {
statusTag = <Tag color="default"></Tag>;
}
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 isUnknown = value === 'UNKNOWN';
let displayLabel, displayColor;
if (isUnknown) {
displayLabel = config.unknownLabel;
displayColor = config.unknownColor;
} else if (isYes) {
displayLabel = config.label;
displayColor = config.yesColor;
} else {
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 }}>
<Typography.Text strong style={{ fontSize: 14, display: 'block', marginBottom: 12 }}></Typography.Text>
<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">{parsedData.video_id}</Descriptions.Item>
<Descriptions.Item label="广告主ID">{parsedData.advertiser_id}</Descriptions.Item>
<Descriptions.Item label="素材ID">{parsedData.material_id}</Descriptions.Item>
<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 }}>
<Tag color={qualityConfig[parsedData.is_ad_high_quality_material]?.color}>
AD优质素材: {qualityConfig[parsedData.is_ad_high_quality_material]?.label}
</Tag>
<Tag color={qualityConfig[parsedData.is_ecp_high_quality_material]?.color}>
: {qualityConfig[parsedData.is_ecp_high_quality_material]?.label}
</Tag>
<Tag color={qualityConfig[parsedData.is_local_high_quality_material]?.color}>
: {qualityConfig[parsedData.is_local_high_quality_material]?.label}
</Tag>
<Tag color={qualityConfig[parsedData.is_inefficient_material]?.color}>
: {qualityConfig[parsedData.is_inefficient_material]?.label}
</Tag>
<Tag color={qualityConfig[parsedData.is_first_publish_material]?.color}>
: {qualityConfig[parsedData.is_first_publish_material]?.label}
</Tag>
{fieldConfig.map(config => {
const value = parsedData[config.key as keyof PreResultData];
const isYes = value === 'YES';
const isUnknown = value === 'UNKNOWN';
let displayLabel, displayColor;
if (isUnknown) {
displayLabel = config.unknownLabel;
displayColor = config.unknownColor;
} else if (isYes) {
displayLabel = config.label;
displayColor = config.yesColor;
} else {
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 && (
@@ -132,10 +162,21 @@ const PreResultDisplay: React.FC<PreResultDisplayProps> = ({ preResult }) => {
);
return (
<Popover content={content} title={null} trigger="hover">
{statusTag}
<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;
export default PreResultDisplay;
+102 -18
View File
@@ -148,6 +148,8 @@ const PreTest: React.FC = () => {
const [pageSize, setPageSize] = useState(10);
const [platform, setPlatform] = useState<string>('AD');
const [areaOptions, setAreaOptions] = useState<any[]>([]);
const [cityCodeMap, setCityCodeMap] = useState<Record<string, string>>({});
const [provinceCityMap, setProvinceCityMap] = useState<Record<string, string[]>>({});
useEffect(() => {
loadRecords();
@@ -174,20 +176,66 @@ const PreTest: React.FC = () => {
const loadProvinceOptions = async () => {
try {
const cachedData = localStorage.getItem('preTestAreaData');
if (cachedData) {
const parsed = JSON.parse(cachedData);
setAreaOptions(parsed.areaOptions);
setCityCodeMap(parsed.cityCodeMap);
setProvinceCityMap(parsed.provinceCityMap);
return;
}
const res = await getArea({ level: 'ONE_LEVEL' });
const options = (res.data || []).map((item: any) => ({
value: item.code,
label: item.name,
isLeaf: false,
}));
setAreaOptions(options);
const provinces = res.data || [];
const map: Record<string, string> = {};
const provinceCity: Record<string, string[]> = {};
const provinceOptions = await Promise.all(
provinces.map(async (province: any) => {
let cities: any[] = [];
try {
const cityRes = await getArea({ level: 'TWO_LEVEL', parent_code: province.code });
cities = (cityRes.data || []).map((city: any) => ({
value: city.code,
label: city.name,
isLeaf: true,
}));
const cityCodes = cities.map((city: any) => city.value);
provinceCity[province.code] = cityCodes;
cities.forEach((city: any) => {
map[city.value] = city.label;
});
} catch (error) {
console.error(`获取省份 ${province.name} 的城市失败`, error);
provinceCity[province.code] = [];
}
return {
value: province.code,
label: province.name,
isLeaf: false,
children: cities,
};
})
);
const cacheData = { areaOptions: provinceOptions, cityCodeMap: map, provinceCityMap: provinceCity };
localStorage.setItem('preTestAreaData', JSON.stringify(cacheData));
setAreaOptions(provinceOptions);
setCityCodeMap(map);
setProvinceCityMap(provinceCity);
} catch (error) {
message.error('获取省份信息失败');
}
};
const loadProvince = async () => {
const cityRes = await getArea({ level: 'TWO_LEVEL' });
console.log(cityRes);
}
const loadCityOptions = async (selectedOptions: any) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
console.log(targetOption);
targetOption.loading = true;
try {
const res = await getArea({ level: 'TWO_LEVEL', parent_code: targetOption.value });
@@ -281,13 +329,25 @@ const PreTest: React.FC = () => {
},
{
title: '受众地区',
dataIndex: 'audience_region',
key: 'audience_region',
dataIndex: 'audienceRegion',
key: 'audienceRegion',
ellipsis: true,
width: 150,
render: (text: string[]) => (
<span style={{ color: '#1e293b', fontSize: 13 }}>{Array.isArray(text) ? text.join(', ') : text || '-'}</span>
),
width: 300,
render: (text: string[]) => {
if (!Array.isArray(text) || text.length === 0) {
return <span style={{ color: '#94a3b8', fontSize: 13 }}>-</span>;
}
const cityNames = text.map(code => cityCodeMap[String(code)] || String(code));
return (
<Input.TextArea
value={cityNames.join(', ') || '-'}
readOnly
autoSize={{ minRows: 1, maxRows: 4 }}
style={{ resize: 'none', border: 'none', background: 'transparent', padding: 0 }}
placeholder="-"
/>
)
},
},
{
title: '网络类型',
@@ -400,11 +460,22 @@ const PreTest: React.FC = () => {
setModalOpen(true);
};
const fillFormWithDetail = (data: any) => {
const fillFormWithDetail = async (data: any) => {
setCurrentRecord(data);
setPlatform(data.platform || 'AD');
const regionCodes = data.audience_region || [];
const regionPaths = regionCodes.map((code: any) => [String(code)]);
const regionCodes = data.audienceRegion || [];
const regionPaths: string[][] = [];
for (const code of regionCodes) {
const cityCode = String(code);
console.log(cityCode);
if (cityCode.length >= 6) {
const provinceCode = cityCode.slice(0, 2);
console.log(provinceCode, cityCode);
loadCityOptions(provinceCode);
regionPaths.push([provinceCode, cityCode]);
}
}
console.log(regionPaths);
form.setFieldsValue({
name: data.name,
note: data.note,
@@ -478,7 +549,17 @@ const PreTest: React.FC = () => {
try {
const values = await form.validateFields();
setSubmitLoading(true);
const regionCodes = (values.audience_region || []).map((path: string[]) => path[path.length - 1]);
console.log(values.audience_region);
const regionCodes: string[] = [];
(values.audience_region || []).forEach((path: string[]) => {
if (path.length === 1) {
const provinceCode = path[0];
const cities = provinceCityMap[provinceCode] || [];
regionCodes.push(...cities);
} else if (path.length >= 2) {
regionCodes.push(path[path.length - 1]);
}
});
const params = {
name: values.name,
note: values.note,
@@ -524,6 +605,11 @@ const PreTest: React.FC = () => {
}
};
// [['34', '340800'],['35', '350100']]
// [['35', '350100']]
// [['34'],['35']]
// [['50']]
const handlePageChange = (page: number, size: number) => {
setCurrentPage(page);
setPageSize(size);
@@ -768,8 +854,6 @@ const PreTest: React.FC = () => {
placeholder="请选择受众地区(可多选)"
style={{ borderRadius: 8, minHeight: 40 }}
options={areaOptions}
loadData={loadCityOptions}
changeOnSelect
maxTagCount="responsive"
/>
</Form.Item>