// ==UserScript== // @name 运盟调度一键复制 // @namespace http://tampermonkey.net/ // @license MIT // @version 2025-10-15-02 // @description 支持运盟系统内车签号和运单号查询复制,方便运盟调度制作每日进港车辆表。 // @author 圆通华北中心一期调度室 // @match https://diaodu.yonmen.com/* // @grant GM_xmlhttpRequest // @grant GM_setClipboard // ==/UserScript== (function() { 'use strict'; // 缓存对象 const cache = { token: null, tokenExpiry: 0, transportInfo: new Map(), detailInfo: new Map() }; // DOM元素缓存 let uiElements = null; // 等待页面加载完成(优化版) function waitForElement(selector, timeout = 5000) { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) { resolve(element); return; } const observer = new MutationObserver((mutations, obs) => { const element = document.querySelector(selector); if (element) { obs.disconnect(); resolve(element); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); reject(new Error('Element not found within timeout')); }, timeout); }); } // 创建UI界面 function createUI() { const container = document.createElement('div'); container.style.cssText = ` position: fixed; bottom: 20px; left: 20px; background: white; border: 1px solid #007bff; border-radius: 6px; padding: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10000; font-family: Arial, sans-serif; width: 150px; font-size: 12px; `; container.innerHTML = `
运盟调度复制工具
`; document.body.appendChild(container); // 缓存UI元素 uiElements = { container, button: container.querySelector('#copyButton'), clearButton: container.querySelector('#clearButton'), input: container.querySelector('#queryNumberInput'), status: container.querySelector('#statusDiv'), queryTypeSelect: container.querySelector('#queryTypeSelect') }; // 添加清空按钮点击事件监听 uiElements.clearButton.addEventListener('click', function() { uiElements.input.value = ''; uiElements.input.focus(); showStatus('输入框已清空', 'info'); // 清空按钮悬停效果 setTimeout(() => { if (uiElements.status) { uiElements.status.style.display = 'none'; } }, 1500); }); // 添加清空按钮悬停效果 uiElements.clearButton.addEventListener('mouseenter', () => { uiElements.clearButton.style.background = '#5a6268'; }); uiElements.clearButton.addEventListener('mouseleave', () => { uiElements.clearButton.style.background = '#6c757d'; }); // 添加查询类型变化事件监听 uiElements.queryTypeSelect.addEventListener('change', function() { const queryType = this.value; if (queryType === 'cq') { uiElements.input.placeholder = '输入车签号码'; } else if (queryType === 'trans') { uiElements.input.placeholder = '输入运单号码'; } uiElements.input.value = ''; // 清空输入框 }); // 添加按钮悬停效果 uiElements.button.addEventListener('mouseenter', () => { uiElements.button.style.background = '#0056b3'; }); uiElements.button.addEventListener('mouseleave', () => { uiElements.button.style.background = '#007bff'; }); return container; } // 显示状态信息(优化版) function showStatus(message, isError = false) { if (!uiElements) return; const statusDiv = uiElements.status; statusDiv.style.display = 'block'; statusDiv.style.background = isError ? '#f8d7da' : '#d4edda'; statusDiv.style.color = isError ? '#721c24' : '#155724'; statusDiv.style.border = isError ? '1px solid #f5c6cb' : '1px solid #c3e6cb'; statusDiv.textContent = message; // 3秒后自动隐藏 setTimeout(() => { if (statusDiv) statusDiv.style.display = 'none'; }, 3000); } // 获取当前页面的token(带缓存) function getToken() { // 检查缓存 if (cache.token && Date.now() < cache.tokenExpiry) { return cache.token; } let token = null; // 尝试从localStorage获取token token = localStorage.getItem('token') || sessionStorage.getItem('token'); if (token) { token = token.replace(/['"]/g, ''); if (token.length === 32) { cache.token = token; cache.tokenExpiry = Date.now() + 300000; // 缓存5分钟 return token; } } // 尝试从cookie获取token const cookies = document.cookie.split(';'); for (let cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'token' && value && value.length === 32) { cache.token = value; cache.tokenExpiry = Date.now() + 300000; return value; } } // 尝试从页面的全局变量获取token if (window.localStorage) { for (let i = 0; i < window.localStorage.length; i++) { const key = window.localStorage.key(i); const value = window.localStorage.getItem(key); if (key && key.toLowerCase().includes('token') && value && value.length === 32) { token = value.replace(/['"]/g, ''); cache.token = token; cache.tokenExpiry = Date.now() + 300000; return token; } } } // 默认token token = '96926dd287ca485aabff7e9300453918'; cache.token = token; cache.tokenExpiry = Date.now() + 300000; return token; } // 发送API请求获取运输信息(带缓存和超时控制,支持车签号和运单号查询) function fetchTransportInfo(queryNumber, token, queryType = 'cq') { // 检查缓存 const cacheKey = `${queryType}_${queryNumber}_${token}`; if (cache.transportInfo.has(cacheKey)) { return Promise.resolve(cache.transportInfo.get(cacheKey)); } return new Promise((resolve, reject) => { let url; if (queryType === 'cq') { // 车签号查询 url = `https://diaodu.yonmen.com/v1/transports/web?date_search_type=1&group_status=&trans_number=&cq_number=${queryNumber}&plate=&vehicle_type=&start_code=&start_name=&run_mode=&end_code=&end_name=&is_bind_driver=&safety_status=&page_index=1&page_size=10&token=${token}`; } else if (queryType === 'trans') { // 运单号查询 url = `https://diaodu.yonmen.com/v1/transports/web?date_search_type=1&group_status=&trans_number=${queryNumber}&cq_number=&plate=&vehicle_type=&start_code=&start_name=&run_mode=&end_code=&end_name=&is_bind_driver=&safety_status=&page_index=1&page_size=10&token=${token}`; } else { reject(new Error('不支持的查询类型')); return; } // 设置10秒超时 const timeoutId = setTimeout(() => { reject(new Error('请求超时')); }, 10000); GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, timeout: 10000, onload: function(response) { clearTimeout(timeoutId); try { const data = JSON.parse(response.responseText); if (data.code === 1000 && data.data && data.data.trans && data.data.trans.length > 0) { const result = data.data.trans[0]; // 缓存结果 cache.transportInfo.set(cacheKey, result); resolve(result); } else { reject(new Error(`未找到${queryType === 'cq' ? '车签号' : '运单号'}为 ${queryNumber} 的运输信息`)); } } catch (e) { reject(new Error('解析响应数据失败')); } }, onerror: function() { clearTimeout(timeoutId); reject(new Error('请求失败')); }, ontimeout: function() { clearTimeout(timeoutId); reject(new Error('请求超时')); } }); }); } // 获取详情页面信息(优化版) function fetchDetailInfo(transNumber, token) { // 检查缓存 const cacheKey = `${transNumber}_${token}`; if (cache.detailInfo.has(cacheKey)) { return Promise.resolve(cache.detailInfo.get(cacheKey)); } return new Promise((resolve, reject) => { const detailApiUrl = `https://diaodu.yonmen.com/v1/transports/detail?trans_number=${transNumber}&token=${token}`; // 设置10秒超时 const timeoutId = setTimeout(() => { // API超时后直接尝试iframe方式,但减少等待时间 fetchDetailInfoByIframe(transNumber, 8000).then(result => { cache.detailInfo.set(cacheKey, result); resolve(result); }).catch(reject); }, 10000); GM_xmlhttpRequest({ method: 'GET', url: detailApiUrl, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, timeout: 10000, onload: function(response) { clearTimeout(timeoutId); try { const data = JSON.parse(response.responseText); if (data.code === 1000 && data.data) { let yjTime = ''; let sjTime = ''; const nodes = data.data.nodes || []; for (let node of nodes) { if (node.address && node.address.includes('河北省廊坊市永清县工业园区小良村一纬道')) { yjTime = node.plan_time || node.expect_time || ''; sjTime = node.actual_time || node.reach_time || ''; break; } } if (!yjTime || !sjTime) { const detail = data.data; yjTime = yjTime || detail.plan_reach_time || detail.expect_reach_time || ''; sjTime = sjTime || detail.actual_reach_time || detail.reach_time || ''; } const result = { yjTime, sjTime }; cache.detailInfo.set(cacheKey, result); resolve(result); } else { // API返回错误,尝试iframe fetchDetailInfoByIframe(transNumber, 8000).then(result => { cache.detailInfo.set(cacheKey, result); resolve(result); }).catch(reject); } } catch (e) { // API解析失败,尝试iframe fetchDetailInfoByIframe(transNumber, 8000).then(result => { cache.detailInfo.set(cacheKey, result); resolve(result); }).catch(reject); } }, onerror: function() { clearTimeout(timeoutId); fetchDetailInfoByIframe(transNumber, 8000).then(result => { cache.detailInfo.set(cacheKey, result); resolve(result); }).catch(reject); }, ontimeout: function() { clearTimeout(timeoutId); fetchDetailInfoByIframe(transNumber, 8000).then(result => { cache.detailInfo.set(cacheKey, result); resolve(result); }).catch(reject); } }); }); } // 通过iframe获取详情页面信息(优化版 - 减少等待时间) function fetchDetailInfoByIframe(transNumber, maxTimeout = 8000) { return new Promise((resolve, reject) => { const detailUrl = `https://diaodu.yonmen.com/#/app/orderDetail?trans_number=${transNumber}`; const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = detailUrl; let timeoutId = setTimeout(() => { if (iframe.parentNode) { document.body.removeChild(iframe); } reject(new Error('获取详情信息超时')); }, maxTimeout); let checkCount = 0; const maxChecks = 8; // 最多检查8次 const checkContent = () => { checkCount++; try { const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; const elements = iframeDoc.querySelectorAll('.formItem-text'); if (elements.length > 0) { let yjTime = ''; let sjTime = ''; let foundTarget = false; for (let element of elements) { const text = element.textContent || element.innerText; if (text.includes('河北省廊坊市永清县工业园区小良村一纬道')) { foundTarget = true; const parent = element.closest('.k-row'); if (parent) { const timeElements = parent.querySelectorAll('.formItem-text'); for (let timeEl of timeElements) { const timeText = timeEl.textContent || timeEl.innerText; if (timeText.includes('预计到车时间:')) { yjTime = timeText.replace('预计到车时间:', '').trim(); } if (timeText.includes('实际到车时间:')) { sjTime = timeText.replace('实际到车时间:', '').trim(); } } } break; } } if (foundTarget && (yjTime || sjTime)) { clearTimeout(timeoutId); if (iframe.parentNode) { document.body.removeChild(iframe); } resolve({ yjTime, sjTime }); return; } } // 如果还没找到且未超过最大检查次数,继续检查 if (checkCount < maxChecks) { setTimeout(checkContent, 1000); } else { clearTimeout(timeoutId); if (iframe.parentNode) { document.body.removeChild(iframe); } reject(new Error('未找到目标地址或时间信息')); } } catch (e) { if (checkCount < maxChecks) { setTimeout(checkContent, 1000); } else { clearTimeout(timeoutId); if (iframe.parentNode) { document.body.removeChild(iframe); } reject(new Error('获取详情页面信息失败: ' + e.message)); } } }; iframe.onload = function() { // 页面加载完成后等待1秒再开始检查 setTimeout(checkContent, 1000); }; iframe.onerror = function() { clearTimeout(timeoutId); if (iframe.parentNode) { document.body.removeChild(iframe); } reject(new Error('加载详情页面失败')); }; document.body.appendChild(iframe); }); } // 初始化 function init() { waitForElement('body').then(() => { createUI(); uiElements.button.addEventListener('click', async function() { const queryNumber = uiElements.input.value.trim().replace(/[^\w\-]/g, ''); // 去除空格和特殊符号,只保留字母、数字、下划线和连字符 const queryType = uiElements.queryTypeSelect.value; if (!queryNumber) { showStatus(`请输入${queryType === 'cq' ? '车签号码' : '运单号码'}`, true); return; } // 禁用按钮防止重复点击 uiElements.button.disabled = true; uiElements.button.style.opacity = '0.6'; uiElements.button.textContent = '查询中...'; try { showStatus('正在获取数据...'); const token = getToken(); console.log('当前获取的token值:', token); showStatus('正在获取运输信息...'); const transportInfo = await fetchTransportInfo(queryNumber, token, queryType); console.log('获取到的运输信息:', transportInfo); // 添加调试日志 const { trans_number, line_name, create_date, vehicle_type, cq_number } = transportInfo; showStatus('正在获取详情信息...'); const detailInfo = await fetchDetailInfo(trans_number, token); const { yjTime, sjTime } = detailInfo; // 修复:无论查询类型如何,都应该保存车签号到剪贴板 const cqNumberForClipboard = cq_number || queryNumber; // 优先使用API返回的车签号,如果没有则使用查询号码 const result = `${cqNumberForClipboard}\t${create_date}\t${line_name}\t${vehicle_type}\t${yjTime}\t${sjTime}`; GM_setClipboard(result); showStatus('复制成功!信息已保存到剪贴板'); } catch (error) { console.error('操作失败:', error); showStatus(`错误: ${error.message}`, true); } finally { // 恢复按钮状态 if (uiElements.button) { uiElements.button.disabled = false; uiElements.button.style.opacity = '1'; uiElements.button.textContent = '一键复制'; } } }); uiElements.input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { uiElements.button.click(); } }); console.log('永门调度一键复制工具已加载(性能优化版)'); }).catch(error => { console.error('初始化失败:', error); }); } // 页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();