添加城市地区缓存

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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -28,7 +28,7 @@
}
})();
</script>
<script type="module" crossorigin src="/assets/index-Cs6AfEXe.js"></script>
<script type="module" crossorigin src="/assets/index-DNQfr5S_.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BhPFzWLH.css">
</head>
<body>
@@ -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>