// ==UserScript== // @name saleorder // @namespace https://scriptcat.org/zh-CN/script-show-page/3813 // @version 1.9 // @description 在'销售订单'增加功能,对外版本 // @author fengxia // @match http://122.13.25.247:8888/sales/orderPage.do* // @match http://47.107.106.156:8888/sales/orderPage.do* // @icon data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDEiIGhlaWdodD0iNDEiIHZpZXdCb3g9IjAgMCA0MSA0MSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBzdHJva2Utd2lkdGg9IjEuNSIgY2xhc3M9ImgtNiB3LTYiIHJvbGU9ImltZyI+PHRpdGxlPkNoYXRHUFQ8L3RpdGxlPjx0ZXh0IHg9Ii05OTk5IiB5PSItOTk5OSI+Q2hhdEdQVDwvdGV4dD48cGF0aCBkPSJNMzcuNTMyNCAxNi44NzA3QzM3Ljk4MDggMTUuNTI0MSAzOC4xMzYzIDE0LjA5NzQgMzcuOTg4NiAxMi42ODU5QzM3Ljg0MDkgMTEuMjc0NCAzNy4zOTM0IDkuOTEwNzYgMzYuNjc2IDguNjg2MjJDMzUuNjEyNiA2LjgzNDA0IDMzLjk4ODIgNS4zNjc2IDMyLjAzNzMgNC40OTg1QzMwLjA4NjQgMy42Mjk0MSAyNy45MDk4IDMuNDAyNTkgMjUuODIxNSAzLjg1MDc4QzI0Ljg3OTYgMi43ODkzIDIzLjcyMTkgMS45NDEyNSAyMi40MjU3IDEuMzYzNDFDMjEuMTI5NSAwLjc4NTU3NSAxOS43MjQ5IDAuNDkxMjY5IDE4LjMwNTggMC41MDAxOTdDMTYuMTcwOCAwLjQ5NTA0NCAxNC4wODkzIDEuMTY4MDMgMTIuMzYxNCAyLjQyMjE0QzEwLjYzMzUgMy42NzYyNCA5LjM0ODUzIDUuNDQ2NjYgOC42OTE3IDcuNDc4MTVDNy4zMDA4NSA3Ljc2Mjg2IDUuOTg2ODYgOC4zNDE0IDQuODM3NyA5LjE3NTA1QzMuNjg4NTQgMTAuMDA4NyAyLjczMDczIDExLjA3ODIgMi4wMjgzOSAxMi4zMTJDMC45NTY0NjQgMTQuMTU5MSAwLjQ5ODkwNSAxNi4yOTg4IDAuNzIxNjk4IDE4LjQyMjhDMC45NDQ0OTIgMjAuNTQ2NyAxLjgzNjEyIDIyLjU0NDkgMy4yNjggMjQuMTI5M0MyLjgxOTY2IDI1LjQ3NTkgMi42NjQxMyAyNi45MDI2IDIuODExODIgMjguMzE0MUMyLjk1OTUxIDI5LjcyNTYgMy40MDcwMSAzMS4wODkyIDQuMTI0MzcgMzIuMzEzOEM1LjE4NzkxIDM0LjE2NTkgNi44MTIzIDM1LjYzMjIgOC43NjMyMSAzNi41MDEzQzEwLjcxNDEgMzcuMzcwNCAxMi44OTA3IDM3LjU5NzMgMTQuOTc4OSAzNy4xNDkyQzE1LjkyMDggMzguMjEwNyAxNy4wNzg2IDM5LjA1ODcgMTguMzc0NyAzOS42MzY2QzE5LjY3MDkgNDAuMjE0NCAyMS4wNzU1IDQwLjUwODcgMjIuNDk0NiA0MC40OTk4QzI0LjYzMDcgNDAuNTA1NCAyNi43MTMzIDM5LjgzMjEgMjguNDQxOCAzOC41NzcyQzMwLjE3MDQgMzcuMzIyMyAzMS40NTU2IDM1LjU1MDYgMzIuMTExOSAzMy41MTc5QzMzLjUwMjcgMzMuMjMzMiAzNC44MTY3IDMyLjY1NDcgMzUuOTY1OSAzMS44MjFDMzcuMTE1IDMwLjk4NzQgMzguMDcyOCAyOS45MTc4IDM4Ljc3NTIgMjguNjg0QzM5Ljg0NTggMjYuODM3MSA0MC4zMDIzIDI0LjY5NzkgNDAuMDc4OSAyMi41NzQ4QzM5Ljg1NTYgMjAuNDUxNyAzOC45NjM5IDE4LjQ1NDQgMzcuNTMyNCAxNi44NzA3Wk0yMi40OTc4IDM3Ljg4NDlDMjAuNzQ0MyAzNy44ODc0IDE5LjA0NTkgMzcuMjczMyAxNy42OTk0IDM2LjE1MDFDMTcuNzYwMSAzNi4xMTcgMTcuODY2NiAzNi4wNTg2IDE3LjkzNiAzNi4wMTYxTDI1LjkwMDQgMzEuNDE1NkMyNi4xMDAzIDMxLjMwMTkgMjYuMjY2MyAzMS4xMzcgMjYuMzgxMyAzMC45Mzc4QzI2LjQ5NjQgMzAuNzM4NiAyNi41NTYzIDMwLjUxMjQgMjYuNTU0OSAzMC4yODI1VjE5LjA1NDJMMjkuOTIxMyAyMC45OThDMjkuOTM4OSAyMS4wMDY4IDI5Ljk1NDEgMjEuMDE5OCAyOS45NjU2IDIxLjAzNTlDMjkuOTc3IDIxLjA1MiAyOS45ODQyIDIxLjA3MDcgMjkuOTg2NyAyMS4wOTAyVjMwLjM4ODlDMjkuOTg0MiAzMi4zNzUgMjkuMTk0NiAzNC4yNzkxIDI3Ljc5MDkgMzUuNjg0MUMyNi4zODcyIDM3LjA4OTIgMjQuNDgzOCAzNy44ODA2IDIyLjQ5NzggMzcuODg0OVpNNi4zOTIyNyAzMS4wMDY0QzUuNTEzOTcgMjkuNDg4OCA1LjE5NzQyIDI3LjcxMDcgNS40OTgwNCAyNS45ODMyQzUuNTU3MTggMjYuMDE4NyA1LjY2MDQ4IDI2LjA4MTggNS43MzQ2MSAyNi4xMjQ0TDEzLjY5OSAzMC43MjQ4QzEzLjg5NzUgMzAuODQwOCAxNC4xMjMzIDMwLjkwMiAxNC4zNTMyIDMwLjkwMkMxNC41ODMgMzAuOTAyIDE0LjgwODggMzAuODQwOCAxNS4wMDczIDMwLjcyNDhMMjQuNzMxIDI1LjExMDNWMjguOTk3OUMyNC43MzIxIDI5LjAxNzcgMjQuNzI4MyAyOS4wMzc2IDI0LjcxOTkgMjkuMDU1NkMyNC43MTE1IDI5LjA3MzYgMjQuNjk4OCAyOS4wODkzIDI0LjY4MjkgMjkuMTAxMkwxNi42MzE3IDMzLjc0OTdDMTQuOTA5NiAzNC43NDE2IDEyLjg2NDMgMzUuMDA5NyAxMC45NDQ3IDM0LjQ5NTRDOS4wMjUwNiAzMy45ODExIDcuMzg3ODUgMzIuNzI2MyA2LjM5MjI3IDMxLjAwNjRaTTQuMjk3MDcgMTMuNjE5NEM1LjE3MTU2IDEyLjA5OTggNi41NTI3OSAxMC45MzY0IDguMTk4ODUgMTAuMzMyN0M4LjE5ODg1IDEwLjQwMTMgOC4xOTQ5MSAxMC41MjI4IDguMTk0OTEgMTAuNjA3MVYxOS44MDhDOC4xOTM1MSAyMC4wMzc4IDguMjUzMzQgMjAuMjYzOCA4LjM2ODIzIDIwLjQ2MjlDOC40ODMxMiAyMC42NjE5IDguNjQ4OTMgMjAuODI2NyA4Ljg0ODYzIDIwLjk0MDRMMTguNTcyMyAyNi41NTQyTDE1LjIwNiAyOC40OTc5QzE1LjE4OTQgMjguNTA4OSAxNS4xNzAzIDI4LjUxNTUgMTUuMTUwNSAyOC41MTczQzE1LjEzMDcgMjguNTE5MSAxNS4xMTA3IDI4LjUxNiAxNS4wOTI0IDI4LjUwODJMNy4wNDA0NiAyMy44NTU3QzUuMzIxMzUgMjIuODYwMSA0LjA2NzE2IDIxLjIyMzUgMy41NTI4OSAxOS4zMDQ2QzMuMDM4NjIgMTcuMzg1OCAzLjMwNjI0IDE1LjM0MTMgNC4yOTcwNyAxMy42MTk0Wk0zMS45NTUgMjAuMDU1NkwyMi4yMzEyIDE0LjQ0MTFMMjUuNTk3NiAxMi40OTgxQzI1LjYxNDIgMTIuNDg3MiAyNS42MzMzIDEyLjQ4MDUgMjUuNjUzMSAxMi40Nzg3QzI1LjY3MjkgMTIuNDc2OSAyNS42OTI4IDEyLjQ4MDEgMjUuNzExMSAxMi40ODc5TDMzLjc2MzEgMTcuMTM2NEMzNC45OTY3IDE3Ljg0OSAzNi4wMDE3IDE4Ljg5ODIgMzYuNjYwNiAyMC4xNjEzQzM3LjMxOTQgMjEuNDI0NCAzNy42MDQ3IDIyLjg0OSAzNy40ODMyIDI0LjI2ODRDMzcuMzYxNyAyNS42ODc4IDM2LjgzODIgMjcuMDQzMiAzNS45NzQzIDI4LjE3NTlDMzUuMTEwMyAyOS4zMDg2IDMzLjk0MTUgMzAuMTcxNyAzMi42MDQ3IDMwLjY2NDFDMzIuNjA0NyAzMC41OTQ3IDMyLjYwNDcgMzAuNDczMyAzMi42MDQ3IDMwLjM4ODlWMjEuMTg4QzMyLjYwNjYgMjAuOTU4NiAzMi41NDc0IDIwLjczMjggMzIuNDMzMiAyMC41MzM4QzMyLjMxOSAyMC4zMzQ4IDMyLjE1NCAyMC4xNjk4IDMxLjk1NSAyMC4wNTU2Wk0zNS4zMDU1IDE1LjAxMjhDMzUuMjQ2NCAxNC45NzY1IDM1LjE0MzEgMTQuOTE0MiAzNS4wNjkgMTQuODcxN0wyNy4xMDQ1IDEwLjI3MTJDMjYuOTA2IDEwLjE1NTQgMjYuNjgwMyAxMC4wOTQzIDI2LjQ1MDQgMTAuMDk0M0MyNi4yMjA2IDEwLjA5NDMgMjUuOTk0OCAxMC4xNTU0IDI1Ljc5NjMgMTAuMjcxMkwxNi4wNzI2IDE1Ljg4NThWMTEuOTk4MkMxNi4wNzE1IDExLjk3ODMgMTYuMDc1MyAxMS45NTg1IDE2LjA4MzcgMTEuOTQwNUMxNi4wOTIxIDExLjkyMjUgMTYuMTA0OCAxMS45MDY4IDE2LjEyMDcgMTEuODk0OUwyNC4xNzE5IDcuMjUwMjVDMjUuNDA1MyA2LjUzOTAzIDI2LjgxNTggNi4xOTM3NiAyOC4yMzgzIDYuMjU0ODJDMjkuNjYwOCA2LjMxNTg5IDMxLjAzNjQgNi43ODA3NyAzMi4yMDQ0IDcuNTk1MDhDMzMuMzcyMyA4LjQwOTM5IDM0LjI4NDIgOS41Mzk0NSAzNC44MzM0IDEwLjg1MzFDMzUuMzgyNiAxMi4xNjY3IDM1LjU0NjQgMTMuNjA5NSAzNS4zMDU1IDE1LjAxMjhaTTE0LjI0MjQgMjEuOTQxOUwxMC44NzUyIDE5Ljk5ODFDMTAuODU3NiAxOS45ODkzIDEwLjg0MjMgMTkuOTc2MyAxMC44MzA5IDE5Ljk2MDJDMTAuODE5NSAxOS45NDQxIDEwLjgxMjIgMTkuOTI1NCAxMC44MDk4IDE5LjkwNThWMTAuNjA3MUMxMC44MTA3IDkuMTgyOTUgMTEuMjE3MyA3Ljc4ODQ4IDExLjk4MTkgNi41ODY5NkMxMi43NDY2IDUuMzg1NDQgMTMuODM3NyA0LjQyNjU5IDE1LjEyNzUgMy44MjI2NEMxNi40MTczIDMuMjE4NjkgMTcuODUyNCAyLjk5NDY0IDE5LjI2NDkgMy4xNzY3QzIwLjY3NzUgMy4zNTg3NiAyMi4wMDg5IDMuOTM5NDEgMjMuMTAzNCA0Ljg1MDY3QzIzLjA0MjcgNC44ODM3OSAyMi45MzcgNC45NDIxNSAyMi44NjY4IDQuOTg0NzNMMTQuOTAyNCA5LjU4NTE3QzE0LjcwMjUgOS42OTg3OCAxNC41MzY2IDkuODYzNTYgMTQuNDIxNSAxMC4wNjI2QzE0LjMwNjUgMTAuMjYxNiAxNC4yNDY2IDEwLjQ4NzcgMTQuMjQ3OSAxMC43MTc1TDE0LjI0MjQgMjEuOTQxOVpNMTYuMDcxIDE3Ljk5OTFMMjAuNDAxOCAxNS40OTc4TDI0LjczMjUgMTcuOTk3NVYyMi45OTg1TDIwLjQwMTggMjUuNDk4M0wxNi4wNzEgMjIuOTk4NVYxNy45OTkxWiIgZmlsbD0iY3VycmVudENvbG9yIj48L3BhdGg+PC9zdmc+ // @require https://scriptcat.org/lib/513/2.1.0/ElementGetter.js // @require https://scriptcat.org/lib/637/1.4.6/ajaxHooker.js // @grant none // ==/UserScript== (function () { 'use strict'; // ==================== 配置 ==================== const SERVER_IP = (/(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/).exec(window.location.href)[0]; const BASE_URL = `http://${SERVER_IP}:8888`; // ==================== 权限校验配置 ==================== // 启动时先访问 CHECK_URL:命中无权限页面 / 请求失败 / 超时 → 禁用脚本全部功能 const CHECK_URL = `${BASE_URL}/basefiles/showWorkJobPage.do`; const CHECK_TIMEOUT_MS = 5000; const NO_PERMISSION_KEYWORD = '无权限访问该页面'; // 会话级缓存 key(区分服务器),一次登录会话只校验一次 const CHECK_CACHE_KEY = `saleorder_perm_${SERVER_IP}`; //const BRAND_FIELD_BASE = { //'05': { model: 'taxtypename', price: 'taxprice', modelLabel: '税收分类:', priceLabel: '含税单价:'}, // '03': { model: 'xxx', price: 'yyy', modelLabel: '税收分类:', priceLabel: '含税单价:' }, //'_default': { model: 'model', price: 'newbuyprice' , modelLabel: '税收分类:', priceLabel: '含税单价:'}, //}; //const BRAND_FIELD_OVERRIDE = { //'122.13.25.247': { // '08': { model: 'specialField_A', price: 'specialPrice_A', modelLabel: '税收分类:', priceLabel: '含税单价:' }, //}, //'47.107.106.156': { // '08': { model: 'specialField_B', price: 'specialPrice_B', modelLabel: '税收分类:', priceLabel: '含税单价:' }, //}, //}; // 品牌字段映射:根据 brand 决定取哪个字段作为 model 和 price,以及对应的显示标签 // 所有服务器共享的默认映射 const BRAND_FIELD_BASE = { // '05': { model: 'taxtypename', price: 'taxprice', modelLabel: '税收分类:', priceLabel: '含税单价:' }, '_default': { model: 'model', price: 'newbuyprice', modelLabel: '规格:', priceLabel: '最新采购价:' }, }; // 仅当某 IP 的某品牌与 BASE 不同时,在此写覆盖项 const BRAND_FIELD_OVERRIDE = { '122.13.25.247': { '12': { model: 'model', price: 'basesaleprice', modelLabel: '规格:', priceLabel: '基准销售价:' }, }, }; function getBrandFields(ip, brand) { const override = BRAND_FIELD_OVERRIDE[ip]; if (override && override[brand]) return override[brand]; return BRAND_FIELD_BASE[brand] || BRAND_FIELD_BASE['_default']; } // 历史价格查询缓存:key = "customer_products_year";换商品/客户时整体清空 const historyCache = new Map(); let historyCacheScope = ''; // 当前缓存对应的 "customer_products" // ==================== 工具函数 ==================== // toast 提示自动消失时间(毫秒) const TOAST_DURATION_MS = 3000; /** 页面顶部非阻塞 toast 提示,替代 alert(重复单、请求失败等) */ function notify(msg) { console.warn('[saleorder]', msg); const toast = document.createElement('div'); toast.textContent = msg; toast.style.cssText = 'position:fixed;top:20px;left:50%;transform:translateX(-50%);z-index:99999;background:#333;color:#fff;padding:8px 16px;border-radius:4px;max-width:60%;font-size:14px;box-shadow:0 2px 8px rgba(0,0,0,.3);transition:opacity .5s;'; document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 500); }, TOAST_DURATION_MS); } /** 防抖 */ function debounce(fn, delay = 500) { let timer = null; return function (...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } /** 通用 POST 请求(返回 Promise) */ function postRequest(path, params) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); const url = new URL(BASE_URL + path); for (const key in params) { if (Array.isArray(params[key])) { params[key].forEach(v => url.searchParams.append(key, v)); } else { url.searchParams.set(key, params[key]); } } xhr.open('post', url); xhr.send(); xhr.onload = function () { if (xhr.status !== 200) { reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`)); } else { try { resolve(JSON.parse(xhr.response)); } catch (e) { reject(new Error('JSON 解析失败')); } } }; xhr.onerror = () => reject(new Error('网络请求失败')); }); } /** 日期区间计算(修复月份 bug) */ function getIntervalDate(year) { let start_date, end_date; if (year) { start_date = `${year}-01-01`; end_date = `${year}-12-31`; } else { const date = new Date(); end_date = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; date.setDate(date.getDate() - 90); start_date = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; } return { start_date, end_date }; } // ==================== 重复订单查询 ==================== function onSourceIdChange() { const customerInput = document.querySelector('#sales-customer-showid-orderAddPage a'); const inputOrder = document.querySelector('#sales-sourceid-orderAddPage'); if (inputOrder && inputOrder.value) { QueryCustomerOrder(customerInput.innerText, inputOrder.value); } } async function QueryCustomerOrder(customer, order) { const query_date = getIntervalDate(); try { const data = await postRequest('/sales/getOrderList.do', { businessdate: query_date.start_date, businessdate1: query_date.end_date, customerid: customer, sourceid: order, isstoragelock: '0', page: '1', rows: '200', sort: 'addtime', order: 'desc', id: '', salesdept: '', status: '', goodsid: '', printsign: '', queryprinttimes: '', urgentlevel: '', lineid: '', salesuserArrs: '' }); if (data.total > 0) { const orderNo = data.rows .filter(row => row.status !== '5') .map(row => row.id) .join(','); if (orderNo) { notify(`有重复订单,订单号为${orderNo}`); } } } catch (e) { console.error('[saleorder] 重复订单查询失败:', e.message); notify(`重复订单查询失败:${e.message}`); } } // ==================== 历史价格查询 ==================== async function queryHistoryPrice(customer, products, year) { // 换商品或客户后旧缓存不再适用,整体清空重新查询 const scope = `${customer}_${products}`; if (scope !== historyCacheScope) { historyCache.clear(); historyCacheScope = scope; } const cacheKey = `${customer}_${products}_${year}`; if (historyCache.has(cacheKey)) { renderHistoryTable(historyCache.get(cacheKey), products, year); return; } const div = $(`.panel-body-noborder.layout-body.${year}`); div.html('
加载中...
'); try { const [returns_order, sale_order] = await Promise.all([ fetchCustomerSalesFlow(customer, products, year), fetchSalesOrderTrack(customer, products, year) ]); const list_order = []; for (const row of returns_order) { list_order.push({ goodsid: row.goodsid, date: row.businessdate, unitnum: row.unitnum, price: row.price, sendnum: row.unitnum, checkprice: row.price, order: row.customerbillid, }); } for (const row of sale_order) { if (row.ordernum > 0) { list_order.push({ goodsid: row.goodsid, date: row.businessdate, unitnum: row.initsendnum, price: row.dispatchprice, sendnum: row.sendnum === undefined ? '未出库' : row.sendnum, checkprice: row.checkprice === undefined ? '未验收' : row.checkprice, order: row.sourceid, }); } } // 排序:日期 → 单号 → 数量绝对值 list_order.sort((a, b) => { const dateDiff = new Date(a.date) - new Date(b.date); if (dateDiff !== 0) return dateDiff; const orderDiff = a.order.localeCompare(b.order); if (orderDiff !== 0) return orderDiff; return Math.abs(a.unitnum) - Math.abs(b.unitnum); }); historyCache.set(cacheKey, list_order); renderHistoryTable(list_order, products, year); } catch (e) { console.error('[saleorder] 历史价格查询失败:', e.message); notify(`历史价格查询失败:${e.message}`); $(`.panel-body-noborder.layout-body.${year}`).html( `查询失败: ${e.message}
` ); } } function renderHistoryTable(list_order, products, year) { let tr_html = ''; let x = 1; if (list_order.length === 0) { tr_html = `${products}无销售`; } else { for (const row of list_order) { const rowClass = (x % 2 === 0) ? 'datagrid-row-alt datagrid-row' : 'datagrid-row'; const style = (row.price !== row.checkprice) ? 'font-weight: bold;color: red;' : ''; tr_html = `| 编码 | 日期 | 数量 | 单价 | 出库数量 | 验收单价 |