|
|
@@ -82,7 +82,7 @@ interface Props {
|
|
|
* 按 modality 取解构对象 - 视频取 videoDetail.deconstruct, 素材/长文取各自 detail
|
|
|
*/
|
|
|
function getDeconstruct(item: VideoMatchEnrichedVO): VideoDetailDeconstruct | undefined {
|
|
|
- if (item.modality === 'MATERIAL') return item.materialDetail?.deconstruct
|
|
|
+ if (item.modality === 'MATERIAL') return item.materialDetail?.deconstruct ?? (item.deconstruct ?? undefined)
|
|
|
if (item.modality === 'ARTICLE') return item.articleDetail?.deconstruct
|
|
|
return item.videoDetail?.deconstruct
|
|
|
}
|
|
|
@@ -407,28 +407,6 @@ export default function RecallResultTable({
|
|
|
|
|
|
/** 素材专属列 */
|
|
|
const materialOnlyCols: ColumnsType<RowItem> = [
|
|
|
- {
|
|
|
- title: '图片张数',
|
|
|
- key: 'imageCount',
|
|
|
- width: 90,
|
|
|
- align: 'right',
|
|
|
- render: (_v, item) => {
|
|
|
- const n = item.materialDetail?.imageCount ?? item.imageList?.length
|
|
|
- return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{n != null ? n : '--'}</span>
|
|
|
- },
|
|
|
- },
|
|
|
- {
|
|
|
- title: '来源',
|
|
|
- key: 'material.source',
|
|
|
- width: 100,
|
|
|
- render: (_v, item) => textOrDash(item.materialDetail?.source),
|
|
|
- },
|
|
|
- {
|
|
|
- title: '上传时间',
|
|
|
- key: 'material.uploadTime',
|
|
|
- width: 140,
|
|
|
- render: (_v, item) => textOrDash(item.materialDetail?.uploadTime),
|
|
|
- },
|
|
|
{
|
|
|
title: '使用次数',
|
|
|
key: 'material.usageCount',
|
|
|
@@ -527,11 +505,129 @@ export default function RecallResultTable({
|
|
|
pointsCol('解构:目的点', '目的点', 240),
|
|
|
]
|
|
|
|
|
|
+ /** 素材质量列 (仅在 MATERIAL 模式且存在 quality 数据时展示) */
|
|
|
+ const materialQualityCols: ColumnsType<RowItem> = [
|
|
|
+ {
|
|
|
+ title: '统计日期',
|
|
|
+ key: 'q.dt',
|
|
|
+ width: 90,
|
|
|
+ align: 'center',
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.dt
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontSize: 12 }}>{v}</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '近7日打开率',
|
|
|
+ key: 'q.ctr7d',
|
|
|
+ width: 100,
|
|
|
+ align: 'right',
|
|
|
+ sorter: (a, b) => (a.materialDetail?.quality?.ctr7d ?? -1) - (b.materialDetail?.quality?.ctr7d ?? -1),
|
|
|
+ sortDirections: ['descend', 'ascend'],
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.ctr7d
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{(v * 100).toFixed(2)}%</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '近7日裂变率',
|
|
|
+ key: 'q.viralRate7d',
|
|
|
+ width: 100,
|
|
|
+ align: 'right',
|
|
|
+ sorter: (a, b) => (a.materialDetail?.quality?.viralRate7d ?? -1) - (b.materialDetail?.quality?.viralRate7d ?? -1),
|
|
|
+ sortDirections: ['descend', 'ascend'],
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.viralRate7d
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{(v * 100).toFixed(2)}%</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '近7日ROI',
|
|
|
+ key: 'q.roi7d',
|
|
|
+ width: 90,
|
|
|
+ align: 'right',
|
|
|
+ sorter: (a, b) => (a.materialDetail?.quality?.roi7d ?? -1) - (b.materialDetail?.quality?.roi7d ?? -1),
|
|
|
+ sortDirections: ['descend', 'ascend'],
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.roi7d
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{v.toFixed(2)}</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '近7日转化',
|
|
|
+ key: 'q.totalConversion7d',
|
|
|
+ width: 110,
|
|
|
+ align: 'right',
|
|
|
+ sorter: (a, b) => (a.materialDetail?.quality?.totalConversion7d ?? -1) - (b.materialDetail?.quality?.totalConversion7d ?? -1),
|
|
|
+ sortDirections: ['descend', 'ascend'],
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.totalConversion7d
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{formatNumber(String(v))}</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '近7日首层UV',
|
|
|
+ key: 'q.firstUv7d',
|
|
|
+ width: 110,
|
|
|
+ align: 'right',
|
|
|
+ sorter: (a, b) => (a.materialDetail?.quality?.firstUv7d ?? -1) - (b.materialDetail?.quality?.firstUv7d ?? -1),
|
|
|
+ sortDirections: ['descend', 'ascend'],
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.firstUv7d
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{formatNumber(String(v))}</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '近7日裂变',
|
|
|
+ key: 'q.t0ViralCount7d',
|
|
|
+ width: 100,
|
|
|
+ align: 'right',
|
|
|
+ sorter: (a, b) => (a.materialDetail?.quality?.t0ViralCount7d ?? -1) - (b.materialDetail?.quality?.t0ViralCount7d ?? -1),
|
|
|
+ sortDirections: ['descend', 'ascend'],
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.t0ViralCount7d
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{formatNumber(String(v))}</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '近7日收入',
|
|
|
+ key: 'q.revenue7d',
|
|
|
+ width: 100,
|
|
|
+ align: 'right',
|
|
|
+ sorter: (a, b) => (a.materialDetail?.quality?.revenue7d ?? -1) - (b.materialDetail?.quality?.revenue7d ?? -1),
|
|
|
+ sortDirections: ['descend', 'ascend'],
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.revenue7d
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{formatNumber(String(v))}</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '近7日消耗',
|
|
|
+ key: 'q.cost7d',
|
|
|
+ width: 100,
|
|
|
+ align: 'right',
|
|
|
+ sorter: (a, b) => (a.materialDetail?.quality?.cost7d ?? -1) - (b.materialDetail?.quality?.cost7d ?? -1),
|
|
|
+ sortDirections: ['descend', 'ascend'],
|
|
|
+ render: (_v, item) => {
|
|
|
+ const v = item.materialDetail?.quality?.cost7d
|
|
|
+ if (v == null) return <Text type="secondary">--</Text>
|
|
|
+ return <span style={{ fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{formatNumber(String(v))}</span>
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
/** 按 modality 拼装最终列 + 计算 scroll.x */
|
|
|
let columns: ColumnsType<RowItem>
|
|
|
if (activeModality === 'MATERIAL') {
|
|
|
- // 素材 Tab: 不展示综合得分列(rov 不适用)
|
|
|
- columns = [titleCol, coverCol, configCodeCol, scoreColumn, ...materialOnlyCols]
|
|
|
+ columns = [titleCol, coverCol, configCodeCol, compositeCol, scoreColumn, ...materialQualityCols, ...materialOnlyCols]
|
|
|
} else if (activeModality === 'ARTICLE') {
|
|
|
columns = [titleCol, coverCol, configCodeCol, scoreColumn, ...articleOnlyCols]
|
|
|
} else {
|