// ==UserScript== // @name 单一窗口-内河运费自动化扣减 // @namespace // // @version 1.20 // @description 监听航次号变化并执行扣减操作,从外部JSON加载扣减标准,解析集装箱表格,自动计算扣减,支持用户修改扣减金额和币制 // @match https://swapp.singlewindow.cn/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @connect linjun.dpdns.org // ==/UserScript== (function() { 'use strict'; // 扣减配置URL const DEDUCTION_CONFIG_URL = 'https://linjun.dpdns.org/dl/neihe.json'; // 添加自定义样式 GM_addStyle(` /* 科技风格弹窗样式 - 不影响页面原有样式 */ #customModal, #confirmModal, #otherRateConfirmModal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.7); z-index: 10000; justify-content: center; align-items: center; /* 去掉毛玻璃效果:backdrop-filter: blur(5px); */ } #customModal .modal-content, #confirmModal .modal-content, #otherRateConfirmModal .modal-content { position: relative; width: auto; /* 改为自适应宽度 */ max-width: 90%; /* 最大宽度为视口的90% */ padding: 25px; background: rgba(20, 25, 40, 0.95); border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.3), 0 0 0 1px rgba(100, 150, 255, 0.2), 0 0 20px rgba(100, 150, 255, 0.1); overflow: hidden; border: 1px solid rgba(100, 150, 255, 0.3); color: #e0e0e0; font-family: 'Segoe UI', system-ui, sans-serif; font-size: 14px; /* 设置字体大小为14号 */ } #customModal .modal-header, #confirmModal .modal-header, #otherRateConfirmModal .modal-header { border-bottom: 1px solid rgba(100, 150, 255, 0.3); padding-bottom: 15px; margin-bottom: 20px; position: relative; } #customModal .modal-header::after, #confirmModal .modal-header::after, #otherRateConfirmModal .modal-header::after { content: ''; position: absolute; bottom: -1px; left: 0; width: 100%; height: 1px; background: linear-gradient(90deg, transparent, #5d8fff, transparent); } #customModal .modal-title, #confirmModal .modal-title, #otherRateConfirmModal .modal-title { font-size: 18px; /* 标题稍大 */ font-weight: 600; color: #ffffff; margin: 0; text-align: center; text-shadow: 0 0 10px rgba(100, 150, 255, 0.5); } #customModal .modal-notice { margin-bottom: 20px; padding: 15px; background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 6px; color: #ffc107; font-size: 13px; line-height: 1.4; } #customModal .modal-body { margin-bottom: 25px; line-height: 1.5; display: flex; /* 添加flex布局 */ gap: 20px; /* 左右间距 */ align-items: stretch; /* 确保子元素高度一致 */ } #confirmModal .modal-body, #otherRateConfirmModal .modal-body { margin-bottom: 25px; line-height: 1.5; } .deduction-details { background: rgba(30, 35, 50, 0.5); border-radius: 8px; padding: 15px; border: 1px solid rgba(100, 150, 255, 0.2); flex: 1 1 50%; /* 确保等宽分配 */ max-height: 300px; overflow-y: auto; width: 100%; min-width: 0; box-sizing: border-box; /* 确保宽度计算包含padding和border */ display: flex; flex-direction: column; } .calculation-form { background: rgba(30, 35, 50, 0.5); border-radius: 8px; padding: 15px; border: 1px solid rgba(100, 150, 255, 0.2); flex: 1 1 50%; /* 确保等宽分配 */ width: 100%; min-width: 0; box-sizing: border-box; /* 确保宽度计算包含padding和border */ display: flex; flex-direction: column; } .deduction-item { display: flex; justify-content: space-between; margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid rgba(100, 150, 255, 0.1); } .deduction-item:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; } .deduction-total { font-weight: bold; color: #5d8fff; margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(100, 150, 255, 0.3); display: flex; justify-content: space-between; align-items: center; } .amount-input { background: rgba(40, 45, 60, 0.8); /* 统一背景色 */ border: 1px solid rgba(100, 150, 255, 0.3); border-radius: 4px; color: #ffffff; padding: 5px 10px; width: 120px; text-align: right; font-size: 14px; /* 移除微调器 */ -moz-appearance: textfield; } .amount-input::-webkit-outer-spin-button, .amount-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .amount-input:focus { outline: none; border-color: #5d8fff; box-shadow: 0 0 5px rgba(93, 143, 255, 0.5); } .currency-display { color: #b0b0b0; font-size: 14px; min-width: 60px; } .form-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } .form-row:last-child { margin-bottom: 0; } .form-label { font-weight: 600; color: #b0b0b0; min-width: 100px; } .form-value { display: flex; align-items: center; gap: 10px; } .final-result { background: rgba(40, 45, 60, 0.8); border-radius: 6px; padding: 10px; margin-top: 10px; border: 1px solid rgba(100, 150, 255, 0.3); font-weight: bold; color: #5d8fff; } #customModal .modal-footer, #confirmModal .modal-footer, #otherRateConfirmModal .modal-footer { display: flex; justify-content: center; gap: 12px; } #customModal .btn, #confirmModal .btn, #otherRateConfirmModal .btn { padding: 12px 24px; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 600; border: none; min-height: 42px; min-width: 120px; display: flex; align-items: center; justify-content: center; white-space: nowrap; transition: all 0.3s ease; position: relative; overflow: hidden; } #customModal .btn::before, #confirmModal .btn::before, #otherRateConfirmModal .btn::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); transition: left 0.5s; } #customModal .btn:hover::before, #confirmModal .btn:hover::before, #otherRateConfirmModal .btn:hover::before { left: 100%; } #customModal .btn-primary, #confirmModal .btn-primary, #otherRateConfirmModal .btn-primary { background: linear-gradient(135deg, #4776E6, #8E54E9); color: white; box-shadow: 0 4px 15px rgba(71, 118, 230, 0.4); } #customModal .btn-primary:hover, #confirmModal .btn-primary:hover, #otherRateConfirmModal .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(71, 118, 230, 0.6); } #customModal .btn-secondary, #confirmModal .btn-secondary, #otherRateConfirmModal .btn-secondary { background: rgba(60, 65, 80, 0.9); color: #b0b0b0; border: 1px solid rgba(100, 150, 255, 0.3); } #customModal .btn-secondary:hover, #confirmModal .btn-secondary:hover, #otherRateConfirmModal .btn-secondary:hover { background: rgba(70, 75, 90, 0.9); color: #ffffff; transform: translateY(-2px); } /* 确认弹窗特定样式 */ #confirmModal .modal-body, #otherRateConfirmModal .modal-body { text-align: center; font-size: 14px; padding: 10px 0; } /* 响应式设计 - 仅针对小屏幕 */ @media (max-width: 900px) { #customModal .modal-content, #confirmModal .modal-content, #otherRateConfirmModal .modal-content { width: 95%; margin: 20px; padding: 20px; } #customModal .modal-body { flex-direction: column; } #customModal .modal-footer, #confirmModal .modal-footer, #otherRateConfirmModal .modal-footer { flex-direction: column; } #customModal .btn, #confirmModal .btn, #otherRateConfirmModal .btn { min-width: 100%; } .form-row { flex-direction: column; align-items: flex-start; gap: 5px; } .form-value { width: 100%; justify-content: space-between; } .deduction-details, .calculation-form { flex-basis: auto; min-width: auto; } } `); // Toast函数 function Toast(message, type, duration) { if (!document.getElementById('tech-toast-styles')) { const style = document.createElement('style'); style.id = 'tech-toast-styles'; style.textContent = `.tech-toast-container{position:fixed;top:30px;right:30px;z-index:10000;display:flex;flex-direction:column;gap:15px}.tech-toast{position:relative;min-width:300px;padding:18px 25px;background:rgba(15,20,35,0.95);border-radius:12px;box-shadow:0 5px 20px rgba(0,0,0,0.3),0 0 0 1px rgba(100,120,255,0.2),0 0 15px rgba(100,150,255,0.3);overflow:hidden;transform:translateX(120%);opacity:0;transition:transform 0.4s cubic-bezier(0.175,0.885,0.32,1.275),opacity 0.3s ease;display:flex;align-items:center;gap:15px}.tech-toast.show{transform:translateX(0);opacity:1}.tech-toast.hide{transform:translateX(120%);opacity:0}.toast-icon{width:40px;height:40px;display:flex;align-items:center;justify-content:center;border-radius:50%;flex-shrink:0;font-weight:bold;font-size:1.2rem}.success .toast-icon{background:rgba(46,204,113,0.15);color:#2ecc71}.error .toast-icon{background:rgba(231,76,60,0.15);color:#e74c3c}.warning .toast-icon{background:rgba(241,196,15,0.15);color:#f1c40f}.info .toast-icon{background:rgba(52,152,219,0.15);color:#3498db}.toast-content{flex:1;text-align:left}.toast-title{font-weight:600;font-size:1.1rem;margin-bottom:5px;color:#e0e0e0}.toast-message{font-size:14px;color:#a0b0c0;line-height:1.4}.progress-bar{position:absolute;bottom:0;left:0;height:4px;background:linear-gradient(90deg,#4776E6,#8E54E9);width:100%;transform-origin:left}.glow-effect{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:12px;box-shadow:inset 0 0 15px rgba(100,150,255,0.3);pointer-events:none;z-index:-1}.holographic-line{position:absolute;bottom:0;left:0;width:100%;height:1px;background:linear-gradient(90deg,transparent,#5d8fff,transparent);opacity:0.7}@media (max-width:600px){.tech-toast-container{top:20px;right:20px;left:20px}.tech-toast{min-width:auto;width:100%;padding:15px 20px}}`; document.head.appendChild(style); } let container = document.querySelector('.tech-toast-container'); if (!container) { container = document.createElement('div'); container.className = 'tech-toast-container'; document.body.appendChild(container); } const toast = document.createElement('div'); toast.className = `tech-toast ${type}`; const icons = {success: '✓', error: '✕', warning: '⚠', info: 'i'}; const titles = {success: '操作成功', error: '发生错误', warning: '警告', info: '信息'}; toast.innerHTML = `
${icons[type]}
${titles[type]}
${message}
`; container.appendChild(toast); setTimeout(() => { toast.classList.add('show'); const progressBar = toast.querySelector('.progress-bar'); progressBar.style.transform = 'scaleX(0)'; progressBar.style.transition = `transform ${duration}ms linear`; setTimeout(() => progressBar.style.transform = 'scaleX(1)', 10); setTimeout(() => { toast.classList.remove('show'); toast.classList.add('hide'); setTimeout(() => toast.remove(), 500); }, duration); }, 10); } // 触发原生decDelare函数 function triggerNativeDecDelare() { const decDelare = document.getElementById('decDelare'); if (decDelare) { // 创建并触发原生点击事件 const clickEvent = new MouseEvent('click', { view: window, bubbles: true, cancelable: true }); decDelare.dispatchEvent(clickEvent); // 如果元素有原生的onclick处理程序,也调用它 if (typeof decDelare.onclick === 'function') { decDelare.onclick(); } } } // 触发原生addHeadBtn函数 function triggerNativeAddHeadBtn() { const addHeadBtn = document.getElementById('addHeadBtn'); if (addHeadBtn) { // 创建并触发原生点击事件 const clickEvent = new MouseEvent('click', { view: window, bubbles: true, cancelable: true }); addHeadBtn.dispatchEvent(clickEvent); // 如果元素有原生的onclick处理程序,也调用它 if (typeof addHeadBtn.onclick === 'function') { addHeadBtn.onclick(); } } } // 从外部JSON加载扣减标准 function loadDeductionConfig() { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: DEDUCTION_CONFIG_URL, onload: function(response) { try { let responseText = response.responseText.trim(); // 如果以BOM开头,移除它 if (responseText.charCodeAt(0) === 0xFEFF) { responseText = responseText.substring(1); } // 尝试解析JSON const config = JSON.parse(responseText); if (Array.isArray(config) && config.length > 0) { resolve(config); } else { reject(new Error('扣减配置格式不正确,应为非空数组')); } } catch (error) { reject(new Error('解析扣减配置JSON失败: ' + error.message)); } }, onerror: function(error) { reject(new Error('加载扣减配置失败: ' + error.statusText)); } }); }); } // 解析集装箱表格,统计各种类型的集装箱数量 function parseContainerTable() { const table = document.getElementById('decContainerTable'); if (!table) { Toast('未找到集装箱表格', 'error', 3000); return null; } const rows = table.querySelectorAll('tbody tr'); if (rows.length === 0) { Toast('集装箱表格为空,请确认已正确录入集装箱信息', 'error', 3000); return null; } const containerStats = {}; rows.forEach(row => { const cells = row.querySelectorAll('td'); if (cells.length >= 3) { const containerType = cells[2].textContent.trim(); if (containerType) { if (!containerStats[containerType]) { containerStats[containerType] = 0; } containerStats[containerType]++; } } }); return containerStats; } // 获取港口名称 function getPortName() { const iEPortName = document.getElementById('iEPortName'); return iEPortName ? iEPortName.value.trim() : null; } // 获取目的港名称 function getDestPortName() { const customMasterName = document.getElementById('customMasterName'); return customMasterName ? customMasterName.value.trim() : null; } // 获取进口日期 function getImportDate() { const iEDate = document.getElementById('iEDate'); return iEDate ? iEDate.value.trim() : null; } // 日期格式转换和比较 function parseDate(dateStr) { if (!dateStr) return null; // 处理YYYY/MM/DD或YYYY-MM-DD格式 const parts = dateStr.split(/[/-]/); if (parts.length === 3) { const year = parseInt(parts[0]); const month = parseInt(parts[1]) - 1; // 月份从0开始 const day = parseInt(parts[2]); return new Date(year, month, day); } // 处理YYYYMMDD格式(如20261010) if (dateStr.length === 8 && /^\d{8}$/.test(dateStr)) { const year = parseInt(dateStr.substring(0, 4)); const month = parseInt(dateStr.substring(4, 6)) - 1; const day = parseInt(dateStr.substring(6, 8)); return new Date(year, month, day); } return null; } // 检查日期是否在范围内 function isDateInRange(dateStr, beginDateStr, endDateStr) { const date = parseDate(dateStr); const beginDate = parseDate(beginDateStr); const endDate = parseDate(endDateStr); if (!date || !beginDate || !endDate) return true; // 如果日期无效,默认通过 return date >= beginDate && date <= endDate; } // 获取杂费原值和币制 function getOtherRateInfo() { const otherRateInput = document.getElementById('otherRate'); const otherCurrNameInput = document.getElementById('otherCurrName'); return { originalRate: otherRateInput ? (parseFloat(otherRateInput.value) || 0) : 0, currency: otherCurrNameInput ? otherCurrNameInput.value.trim() : '人民币', rateElement: otherRateInput, currencyElement: otherCurrNameInput }; } // 计算扣减金额 function calculateDeduction(containerStats, deductionConfig) { if (!containerStats || !deductionConfig) { return { total: 0, details: [], portMatch: false }; } // 获取港口名称 const portName = getPortName(); if (!portName) { Toast('未找到港口名称,请确认已正确录入港口信息', 'error', 3000); return { total: 0, details: [], portMatch: false }; } // 获取目的港名称 const destPortName = getDestPortName(); if (!destPortName) { Toast('未找到目的港名称,请确认已正确录入目的港信息', 'error', 3000); return { total: 0, details: [], portMatch: false }; } // 获取进口日期 const importDate = getImportDate(); if (!importDate) { Toast('未找到进口日期,请确认已正确录入进口日期', 'error', 3000); return { total: 0, details: [], portMatch: false }; } // 检查港口名称、目的港名称和进口日期是否与扣减配置中的完全匹配 const matchedPortConfigs = deductionConfig.filter(item => item.entryport === portName && item.destport === destPortName && isDateInRange(importDate, item.begin_date, item.end_date) ); if (matchedPortConfigs.length === 0) { Toast(`进境口岸"${portName}"或申报口岸"${destPortName}"或进口日期"${importDate}"无扣减参数,请联系税管科`, 'error', 5000); return { total: 0, details: [], portMatch: false, portName: portName, destPortName: destPortName }; } let totalDeduction = 0; const deductionDetails = []; // 对每种集装箱类型计算扣减 Object.keys(containerStats).forEach(containerType => { const count = containerStats[containerType]; // 在匹配的港口配置中查找匹配的箱型 const matchedConfig = matchedPortConfigs.find(item => { // 箱型需要部分匹配 const typeMatch = containerType.includes(item.type) || item.type.includes(containerType); return typeMatch; }); if (matchedConfig) { const deduction = matchedConfig.price * count; totalDeduction += deduction; deductionDetails.push({ type: containerType, count: count, price: matchedConfig.price, deduction: deduction, port: portName, destPort: destPortName }); } else { // 如果没有匹配的配置,使用默认价格 const defaultPrice = 1350; // 默认价格 const deduction = defaultPrice * count; totalDeduction += deduction; deductionDetails.push({ type: containerType, count: count, price: defaultPrice, deduction: deduction, note: '(使用默认价格)', port: portName, destPort: destPortName }); } }); return { total: totalDeduction, details: deductionDetails, port: portName, destPort: destPortName, importDate: importDate, portMatch: true }; } // 生成扣减详情HTML function generateDeductionDetailsHTML(deductionResult) { if (!deductionResult || deductionResult.details.length === 0) { if (deductionResult && !deductionResult.portMatch) { return `
进境口岸"${deductionResult.portName}"或申报口岸"${deductionResult.destPortName}"无扣减参数,请联系税管科
`; } return '
未检测到可扣减的集装箱
'; } let html = '
'; // 显示港口信息和进口日期 - 修改为换行显示 html += `
进境口岸:${deductionResult.port}
申报口岸:${deductionResult.destPort}
进口日期:${deductionResult.importDate}
`; deductionResult.details.forEach(detail => { html += `
${detail.type} ${detail.count}箱 × ${detail.price}元 = ${detail.deduction}元 ${detail.note || ''}
`; }); // 修改总计扣减金额为可编辑输入框,单位改为元人民币 html += `
总计扣减金额
元人民币
`; return html; } // 生成计算表单HTML function generateCalculationFormHTML(otherRateInfo, deductionAmount) { const finalRate = otherRateInfo.originalRate - deductionAmount; return `
杂费原值:
${otherRateInfo.currency}
扣减金额:
元人民币
最终杂费:
人民币
最终结果:${finalRate.toFixed(2)} 人民币
`; } // 创建自定义弹窗 function createModals() { const modalHTML = `
`; // 将弹窗添加到页面 document.body.insertAdjacentHTML('beforeend', modalHTML); } // 检查条件并触发功能 function checkConditionsAndTrigger(deductionConfig) { const cusVoyageNoInput = document.getElementById('cusVoyageNo'); const cusTrafModeNameInput = document.getElementById('cusTrafModeName'); if (!cusVoyageNoInput || !cusTrafModeNameInput) { return false; } const voyageValue = cusVoyageNoInput.value; const trafModeValue = cusTrafModeNameInput.value; // 检查是否包含@符号且运输方式为水路运输 if (voyageValue.includes('@') && trafModeValue === '水路运输') { Toast('检测到内支线水路运输,触发扣减功能', 'info', 5000); // 解析集装箱表格 const containerStats = parseContainerTable(); if (!containerStats) { return false; } // 计算扣减金额 const deductionResult = calculateDeduction(containerStats, deductionConfig); // 如果港口不匹配,不显示扣减弹窗 if (!deductionResult.portMatch) { return; } // 获取杂费信息 const otherRateInfo = getOtherRateInfo(); // 显示自定义弹窗 const customModal = document.getElementById('customModal'); if (customModal) { // 设置弹窗顶部的通知 const modalNotice = document.getElementById('modalNotice'); if (modalNotice) { modalNotice.innerHTML = `
提示:因插件无法获取内支线目的港,以申报港口默认作为目的港扣减,下方扣减金额支持手工调整
`; } // 更新扣减详情显示 const deductionDetailsElement = document.getElementById('deductionDetails'); if (deductionDetailsElement) { deductionDetailsElement.innerHTML = generateDeductionDetailsHTML(deductionResult); } // 更新计算表单显示 const calculationFormElement = document.getElementById('calculationForm'); if (calculationFormElement) { calculationFormElement.innerHTML = generateCalculationFormHTML(otherRateInfo, deductionResult.total); } // 添加实时计算监听和联动逻辑 const totalDeductionInput = document.getElementById('totalDeductionInput'); const deductionAmountInput = document.getElementById('deductionAmountInput'); const originalRateInput = document.getElementById('originalRateInput'); const finalRateInput = document.getElementById('finalRateInput'); if (totalDeductionInput && deductionAmountInput && originalRateInput && finalRateInput) { // 初始化联动:设置扣减金额与总计扣减金额一致 deductionAmountInput.value = totalDeductionInput.value; const updateCalculation = () => { // 更新扣减金额与总计扣减金额保持一致 deductionAmountInput.value = totalDeductionInput.value; const originalRate = parseFloat(originalRateInput.value) || 0; const deductionAmount = parseFloat(totalDeductionInput.value) || 0; const finalRate = originalRate - deductionAmount; finalRateInput.value = finalRate.toFixed(2); // 更新最终结果显示 const finalResultElement = document.querySelector('.final-result'); if (finalResultElement) { finalResultElement.textContent = `最终结果:${finalRate.toFixed(2)} 人民币`; } }; // 监听总计扣减金额的变化 totalDeductionInput.addEventListener('input', updateCalculation); // 监听杂费原值的变化 originalRateInput.addEventListener('input', updateCalculation); } customModal.style.display = 'flex'; } return true; } return false; } // 检查decDelare点击前的条件 function checkDecDelareConditions() { const cusVoyageNoInput = document.getElementById('cusVoyageNo'); const noteS = document.getElementById('noteS'); if (!cusVoyageNoInput || !noteS) { return false; } const voyageValue = cusVoyageNoInput.value; const noteValue = noteS.value; // 检查是否包含@符号但不包含内河减字样 if (voyageValue.includes('@') && !noteValue.includes('内河减')) { return true; } return false; } // 执行扣减操作 function performDeduction() { // 获取用户修改后的扣减金额和杂费信息 const totalDeductionInput = document.getElementById('totalDeductionInput'); const originalRateInput = document.getElementById('originalRateInput'); if (!totalDeductionInput || !originalRateInput) { Toast('未找到扣减金额输入框', 'error', 3000); return false; } const deductionAmount = parseFloat(totalDeductionInput.value) || 0; const originalRate = parseFloat(originalRateInput.value) || 0; const finalRate = originalRate - deductionAmount; // 获取杂费输入框和币制输入框 const otherRateInfo = getOtherRateInfo(); if (!otherRateInfo.rateElement || !otherRateInfo.currencyElement) { Toast('未找到杂费或币制输入框', 'error', 3000); return false; } // 更新杂费值和币制(最终杂费使用人民币) otherRateInfo.rateElement.value = finalRate.toFixed(2); otherRateInfo.currencyElement.value = '人民币'; // 更新备注 const noteS = document.getElementById('noteS'); if (noteS) { const prefix = `内河减${deductionAmount.toFixed(2)}元。 `; noteS.value = prefix + (noteS.value || ''); } // 关闭弹窗 const customModal = document.getElementById('customModal'); if (customModal) { customModal.style.display = 'none'; } Toast(`扣减操作完成!杂费已更新为${finalRate.toFixed(2)} 人民币`, 'success', 5000); return true; } // 初始化监听 function initListener(deductionConfig) { const cusVoyageNoInput = document.getElementById('cusVoyageNo'); const cusTrafModeNameInput = document.getElementById('cusTrafModeName'); const decDelare = document.getElementById('decDelare'); const addHeadBtn = document.getElementById('addHeadBtn'); const customModal = document.getElementById('customModal'); const confirmModal = document.getElementById('confirmModal'); const otherRateConfirmModal = document.getElementById('otherRateConfirmModal'); const confirmBtn = document.getElementById('confirmBtn'); const cancelBtn = document.getElementById('cancelBtn'); const confirmProceedBtn = document.getElementById('confirmProceedBtn'); const confirmCancelBtn = document.getElementById('confirmCancelBtn'); const otherRateConfirmBtn = document.getElementById('otherRateConfirmBtn'); const otherRateCancelBtn = document.getElementById('otherRateCancelBtn'); if (!cusVoyageNoInput) { // 如果元素不存在,等待一段时间后重试 setTimeout(() => initListener(deductionConfig), 1000); return; } // 监听输入变化 cusVoyageNoInput.addEventListener('input', () => checkConditionsAndTrigger(deductionConfig)); if (cusTrafModeNameInput) { cusTrafModeNameInput.addEventListener('input', () => checkConditionsAndTrigger(deductionConfig)); } // 监听decDelare点击事件 if (decDelare) { // 使用命名函数以便后续移除 const handleDecDelareClick = function(event) { if (checkDecDelareConditions()) { // 阻止默认行为 event.preventDefault(); event.stopImmediatePropagation(); // 显示确认弹窗 if (confirmModal) { confirmModal.style.display = 'flex'; } return false; } // 校验通过,不阻止事件,让原生函数继续执行 }; // 使用捕获阶段确保我们的监听器先执行 decDelare.addEventListener('click', handleDecDelareClick, true); // 存储事件处理函数以便后续移除 window.decDelareHandler = handleDecDelareClick; } // 监听addHeadBtn点击事件 if (addHeadBtn) { // 使用命名函数以便后续移除 const handleAddHeadBtnClick = function(event) { if (checkDecDelareConditions()) { // 阻止默认行为 event.preventDefault(); event.stopImmediatePropagation(); // 显示确认弹窗 if (confirmModal) { confirmModal.style.display = 'flex'; } return false; } // 校验通过,不阻止事件,让原生函数继续执行 }; // 使用捕获阶段确保我们的监听器先执行 addHeadBtn.addEventListener('click', handleAddHeadBtnClick, true); // 存储事件处理函数以便后续移除 window.addHeadBtnHandler = handleAddHeadBtnClick; } // 确认弹窗的进行扣减按钮 if (confirmProceedBtn) { confirmProceedBtn.addEventListener('click', function() { if (confirmModal) { confirmModal.style.display = 'none'; } // 解析集装箱表格并计算扣减 const containerStats = parseContainerTable(); if (containerStats) { const deductionResult = calculateDeduction(containerStats, deductionConfig); // 如果港口不匹配,不显示扣减弹窗 if (!deductionResult.portMatch) { return; } // 获取杂费信息 const otherRateInfo = getOtherRateInfo(); // 设置弹窗顶部的通知 const modalNotice = document.getElementById('modalNotice'); if (modalNotice) { modalNotice.innerHTML = `
提示:因插件无法获取内支线目的港,以申报港口默认作为目的港扣减,下方扣减金额支持手工调整
`; } // 更新扣减详情显示 const deductionDetailsElement = document.getElementById('deductionDetails'); if (deductionDetailsElement) { deductionDetailsElement.innerHTML = generateDeductionDetailsHTML(deductionResult); } // 更新计算表单显示 const calculationFormElement = document.getElementById('calculationForm'); if (calculationFormElement) { calculationFormElement.innerHTML = generateCalculationFormHTML(otherRateInfo, deductionResult.total); } // 添加实时计算监听和联动逻辑 const totalDeductionInput = document.getElementById('totalDeductionInput'); const deductionAmountInput = document.getElementById('deductionAmountInput'); const originalRateInput = document.getElementById('originalRateInput'); const finalRateInput = document.getElementById('finalRateInput'); if (totalDeductionInput && deductionAmountInput && originalRateInput && finalRateInput) { // 初始化联动:设置扣减金额与总计扣减金额一致 deductionAmountInput.value = totalDeductionInput.value; const updateCalculation = () => { // 更新扣减金额与总计扣减金额保持一致 deductionAmountInput.value = totalDeductionInput.value; const originalRate = parseFloat(originalRateInput.value) || 0; const deductionAmount = parseFloat(totalDeductionInput.value) || 0; const finalRate = originalRate - deductionAmount; finalRateInput.value = finalRate.toFixed(2); // 更新最终结果显示 const finalResultElement = document.querySelector('.final-result'); if (finalResultElement) { finalResultElement.textContent = `最终结果:${finalRate.toFixed(2)} 人民币`; } }; // 监听总计扣减金额的变化 totalDeductionInput.addEventListener('input', updateCalculation); // 监听杂费原值的变化 originalRateInput.addEventListener('input', updateCalculation); } if (customModal) { customModal.style.display = 'flex'; } } }); } // 确认弹窗的直接申报按钮 - 点击后触发原生decDelare函数并取消校验绑定 if (confirmCancelBtn) { confirmCancelBtn.addEventListener('click', function() { if (confirmModal) { confirmModal.style.display = 'none'; } // 移除对decDelare的校验绑定 const decDelare = document.getElementById('decDelare'); if (decDelare && window.decDelareHandler) { decDelare.removeEventListener('click', window.decDelareHandler, true); } // 移除对addHeadBtn的校验绑定 const addHeadBtn = document.getElementById('addHeadBtn'); if (addHeadBtn && window.addHeadBtnHandler) { addHeadBtn.removeEventListener('click', window.addHeadBtnHandler, true); } // 触发原生的decDelare函数 triggerNativeDecDelare(); }); } // 确认按钮点击事件 if (confirmBtn) { confirmBtn.addEventListener('click', function() { performDeduction(); }); } // 取消按钮点击事件 if (cancelBtn) { cancelBtn.addEventListener('click', function() { const customModal = document.getElementById('customModal'); if (customModal) { customModal.style.display = 'none'; } }); } // 确认申报无误按钮 - 点击后继续扣减操作并触发decDelare if (otherRateConfirmBtn) { otherRateConfirmBtn.addEventListener('click', function() { if (otherRateConfirmModal) { otherRateConfirmModal.style.display = 'none'; } // 无论扣减操作是否成功,都触发decDelare原生函数 triggerNativeDecDelare(); // 继续执行扣减操作 performDeduction(); }); } // 补充申报按钮 if (otherRateCancelBtn) { otherRateCancelBtn.addEventListener('click', function() { if (otherRateConfirmModal) { otherRateConfirmModal.style.display = 'none'; } // 用户可以手动补充杂费 Toast('请补充杂费信息', 'info', 3000); }); } // 点击弹窗外区域关闭弹窗 if (customModal) { customModal.addEventListener('click', function(e) { if (e.target === customModal) { customModal.style.display = 'none'; } }); } if (confirmModal) { confirmModal.addEventListener('click', function(e) { if (e.target === confirmModal) { confirmModal.style.display = 'none'; } }); } if (otherRateConfirmModal) { otherRateConfirmModal.addEventListener('click', function(e) { if (e.target === otherRateConfirmModal) { otherRateConfirmModal.style.display = 'none'; } }); } } // 主初始化函数 async function main() { try { // 从外部JSON加载扣减标准 const deductionConfig = await loadDeductionConfig(); // 创建弹窗 createModals(); // 初始化监听器 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => initListener(deductionConfig)); } else { initListener(deductionConfig); } // 监听动态内容加载(适用于单页面应用) const observer = new MutationObserver(function(mutations) { for (let mutation of mutations) { if (mutation.type === 'childList') { for (let node of mutation.addedNodes) { if (node.nodeType === 1) { // Element node if (node.id === 'cusVoyageNo' || node.querySelector('#cusVoyageNo') || node.id === 'decDelare' || node.querySelector('#decDelare') || node.id === 'addHeadBtn' || node.querySelector('#addHeadBtn') || node.id === 'decContainerTable' || node.querySelector('#decContainerTable') || node.id === 'iEPortName' || node.querySelector('#iEPortName') || node.id === 'customMasterName' || node.querySelector('#customMasterName') || node.id === 'iEDate' || node.querySelector('#iEDate') || node.id === 'otherRate' || node.querySelector('#otherRate') || node.id === 'otherCurrName' || node.querySelector('#otherCurrName')) { initListener(deductionConfig); break; } } } } } }); observer.observe(document.body, { childList: true, subtree: true }); } catch (error) { Toast('加载扣减配置失败: ' + error.message, 'error', 5000); console.error('扣减配置加载失败:', error); } } // 启动主函数 main(); })();