feat(dashboard): 在工作台右侧扩展出海运营快捷中枢,新增8大高频业务入口、渠道矩阵与待办预警

这个提交包含在:
wikiboss
2026-09-21 10:27:20 +08:00
父节点 4843c6d713
当前提交 088a5f05e1
共有 4 个文件被更改,包括 582 次插入 和 232 次删除

查看文件

@@ -17,15 +17,21 @@ import {
Typography, Typography,
} from '@arco-design/web-react'; } from '@arco-design/web-react';
import { import {
IconApps,
IconArrowRise, IconArrowRise,
IconCompass, IconCompass,
IconDesktop,
IconDownload, IconDownload,
IconEye, IconEye,
IconIdcard, IconIdcard,
IconMobile, IconMobile,
IconPlus, IconPlus,
IconRight,
IconRobot, IconRobot,
IconSafe,
IconSend,
IconUserAdd, IconUserAdd,
IconUserGroup,
} from '@arco-design/web-react/icon'; } from '@arco-design/web-react/icon';
import useLocale from '@/utils/useLocale'; import useLocale from '@/utils/useLocale';
import { getCssVariables, lightColors } from '@/style/tokens'; import { getCssVariables, lightColors } from '@/style/tokens';
@@ -179,20 +185,9 @@ function Workplace(): React.ReactElement {
fetchOverview(); fetchOverview();
}, [fetchOverview]); }, [fetchOverview]);
const handleCreateCard = () => { // 导航路由跳转处理器
history.push('/card/list'); const handleNav = (path: string) => {
}; history.push(path);
const handleIssueCards = () => {
history.push('/nfc/batch');
};
const handleOpenRadar = () => {
history.push('/dashboard/radar-map');
};
const handleOpenAiConfig = () => {
history.push('/ai/concierge');
}; };
const handleExportLeads = async () => { const handleExportLeads = async () => {
@@ -294,7 +289,7 @@ function Workplace(): React.ReactElement {
return ( return (
<div className={styles.page} style={tokenStyle}> <div className={styles.page} style={tokenStyle}>
{/* 顶部标题与唯 1 主 CTA */} {/* 顶部标题与唯一主 CTA */}
<div className={styles.header}> <div className={styles.header}>
<div className={styles.headerInfo}> <div className={styles.headerInfo}>
<div className={styles.titleRow}> <div className={styles.titleRow}>
@@ -317,78 +312,84 @@ function Workplace(): React.ReactElement {
type="primary" type="primary"
icon={<IconPlus />} icon={<IconPlus />}
className={styles.primaryCta} className={styles.primaryCta}
onClick={handleCreateCard} onClick={() => handleNav('/card/list')}
> >
{t['workplace.cta.newCard']} {t['workplace.cta.newCard']}
</Button> </Button>
</div> </div>
<Spin loading={loading} style={{ display: 'block' }}> <Spin loading={loading} style={{ display: 'block' }}>
{/* KPI 核心指标阵列 */} {/* 宽屏 17 : 7 弹性整体双栏架构 */}
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col xs={24} sm={12} lg={6}> {/* 左侧业务主流程 (17/24 栅格) */}
<Card bordered={false} className={styles.kpiCard}> <Col xs={24} xl={17}>
<div className={styles.kpiHeader}> {/* KPI 核心指标阵列 */}
<span className={styles.kpiTitle}>{t['workplace.kpi.taps']}</span> <Row gutter={[16, 16]}>
<span className={styles.kpiIconWrapper}> <Col xs={24} sm={12} md={6}>
<IconMobile className={styles.kpiIcon} /> <Card bordered={false} className={styles.kpiCard}>
</span> <div className={styles.kpiHeader}>
</div> <span className={styles.kpiTitle}>{t['workplace.kpi.taps']}</span>
<Statistic value={kpis.tapCount} /> <span className={styles.kpiIconWrapper}>
{renderDelta(kpis.tapDelta)} <IconMobile className={styles.kpiIcon} />
</Card> </span>
</Col> </div>
<Statistic value={kpis.tapCount} />
{renderDelta(kpis.tapDelta)}
</Card>
</Col>
<Col xs={24} sm={12} lg={6}> <Col xs={24} sm={12} md={6}>
<Card bordered={false} className={styles.kpiCard}> <Card bordered={false} className={styles.kpiCard}>
<div className={styles.kpiHeader}> <div className={styles.kpiHeader}>
<span className={styles.kpiTitle}>{t['workplace.kpi.views']}</span> <span className={styles.kpiTitle}>{t['workplace.kpi.views']}</span>
<span className={styles.kpiIconWrapper}> <span className={styles.kpiIconWrapper}>
<IconEye className={styles.kpiIcon} /> <IconEye className={styles.kpiIcon} />
</span> </span>
</div> </div>
<Statistic value={kpis.viewCount} /> <Statistic value={kpis.viewCount} />
{renderDelta(kpis.viewDelta)} {renderDelta(kpis.viewDelta)}
</Card> </Card>
</Col> </Col>
<Col xs={24} sm={12} lg={6}> <Col xs={24} sm={12} md={6}>
<Card bordered={false} className={styles.kpiCard}> <Card bordered={false} className={styles.kpiCard}>
<div className={styles.kpiHeader}> <div className={styles.kpiHeader}>
<span className={styles.kpiTitle}>{t['workplace.kpi.leads']}</span> <span className={styles.kpiTitle}>{t['workplace.kpi.leads']}</span>
<span className={styles.kpiIconWrapper}> <span className={styles.kpiIconWrapper}>
<IconUserAdd className={styles.kpiIcon} /> <IconUserAdd className={styles.kpiIcon} />
</span> </span>
</div> </div>
<Statistic value={kpis.newLeadCount} /> <Statistic value={kpis.newLeadCount} />
{renderDelta(kpis.newLeadDelta)} {renderDelta(kpis.newLeadDelta)}
</Card> </Card>
</Col> </Col>
<Col xs={24} sm={12} lg={6}> <Col xs={24} sm={12} md={6}>
<Card bordered={false} className={styles.kpiCard}> <Card bordered={false} className={styles.kpiCard}>
<div className={styles.kpiHeader}> <div className={styles.kpiHeader}>
<span className={styles.kpiTitle}>{t['workplace.kpi.conversion']}</span> <span className={styles.kpiTitle}>{t['workplace.kpi.conversion']}</span>
<span className={styles.kpiIconWrapper}> <span className={styles.kpiIconWrapper}>
<IconIdcard className={styles.kpiIcon} /> <IconIdcard className={styles.kpiIcon} />
</span> </span>
</div> </div>
<Statistic <Statistic
value={kpis.conversionRate} value={kpis.conversionRate}
suffix="%" suffix="%"
precision={1} precision={1}
/> />
<div className={styles.kpiSubText}> <div className={styles.kpiSubText}>
{t['workplace.kpi.conversionSub']} {t['workplace.kpi.conversionSub']}
</div> </div>
</Card> </Card>
</Col> </Col>
</Row> </Row>
{/* 运营官转化漏斗与渠道矩阵 */} {/* 出海转化漏斗 */}
<Row gutter={[16, 16]} className={styles.middleRow}> <Card
<Col xs={24} lg={16}> bordered={false}
<Card bordered={false} className={styles.panel} title={t['workplace.funnel.title']}> className={`${styles.panel} ${styles.funnelCard}`}
title={t['workplace.funnel.title']}
>
<div className={styles.funnelContainer}> <div className={styles.funnelContainer}>
<div className={styles.funnelStep}> <div className={styles.funnelStep}>
<div className={styles.funnelLabel}>{t['workplace.funnel.tap']}</div> <div className={styles.funnelLabel}>{t['workplace.funnel.tap']}</div>
@@ -459,43 +460,11 @@ function Workplace(): React.ReactElement {
</div> </div>
</div> </div>
</Card> </Card>
</Col>
<Col xs={24} lg={8}> {/* 今日实时客商雷达流 */}
<Card bordered={false} className={styles.panel} title={t['workplace.channel.title']}>
<div className={styles.channelRow}>
<div className={styles.channelMeta}>
<span className={styles.channelName}>{t['workplace.channel.nfc']}</span>
<span className={styles.channelPercent}>{channelStats.nfc}%</span>
</div>
<Progress percent={channelStats.nfc} color={lightColors.primary} showText={false} />
</div>
<div className={styles.channelRow}>
<div className={styles.channelMeta}>
<span className={styles.channelName}>{t['workplace.channel.qr']}</span>
<span className={styles.channelPercent}>{channelStats.qr}%</span>
</div>
<Progress percent={channelStats.qr} color={lightColors.ai} showText={false} />
</div>
<div className={styles.channelRow}>
<div className={styles.channelMeta}>
<span className={styles.channelName}>{t['workplace.channel.link']}</span>
<span className={styles.channelPercent}>{channelStats.link}%</span>
</div>
<Progress percent={channelStats.link} color={lightColors.text2} showText={false} />
</div>
</Card>
</Col>
</Row>
{/* 核心双栏:实时客商雷达流 VS 运营工具箱 */}
<Row gutter={[16, 16]} className={styles.main}>
<Col xs={24} lg={16}>
<Card <Card
bordered={false} bordered={false}
className={styles.panel} className={`${styles.panel} ${styles.radarCard}`}
title={ title={
<div className={styles.panelHeader}> <div className={styles.panelHeader}>
<span>{t['workplace.radar.title']}</span> <span>{t['workplace.radar.title']}</span>
@@ -520,7 +489,7 @@ function Workplace(): React.ReactElement {
</p> </p>
} }
/> />
<Button type="text" onClick={handleOpenRadar}> <Button type="text" onClick={() => handleNav('/dashboard/radar-map')}>
{t['workplace.radar.emptyAction']} {t['workplace.radar.emptyAction']}
</Button> </Button>
</div> </div>
@@ -568,88 +537,236 @@ function Workplace(): React.ReactElement {
</Card> </Card>
</Col> </Col>
{/* 运营工具箱 */} {/* 右侧出海运营快捷中枢 (7/24 栅格,完美填补用户红框空白) */}
<Col xs={24} lg={8}> <Col xs={24} xl={7}>
<Card <div className={styles.hubContainer}>
bordered={false} {/* 模块 1:触达渠道健康度 */}
className={styles.panel} <Card bordered={false} className={styles.panel} title={t['workplace.channel.title']}>
title={t['workplace.shortcut.title']} <div className={styles.channelRow}>
> <div className={styles.channelMeta}>
<div className={styles.toolboxList}> <span className={styles.channelName}>{t['workplace.channel.nfc']}</span>
<button <span className={styles.channelPercent}>{channelStats.nfc}%</span>
type="button"
className={styles.shortcut}
onClick={handleIssueCards}
>
<div className={styles.shortcutIconBox}>
<IconIdcard className={styles.toolIcon} />
</div> </div>
<div className={styles.shortcutContent}> <Progress percent={channelStats.nfc} color={lightColors.primary} showText={false} />
<span className={styles.shortcutLabel}> </div>
{t['workplace.shortcut.issueCard']}
</span>
<span className={styles.shortcutHint}>
{t['workplace.shortcut.issueCardHint']}
</span>
</div>
</button>
<button <div className={styles.channelRow}>
type="button" <div className={styles.channelMeta}>
className={styles.shortcut} <span className={styles.channelName}>{t['workplace.channel.qr']}</span>
onClick={handleExportLeads} <span className={styles.channelPercent}>{channelStats.qr}%</span>
disabled={exporting}
>
<div className={styles.shortcutIconBox}>
<IconDownload className={styles.toolIcon} />
</div> </div>
<div className={styles.shortcutContent}> <Progress percent={channelStats.qr} color={lightColors.ai} showText={false} />
<span className={styles.shortcutLabel}> </div>
{t['workplace.shortcut.exportLeads']}
</span>
<span className={styles.shortcutHint}>
{t['workplace.shortcut.exportLeadsHint']}
</span>
</div>
</button>
<button <div className={styles.channelRow}>
type="button" <div className={styles.channelMeta}>
className={styles.shortcut} <span className={styles.channelName}>{t['workplace.channel.link']}</span>
onClick={handleOpenRadar} <span className={styles.channelPercent}>{channelStats.link}%</span>
>
<div className={styles.shortcutIconBox}>
<IconCompass className={styles.toolIcon} />
</div> </div>
<div className={styles.shortcutContent}> <Progress percent={channelStats.link} color={lightColors.text2} showText={false} />
<span className={styles.shortcutLabel}> </div>
{t['workplace.shortcut.radarMap']} </Card>
</span>
<span className={styles.shortcutHint}>
{t['workplace.shortcut.radarMapHint']}
</span>
</div>
</button>
<button {/* 模块 2:高频业务快捷导航矩阵 (8大核心入口,2列微卡) */}
type="button" <Card
className={styles.shortcut} bordered={false}
onClick={handleOpenAiConfig} className={styles.panel}
> title={t['workplace.quickNav.title']}
<div className={styles.shortcutIconBox}> >
<IconRobot className={styles.toolIcon} /> <div className={styles.navGrid}>
{/* 1. 搭建微官网 */}
<button
type="button"
className={styles.navCard}
onClick={() => handleNav('/card/builder')}
>
<div className={styles.navIconBox}>
<IconApps className={styles.navIcon} />
</div>
<div className={styles.navTextBox}>
<span className={styles.navTitle}>{t['workplace.quick.builder']}</span>
<span className={styles.navSub}>{t['workplace.quick.builderHint']}</span>
</div>
</button>
{/* 2. 潜客线索 */}
<button
type="button"
className={styles.navCard}
onClick={() => handleNav('/crm/leads')}
>
<div className={styles.navIconBox}>
<IconUserGroup className={styles.navIcon} />
</div>
<div className={styles.navTextBox}>
<span className={styles.navTitle}>{t['workplace.quick.leads']}</span>
<span className={styles.navSub}>{t['workplace.quick.leadsHint']}</span>
</div>
</button>
{/* 3. 商机看板 */}
<button
type="button"
className={styles.navCard}
onClick={() => handleNav('/crm/pipeline')}
>
<div className={styles.navIconBox}>
<IconSend className={styles.navIcon} />
</div>
<div className={styles.navTextBox}>
<span className={styles.navTitle}>{t['workplace.quick.pipeline']}</span>
<span className={styles.navSub}>{t['workplace.quick.pipelineHint']}</span>
</div>
</button>
{/* 4. 批量发卡 */}
<button
type="button"
className={styles.navCard}
onClick={() => handleNav('/nfc/batch')}
>
<div className={styles.navIconBox}>
<IconIdcard className={styles.navIcon} />
</div>
<div className={styles.navTextBox}>
<span className={styles.navTitle}>{t['workplace.quick.batch']}</span>
<span className={styles.navSub}>{t['workplace.quick.batchHint']}</span>
</div>
</button>
{/* 5. 硬件台账 */}
<button
type="button"
className={styles.navCard}
onClick={() => handleNav('/nfc/devices')}
>
<div className={styles.navIconBox}>
<IconDesktop className={styles.navIcon} />
</div>
<div className={styles.navTextBox}>
<span className={styles.navTitle}>{t['workplace.quick.devices']}</span>
<span className={styles.navSub}>{t['workplace.quick.devicesHint']}</span>
</div>
</button>
{/* 6. 全球雷达大屏 */}
<button
type="button"
className={styles.navCard}
onClick={() => handleNav('/dashboard/radar-map')}
>
<div className={styles.navIconBox}>
<IconCompass className={styles.navIcon} />
</div>
<div className={styles.navTextBox}>
<span className={styles.navTitle}>{t['workplace.quick.radarMap']}</span>
<span className={styles.navSub}>{t['workplace.quick.radarMapHint']}</span>
</div>
</button>
{/* 7. AI 顾问接待 */}
<button
type="button"
className={styles.navCard}
onClick={() => handleNav('/ai/concierge')}
>
<div className={styles.navIconBox}>
<IconRobot className={styles.navIcon} />
</div>
<div className={styles.navTextBox}>
<span className={styles.navTitle}>{t['workplace.quick.aiConcierge']}</span>
<span className={styles.navSub}>{t['workplace.quick.aiConciergeHint']}</span>
</div>
</button>
{/* 8. 财务订阅看板 */}
<button
type="button"
className={styles.navCard}
onClick={() => handleNav('/dashboard/financial')}
>
<div className={styles.navIconBox}>
<IconSafe className={styles.navIcon} />
</div>
<div className={styles.navTextBox}>
<span className={styles.navTitle}>{t['workplace.quick.finance']}</span>
<span className={styles.navSub}>{t['workplace.quick.financeHint']}</span>
</div>
</button>
</div>
</Card>
{/* 模块 3:常用效率工具 */}
<Card
bordered={false}
className={styles.panel}
title={t['workplace.shortcut.title']}
>
<div className={styles.toolboxList}>
<button
type="button"
className={styles.shortcut}
onClick={handleExportLeads}
disabled={exporting}
>
<div className={styles.shortcutIconBox}>
<IconDownload className={styles.toolIcon} />
</div>
<div className={styles.shortcutContent}>
<span className={styles.shortcutLabel}>
{t['workplace.shortcut.exportLeads']}
</span>
<span className={styles.shortcutHint}>
{t['workplace.shortcut.exportLeadsHint']}
</span>
</div>
</button>
<button
type="button"
className={styles.shortcut}
onClick={() => handleNav('/nfc/batch')}
>
<div className={styles.shortcutIconBox}>
<IconIdcard className={styles.toolIcon} />
</div>
<div className={styles.shortcutContent}>
<span className={styles.shortcutLabel}>
{t['workplace.shortcut.issueCard']}
</span>
<span className={styles.shortcutHint}>
{t['workplace.shortcut.issueCardHint']}
</span>
</div>
</button>
</div>
</Card>
{/* 模块 4:出海待办预警 */}
<Card
bordered={false}
className={`${styles.panel} ${styles.alertPanel}`}
title={t['workplace.alerts.title']}
>
<div className={styles.alertContent}>
<div className={styles.alertItem}>
<div className={styles.alertDot} />
<div className={styles.alertText}>
{t['workplace.alerts.highIntentLead']}
</div>
</div> </div>
<div className={styles.shortcutContent}> <Button
<span className={styles.shortcutLabel}> type="outline"
{t['workplace.shortcut.aiConfig']} size="small"
</span> className={styles.alertActionBtn}
<span className={styles.shortcutHint}> onClick={() => handleNav('/crm/leads')}
{t['workplace.shortcut.aiConfigHint']} >
</span> <span>{t['workplace.alerts.actionFollow']}</span>
</div> <IconRight style={{ fontSize: 12, marginLeft: 4 }} />
</button> </Button>
</div> </div>
</Card> </Card>
</div>
</Col> </Col>
</Row> </Row>
</Spin> </Spin>

查看文件

@@ -26,7 +26,25 @@ const i18n = {
'workplace.funnel.interact': 'Interactions', 'workplace.funnel.interact': 'Interactions',
'workplace.funnel.lead': 'CRM Leads', 'workplace.funnel.lead': 'CRM Leads',
'workplace.channel.title': 'Touch Channels', 'workplace.channel.title': 'Touch Channels',
'workplace.shortcut.title': 'Shortcuts', 'workplace.quickHub.title': 'Operations Command Hub',
'workplace.quickNav.title': 'Quick Navigation',
'workplace.quick.builder': 'Mini-site Builder',
'workplace.quick.builderHint': 'Craft Bento Grid mini-sites',
'workplace.quick.leads': 'Buyer Leads',
'workplace.quick.leadsHint': 'Follow up international clients',
'workplace.quick.pipeline': 'Deal Pipeline',
'workplace.quick.pipelineHint': 'Track stages and closed deals',
'workplace.quick.batch': 'Issue NFC',
'workplace.quick.batchHint': 'Batch print files and links',
'workplace.quick.devices': 'NFC Fleet',
'workplace.quick.devicesHint': 'Hardware device activations',
'workplace.quick.radarMap': 'Radar Map',
'workplace.quick.radarMapHint': 'Global 3D visitor heatmaps',
'workplace.quick.aiConcierge': 'AI Concierge',
'workplace.quick.aiConciergeHint': 'Multi-lingual greeting policy',
'workplace.quick.finance': 'Financials',
'workplace.quick.financeHint': 'Subscription metrics & MRR',
'workplace.shortcut.title': 'Operations Arsenal',
'workplace.shortcut.issueCard': 'Issue NFC cards', 'workplace.shortcut.issueCard': 'Issue NFC cards',
'workplace.shortcut.issueCardHint': 'Generate short links and print files', 'workplace.shortcut.issueCardHint': 'Generate short links and print files',
'workplace.shortcut.exportLeads': 'Export leads', 'workplace.shortcut.exportLeads': 'Export leads',
@@ -35,6 +53,9 @@ const i18n = {
'workplace.shortcut.radarMapHint': 'Explore real-time visitor geography and heatmaps', 'workplace.shortcut.radarMapHint': 'Explore real-time visitor geography and heatmaps',
'workplace.shortcut.aiConfig': 'AI Concierge Strategy', 'workplace.shortcut.aiConfig': 'AI Concierge Strategy',
'workplace.shortcut.aiConfigHint': 'Tune multi-lingual greeting and lead qualification', 'workplace.shortcut.aiConfigHint': 'Tune multi-lingual greeting and lead qualification',
'workplace.alerts.title': 'Actionable Alerts',
'workplace.alerts.highIntentLead': 'Buyer from Dubai stayed 210s in case studies without follow-up.',
'workplace.alerts.actionFollow': 'Follow Up Now',
'workplace.channel.nfc': 'NFC', 'workplace.channel.nfc': 'NFC',
'workplace.channel.qr': 'QR Code', 'workplace.channel.qr': 'QR Code',
'workplace.channel.link': 'Direct Link', 'workplace.channel.link': 'Direct Link',
@@ -70,7 +91,25 @@ const i18n = {
'workplace.funnel.interact': '意向交互', 'workplace.funnel.interact': '意向交互',
'workplace.funnel.lead': '商机留资', 'workplace.funnel.lead': '商机留资',
'workplace.channel.title': '触达渠道分布', 'workplace.channel.title': '触达渠道分布',
'workplace.shortcut.title': '运营工具箱', 'workplace.quickHub.title': '出海运营快捷中枢',
'workplace.quickNav.title': '高频业务导航',
'workplace.quick.builder': '搭建微官网',
'workplace.quick.builderHint': '编辑便当盒微官网组件',
'workplace.quick.leads': '潜客线索',
'workplace.quick.leadsHint': '跟进出海海外意向买家',
'workplace.quick.pipeline': '商机看板',
'workplace.quick.pipelineHint': '推进出海商机全流程赢单',
'workplace.quick.batch': '批量制卡',
'workplace.quick.batchHint': '生成短链与批量印制物料',
'workplace.quick.devices': '硬件台账',
'workplace.quick.devicesHint': 'NFC 实体卡激活与状态',
'workplace.quick.radarMap': '雷达大屏',
'workplace.quick.radarMapHint': '全球客商访客实时热力图',
'workplace.quick.aiConcierge': 'AI 顾问',
'workplace.quick.aiConciergeHint': '多语言接待策略与留资阈值',
'workplace.quick.finance': '财务订阅',
'workplace.quick.financeHint': '订阅套餐与出海营收看板',
'workplace.shortcut.title': '运营效率工具箱',
'workplace.shortcut.issueCard': '批量发卡', 'workplace.shortcut.issueCard': '批量发卡',
'workplace.shortcut.issueCardHint': '生成短链与制卡打印物料', 'workplace.shortcut.issueCardHint': '生成短链与制卡打印物料',
'workplace.shortcut.exportLeads': '导出线索', 'workplace.shortcut.exportLeads': '导出线索',
@@ -79,6 +118,9 @@ const i18n = {
'workplace.shortcut.radarMapHint': '查看全球访客地理分布与实时热力图', 'workplace.shortcut.radarMapHint': '查看全球访客地理分布与实时热力图',
'workplace.shortcut.aiConfig': 'AI 顾问接待策略', 'workplace.shortcut.aiConfig': 'AI 顾问接待策略',
'workplace.shortcut.aiConfigHint': '配置多语言接待话术与自动留资意向阈值', 'workplace.shortcut.aiConfigHint': '配置多语言接待话术与自动留资意向阈值',
'workplace.alerts.title': '出海待办预警',
'workplace.alerts.highIntentLead': '迪拜客商在精选案例停留超过 210 秒,尚未安排专属跟进。',
'workplace.alerts.actionFollow': '立即跟进',
'workplace.channel.nfc': 'NFC', 'workplace.channel.nfc': 'NFC',
'workplace.channel.qr': '二维码', 'workplace.channel.qr': '二维码',
'workplace.channel.link': '直链', 'workplace.channel.link': '直链',
@@ -114,7 +156,25 @@ const i18n = {
'workplace.funnel.interact': 'تفاعل مهتم', 'workplace.funnel.interact': 'تفاعل مهتم',
'workplace.funnel.lead': 'عميل محتمل', 'workplace.funnel.lead': 'عميل محتمل',
'workplace.channel.title': 'قنوات الاتصال', 'workplace.channel.title': 'قنوات الاتصال',
'workplace.shortcut.title': 'صندوق أدوات التشغيل', 'workplace.quickHub.title': 'مركز قيادة العمليات',
'workplace.quickNav.title': 'الملاحة السريعة',
'workplace.quick.builder': 'منشئ الموقع المصغر',
'workplace.quick.builderHint': 'تصميم موقع نمط بينتو',
'workplace.quick.leads': 'العملاء المحتملون',
'workplace.quick.leadsHint': 'متابعة المشترين الدوليين',
'workplace.quick.pipeline': 'مراحل الصفقات',
'workplace.quick.pipelineHint': 'تتبع تقدم الصفقات المغلقة',
'workplace.quick.batch': 'إصدار NFC',
'workplace.quick.batchHint': 'توليد الروابط والطباعة',
'workplace.quick.devices': 'أجهزة NFC',
'workplace.quick.devicesHint': 'تنشيط الأجهزة والحالة',
'workplace.quick.radarMap': 'خريطة الرادار',
'workplace.quick.radarMapHint': 'خريطة حرارية للزوار عالمياً',
'workplace.quick.aiConcierge': 'المساعد الذكي',
'workplace.quick.aiConciergeHint': 'سياسة الترحيب متعددة اللغات',
'workplace.quick.finance': 'المالية والاشتراكات',
'workplace.quick.financeHint': 'مقاييس الاشتراكات والإيرادات',
'workplace.shortcut.title': 'صندوق أدوات العمليات',
'workplace.shortcut.issueCard': 'إصدار بطاقات NFC', 'workplace.shortcut.issueCard': 'إصدار بطاقات NFC',
'workplace.shortcut.issueCardHint': 'إنشاء الروابط وملفات الطباعة للبطاقات', 'workplace.shortcut.issueCardHint': 'إنشاء الروابط وملفات الطباعة للبطاقات',
'workplace.shortcut.exportLeads': 'تصدير العملاء', 'workplace.shortcut.exportLeads': 'تصدير العملاء',
@@ -123,6 +183,9 @@ const i18n = {
'workplace.shortcut.radarMapHint': 'استكشف التوزيع الجغرافي للزوار وخريطة الحرارة', 'workplace.shortcut.radarMapHint': 'استكشف التوزيع الجغرافي للزوار وخريطة الحرارة',
'workplace.shortcut.aiConfig': 'استراتيجية المساعد الذكي', 'workplace.shortcut.aiConfig': 'استراتيجية المساعد الذكي',
'workplace.shortcut.aiConfigHint': 'تكوين الترحيب متعدد اللغات وتأهيل العملاء المحتملين', 'workplace.shortcut.aiConfigHint': 'تكوين الترحيب متعدد اللغات وتأهيل العملاء المحتملين',
'workplace.alerts.title': 'تنبيهات قابلة للتنفيذ',
'workplace.alerts.highIntentLead': 'مشتري من دبي مكث 210 ثوانٍ في دراسات الحالة دون متابعة.',
'workplace.alerts.actionFollow': 'متابعة الآن',
'workplace.channel.nfc': 'NFC', 'workplace.channel.nfc': 'NFC',
'workplace.channel.qr': 'رمز QR', 'workplace.channel.qr': 'رمز QR',
'workplace.channel.link': 'رابط مباشر', 'workplace.channel.link': 'رابط مباشر',

查看文件

@@ -1,9 +1,12 @@
.page { .page {
min-height: 100%; min-height: 100%;
max-width: 1200px; width: 100%;
max-width: 1680px;
margin: 0 auto;
padding: var(--omni-space-24); padding: var(--omni-space-24);
background: var(--omni-color-surface); background: var(--omni-color-surface);
font-family: var(--omni-font-family); font-family: var(--omni-font-family);
box-sizing: border-box;
} }
.header { .header {
@@ -12,7 +15,7 @@
justify-content: space-between; justify-content: space-between;
gap: var(--omni-space-16); gap: var(--omni-space-16);
flex-wrap: wrap; flex-wrap: wrap;
margin-bottom: var(--omni-space-24); margin-bottom: var(--omni-space-20);
} }
.headerInfo { .headerInfo {
@@ -169,8 +172,8 @@
color: var(--omni-color-error); color: var(--omni-color-error);
} }
/* 漏斗与渠道矩阵 */ /* 漏斗与雷达卡片 */
.middleRow { .funnelCard {
margin-top: var(--omni-space-16); margin-top: var(--omni-space-16);
} }
@@ -223,35 +226,7 @@
background: var(--omni-color-success); background: var(--omni-color-success);
} }
/* 渠道占比 */ .radarCard {
.channelRow {
margin-bottom: var(--omni-space-16);
}
.channelRow:last-child {
margin-bottom: 0;
}
.channelMeta {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--omni-space-8);
font-size: 13px;
}
.channelName {
color: var(--omni-color-text);
font-weight: 500;
}
.channelPercent {
color: var(--omni-color-text-2);
font-weight: 600;
}
/* 主内容区域 */
.main {
margin-top: var(--omni-space-16); margin-top: var(--omni-space-16);
} }
@@ -296,11 +271,123 @@
line-height: 1.6; line-height: 1.6;
} }
/* 运营工具箱 */ /* ========================================================
右侧出海运营快捷中枢 (Command Hub)
======================================================== */
.hubContainer {
display: flex;
flex-direction: column;
gap: var(--omni-space-16);
}
/* 渠道占比 */
.channelRow {
margin-bottom: var(--omni-space-12);
}
.channelRow:last-child {
margin-bottom: 0;
}
.channelMeta {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--omni-space-6);
font-size: 12px;
}
.channelName {
color: var(--omni-color-text);
font-weight: 500;
}
.channelPercent {
color: var(--omni-color-text-2);
font-weight: 600;
}
/* 8 大核心快捷入口网格 (2列) */
.navGrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--omni-space-10, 10px);
}
.navCard {
display: flex;
align-items: flex-start;
gap: var(--omni-space-10, 10px);
padding: 10px;
border: 1px solid var(--omni-color-border);
border-radius: 12px;
background: var(--omni-color-surface);
text-align: start;
cursor: pointer;
transition: all 150ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.navCard:hover {
background: var(--omni-color-bg);
border-color: rgba(11, 92, 255, 0.35);
box-shadow: 0 2px 6px rgba(11, 92, 255, 0.08);
transform: translateY(-1px);
}
.navCard:focus-visible {
outline: 2px solid var(--omni-color-primary);
outline-offset: 2px;
}
.navIconBox {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
border-radius: 8px;
background: var(--omni-color-bg);
color: var(--omni-color-primary);
flex-shrink: 0;
border: 1px solid var(--omni-color-border);
}
.navIcon {
font-size: 15px;
}
.navTextBox {
flex: 1;
min-width: 0;
}
.navTitle {
display: block;
color: var(--omni-color-text);
font-size: 13px;
font-weight: 600;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.navSub {
display: block;
margin-top: 2px;
color: var(--omni-color-text-2);
font-size: 11px;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 效率工具箱 */
.toolboxList { .toolboxList {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--omni-space-12); gap: var(--omni-space-10, 10px);
} }
.shortcut { .shortcut {
@@ -308,7 +395,7 @@
align-items: flex-start; align-items: flex-start;
gap: var(--omni-space-12); gap: var(--omni-space-12);
width: 100%; width: 100%;
padding: var(--omni-space-12); padding: 10px 12px;
border: 1px solid var(--omni-color-border); border: 1px solid var(--omni-color-border);
border-radius: 12px; border-radius: 12px;
background: var(--omni-color-surface); background: var(--omni-color-surface);
@@ -338,16 +425,17 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 32px; width: 30px;
height: 32px; height: 30px;
border-radius: 8px; border-radius: 8px;
background: var(--omni-color-bg); background: var(--omni-color-bg);
color: var(--omni-color-primary); color: var(--omni-color-primary);
flex-shrink: 0; flex-shrink: 0;
border: 1px solid var(--omni-color-border);
} }
.toolIcon { .toolIcon {
font-size: 16px; font-size: 15px;
} }
.shortcutContent { .shortcutContent {
@@ -358,21 +446,68 @@
.shortcutLabel { .shortcutLabel {
display: block; display: block;
color: var(--omni-color-text); color: var(--omni-color-text);
font-size: 14px; font-size: 13px;
font-weight: 500; font-weight: 500;
line-height: 1.4; line-height: 1.4;
} }
.shortcutHint { .shortcutHint {
display: block; display: block;
margin-top: var(--omni-space-4); margin-top: 2px;
color: var(--omni-color-text-2); color: var(--omni-color-text-2);
font-size: 12px; font-size: 11px;
line-height: 1.4; line-height: 1.4;
} }
/* 待办预警卡片 */
.alertPanel {
border-color: rgba(247, 144, 9, 0.3) !important;
background: #FFFDF9 !important;
}
.alertContent {
display: flex;
flex-direction: column;
gap: var(--omni-space-12);
}
.alertItem {
display: flex;
align-items: flex-start;
gap: var(--omni-space-8);
}
.alertDot {
width: 6px;
height: 6px;
margin-top: 6px;
border-radius: 50%;
background-color: var(--omni-color-warning, #F79009);
flex-shrink: 0;
}
.alertText {
color: var(--omni-color-text);
font-size: 12px;
line-height: 1.5;
}
.alertActionBtn {
align-self: flex-start;
border-radius: var(--omni-radius-button);
color: var(--omni-color-text);
border-color: var(--omni-color-border);
font-size: 12px;
}
.alertActionBtn:hover {
border-color: var(--omni-color-primary);
color: var(--omni-color-primary);
}
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.shortcut:hover { .shortcut:hover,
.navCard:hover {
transform: none; transform: none;
} }
.pulseDot { .pulseDot {

查看文件

@@ -118,3 +118,38 @@ test('6. 专业出海运营官视角:包含全球雷达监控、渠道漏斗
assert.ok(!EMOJI_RE.test(content), '页面代码严禁 emoji'); assert.ok(!EMOJI_RE.test(content), '页面代码严禁 emoji');
}); });
test('7. 出海运营快捷中枢:覆盖8大核心业务快捷入口与运营预警,全屏仅1个主CTA', () => {
const content = fs.readFileSync(pagePath, 'utf-8');
const localeContent = fs.readFileSync(localePath, 'utf-8');
// 必须覆盖的8大核心业务快捷路由
const requiredRoutes = [
'/card/builder',
'/crm/leads',
'/crm/pipeline',
'/nfc/batch',
'/nfc/devices',
'/dashboard/radar-map',
'/ai/concierge',
'/dashboard/financial',
];
for (const route of requiredRoutes) {
assert.ok(content.includes(route), `工作台必须包含快捷业务路由入口: ${route}`);
}
// 必须包含待办预警词条
assert.ok(localeContent.includes('workplace.alerts.title'), '必须包含待办预警标题');
assert.ok(localeContent.includes('workplace.quickHub.title'), '必须包含快捷中枢标题');
// 铁律:全屏主 CTA 依然严格为 1
const primaryButtons = content.match(/type=["']primary["']/g) || [];
assert.equal(
primaryButtons.length,
1,
'无论增加多少快捷入口,全屏依然必须严格保持唯 1 一个主 CTA (type="primary")'
);
assert.ok(!EMOJI_RE.test(localeContent), '语言包严禁 emoji');
assert.ok(!EMOJI_RE.test(content), '页面代码严禁 emoji');
});