feat: 同步管理端、用户端与后端业务,并接通线上 Web 接口
补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
这个提交包含在:
@@ -114,3 +114,89 @@ test('4. settings/audit-logs 语言包必须包含中英文案且零 emoji、零
|
||||
'语言包严禁包含 emoji'
|
||||
);
|
||||
});
|
||||
|
||||
test('5. R18: 必须支持按 action 筛选,并将所选动作作为 page 请求参数发出', () => {
|
||||
const content = readPage();
|
||||
const localeContent = fs.readFileSync(auditLocalePath, 'utf-8');
|
||||
|
||||
assert.ok(
|
||||
content.includes('selectedAction'),
|
||||
'必须具备 selectedAction 筛选状态'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('ACTION_OPTIONS') ||
|
||||
(content.includes('nfc_freeze') && content.includes('lead_batch_export')),
|
||||
'必须提供可选动作列表'
|
||||
);
|
||||
assert.ok(
|
||||
/if\s*\(\s*filters\.action\s*\)\s*\{[\s\S]*?payload\.action\s*=\s*filters\.action/.test(
|
||||
content
|
||||
),
|
||||
'仅当已选动作时写入 payload.action;清空筛选时不带空动作'
|
||||
);
|
||||
assert.ok(
|
||||
!/payload\.action\s*=\s*['"]['"]/.test(content),
|
||||
'禁止把空字符串硬编码进 payload.action'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('setSelectedAction'),
|
||||
'重置/变更筛选必须能更新 selectedAction'
|
||||
);
|
||||
assert.ok(
|
||||
localeContent.includes("'auditLogs.filter.action'"),
|
||||
'locale 必须包含 auditLogs.filter.action'
|
||||
);
|
||||
assert.ok(
|
||||
localeContent.includes('auditLogs.actionType.nfc_freeze') &&
|
||||
localeContent.includes('auditLogs.actionType.lead_batch_export'),
|
||||
'locale 必须覆盖动作选项文案'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes("t['auditLogs.filter.action']"),
|
||||
'action 筛选 placeholder 必须走 locale'
|
||||
);
|
||||
});
|
||||
|
||||
test('6. R18: 请求失败或 code 非 1000 必须清空表格,禁止残留示例日志', () => {
|
||||
const content = readPage();
|
||||
|
||||
assert.ok(
|
||||
/useState\s*<\s*BaseAuditLog\s*\[\s*\]\s*>\s*\(\s*\[\s*\]\s*\)/.test(
|
||||
content
|
||||
),
|
||||
'初始 dataList 必须为空数组'
|
||||
);
|
||||
// else(非 1000)与 catch 两处都必须清空
|
||||
const clearCalls = content.match(/setDataList\(\[\]\)/g) || [];
|
||||
assert.ok(
|
||||
clearCalls.length >= 2,
|
||||
'code 非 1000 与 catch 均须 setDataList([]) 清空表格'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('setTotal(0)'),
|
||||
'失败时 total 也须归零'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes("t['auditLogs.request.fail']"),
|
||||
'失败提示必须走 locale,禁止硬编码'
|
||||
);
|
||||
assert.ok(
|
||||
!content.includes('Mock.js') &&
|
||||
!content.includes('mockList') &&
|
||||
!content.includes('示例日志') &&
|
||||
!content.includes('sampleLogs') &&
|
||||
!content.includes('fakeLogs'),
|
||||
'禁止写死示例日志行或 mock 列表'
|
||||
);
|
||||
assert.ok(
|
||||
!/operatorName:\s*['"][^'"]+['"]/.test(content) &&
|
||||
!/detail:\s*['"][^'"]+['"]/.test(content) &&
|
||||
!/ip:\s*['"]\d+\.\d+\.\d+\.\d+['"]/.test(content),
|
||||
'禁止内联写死审计日志行字段'
|
||||
);
|
||||
// 成功分支才写入 list;失败不得回退到本地示例数组
|
||||
assert.ok(
|
||||
/res\.data\.list/.test(content) || /data\.list/.test(content),
|
||||
'表格数据必须来自接口 list'
|
||||
);
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户