// ==UserScript==
// @name 单一窗口-内河运费自动化扣减
// @namespace //
// @version 1.21
// @description 监听航次号变化并执行扣减操作,从外部JSON加载扣减标准,解析集装箱表格,自动计算扣减,支持用户修改扣减金额和币制
// @match https://swapp.singlewindow.cn/*
// @match https://www.singlewindow.sd.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 `
`;
}
// 创建自定义弹窗
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();
})();