(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接口配置

访问令牌仅保存在服务器端,接口路径为固定协议,不开放修改。

当前 SRM同步仍使用上面的旧看板地址,此项用于维护 SAP 环境地址。

接口清单

以下路径来自旧看板源码,仅供核对。

单据类型用途请求方式接口路径
`); 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) + '

接口返回字段

完整保留本次 HTTP 拉取字段。

' + fields + '

重推记录

' + logs + '
时间操作人结果返回信息
'; } 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(); })();