// ==UserScript==
// @name 单一窗口-内河运费自动化扣减
// @namespace //
// @version 1.27
// @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/neihev2.json";
// 添加自定义样式
GM_addStyle(`
/* 科技风格弹窗样式 - 不影响页面原有样式 */
#customModal, #confirmModal, #otherRateConfirmModal, #otherMarkConfirmModal {
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;
}
#customModal .modal-content, #confirmModal .modal-content, #otherRateConfirmModal .modal-content, #otherMarkConfirmModal .modal-content {
position: relative;
width: auto;
max-width: 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;
}
#customModal .modal-header, #confirmModal .modal-header, #otherRateConfirmModal .modal-header, #otherMarkConfirmModal .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, #otherMarkConfirmModal .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, #otherMarkConfirmModal .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;
gap: 20px;
align-items: stretch;
}
#confirmModal .modal-body, #otherRateConfirmModal .modal-body, #otherMarkConfirmModal .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;
display: flex;
flex-direction: column;
}
.yellow-confirm-notice {
background: rgba(255, 193, 7, 0.1) !important;
border: 1px solid rgba(255, 193, 7, 0.3) !important;
border-radius: 6px !important;
color: #ffc107 !important;
padding: 15px !important;
margin: 10px 0 !important;
font-weight: bold !important;
}
.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;
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, #otherMarkConfirmModal .modal-footer {
display: flex;
justify-content: center;
gap: 12px;
}
#customModal .btn, #confirmModal .btn, #otherRateConfirmModal .btn, #otherMarkConfirmModal .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, #otherMarkConfirmModal .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, #otherMarkConfirmModal .btn:hover::before {
left: 100%;
}
#customModal .btn-primary, #confirmModal .btn-primary, #otherRateConfirmModal .btn-primary, #otherMarkConfirmModal .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, #otherMarkConfirmModal .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, #otherMarkConfirmModal .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, #otherMarkConfirmModal .btn-secondary:hover {
background: rgba(70, 75, 90, 0.9);
color: #ffffff;
transform: translateY(-2px);
}
#confirmModal .modal-body, #otherRateConfirmModal .modal-body, #otherMarkConfirmModal .modal-body {
text-align: center;
font-size: 14px;
padding: 10px 0;
}
@media (max-width: 900px) {
#customModal .modal-content, #confirmModal .modal-content, #otherRateConfirmModal .modal-content, #otherMarkConfirmModal .modal-content {
width: 95%;
margin: 20px;
padding: 20px;
}
#customModal .modal-body {
flex-direction: column;
}
#customModal .modal-footer, #confirmModal .modal-footer, #otherRateConfirmModal .modal-footer, #otherMarkConfirmModal .modal-footer {
flex-direction: column;
}
#customModal .btn, #confirmModal .btn, #otherRateConfirmModal .btn, #otherMarkConfirmModal .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);
}
// 设置隐藏元素值
function setHiddenFieldValues() {
// 设置otherCurr为"CNY"
const otherCurrInput = document.getElementById("otherCurr");
if (otherCurrInput) {
otherCurrInput.value = "CNY";
}
// 设置otherMark为"3"
const otherMarkInput = document.getElementById("otherMark");
if (otherMarkInput) {
otherMarkInput.value = "3";
}
// 设置otherCurrName为"人民币"
const otherCurrNameInput = document.getElementById("otherCurrName");
if (otherCurrNameInput) {
otherCurrNameInput.value = "人民币";
}
// 设置otherMarkName为"总价"
const otherMarkNameInput = document.getElementById("otherMarkName");
if (otherMarkNameInput) {
otherMarkNameInput.value = "总价";
}
}
// 触发原生decDelare函数
function triggerNativeDecDelare() {
const decDelare = document.getElementById("decDelare");
if (decDelare) {
const clickEvent = new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: true,
});
decDelare.dispatchEvent(clickEvent);
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);
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();
if (responseText.charCodeAt(0) === 0xfeff) {
responseText = responseText.substring(1);
}
const config = JSON.parse(responseText);
console.log(config);
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;
const parts = dateStr.split(/[/-]/);
if (parts.length === 3) {
const year = parseInt(parts[0]);
const month = parseInt(parts[1]) - 1;
const day = parseInt(parts[2]);
return new Date(year, month, day);
}
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 isOtherMarkNameEmpty() {
const otherMarkNameInput = document.getElementById("otherMarkName");
return otherMarkNameInput && !otherMarkNameInput.value.trim();
}
// 计算扣减金额
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) => {
console.log(item);
const entryMatch = item.entryport === portName;
const destMatch = Array.isArray(item.destport)
? item.destport.includes(destPortName)
: item.destport === destPortName;
const dateMatch = isDateInRange(
importDate,
item.begin_date,
item.end_date
);
return entryMatch && destMatch && dateMatch;
});
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 = `
检测到货物内支线运输,但未申报扣减,是否马上开始进行扣减操作?
请确认
1.目前提供的CIF/C&F货值包含的运费或FOB/EXW单独申报的运费为启运地至烟台的运费;
2.该票货物为整柜(非拼箱货)。
检测到未申报任何杂费,系统将自动置入‘总价’及扣减费用,是否确认?
`;
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);
// 设置隐藏元素值
setHiddenFieldValues();
// 检查otherMarkName是否为空
if (isOtherMarkNameEmpty()) {
const otherMarkConfirmModal = document.getElementById(
"otherMarkConfirmModal"
);
if (otherMarkConfirmModal) {
otherMarkConfirmModal.style.display = "flex";
}
return true;
}
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() {
// 设置隐藏元素值
setHiddenFieldValues();
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 = "人民币";
// 设置otherMarkName为"总价"
const otherMarkNameInput = document.getElementById("otherMarkName");
if (otherMarkNameInput) {
otherMarkNameInput.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 otherMarkConfirmModal = document.getElementById(
"otherMarkConfirmModal"
);
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");
const otherMarkConfirmBtn = document.getElementById("otherMarkConfirmBtn");
const otherMarkCancelBtn = document.getElementById("otherMarkCancelBtn");
if (!cusVoyageNoInput) {
setTimeout(() => initListener(deductionConfig), 1000);
return;
}
cusVoyageNoInput.addEventListener("input", () =>
checkConditionsAndTrigger(deductionConfig)
);
if (cusTrafModeNameInput) {
cusTrafModeNameInput.addEventListener("input", () =>
checkConditionsAndTrigger(deductionConfig)
);
}
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;
}
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";
}
}
});
}
if (confirmCancelBtn) {
confirmCancelBtn.addEventListener("click", function () {
if (confirmModal) {
confirmModal.style.display = "none";
}
const decDelare = document.getElementById("decDelare");
if (decDelare && window.decDelareHandler) {
decDelare.removeEventListener("click", window.decDelareHandler, true);
}
const addHeadBtn = document.getElementById("addHeadBtn");
if (addHeadBtn && window.addHeadBtnHandler) {
addHeadBtn.removeEventListener(
"click",
window.addHeadBtnHandler,
true
);
}
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";
}
});
}
if (otherRateConfirmBtn) {
otherRateConfirmBtn.addEventListener("click", function () {
if (otherRateConfirmModal) {
otherRateConfirmModal.style.display = "none";
}
triggerNativeDecDelare();
performDeduction();
});
}
if (otherRateCancelBtn) {
otherRateCancelBtn.addEventListener("click", function () {
if (otherRateConfirmModal) {
otherRateConfirmModal.style.display = "none";
}
Toast("请补充杂费信息", "info", 3000);
});
}
if (otherMarkConfirmBtn) {
otherMarkConfirmBtn.addEventListener("click", function () {
if (otherMarkConfirmModal) {
otherMarkConfirmModal.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";
}
}
});
}
if (otherMarkCancelBtn) {
otherMarkCancelBtn.addEventListener("click", function () {
if (otherMarkConfirmModal) {
otherMarkConfirmModal.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";
}
});
}
if (otherMarkConfirmModal) {
otherMarkConfirmModal.addEventListener("click", function (e) {
if (e.target === otherMarkConfirmModal) {
otherMarkConfirmModal.style.display = "none";
}
});
}
}
// 主初始化函数
async function main() {
try {
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) {
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 === "otherCurr" ||
node.querySelector("#otherCurr") ||
node.id === "otherCurrName" ||
node.querySelector("#otherCurrName") ||
node.id === "otherMark" ||
node.querySelector("#otherMark") ||
node.id === "otherMarkName" ||
node.querySelector("#otherMarkName")
) {
initListener(deductionConfig);
break;
}
}
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
} catch (error) {
Toast("加载扣减配置失败: " + error.message, "error", 5000);
console.error("扣减配置加载失败:", error);
}
}
// 启动主函数
main();
})();