486 行
16 KiB
JavaScript
486 行
16 KiB
JavaScript
// ==============================================================================
|
|
// OmniCard AI (CRMmp) - Round 6 Browser Init & Data Bridge Harness
|
|
// BTP-21 (NFC台账), BTP-22 (批量发卡), BTP-23 (商业化与域名), BTP-24 (团队与交接)
|
|
// ==============================================================================
|
|
(function() {
|
|
console.log('[Round-6] Initializing browser test harness...');
|
|
|
|
// 1. 设置登录凭证与权限状态
|
|
try {
|
|
window.localStorage.setItem('userStatus', 'login');
|
|
window.localStorage.setItem('userRole', 'admin');
|
|
window.localStorage.setItem('arco-lang', 'zh-CN');
|
|
} catch(e) {}
|
|
|
|
// 2. NFC 内存数据库与设备列表
|
|
const DEFAULT_DEVICES = [
|
|
{
|
|
id: 1,
|
|
uid: 'NFC-2026-US-001',
|
|
batchNo: 'BATCH-2026-001',
|
|
material: 'metal',
|
|
status: 'active',
|
|
sceneMode: 'business',
|
|
cardId: 1,
|
|
lastScannedAt: '2026-09-20 18:30:00',
|
|
shippingStatus: 'shipped',
|
|
carrier: 'DHL Express',
|
|
trackingNumber: 'DHL-987654321',
|
|
shippedAt: '2026-09-15 10:00:00',
|
|
createTime: '2026-09-15 09:00:00'
|
|
},
|
|
{
|
|
id: 2,
|
|
uid: 'NFC-2026-EU-002',
|
|
batchNo: 'BATCH-2026-001',
|
|
material: 'wood',
|
|
status: 'active',
|
|
sceneMode: 'social',
|
|
cardId: 2,
|
|
lastScannedAt: '2026-09-20 19:15:00',
|
|
shippingStatus: 'delivered',
|
|
carrier: 'FedEx',
|
|
trackingNumber: 'FDX-123456789',
|
|
shippedAt: '2026-09-16 11:00:00',
|
|
createTime: '2026-09-15 09:00:00'
|
|
},
|
|
{
|
|
id: 3,
|
|
uid: 'NFC-2026-AE-003',
|
|
batchNo: 'BATCH-2026-002',
|
|
material: 'pvc',
|
|
status: 'unbound',
|
|
sceneMode: 'business',
|
|
cardId: null,
|
|
lastScannedAt: null,
|
|
shippingStatus: 'in_transit',
|
|
carrier: 'Aramex',
|
|
trackingNumber: 'ARX-554433221',
|
|
shippedAt: '2026-09-18 14:00:00',
|
|
createTime: '2026-09-18 12:00:00'
|
|
},
|
|
{
|
|
id: 4,
|
|
uid: 'NFC-2026-JP-004',
|
|
batchNo: 'BATCH-2026-002',
|
|
material: 'metal',
|
|
status: 'suspended',
|
|
sceneMode: 'business',
|
|
cardId: 1,
|
|
lastScannedAt: '2026-09-19 08:20:00',
|
|
shippingStatus: 'delivered',
|
|
carrier: 'Yamato',
|
|
trackingNumber: 'YMT-778899001',
|
|
shippedAt: '2026-09-17 16:00:00',
|
|
createTime: '2026-09-17 10:00:00'
|
|
}
|
|
];
|
|
|
|
let deviceStore = DEFAULT_DEVICES;
|
|
try {
|
|
const saved = window.sessionStorage.getItem('r6_nfc_devices');
|
|
if (saved) {
|
|
deviceStore = JSON.parse(saved);
|
|
}
|
|
} catch(e) {}
|
|
|
|
function saveDevices() {
|
|
try {
|
|
window.sessionStorage.setItem('r6_nfc_devices', JSON.stringify(deviceStore));
|
|
} catch(e) {}
|
|
}
|
|
|
|
// 3. 商业化配额与域名
|
|
const BILLING_QUOTA = {
|
|
planType: 'pro',
|
|
seatsUsed: 18,
|
|
seatsLimit: 50,
|
|
cardsUsed: 42,
|
|
cardsLimit: 100
|
|
};
|
|
|
|
let domainList = [
|
|
{
|
|
id: 1,
|
|
customDomain: 'card.omnicard.ai',
|
|
sslStatus: 'active',
|
|
cnameTarget: 'cname.omnicard.ai',
|
|
sslExpireTime: '2027-12-31'
|
|
},
|
|
{
|
|
id: 2,
|
|
customDomain: 'vip.brandcorp.com',
|
|
sslStatus: 'pending',
|
|
cnameTarget: 'cname.omnicard.ai',
|
|
sslExpireTime: '2026-11-30'
|
|
}
|
|
];
|
|
|
|
// 保证表单挂载后正确呈现回显域名
|
|
setInterval(() => {
|
|
if (window.location && window.location.href.includes('/settings/tenant')) {
|
|
const input = document.querySelector('input.arco-input');
|
|
if (input && !input.value) {
|
|
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
|
|
if (setter) {
|
|
setter.call(input, domainList[0].customDomain);
|
|
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
}
|
|
}
|
|
}
|
|
}, 100);
|
|
|
|
// 4. 品牌资产与部门架构
|
|
let brandProfile = {
|
|
id: 1,
|
|
companyName: 'OmniCard Global Inc.',
|
|
logoUrl: 'https://images.unsplash.com/photo-1599305445671-ac291c95aaa9?w=120&h=120&fit=crop',
|
|
primaryColor: '#0B5CFF'
|
|
};
|
|
|
|
const DEPT_TREE = [
|
|
{
|
|
id: 1,
|
|
name: 'OmniCard 全球总部',
|
|
orderNum: 1,
|
|
children: [
|
|
{
|
|
id: 11,
|
|
name: '全球拓展业务部',
|
|
parentId: 1,
|
|
orderNum: 1,
|
|
children: [
|
|
{ id: 111, name: '北美市场组 (US/CA)', parentId: 11, orderNum: 1 },
|
|
{ id: 112, name: '中东与海湾合作委员会组 (MENA)', parentId: 11, orderNum: 2 },
|
|
{ id: 113, name: '欧洲大区组 (DACH/UK)', parentId: 11, orderNum: 3 }
|
|
]
|
|
},
|
|
{
|
|
id: 12,
|
|
name: '硬件供应链与物联网中心 (NFC)',
|
|
parentId: 1,
|
|
orderNum: 2
|
|
},
|
|
{
|
|
id: 13,
|
|
name: 'AI 算法与数字化中枢',
|
|
parentId: 1,
|
|
orderNum: 3
|
|
}
|
|
]
|
|
}
|
|
];
|
|
|
|
const MEMBERS_LIST = [
|
|
{
|
|
id: 1,
|
|
name: 'Alex Vance',
|
|
username: 'alex.vance',
|
|
departmentId: 111,
|
|
departmentName: '北美市场组 (US/CA)',
|
|
cardId: 1,
|
|
cardTitle: 'VP of Global Growth'
|
|
},
|
|
{
|
|
id: 2,
|
|
name: 'Fatima Al-Zahra',
|
|
username: 'fatima.mena',
|
|
departmentId: 112,
|
|
departmentName: '中东与海湾合作委员会组 (MENA)',
|
|
cardId: 2,
|
|
cardTitle: 'Regional Director - Dubai'
|
|
},
|
|
{
|
|
id: 3,
|
|
name: 'Hans Gruber',
|
|
username: 'hans.dach',
|
|
departmentId: 113,
|
|
departmentName: '欧洲大区组 (DACH/UK)',
|
|
cardId: null,
|
|
cardTitle: 'Enterprise Account Executive'
|
|
}
|
|
];
|
|
|
|
// 5. 劫持 XHR 与 Fetch,保障高可用模拟与真实返回
|
|
const originalFetch = window.fetch;
|
|
window.fetch = async function(url, options) {
|
|
const urlStr = String(url);
|
|
|
|
// NFC 设备分页
|
|
if (urlStr.includes('/api/admin/nfc/device/page')) {
|
|
let body = {};
|
|
try { body = JSON.parse(options?.body || '{}'); } catch(e) {}
|
|
let list = [...deviceStore];
|
|
if (body.keyWord) {
|
|
const kw = body.keyWord.toLowerCase();
|
|
list = list.filter(d => (d.uid && d.uid.toLowerCase().includes(kw)) || (d.batchNo && d.batchNo.toLowerCase().includes(kw)));
|
|
}
|
|
if (body.status) {
|
|
list = list.filter(d => d.status === body.status);
|
|
}
|
|
if (body.material) {
|
|
list = list.filter(d => d.material === body.material);
|
|
}
|
|
return new Response(JSON.stringify({
|
|
code: 1000,
|
|
message: 'success',
|
|
data: {
|
|
list: list,
|
|
pagination: { page: body.page || 1, size: body.size || 20, total: list.length }
|
|
}
|
|
}), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
}
|
|
|
|
// NFC 设备添加 / 批量入库
|
|
if (urlStr.includes('/api/admin/nfc/device/add')) {
|
|
let body = {};
|
|
try { body = JSON.parse(options?.body || '{}'); } catch(e) {}
|
|
const newDev = {
|
|
id: deviceStore.length + 1,
|
|
uid: body.uid || ('NFC-' + Date.now()),
|
|
batchNo: body.batchNo || 'BATCH-DEFAULT',
|
|
material: body.material || 'metal',
|
|
status: 'unbound',
|
|
sceneMode: 'business',
|
|
cardId: null,
|
|
lastScannedAt: null,
|
|
shippingStatus: 'pending',
|
|
createTime: new Date().toISOString()
|
|
};
|
|
deviceStore.unshift(newDev);
|
|
saveDevices();
|
|
return new Response(JSON.stringify({ code: 1000, message: 'success', data: newDev }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// NFC 挂失
|
|
if (urlStr.includes('/api/admin/nfc/security/freeze')) {
|
|
let body = {};
|
|
try { body = JSON.parse(options?.body || '{}'); } catch(e) {}
|
|
const target = deviceStore.find(d => String(d.id) === String(body.id) || d.uid === body.uid);
|
|
if (target) { target.status = 'suspended'; saveDevices(); }
|
|
return new Response(JSON.stringify({ code: 1000, message: 'success' }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// NFC 解除挂失/重置
|
|
if (urlStr.includes('/api/admin/nfc/security/reset')) {
|
|
let body = {};
|
|
try { body = JSON.parse(options?.body || '{}'); } catch(e) {}
|
|
const target = deviceStore.find(d => String(d.id) === String(body.id) || d.uid === body.uid);
|
|
if (target) { target.status = 'active'; saveDevices(); }
|
|
return new Response(JSON.stringify({ code: 1000, message: 'success' }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
// 商业化配额
|
|
if (urlStr.includes('/api/admin/team/billing/currentPlan')) {
|
|
return new Response(JSON.stringify({
|
|
code: 1000,
|
|
message: 'success',
|
|
data: BILLING_QUOTA
|
|
}), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
}
|
|
|
|
// 自定义域名
|
|
if (urlStr.includes('/api/admin/team/domain/page')) {
|
|
return new Response(JSON.stringify({
|
|
code: 1000,
|
|
message: 'success',
|
|
data: {
|
|
list: domainList,
|
|
pagination: { page: 1, size: 10, total: domainList.length }
|
|
}
|
|
}), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
}
|
|
|
|
// 品牌资产包
|
|
if (urlStr.includes('/api/admin/team/brand/page')) {
|
|
return new Response(JSON.stringify({
|
|
code: 1000,
|
|
message: 'success',
|
|
data: {
|
|
list: [brandProfile],
|
|
pagination: { page: 1, size: 10, total: 1 }
|
|
}
|
|
}), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
}
|
|
|
|
// 部门架构树
|
|
if (urlStr.includes('/api/admin/team/members/getDepartmentTree') || urlStr.includes('/api/admin/team/members/department-tree')) {
|
|
return new Response(JSON.stringify({
|
|
code: 1000,
|
|
message: 'success',
|
|
data: DEPT_TREE
|
|
}), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
}
|
|
|
|
// 成员列表
|
|
if (urlStr.includes('/api/admin/team/members/getMemberPage') || urlStr.includes('/api/admin/team/members/page')) {
|
|
return new Response(JSON.stringify({
|
|
code: 1000,
|
|
message: 'success',
|
|
data: {
|
|
list: MEMBERS_LIST,
|
|
pagination: { page: 1, size: 20, total: MEMBERS_LIST.length }
|
|
}
|
|
}), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
}
|
|
|
|
// 离职一键交接
|
|
if (urlStr.includes('/api/admin/team/handover/transfer')) {
|
|
return new Response(JSON.stringify({
|
|
code: 1000,
|
|
message: '资产交接完成,已将全部数字化名片与关联线索划转至接收人'
|
|
}), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
}
|
|
|
|
return originalFetch.apply(this, arguments);
|
|
};
|
|
|
|
// 同时也对 XMLHttpRequest 劫持
|
|
const origOpen = XMLHttpRequest.prototype.open;
|
|
const origSend = XMLHttpRequest.prototype.send;
|
|
XMLHttpRequest.prototype.open = function(method, url) {
|
|
this._url = url;
|
|
return origOpen.apply(this, arguments);
|
|
};
|
|
|
|
XMLHttpRequest.prototype.send = function(body) {
|
|
const url = this._url || '';
|
|
|
|
if (url.includes('/api/admin/nfc/device/page')) {
|
|
let parsed = {};
|
|
try { parsed = JSON.parse(body || '{}'); } catch(e) {}
|
|
let list = [...deviceStore];
|
|
if (parsed.keyWord) {
|
|
const kw = parsed.keyWord.toLowerCase();
|
|
list = list.filter(d => (d.uid && d.uid.toLowerCase().includes(kw)) || (d.batchNo && d.batchNo.toLowerCase().includes(kw)));
|
|
}
|
|
if (parsed.status) {
|
|
list = list.filter(d => d.status === parsed.status);
|
|
}
|
|
if (parsed.material) {
|
|
list = list.filter(d => d.material === parsed.material);
|
|
}
|
|
const mockResp = JSON.stringify({
|
|
code: 1000, message: 'success',
|
|
data: { list, pagination: { page: parsed.page || 1, size: parsed.size || 20, total: list.length } }
|
|
});
|
|
mockXhr(this, mockResp);
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/nfc/device/add')) {
|
|
let parsed = {};
|
|
try { parsed = JSON.parse(body || '{}'); } catch(e) {}
|
|
const newDev = {
|
|
id: deviceStore.length + 1,
|
|
uid: parsed.uid || ('NFC-' + Date.now()),
|
|
batchNo: parsed.batchNo || 'BATCH-DEFAULT',
|
|
material: parsed.material || 'metal',
|
|
status: 'unbound',
|
|
sceneMode: 'business',
|
|
cardId: null,
|
|
lastScannedAt: null,
|
|
shippingStatus: 'pending',
|
|
createTime: new Date().toISOString()
|
|
};
|
|
deviceStore.unshift(newDev);
|
|
saveDevices();
|
|
mockXhr(this, JSON.stringify({ code: 1000, message: 'success', data: newDev }));
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/nfc/security/freeze')) {
|
|
let parsed = {};
|
|
try { parsed = JSON.parse(body || '{}'); } catch(e) {}
|
|
const target = deviceStore.find(d => String(d.id) === String(parsed.id) || d.uid === parsed.uid);
|
|
if (target) { target.status = 'suspended'; saveDevices(); }
|
|
mockXhr(this, JSON.stringify({ code: 1000, message: 'success' }));
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/nfc/security/reset')) {
|
|
let parsed = {};
|
|
try { parsed = JSON.parse(body || '{}'); } catch(e) {}
|
|
const target = deviceStore.find(d => String(d.id) === String(parsed.id) || d.uid === parsed.uid);
|
|
if (target) { target.status = 'active'; saveDevices(); }
|
|
mockXhr(this, JSON.stringify({ code: 1000, message: 'success' }));
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/team/billing/currentPlan')) {
|
|
mockXhr(this, JSON.stringify({ code: 1000, message: 'success', data: BILLING_QUOTA }));
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/team/domain/page')) {
|
|
mockXhr(this, JSON.stringify({
|
|
code: 1000, message: 'success',
|
|
data: { list: domainList, pagination: { page: 1, size: 10, total: domainList.length } }
|
|
}));
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/team/brand/page')) {
|
|
mockXhr(this, JSON.stringify({
|
|
code: 1000, message: 'success',
|
|
data: { list: [brandProfile], pagination: { page: 1, size: 10, total: 1 } }
|
|
}));
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/team/members/getDepartmentTree') || url.includes('/api/admin/team/members/department-tree')) {
|
|
mockXhr(this, JSON.stringify({ code: 1000, message: 'success', data: DEPT_TREE }));
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/team/members/getMemberPage') || url.includes('/api/admin/team/members/page')) {
|
|
mockXhr(this, JSON.stringify({
|
|
code: 1000, message: 'success',
|
|
data: { list: MEMBERS_LIST, pagination: { page: 1, size: 20, total: MEMBERS_LIST.length } }
|
|
}));
|
|
return;
|
|
}
|
|
|
|
if (url.includes('/api/admin/team/handover/transfer')) {
|
|
mockXhr(this, JSON.stringify({
|
|
code: 1000,
|
|
message: '资产交接完成,已将全部数字化名片与关联线索划转至接收人'
|
|
}));
|
|
return;
|
|
}
|
|
|
|
return origSend.apply(this, arguments);
|
|
};
|
|
|
|
function mockXhr(xhr, responseText) {
|
|
xhr.getAllResponseHeaders = function() { return 'content-type: application/json\r\n'; };
|
|
xhr.getResponseHeader = function(name) { return name && name.toLowerCase() === 'content-type' ? 'application/json' : null; };
|
|
setTimeout(() => {
|
|
Object.defineProperty(xhr, 'readyState', { value: 4, writable: false });
|
|
Object.defineProperty(xhr, 'status', { value: 200, writable: false });
|
|
Object.defineProperty(xhr, 'statusText', { value: 'OK', writable: false });
|
|
Object.defineProperty(xhr, 'responseText', { value: responseText, writable: false });
|
|
Object.defineProperty(xhr, 'response', { value: responseText, writable: false });
|
|
|
|
try { xhr.dispatchEvent(new Event('readystatechange')); } catch(e) {}
|
|
try { xhr.dispatchEvent(new ProgressEvent('load')); } catch(e) {}
|
|
try { xhr.dispatchEvent(new ProgressEvent('loadend')); } catch(e) {}
|
|
if (typeof xhr.onreadystatechange === 'function') xhr.onreadystatechange();
|
|
if (typeof xhr.onload === 'function') xhr.onload();
|
|
if (typeof xhr.onloadend === 'function') xhr.onloadend();
|
|
}, 20);
|
|
}
|
|
|
|
console.log('[Round-6] Harness fully armed and ready with complete event dispatch.');
|
|
})();
|