fix(admin): 修复工作台宽屏居中偏右与面包屑错位问题并升级转化漏斗视觉流转

这个提交包含在:
wikiboss
2026-09-21 13:54:44 +08:00
父节点 84385f4f8e
当前提交 e507818792
共有 3 个文件被更改,包括 182 次插入 和 52 次删除

查看文件

@@ -281,6 +281,44 @@ function Workplace(): React.ReactElement {
};
}, [overview.radarFeed]);
const interactCount = useMemo(
() => overview.radarFeed.filter((f) => f.stayDurationMs > 10000).length,
[overview.radarFeed]
);
const funnelMetrics = useMemo(() => {
const tap = kpis.tapCount;
const view = kpis.viewCount;
const interact = interactCount;
const lead = kpis.newLeadCount;
const interactRatio = view > 0 ? Math.min(100, Math.round((interact / view) * 100)) : 0;
const leadRatio = view > 0 ? Math.min(100, Math.round((lead / view) * 100)) : 0;
return {
tap: {
value: tap,
ratioText: '100%',
barWidth: '100%',
},
view: {
value: view,
ratioText: tap > 0 ? `${Math.min(100, Math.round((view / tap) * 100))}%` : '100%',
barWidth: `${Math.min(100, Math.max(15, tap > 0 ? (view / tap) * 100 : 80))}%`,
},
interact: {
value: interact,
ratioText: `${interactRatio}%`,
barWidth: `${Math.min(100, Math.max(12, interactRatio))}%`,
},
lead: {
value: lead,
ratioText: `${leadRatio}%`,
barWidth: `${Math.min(100, Math.max(10, leadRatio))}%`,
},
};
}, [kpis.tapCount, kpis.viewCount, kpis.newLeadCount, interactCount]);
// 雷达客商列表(根据过滤器筛选)
const filteredFeed = useMemo(() => {
if (feedFilter === 'high_intent') {
@@ -394,71 +432,62 @@ function Workplace(): React.ReactElement {
>
<div className={styles.funnelContainer}>
<div className={styles.funnelStep}>
<div className={styles.funnelLabel}>{t['workplace.funnel.tap']}</div>
<div className={styles.funnelValue}>{kpis.tapCount}</div>
<div className={styles.funnelHeader}>
<span className={styles.funnelLabel}>{t['workplace.funnel.tap']}</span>
<span className={styles.funnelRate}>{funnelMetrics.tap.ratioText}</span>
</div>
<div className={styles.funnelValue}>{funnelMetrics.tap.value}</div>
<div className={styles.funnelBarWrapper}>
<div className={styles.funnelBar} style={{ width: '100%' }} />
<div className={styles.funnelBar} style={{ width: funnelMetrics.tap.barWidth }} />
</div>
<div className={styles.funnelMeta}>
<span>Base</span>
</div>
</div>
<div className={styles.funnelStep}>
<div className={styles.funnelLabel}>{t['workplace.funnel.view']}</div>
<div className={styles.funnelValue}>{kpis.viewCount}</div>
<div className={styles.funnelHeader}>
<span className={styles.funnelLabel}>{t['workplace.funnel.view']}</span>
<span className={styles.funnelRate}>{funnelMetrics.view.ratioText}</span>
</div>
<div className={styles.funnelValue}>{funnelMetrics.view.value}</div>
<div className={styles.funnelBarWrapper}>
<div
className={styles.funnelBar}
style={{
width: `${Math.min(
100,
Math.max(15, kpis.tapCount > 0 ? (kpis.viewCount / kpis.tapCount) * 100 : 80)
)}%`,
}}
/>
<div className={styles.funnelBar} style={{ width: funnelMetrics.view.barWidth }} />
</div>
<div className={styles.funnelMeta}>
<span>Taps &rarr; Views</span>
</div>
</div>
<div className={styles.funnelStep}>
<div className={styles.funnelLabel}>{t['workplace.funnel.interact']}</div>
<div className={styles.funnelValue}>
{overview.radarFeed.filter((f) => f.stayDurationMs > 10000).length}
<div className={styles.funnelHeader}>
<span className={styles.funnelLabel}>{t['workplace.funnel.interact']}</span>
<span className={styles.funnelRate}>{funnelMetrics.interact.ratioText}</span>
</div>
<div className={styles.funnelValue}>{funnelMetrics.interact.value}</div>
<div className={styles.funnelBarWrapper}>
<div
className={styles.funnelBar}
style={{
width: `${Math.min(
100,
Math.max(
15,
kpis.viewCount > 0
? (overview.radarFeed.filter((f) => f.stayDurationMs > 10000).length /
kpis.viewCount) *
100
: 50
)
)}%`,
}}
/>
<div className={styles.funnelBar} style={{ width: funnelMetrics.interact.barWidth }} />
</div>
<div className={styles.funnelMeta}>
<span>Views &rarr; Engaged</span>
</div>
</div>
<div className={styles.funnelStep}>
<div className={styles.funnelLabel}>{t['workplace.funnel.lead']}</div>
<div className={styles.funnelValue}>{kpis.newLeadCount}</div>
<div className={styles.funnelHeader}>
<span className={styles.funnelLabel}>{t['workplace.funnel.lead']}</span>
<span className={styles.funnelRate}>{funnelMetrics.lead.ratioText}</span>
</div>
<div className={styles.funnelValue}>{funnelMetrics.lead.value}</div>
<div className={styles.funnelBarWrapper}>
<div
className={`${styles.funnelBar} ${styles.funnelBarLead}`}
style={{
width: `${Math.min(
100,
Math.max(
15,
kpis.viewCount > 0 ? (kpis.newLeadCount / kpis.viewCount) * 100 : 25
)
)}%`,
}}
style={{ width: funnelMetrics.lead.barWidth }}
/>
</div>
<div className={styles.funnelMeta}>
<span>Engaged &rarr; Leads</span>
</div>
</div>
</div>
</Card>

查看文件

@@ -1,8 +1,7 @@
.page {
min-height: 100%;
width: 100%;
max-width: 1200px;
margin: 0 auto;
margin: 0;
font-family: var(--omni-font-family);
box-sizing: border-box;
}
@@ -191,32 +190,46 @@
.funnelStep {
display: flex;
flex-direction: column;
position: relative;
}
.funnelHeader {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: var(--omni-space-4);
}
.funnelLabel {
font-size: 13px;
font-weight: 500;
color: var(--omni-color-text-2);
margin-bottom: var(--omni-space-4);
}
.funnelRate {
font-size: 12px;
color: var(--omni-color-primary);
font-weight: 600;
}
.funnelValue {
font-size: 20px;
font-size: 22px;
font-weight: 600;
color: var(--omni-color-text);
margin-bottom: var(--omni-space-8);
}
.funnelBarWrapper {
height: 4px;
background: var(--omni-color-surface);
border-radius: 2px;
height: 6px;
background: rgba(11, 92, 255, 0.08);
border-radius: 3px;
overflow: hidden;
}
.funnelBar {
height: 100%;
background: var(--omni-color-primary);
border-radius: 2px;
border-radius: 3px;
transition: width 300ms ease;
}
@@ -224,6 +237,15 @@
background: var(--omni-color-success);
}
.funnelMeta {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: var(--omni-space-4);
font-size: 11px;
color: var(--omni-color-text-2);
}
.radarCard {
margin-top: var(--omni-space-16);
}