|
@@ -324,24 +324,37 @@ html_content = f"""<!DOCTYPE html>
|
|
|
const rows = currentRowOrder.filter(r => data.rows.includes(r));
|
|
const rows = currentRowOrder.filter(r => data.rows.includes(r));
|
|
|
const cols = currentColOrder.filter(c => data.cols.includes(c));
|
|
const cols = currentColOrder.filter(c => data.cols.includes(c));
|
|
|
|
|
|
|
|
- // 生成表头(可点击排序)
|
|
|
|
|
|
|
+ // 计算行/列的UV总和(用于tooltip)
|
|
|
|
|
+ const uvData = data.uv;
|
|
|
|
|
+ const rowUvTotals = {{}};
|
|
|
|
|
+ const colUvTotals = {{}};
|
|
|
|
|
+ rows.forEach(r => {{
|
|
|
|
|
+ rowUvTotals[r] = cols.reduce((sum, c) => sum + (uvData[r]?.[c] || 0), 0);
|
|
|
|
|
+ }});
|
|
|
|
|
+ cols.forEach(c => {{
|
|
|
|
|
+ colUvTotals[c] = rows.reduce((sum, r) => sum + (uvData[r]?.[c] || 0), 0);
|
|
|
|
|
+ }});
|
|
|
|
|
+
|
|
|
|
|
+ // 生成表头(可点击排序,带tooltip)
|
|
|
const metricLabels = {{ uv: '点击UV', ror: '整体裂变率', orig: '头部裂变率', rec: '推荐裂变率' }};
|
|
const metricLabels = {{ uv: '点击UV', ror: '整体裂变率', orig: '头部裂变率', rec: '推荐裂变率' }};
|
|
|
document.getElementById('matrix-header').innerHTML = `
|
|
document.getElementById('matrix-header').innerHTML = `
|
|
|
<tr>
|
|
<tr>
|
|
|
<th style="cursor:pointer;font-size:10px;line-height:1.3" onclick="sortByRowSum()">头部品类(行) ↕<br>再分享品类(列)→</th>
|
|
<th style="cursor:pointer;font-size:10px;line-height:1.3" onclick="sortByRowSum()">头部品类(行) ↕<br>再分享品类(列)→</th>
|
|
|
- ${{cols.map(c => `<th style="cursor:pointer" onclick="sortByCol('${{c}}')">${{c}}</th>`).join('')}}
|
|
|
|
|
|
|
+ ${{cols.map(c => `<th style="cursor:pointer" onclick="sortByCol('${{c}}')" title="再分享品类: ${{c}} 点击UV: ${{colUvTotals[c].toLocaleString()}}">${{c}}</th>`).join('')}}
|
|
|
</tr>
|
|
</tr>
|
|
|
`;
|
|
`;
|
|
|
|
|
|
|
|
- // 生成数据行(行头可点击排序)
|
|
|
|
|
|
|
+ // 生成数据行(行头可点击排序,带tooltip)
|
|
|
document.getElementById('matrix-body').innerHTML = rows.map(r => {{
|
|
document.getElementById('matrix-body').innerHTML = rows.map(r => {{
|
|
|
const cells = cols.map(c => {{
|
|
const cells = cols.map(c => {{
|
|
|
const val = metricData[r]?.[c] || 0;
|
|
const val = metricData[r]?.[c] || 0;
|
|
|
|
|
+ const cellUv = uvData[r]?.[c] || 0;
|
|
|
const bg = getGradient(val, maxVal);
|
|
const bg = getGradient(val, maxVal);
|
|
|
const display = metric === 'uv' ? parseInt(val).toLocaleString() : val.toFixed(4);
|
|
const display = metric === 'uv' ? parseInt(val).toLocaleString() : val.toFixed(4);
|
|
|
- return `<td style="background:${{bg}}">${{display}}</td>`;
|
|
|
|
|
|
|
+ const tipMetric = metric === 'uv' ? '' : `${{metricLabels[metric]}}: ${{val.toFixed(4)}} `;
|
|
|
|
|
+ return `<td style="background:${{bg}}" title="头部: ${{r}} 再分享: ${{c}} ${{tipMetric}}点击UV: ${{cellUv.toLocaleString()}}">${{display}}</td>`;
|
|
|
}}).join('');
|
|
}}).join('');
|
|
|
- return `<tr><td style="cursor:pointer;background:#f5f5f5" onclick="sortByRow('${{r}}')">${{r}}</td>${{cells}}</tr>`;
|
|
|
|
|
|
|
+ return `<tr><td style="cursor:pointer;background:#f5f5f5" onclick="sortByRow('${{r}}')" title="头部品类: ${{r}} 点击UV: ${{rowUvTotals[r].toLocaleString()}}">${{r}}</td>${{cells}}</tr>`;
|
|
|
}}).join('');
|
|
}}).join('');
|
|
|
}}
|
|
}}
|
|
|
|
|
|