feat: 同步管理端、用户端与后端业务,并接通线上 Web 接口

补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。

Co-authored-by: Cursor <cursoragent@cursor.com>
这个提交包含在:
wikiboss
2026-09-23 18:01:47 +08:00
父节点 8ea9c25386
当前提交 62e0ae4435
共有 227 个文件被更改,包括 21855 次插入 和 5264 次删除

查看文件

@@ -57,13 +57,18 @@ test('3. 大屏页面必须存在且使用 Arco Design 官方核心组件', () =
assert.ok(content.includes('Progress'), '必须使用 Arco Progress 展示占比进度条');
});
test('4. 大屏页面必须对接雷达聚合分析 API 且只设置 1 个主 CTA', () => {
test('4. 大屏页面必须对接雷达热力聚合 API 且只设置 1 个主 CTA', () => {
const content = fs.readFileSync(radarMapPagePath, 'utf-8');
// 接口校验
// 国家/地区分布必须来自已有热力聚合接口(analytics 内含 countryDistribution,或独立 distribution)
assert.ok(
content.includes('/api/admin/crm/radar/analytics') || content.includes('/api/admin/crm/radar/distribution'),
'必须对接雷达分析或国家分布 API'
content.includes('/api/admin/crm/radar/analytics') ||
content.includes('/api/admin/crm/radar/distribution'),
'必须对接雷达热力聚合 analytics 或 distribution API'
);
assert.ok(
content.includes('countryDistribution') || content.includes('/radar/distribution'),
'国家/地区分布必须绑定接口返回的 countryDistribution(或 distribution)'
);
// 瑞士国际主义与一屏单主 CTA 原则: 统计 type="primary" 按钮数量
@@ -75,6 +80,63 @@ test('4. 大屏页面必须对接雷达聚合分析 API 且只设置 1 个主 CT
);
});
test('4b. 国家分布禁止写死占比;请求失败或无数据必须空态', () => {
const content = fs.readFileSync(radarMapPagePath, 'utf-8');
const localeContent = fs.readFileSync(radarMapLocalePath, 'utf-8');
// 禁止演示用硬编码国家名与写死占比
const bannedCountries = [
'United States',
'United Kingdom',
'Singapore',
'Germany',
'Japan',
'China',
'Dubai',
'Saudi Arabia',
];
for (const name of bannedCountries) {
assert.ok(!content.includes(name), `禁止硬编码国家/地区名: ${name}`);
}
assert.ok(
!/percentage\s*:\s*\d+/.test(content),
'禁止在页面中写死 percentage 国家占比'
);
assert.ok(
!/visitorCount\s*:\s*[1-9]/.test(content),
'禁止在页面中写死非零 visitorCount 演示数据'
);
// 失败或非 1000 必须清空分布,不得残留假占比
assert.ok(
content.includes('countryDistribution: []') ||
content.includes('EMPTY_ANALYTICS') ||
content.includes('emptyAnalytics'),
'必须具备空的 countryDistribution 初始/空态结构'
);
assert.ok(
/setAnalyticsData\(\s*(EMPTY_ANALYTICS|emptyAnalytics)/.test(content) ||
/setAnalyticsData\(\s*\{\s*[\s\S]*countryDistribution\s*:\s*\[\s*\]/.test(content),
'接口失败必须 setAnalyticsData 清空为国家空分布'
);
// 空态文案走 locale
assert.ok(
content.includes('radarMap.status.empty') || content.includes("t['radarMap.status.empty']"),
'空态必须引用 radarMap.status.empty'
);
assert.ok(
content.includes('Empty') || content.includes('noDataElement'),
'无数据必须使用 Empty 或 Table noDataElement'
);
assert.ok(localeContent.includes('radarMap.status.empty'), 'locale 必须包含空态文案');
assert.ok(localeContent.includes('radarMap.status.loadFailed'), 'locale 必须包含加载失败文案');
assert.ok(
localeContent.includes('radarMap.country.unknown'),
'locale 必须包含未知国家/地区文案'
);
});
test('5. 必须使用设计 Token 系统且保持严谨纯粹 (零 any、零 emoji)', () => {
const content = fs.readFileSync(radarMapPagePath, 'utf-8');