(function () {
'use strict';
const API = '/api/srm-monitor';
const DOCS = {
purchase_order: {label: '采购订单'},
inspect_bill: {label: '检验批'},
instock_return: {label: '入库返回'}
};
const FIELD_LABELS = {
id: '来源记录主键', billNo: '单据编号', srcBillNo: '来源单据编号',
purOrderBillNo: '采购订单号', typeId: '单据类型', documentDate: '单据日期',
supplier: '供应商', settleCurrency: '结算币别', organization: '采购组织',
inspectorId: '检验员', createDate: '创建时间', modifyDate: '接收时间',
documentStatus: '接收状态', text: '接收错误原因', materialId: '物料编码',
srcEntryId: '来源分录主键', purOrderSeq: '采购订单行号', unitId: '单位',
stockQty: '入库数量', stockId: '仓库', note: '备注',
materialVoucher: '物料凭证', materialVoucherSeq: '凭证行号',
materialVoucherYear: '凭证年度', errorMsg: '错误信息', amount: '金额',
taxCode: '税码', unQualifiedAmount: '不合格数量', qualifiedAmount: '合格数量',
type: '单据区分', version: '版本', manual: '手工下单',
text1: '备用字段1', text2: '备用字段2', text3: '备用字段3',
text4: '备用字段4', text5: '备用字段5'
};
const state = {
monitorType: 'purchase_order', monitorPage: 1, monitorPages: 1,
monitorSize: 50, lookupPage: 1, lookupPages: 1, lookupSize: 50,
refreshInterval: 60, timer: null
};
function html(value) {
return String(value == null ? '' : value).replace(/[&<>"']/g, function (char) {
return {'&': '&', '<': '<', '>': '>', '"': '"', "'": '''}[char];
});
}
function text(value, fallback) {
const result = value == null || value === '' ? (fallback == null ? '-' : fallback) : value;
return html(result);
}
function notify(message) {
if (typeof window.toast === 'function') window.toast(message);
}
function canOperate(resource) {
return typeof window.hasOperateResource !== 'function' || window.hasOperateResource(resource);
}
function statusTag(value, active) {
if (value === 'success') return '传输成功';
if (value === 'resolved' || active === 0) return '异常已消失';
return '传输失败';
}
function replayTag(value) {
if (value === 'processing') return '推送中';
if (value === 'success') return '重推成功';
if (value === 'failed') return '重推失败';
return '未重推';
}
function pageInfo(page, pages, total) {
return '第 ' + page + ' / ' + pages + ' 页,共 ' + total + ' 条';
}
function installStyles() {
if (document.getElementById('srmMonitorStyles')) return;
const style = document.createElement('style');
style.id = 'srmMonitorStyles';
style.textContent = `
.srm-metrics{display:grid;grid-template-columns:repeat(3,minmax(180px,1fr));gap:12px;margin:12px 0}
.srm-metric{border:1px solid #d8e1ea;border-radius:6px;background:#fff;padding:14px 16px;min-height:86px}
.srm-metric strong{display:block;font-size:28px;line-height:34px;color:#172033;margin-top:5px}
.srm-metric span{color:#667085;font-size:13px}.srm-metric[data-active="1"]{border-left:4px solid #d92d20}
.srm-status{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:8px 0 12px;color:#475467;font-size:13px}
.srm-tabs{display:inline-flex;border:1px solid #cfd8e3;border-radius:6px;overflow:hidden;background:#fff}
.srm-tabs button{border:0;border-right:1px solid #cfd8e3;border-radius:0;background:#fff;color:#344054;padding:8px 16px;min-width:100px}
.srm-tabs button:last-child{border-right:0}.srm-tabs button.active{background:#1769aa;color:#fff}
.srm-toolbar{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap}.srm-toolbar>div{min-width:170px}
.srm-toolbar .srm-grow{flex:1;min-width:220px}.srm-toolbar label{display:block;margin-bottom:5px}
.srm-link{color:#1976d2;text-decoration:none;font-weight:600}.srm-link:hover{text-decoration:underline}
.srm-tag{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;white-space:nowrap}
.srm-tag.ok{background:#ecfdf3;color:#027a48}.srm-tag.bad{background:#fef3f2;color:#b42318}
.srm-tag.warn{background:#fffaeb;color:#b54708}.srm-tag.muted{background:#f2f4f7;color:#475467}
.srm-monitor-table{width:100%;min-width:1420px}.srm-monitor-table th{white-space:nowrap}
.srm-monitor-table .srm-info-cell{width:34%;min-width:420px}
.srm-error{display:block;max-width:none;white-space:normal;word-break:break-word;line-height:1.45;color:#b42318}
.srm-error.is-history{color:#667085}.srm-message-line+.srm-message-line{margin-top:5px}
.srm-message-label{font-weight:600;color:#475467}.srm-replay-cell,.srm-replay-time,.srm-operation{white-space:nowrap}
.srm-action-group{display:flex;align-items:center;gap:9px}.srm-batch-progress{display:none;min-width:155px;font-size:13px;white-space:nowrap}
.srm-batch-progress.running{display:inline;color:#b54708}.srm-batch-progress.complete{display:inline;color:#027a48}.srm-batch-progress.failed{display:inline;color:#b42318}
.srm-detail-grid{display:grid;grid-template-columns:repeat(4,minmax(150px,1fr));border:1px solid #d8e1ea;border-radius:6px;overflow:hidden;margin-bottom:14px}
.srm-detail-grid>div{padding:10px 12px;border-right:1px solid #e4e7ec;border-bottom:1px solid #e4e7ec;min-height:62px}
.srm-detail-grid small{display:block;color:#667085;margin-bottom:5px}.srm-detail-grid b{font-weight:600;word-break:break-word}
.srm-field-table td:first-child{width:190px;color:#475467;font-weight:600}.srm-field-table td{vertical-align:top;word-break:break-word}
.srm-config-grid{display:grid;grid-template-columns:repeat(2,minmax(240px,1fr));gap:14px}.srm-config-grid label{display:block;margin-bottom:6px}
.srm-switches{display:flex;gap:22px;flex-wrap:wrap;margin-top:15px}.srm-switches label{display:flex;align-items:center;gap:7px}
@media(max-width:900px){.srm-metrics,.srm-config-grid{grid-template-columns:1fr}.srm-detail-grid{grid-template-columns:repeat(2,minmax(130px,1fr))}}
@media(max-width:560px){.srm-detail-grid{grid-template-columns:1fr}.srm-tabs{display:flex;width:100%}.srm-tabs button{flex:1;min-width:0;padding:8px 5px}}
`;
document.head.appendChild(style);
}
function installNavigation() {
const side = document.querySelector('.side');
if (!side || document.getElementById('navSrmMonitor')) return;
const group = document.createElement('div');
group.className = 'navgroup';
group.id = 'navSrmMonitor';
group.innerHTML = '🔄 SRM传输监控▸';
const sub = document.createElement('div');
sub.className = 'navsub';
sub.id = 'navSrmMonitorSub';
sub.innerHTML = [
'
异常监控
',
'单据查询
',
'接口配置
'
].join('');
const before = document.getElementById('navSystem');
side.insertBefore(group, before || null);
side.insertBefore(sub, before || null);
group.addEventListener('click', function () {
const open = sub.style.display === 'block';
sub.style.display = open ? 'none' : 'block';
group.querySelector('.caret').textContent = open ? '▸' : '▾';
});
sub.querySelector('[data-s="srm-monitor"]').addEventListener('click', function () {
openPage('srm-monitor', 'SRM异常监控', '采购订单、检验批和入库返回的传输异常与重推状态。');
loadMonitor();
});
sub.querySelector('[data-s="srm-lookup"]').addEventListener('click', function () {
openPage('srm-lookup', 'SRM单据查询', '按单据类型和编号查询已落库的完整字段与重推记录。');
});
sub.querySelector('[data-s="srm-config"]').addEventListener('click', function () {
openPage('srm-config', 'SRM接口配置', '维护旧看板连接参数、同步开关并查看固定接口清单。');
loadConfig();
});
}
function installPages() {
const main = document.querySelector('main.main');
if (!main || document.getElementById('srm-monitor')) return;
main.insertAdjacentHTML('beforeend', `
SRM异常监控
异常数据由旧看板接口定时同步到本系统。
SRM单据查询
成功记录从本模块启用后开始积累,不追溯启用前已成功的历史单据。
SRM接口配置
访问令牌仅保存在服务器端,接口路径为固定协议,不开放修改。
`);
bindEvents();
renderTabs();
}
function bindEvents() {
document.getElementById('srmRefreshBtn').onclick = loadMonitor;
document.getElementById('srmSyncBtn').onclick = syncNow;
document.getElementById('srmMonitorQuery').onclick = function () { state.monitorPage = 1; loadMonitorRecords(); };
document.getElementById('srmBatchReplayBtn').onclick = function () { batchReplay(selectedIds('srmMonitorBody')); };
document.getElementById('srmMonitorBill').addEventListener('keydown', function (event) { if (event.key === 'Enter') document.getElementById('srmMonitorQuery').click(); });
document.getElementById('srmMonitorPrev').onclick = function () { if (state.monitorPage > 1) { state.monitorPage--; loadMonitorRecords(); } };
document.getElementById('srmMonitorNext').onclick = function () { if (state.monitorPage < state.monitorPages) { state.monitorPage++; loadMonitorRecords(); } };
document.getElementById('srmMonitorSize').onchange = function () { state.monitorSize = Number(this.value) || 50; state.monitorPage = 1; loadMonitorRecords(); };
document.getElementById('srmLookupQuery').onclick = function () { state.lookupPage = 1; loadLookup(); };
document.getElementById('srmLookupBatchReplayBtn').onclick = function () { batchReplay(selectedIds('srmLookupBody')); };
document.getElementById('srmDeleteHistoryBtn').onclick = deleteSelectedHistory;
document.getElementById('srmLookupSelectAll').onchange = function () { togglePageSelection('srmLookupBody', this.checked); };
document.getElementById('srmLookupBill').addEventListener('keydown', function (event) { if (event.key === 'Enter') document.getElementById('srmLookupQuery').click(); });
document.getElementById('srmLookupPrev').onclick = function () { if (state.lookupPage > 1) { state.lookupPage--; loadLookup(); } };
document.getElementById('srmLookupNext').onclick = function () { if (state.lookupPage < state.lookupPages) { state.lookupPage++; loadLookup(); } };
document.getElementById('srmLookupSize').onchange = function () { state.lookupSize = Number(this.value) || 50; state.lookupPage = 1; loadLookup(); };
document.getElementById('srmSaveConfigBtn').onclick = saveConfig;
document.getElementById('srmTestBtn').onclick = testConnection;
}
function openPage(id, heading, description) {
document.querySelectorAll('.nav,.navchild,.navgrandchild').forEach(function (item) { item.classList.remove('active'); });
const nav = document.querySelector('[data-s="' + id + '"]');
if (nav) nav.classList.add('active');
document.querySelectorAll('.section').forEach(function (item) { item.classList.remove('active'); });
const section = document.getElementById(id);
if (section) section.classList.add('active');
const title = document.getElementById('title');
const desc = document.getElementById('desc');
if (title) title.textContent = heading;
if (desc) desc.textContent = description;
if (typeof window.activateWorkspacePage === 'function') window.activateWorkspacePage(id);
}
function renderTabs() {
document.getElementById('srmDocTabs').innerHTML = Object.keys(DOCS).map(function (key) {
return '';
}).join('');
document.querySelectorAll('#srmDocTabs button').forEach(function (button) {
button.onclick = function () {
state.monitorType = button.dataset.type;
state.monitorPage = 1;
renderTabs();
loadMonitorRecords();
};
});
}
async function loadSummary() {
try {
const data = await apiGet(API + '/summary');
state.refreshInterval = Number(data.refresh_interval) || 60;
document.getElementById('srmMetrics').innerHTML = (data.items || []).map(function (item) {
return '' + html(item.label) + (item.enabled ? '' : '(已停用)') + '' + Number(item.count || 0) + '当前失败 · 成功 ' + Number(item.success_count || 0) + ' · 异常已消失 ' + Number(item.resolved_count || 0) + '
';
}).join('');
const configured = data.configured ? '服务已配置' : '服务未配置';
document.getElementById('srmSyncStatus').innerHTML = configured + '最近同步:' + text(data.last_sync_at, '尚未同步') + '' + text(data.last_sync_message) + '';
} catch (error) {
document.getElementById('srmSyncStatus').innerHTML = '读取失败' + html(error.message) + '';
}
scheduleRefresh();
}
function monitorColumns(type) {
if (type === 'purchase_order') return ['选择', '单据编号', '传输状态', '采购日期', '供应商', '采购组织', '接收状态', '金蝶信息', '异常/推送返回信息', '最近重推', '最近一次推送时间', '操作'];
if (type === 'inspect_bill') return ['选择', '检验批号', '传输状态', '检验员', '创建时间', '接收时间', '接收状态', '异常/推送返回信息', '最近重推', '最近一次推送时间', '操作'];
return ['选择', '来源单号', '传输状态', '采购订单号', '物料编码', '入库数量', '仓库', '物料凭证', '接收状态', '异常/推送返回信息', '最近重推', '最近一次推送时间', '操作'];
}
function replayButton(row) {
return canOperate('srm_monitor.monitor') ? '' : '-';
}
function messageInfo(row) {
const historical = !row.is_active || row.transmission_status === 'success';
const errorLabel = historical ? '原异常:' : '当前异常:';
const errorClass = historical ? 'srm-error is-history' : 'srm-error';
const original = '' + errorLabel + '' + text(row.error_message) + '
';
if (!row.last_replay_message) return original;
return original + '最近推送返回:' + text(row.last_replay_message) + '
';
}
function monitorRow(row) {
const payload = row.payload || {};
const link = '' + text(row.bill_no) + '';
const messages = messageInfo(row);
const replay = replayTag(row.last_replay_status);
const select = '';
const transmission = statusTag(row.transmission_status, row.is_active);
const replayCells = '' + replay + ' | ' + text(row.last_replayed_at) + ' | ' + replayButton(row) + ' | ';
if (row.doc_type === 'purchase_order') return '| ' + select + ' | ' + link + ' | ' + transmission + ' | ' + text(payload.documentDate) + ' | ' + text(payload.supplier) + ' | ' + text(payload.organization) + ' | ' + text(row.document_status) + ' | ' + text(row.kingdee_bill_no) + ' | ' + messages + ' | ' + replayCells + '
';
if (row.doc_type === 'inspect_bill') return '| ' + select + ' | ' + link + ' | ' + transmission + ' | ' + text(payload.inspectorId) + ' | ' + text(payload.createDate) + ' | ' + text(payload.modifyDate) + ' | ' + text(row.document_status) + ' | ' + messages + ' | ' + replayCells + '
';
return '| ' + select + ' | ' + link + ' | ' + transmission + ' | ' + text(payload.purOrderBillNo) + ' | ' + text(payload.materialId) + ' | ' + text(payload.stockQty) + ' | ' + text(payload.stockId) + ' | ' + text(payload.materialVoucher) + ' | ' + text(row.document_status) + ' | ' + messages + ' | ' + replayCells + '
';
}
async function loadMonitorRecords() {
const bill = document.getElementById('srmMonitorBill').value.trim();
const status = document.getElementById('srmMonitorStatus').value;
let url = API + '/records?status=' + encodeURIComponent(status) + '&doc_type=' + encodeURIComponent(state.monitorType) + '&page=' + state.monitorPage + '&page_size=' + state.monitorSize;
if (bill) url += '&bill_no=' + encodeURIComponent(bill);
const columns = monitorColumns(state.monitorType);
document.getElementById('srmMonitorHead').innerHTML = '' + columns.map(function (item, index) { return index === 0 ? ' | ' : '' + item + ' | '; }).join('') + '
';
try {
const data = await apiGet(url);
state.monitorPages = Math.max(1, Math.ceil(Number(data.total || 0) / Number(data.page_size || state.monitorSize)));
state.monitorPage = Math.min(state.monitorPages, Number(data.page || 1));
document.getElementById('srmMonitorBody').innerHTML = (data.rows || []).map(monitorRow).join('') || '当前没有异常记录 |
';
document.getElementById('srmMonitorPageInfo').textContent = pageInfo(state.monitorPage, state.monitorPages, data.total || 0);
document.getElementById('srmMonitorSelectAll').onchange = function () { togglePageSelection('srmMonitorBody', this.checked); };
} catch (error) {
document.getElementById('srmMonitorBody').innerHTML = '加载失败:' + html(error.message) + ' |
';
}
applySrmPermissions();
}
async function loadMonitor() {
await Promise.all([loadSummary(), loadMonitorRecords()]);
}
async function syncNow() {
const button = document.getElementById('srmSyncBtn');
button.disabled = true;
const old = button.textContent;
button.textContent = '同步中...';
try {
const result = await apiPost(API + '/sync', {});
notify(result.message || '同步完成');
await loadMonitor();
} catch (error) {
notify('同步失败:' + error.message);
} finally {
button.disabled = false;
button.textContent = old;
}
}
function lookupRow(row) {
const link = '' + text(row.bill_no) + '';
const select = '';
return '| ' + select + ' | ' + text(DOCS[row.doc_type] && DOCS[row.doc_type].label, row.doc_type) + ' | ' + link + ' | ' + statusTag(row.transmission_status, row.is_active) + ' | ' + text(row.document_status) + ' | ' + text(row.kingdee_bill_no || row.kingdee_id) + ' | ' + messageInfo(row) + ' | ' + text(row.last_seen_at) + ' | ' + replayTag(row.last_replay_status) + ' | ' + text(row.last_replayed_at) + ' |
';
}
async function loadLookup() {
const type = document.getElementById('srmLookupType').value;
const bill = document.getElementById('srmLookupBill').value.trim();
const status = document.getElementById('srmLookupStatus').value;
let url = API + '/records?status=' + encodeURIComponent(status) + '&page=' + state.lookupPage + '&page_size=' + state.lookupSize;
if (type) url += '&doc_type=' + encodeURIComponent(type);
if (bill) url += '&bill_no=' + encodeURIComponent(bill);
try {
const data = await apiGet(url);
state.lookupPages = Math.max(1, Math.ceil(Number(data.total || 0) / Number(data.page_size || state.lookupSize)));
state.lookupPage = Math.min(state.lookupPages, Number(data.page || 1));
document.getElementById('srmLookupBody').innerHTML = (data.rows || []).map(lookupRow).join('') || '没有找到匹配记录 |
';
document.getElementById('srmLookupPageInfo').textContent = pageInfo(state.lookupPage, state.lookupPages, data.total || 0);
} catch (error) {
document.getElementById('srmLookupBody').innerHTML = '查询失败:' + html(error.message) + ' |
';
}
}
function renderDetail(data) {
const fields = Object.keys(data.payload || {}).map(function (key) {
const value = data.payload[key];
const rendered = value != null && typeof value === 'object' ? '' + html(JSON.stringify(value, null, 2)) + '
' : text(value);
return '| ' + html(FIELD_LABELS[key] || key) + ' ' + html(key) + ' | ' + rendered + ' |
';
}).join('');
const logs = (data.replay_logs || []).map(function (row) {
return '| ' + text(row.created_at) + ' | ' + text(row.operator_name) + ' | ' + replayTag(row.replay_status) + ' | ' + text(row.response_message) + ' |
';
}).join('') || '尚无重推记录 |
';
document.getElementById('srmRecordDetail').innerHTML = '' + html(data.doc_label) + ' · ' + text(data.bill_no) + '
来源记录主键:' + text(data.source_id) + '
' + (data.is_active && canOperate('srm_monitor.monitor') ? '
' : '') + '
传输状态' + statusTag(data.transmission_status, data.is_active) + '
接收状态' + text(data.document_status) + '
金蝶单号/主键' + text(data.kingdee_bill_no || data.kingdee_id) + '
最近重推' + replayTag(data.last_replay_status) + '
最近一次推送时间' + text(data.last_replayed_at) + '
首次发现' + text(data.first_seen_at) + '
最后发现' + text(data.last_seen_at) + '
状态确认时间' + text(data.status_verified_at || data.resolved_at) + '
' + (data.is_active ? '当前异常信息' : '原异常信息') + '' + text(data.error_message) + '
最近推送返回' + text(data.last_replay_message) + '
';
}
async function openRecord(recordId) {
openPage('srm-lookup', 'SRM单据查询', '按单据类型和编号查询已落库的完整字段与重推记录。');
try {
const data = await apiGet(API + '/records/' + recordId);
document.getElementById('srmLookupType').value = data.doc_type;
document.getElementById('srmLookupBill').value = data.bill_no || '';
await loadLookup();
renderDetail(data);
} catch (error) {
notify('加载单据详情失败:' + error.message);
}
}
async function replayRecord(recordId, button) {
if (!confirm('确定重新推送这条单据吗?系统会记录本次操作结果。')) return;
const old = button ? button.textContent : '';
if (button) { button.disabled = true; button.textContent = '重推中...'; }
try {
const result = await apiPost(API + '/records/' + recordId + '/replay', {});
notify(result.message || '重推请求执行成功');
if (document.getElementById('srm-monitor').classList.contains('active')) await loadMonitor();
else await openRecord(recordId);
} catch (error) {
notify('重推失败:' + error.message);
if (document.getElementById('srm-lookup').classList.contains('active')) await openRecord(recordId);
} finally {
if (button) { button.disabled = false; button.textContent = old; }
}
}
function selectedRows(bodyId) {
return Array.prototype.map.call(
document.querySelectorAll('#' + bodyId + ' .srm-row-select:checked'),
function (item) {
return {id: Number(item.value), active: item.dataset.active === '1'};
}
);
}
function selectedIds(bodyId) {
return selectedRows(bodyId).map(function (item) { return item.id; });
}
function togglePageSelection(bodyId, checked) {
document.querySelectorAll('#' + bodyId + ' .srm-row-select').forEach(function (item) {
item.checked = checked;
});
}
async function batchReplay(recordIds) {
if (!recordIds.length) return notify('请先勾选需要重推的当前异常记录');
if (recordIds.length > 100) return notify('单次最多批量重推100条记录');
const rows = selectedRows(
document.getElementById('srm-monitor').classList.contains('active') ? 'srmMonitorBody' : 'srmLookupBody'
);
if (rows.some(function (item) { return !item.active; })) return notify('批量重推只能选择当前失败记录');
if (!confirm('确定批量重推已选择的 ' + recordIds.length + ' 条记录吗?系统会逐条保存结果。')) return;
const activeMonitor = document.getElementById('srm-monitor').classList.contains('active');
const bodyId = activeMonitor ? 'srmMonitorBody' : 'srmLookupBody';
const progress = document.getElementById(activeMonitor ? 'srmBatchReplayState' : 'srmLookupBatchReplayState');
const buttons = [document.getElementById('srmBatchReplayBtn'), document.getElementById('srmLookupBatchReplayBtn')];
buttons.forEach(function (button) { if (button) button.disabled = true; });
document.querySelectorAll('#' + bodyId + ' .srm-row-select:checked').forEach(function (checkbox) {
const cell = checkbox.closest('tr').querySelector('[data-srm-replay-status]');
if (cell) cell.innerHTML = replayTag('processing');
});
progress.className = 'srm-batch-progress running';
progress.textContent = '推送中,共 ' + recordIds.length + ' 条...';
try {
const result = await apiPost(API + '/records/batch-replay', {record_ids: recordIds});
notify('批量重推完成:成功 ' + result.success_count + ' 条,失败 ' + result.failed_count + ' 条');
progress.className = 'srm-batch-progress ' + (result.failed_count ? 'failed' : 'complete');
progress.textContent = '完成:成功 ' + result.success_count + ' 条,失败 ' + result.failed_count + ' 条';
if (activeMonitor) await loadMonitor();
else await loadLookup();
} catch (error) {
notify('批量重推失败:' + error.message);
progress.className = 'srm-batch-progress failed';
progress.textContent = '推送失败:' + error.message;
} finally {
buttons.forEach(function (button) { if (button) button.disabled = false; });
}
}
async function deleteSelectedHistory() {
const rows = selectedRows('srmLookupBody');
if (!rows.length) return notify('请先勾选需要删除的历史记录');
if (rows.some(function (item) { return item.active; })) return notify('当前失败记录不能删除,请只选择传输成功或异常已消失记录');
if (!confirm('确定删除已选择的 ' + rows.length + ' 条本地历史吗?对应的本地重推记录也会删除,旧系统和SRM数据不受影响。')) return;
const button = document.getElementById('srmDeleteHistoryBtn');
button.disabled = true;
try {
const result = await apiPost(API + '/records/batch-delete-history', {record_ids: rows.map(function (item) { return item.id; })});
notify('已删除 ' + result.deleted + ' 条本地历史记录');
document.getElementById('srmRecordDetail').innerHTML = '';
await loadLookup();
await loadSummary();
} catch (error) {
notify('删除失败:' + error.message);
} finally {
button.disabled = false;
}
}
async function loadConfig() {
try {
const data = await apiGet(API + '/config');
const config = data.config || {};
document.getElementById('srmBaseUrl').value = config.base_url || '';
document.getElementById('srmSapBaseUrl').value = config.sap_base_url || 'http://192.168.16.208';
document.getElementById('srmAccessToken').value = config.access_token || '';
document.getElementById('srmTimeout').value = config.timeout || 30;
document.getElementById('srmRefreshInterval').value = config.refresh_interval || 60;
document.getElementById('srmSyncEnabled').checked = !!config.sync_enabled;
document.getElementById('srmPurchaseEnabled').checked = !!config.purchase_enabled;
document.getElementById('srmInspectEnabled').checked = !!config.inspect_enabled;
document.getElementById('srmInstockEnabled').checked = !!config.instock_enabled;
document.getElementById('srmEndpointBody').innerHTML = (data.endpoints || []).map(function (row) {
return '| ' + text(row.label) + ' | ' + text(row.purpose) + ' | ' + text(row.method) + ' | ' + text(row.path) + ' |
';
}).join('');
} catch (error) {
notify('读取SRM接口配置失败:' + error.message);
}
applySrmPermissions();
}
function configBody() {
return {
base_url: document.getElementById('srmBaseUrl').value.trim(),
sap_base_url: document.getElementById('srmSapBaseUrl').value.trim(),
access_token: document.getElementById('srmAccessToken').value.trim(),
timeout: Number(document.getElementById('srmTimeout').value) || 30,
refresh_interval: Number(document.getElementById('srmRefreshInterval').value) || 60,
sync_enabled: document.getElementById('srmSyncEnabled').checked ? 1 : 0,
purchase_enabled: document.getElementById('srmPurchaseEnabled').checked ? 1 : 0,
inspect_enabled: document.getElementById('srmInspectEnabled').checked ? 1 : 0,
instock_enabled: document.getElementById('srmInstockEnabled').checked ? 1 : 0
};
}
async function saveConfig() {
try {
const result = await apiPut(API + '/config', configBody());
notify('SRM接口配置已保存');
document.getElementById('srmAccessToken').value = result.config.access_token || '';
scheduleRefresh();
} catch (error) {
notify('保存失败:' + error.message);
}
}
async function testConnection() {
const target = document.getElementById('srmTestResult');
target.innerHTML = '正在测试已保存的连接配置...';
try {
const result = await apiPost(API + '/config/test', {});
target.innerHTML = (result.results || []).map(function (row) {
return '' + html(row.label) + ':' + (row.ok ? '成功,异常 ' + row.count + ' 条' : html(row.message)) + '';
}).join('');
notify(result.ok ? 'SRM接口连接正常' : '部分接口连接失败');
} catch (error) {
target.innerHTML = '连接失败:' + html(error.message) + '';
}
}
function applySrmPermissions() {
if (typeof window.hasResource !== 'function') return;
const mapping = {
'srm-monitor': 'srm_monitor.monitor',
'srm-lookup': 'srm_monitor.lookup',
'srm-config': 'srm_monitor.config'
};
document.querySelectorAll('#navSrmMonitorSub [data-s]').forEach(function (nav) {
nav.style.display = window.hasResource(mapping[nav.dataset.s]) ? '' : 'none';
});
const visible = Array.prototype.some.call(document.querySelectorAll('#navSrmMonitorSub [data-s]'), function (nav) { return nav.style.display !== 'none'; });
const group = document.getElementById('navSrmMonitor');
if (group) group.style.display = visible ? '' : 'none';
document.querySelectorAll('#srm-monitor [data-resource-action]').forEach(function (item) {
item.style.display = canOperate(item.dataset.resourceAction) ? '' : 'none';
});
document.querySelectorAll('#srm-config [data-resource-action]').forEach(function (item) {
item.style.display = canOperate(item.dataset.resourceAction) ? '' : 'none';
});
document.querySelectorAll('#srm-lookup [data-resource-action]').forEach(function (item) {
item.style.display = canOperate(item.dataset.resourceAction) ? '' : 'none';
});
}
function scheduleRefresh() {
if (state.timer) clearTimeout(state.timer);
state.timer = setTimeout(async function tick() {
if (document.getElementById('srm-monitor').classList.contains('active')) await loadMonitor();
else scheduleRefresh();
}, Math.max(30, state.refreshInterval) * 1000);
}
const previousPermissions = window.applyNavPermissions;
window.applyNavPermissions = function () {
if (previousPermissions) previousPermissions();
applySrmPermissions();
};
window.openSrmRecord = openRecord;
window.replaySrmRecord = replayRecord;
installStyles();
installPages();
installNavigation();
applySrmPermissions();
scheduleRefresh();
})();