您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在电子税务局旁边显示一个小窗口,方便登录与切换不同的公司。
// ==UserScript== // @name 全国统一规范电子税务局税号填写辅助工具 // @namespace http://tampermonkey.net/ // @version 3.1 // @description 在电子税务局旁边显示一个小窗口,方便登录与切换不同的公司。 // @author Herohub // @match https://*.chinatax.gov.cn:8443/* // @license MIT // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function () { 'use strict'; // 判断是否为登录页面或切换公司页面,若都不是则直接退出脚本执行 const isLoginPage = window.location.href.includes('login?redirect_uri'); const isIdentitySwitchPage = window.location.href.includes('identitySwitch'); if (!isLoginPage &&!isIdentitySwitchPage) return; // 创建浮窗元素并设置样式 const createFloatWindow = () => { const floatWindow = document.createElement('div'); Object.assign(floatWindow.style, { position: 'fixed', top: '50%', right: '0', // 贴紧右侧 transform: 'translateY(-50%)', background: '#fff', border: '1px solid #ccc', padding: '15px', borderRadius: '8px 0 0 8px', // 左侧圆角,右侧直角 boxShadow: '0 4px 8px rgba(0,0,0,.1)', fontFamily: 'Arial,sans-serif', zIndex: 10000, overflow: 'hidden', transition: 'width 0.3s cubic-bezier(0.4, 0, 0.2, 1)' // 更平滑的宽度变化动画 }); return floatWindow; }; const floatWindow = createFloatWindow(); document.body.appendChild(floatWindow); // 创建左侧调整宽度的手柄 const leftResizeHandle = document.createElement('div'); Object.assign(leftResizeHandle.style, { position: 'absolute', top: '0', left: '0', width: '5px', height: '100%', cursor: 'ew-resize', background: 'transparent', zIndex: 10, display: 'none', transition: 'background 0.2s ease' }); leftResizeHandle.title = '拖动调整宽度'; floatWindow.appendChild(leftResizeHandle); // 创建按钮元素并设置样式和点击事件 const createButton = (text, clickHandler, isDelete = false, customStyles = {}) => { const button = document.createElement('button'); const styles = { marginRight: '5px', padding: '5px 10px', border: 'none', borderRadius: '3px', background: isDelete? '#dc3545' : '#007BFF', color: '#fff', cursor: 'pointer', transition: 'background-color 0.3s ease, transform 0.2s ease, box-shadow 0.2s ease', ...customStyles }; Object.assign(button.style, styles); button.textContent = text; button.addEventListener('click', clickHandler); // 鼠标悬停效果 button.addEventListener('mouseover', () => { button.style.background = isDelete? '#c82333' : '#0056b3'; button.style.transform = 'translateY(-1px)'; button.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; }); button.addEventListener('mouseout', () => { button.style.background = isDelete? '#dc3545' : '#007BFF'; button.style.transform = 'translateY(0)'; button.style.boxShadow = 'none'; }); return button; }; // 创建拖动按钮 const createDragButton = () => { const dragButton = document.createElement('button'); Object.assign(dragButton.style, { marginRight: '5px', padding: '2px 6px', border: '1px solid #ddd', borderRadius: '3px', background: '#f8f9fa', color: '#6c757d', cursor: 'grab', fontSize: '12px', transition: 'all 0.2s ease' }); dragButton.innerHTML = '☰'; // 汉堡图标作为拖动指示器 dragButton.title = '拖动调整顺序'; // 拖动时改变样式 dragButton.addEventListener('mousedown', () => { dragButton.style.cursor = 'grabbing'; dragButton.style.background = '#e9ecef'; dragButton.style.transform = 'scale(1.1)'; }); dragButton.addEventListener('mouseup', () => { dragButton.style.cursor = 'grab'; dragButton.style.background = '#f8f9fa'; dragButton.style.transform = 'scale(1)'; }); dragButton.addEventListener('mouseover', () => { if (dragButton.style.cursor!== 'grabbing') { dragButton.style.background = '#e9ecef'; dragButton.style.transform = 'scale(1.05)'; } }); dragButton.addEventListener('mouseout', () => { if (dragButton.style.cursor!== 'grabbing') { dragButton.style.background = '#f8f9fa'; dragButton.style.transform = 'scale(1)'; } }); return dragButton; }; // 定义全局变量 let isEditMode = false; let lastSelectedItem = GM_getValue('lastSelectedItem', null); let originalWidth = null; let windowWidth = GM_getValue('windowWidth', 450); // 统一窗口宽度 let isAutoFillAccount = GM_getValue('isAutoFillAccount', false); let accountInfo = GM_getValue('accountInfo', null); let isCollapsed = GM_getValue('isCollapsed', false); let isInitialLoad = true; // 标记是否为初始加载 // 拖动相关变量 let draggedItem = null; let initialY = 0; let initialOffsetY = 0; // 收起/展开按钮 const toggleCollapseButton = document.createElement('div'); Object.assign(toggleCollapseButton.style, { position: 'fixed', right: isCollapsed? '0' : windowWidth + 'px', top: '50%', transform: 'translateY(-50%)', width: '40px', height: '80px', background: '#2196F3', color: '#ffffff', border: '2px solid #0b7dda', borderRadius: '8px 0 0 8px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '-3px 0 6px rgba(0,0,0,0.2)', zIndex: 9999, fontSize: '24px', fontWeight: 'bold', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', opacity: '1', pointerEvents: 'auto' }); // 根据存储的状态设置初始箭头 toggleCollapseButton.innerHTML = isCollapsed? '≪' : '≫'; toggleCollapseButton.addEventListener('click', toggleCollapse); // 添加悬停效果 toggleCollapseButton.addEventListener('mouseover', () => { toggleCollapseButton.style.background = '#0b7dda'; toggleCollapseButton.style.transform = `translateY(-50%) scale(1.1)`; }); toggleCollapseButton.addEventListener('mouseout', () => { toggleCollapseButton.style.background = '#2196F3'; toggleCollapseButton.style.transform = 'translateY(-50%) scale(1)'; }); document.body.appendChild(toggleCollapseButton); // 主内容容器 const mainContent = document.createElement('div'); // 根据存储的状态设置初始显示 mainContent.style.display = isCollapsed? 'none' : 'block'; floatWindow.appendChild(mainContent); // 添加按钮 const addButton = createButton('添加', addItem); mainContent.appendChild(addButton); // 编辑/完成按钮 let editToggleButton = createButton('编辑', toggleEditMode); mainContent.appendChild(editToggleButton); // 导入按钮 const importButton = createButton('导入', importData); importButton.style.display = 'none'; mainContent.appendChild(importButton); // 导出按钮 const exportButton = createButton('导出', exportData); exportButton.style.display = 'none'; mainContent.appendChild(exportButton); // 账号自动填写开关按钮 const accountAutoFillSwitch = createButton( isAutoFillAccount? '自动填账号密码: 开' : '自动填账号密码: 关', toggleAccountAutoFill, false, { flexGrow: 1, marginLeft: '5px' } ); mainContent.appendChild(accountAutoFillSwitch); // 分割线 const separator = document.createElement('hr'); separator.style.cssText = 'margin:15px 0;border:none;border-top:1px solid #e0e0e0'; mainContent.appendChild(separator); // 内容列表容器 const itemList = document.createElement('div'); itemList.className = 'item-list-container'; itemList.style.transition = 'all 0.2s ease'; mainContent.appendChild(itemList); // 设置左侧调整功能 setupLeftResizeFunctionality(); // 初始应用收起状态样式 if (isCollapsed) { floatWindow.style.width = '0px'; floatWindow.style.padding = '0'; } else { floatWindow.style.width = windowWidth + 'px'; } // 加载已保存的税号内容 const savedItems = GM_getValue('autoFillItems', []); savedItems.forEach(item => { addItemToWindow(item.content, item.note, itemList); }); // 收起/展开功能 function toggleCollapse() { isCollapsed =!isCollapsed; GM_setValue('isCollapsed', isCollapsed); if (isCollapsed) { mainContent.style.display = 'none'; floatWindow.style.width = '0px'; floatWindow.style.padding = '0'; toggleCollapseButton.innerHTML = '≪'; toggleCollapseButton.style.right = '0'; } else { mainContent.style.display = 'block'; floatWindow.style.width = windowWidth + 'px'; floatWindow.style.padding = '15px'; toggleCollapseButton.innerHTML = '≫'; toggleCollapseButton.style.right = windowWidth + 'px'; } } // 左侧边缘调整宽度功能 function setupLeftResizeFunctionality() { let isResizing = false; let startX, startWidth; leftResizeHandle.addEventListener('mousedown', (e) => { if (!isEditMode || isCollapsed) return; isResizing = true; startX = e.clientX; startWidth = parseFloat(getComputedStyle(floatWindow).width); document.body.style.cursor = 'ew-resize'; floatWindow.style.transition = 'none'; leftResizeHandle.style.background = 'rgba(0, 123, 255, 0.3)'; document.addEventListener('mousemove', resize); document.addEventListener('mouseup', stopResize); }); function resize(e) { if (!isResizing) return; e.preventDefault(); const widthDiff = startX - e.clientX; const newWidth = Math.max(400, startWidth + widthDiff); floatWindow.style.width = `${newWidth}px`; if (!isCollapsed) { toggleCollapseButton.style.right = newWidth + 'px'; } } function stopResize() { isResizing = false; document.body.style.cursor = ''; floatWindow.style.transition = ''; leftResizeHandle.style.background = 'transparent'; document.removeEventListener('mousemove', resize); document.removeEventListener('mouseup', stopResize); windowWidth = parseFloat(getComputedStyle(floatWindow).width); GM_setValue('windowWidth', windowWidth); } } // 添加新条目函数 function addItem() { const modal = createAddModal(); document.body.appendChild(modal); } // 创建添加新条目模态框 function createAddModal() { const modal = document.createElement('div'); Object.assign(modal.style, { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: '#fff', border: '1px solid #ccc', padding: '20px', borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)', zIndex: 10001, display: 'flex', flexDirection: 'column', gap: '10px', width: '300px', opacity: '0', transform: 'translate(-50%, -50%) scale(0.95)', transition: 'opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)' }); // 添加模态框背景遮罩 const overlay = document.createElement('div'); Object.assign(overlay.style, { position: 'fixed', top: '0', left: '0', right: '0', bottom: '0', background: 'rgba(0,0,0,0.5)', zIndex: 10000, opacity: '0', transition: 'opacity 0.3s ease' }); document.body.appendChild(overlay); // 触发动画 setTimeout(() => { modal.style.opacity = '1'; modal.style.transform = 'translate(-50%, -50%) scale(1)'; overlay.style.opacity = '0.5'; }, 10); const taxNumberInput = document.createElement('input'); taxNumberInput.placeholder = '请输入税号'; taxNumberInput.style.padding = '8px'; taxNumberInput.style.border = '1px solid #ccc'; taxNumberInput.style.borderRadius = '3px'; taxNumberInput.style.transition = 'border-color 0.2s ease'; taxNumberInput.addEventListener('focus', () => { taxNumberInput.style.borderColor = '#007BFF'; taxNumberInput.style.boxShadow = '0 0 0 2px rgba(0, 123, 255, 0.25)'; }); taxNumberInput.addEventListener('blur', () => { taxNumberInput.style.borderColor = '#ccc'; taxNumberInput.style.boxShadow = 'none'; }); modal.appendChild(taxNumberInput); const noteInput = document.createElement('input'); noteInput.placeholder = '请输入备注'; noteInput.style.padding = '8px'; noteInput.style.border = '1px solid #ccc'; noteInput.style.borderRadius = '3px'; noteInput.style.transition = 'border-color 0.2s ease'; noteInput.addEventListener('focus', () => { noteInput.style.borderColor = '#007BFF'; noteInput.style.boxShadow = '0 0 0 2px rgba(0, 123, 255, 0.25)'; }); noteInput.addEventListener('blur', () => { noteInput.style.borderColor = '#ccc'; noteInput.style.boxShadow = 'none'; }); modal.appendChild(noteInput); const confirmButton = createButton('确认', () => { const taxNumber = taxNumberInput.value.trim(); const note = noteInput.value.trim(); if (!taxNumber ||!note) { alert('税号和备注都不能为空,请重新输入。'); return; } addItemToWindow(taxNumber, note, itemList); const items = GM_getValue('autoFillItems', []); items.push({ content: taxNumber, note }); GM_setValue('autoFillItems', items); // 关闭动画 modal.style.opacity = '0'; modal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(modal); document.body.removeChild(overlay); }, 300); }); modal.appendChild(confirmButton); const cancelButton = createButton('取消', () => { // 关闭动画 modal.style.opacity = '0'; modal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(modal); document.body.removeChild(overlay); }, 300); }, true); modal.appendChild(cancelButton); // 点击遮罩关闭模态框 overlay.addEventListener('click', () => { modal.style.opacity = '0'; modal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(modal); document.body.removeChild(overlay); }, 300); }); return modal; } // 辅助函数:查找包含特定文本的元素 function findElementWithText(container, selector, text) { const elements = container.querySelectorAll(selector); for (let i = 0; i < elements.length; i++) { // 修剪空白字符并忽略大小写进行比较 if (elements[i].textContent.trim().toLowerCase().includes(text.trim().toLowerCase())) { return elements[i]; } } return null; } // 在主文档和 iframe 中查找元素 function findElementInIframes(selector, text = null) { // 先在主文档中查找 let element; if (text) { element = findElementWithText(document, selector, text); } else { element = document.querySelector(selector); } if (element) return element; // 再在所有 iframe 中查找 const iframes = document.querySelectorAll('iframe'); for (let i = 0; i < iframes.length; i++) { try { const doc = iframes[i].contentDocument; if (text) { element = findElementWithText(doc, selector, text); } else { element = doc.querySelector(selector); } if (element) { return element; } } catch (e) { console.log('跨域iframe无法访问,已跳过:', e); continue; // 跨域 iframe 可能无法访问,直接跳过 } } return null; } // 将新条目添加到窗口列表中 function addItemToWindow(content, note, container) { const itemDiv = document.createElement('div'); itemDiv.className = 'tax-item'; Object.assign(itemDiv.style, { padding: '8px 0', cursor: 'pointer', borderBottom: '1px solid #f0f0f0', transition: 'background-color 0.3s ease, transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease', userSelect: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '5px', opacity: '0', transform: 'translateY(5px)' }); // 触发淡入动画 setTimeout(() => { itemDiv.style.opacity = '1'; itemDiv.style.transform = 'translateY(0)'; }, 50); // 创建拖动按钮容器(默认隐藏) const dragContainer = document.createElement('div'); dragContainer.className = 'drag-container'; dragContainer.style.display = 'none'; const dragButton = createDragButton(); dragContainer.appendChild(dragButton); itemDiv.appendChild(dragContainer); const textSpan = document.createElement('span'); textSpan.textContent = note; textSpan.style.flexGrow = 1; textSpan.style.overflow = 'hidden'; textSpan.style.textOverflow = 'ellipsis'; textSpan.style.whiteSpace = 'nowrap'; itemDiv.appendChild(textSpan); // 创建编辑和删除按钮容器 const buttonContainer = document.createElement('div'); buttonContainer.className = 'action-buttons'; buttonContainer.style.display = 'flex'; // 编辑按钮 const editBtn = createButton( '编辑', () => editItem(itemDiv, content, note), false, { padding: '3px 8px', fontSize: '12px', marginRight: '3px' } ); editBtn.className = 'edit-btn'; editBtn.style.display = 'none'; // 删除按钮 const deleteBtn = createButton( '删除', () => deleteItem(itemDiv, content), true, { padding: '3px 8px', fontSize: '12px' } ); deleteBtn.className = 'delete-btn'; deleteBtn.style.display = 'none'; buttonContainer.appendChild(editBtn); buttonContainer.appendChild(deleteBtn); itemDiv.appendChild(buttonContainer); // 点击条目文本区域填充税号 textSpan.addEventListener('click', () => { if (isEditMode) return; // 编辑模式下点击文本不填充 setTimeout(() => { // 根据不同页面使用不同的选择器查找税号输入框 // 优先通过placeholder="统一社会信用代码/纳税人识别号"查找 let taxInput = findElementInIframes('input[placeholder="统一社会信用代码/纳税人识别号"]') || findElementInIframes('input.el-input__inner[placeholder="统一社会信用代码/纳税人识别号"]'); // 如果未找到,再使用原有策略 if (!taxInput) { if (isLoginPage) { taxInput = findElementInIframes('input.el-input__inner', '统一社会信用代码/纳税人识别号') || findElementInIframes('input.el-input__inner'); } else if (isIdentitySwitchPage) { // 切换公司页面可能使用不同的选择器 taxInput = findElementInIframes('input.el-input__inner', '统一社会信用代码/纳税人识别号') || findElementInIframes('input.el-input__inner', '请输入纳税人识别号') || findElementInIframes('input.el-input__inner'); } } if (taxInput) { taxInput.value = content; triggerInputEvents(taxInput); // 在enterprise环境下自动点击查询、确定和切换按钮 setTimeout(() => { // 尝试查找并点击查询按钮 let actionButton = findElementInIframes('button.el-button.el-button--primary', '查询'); // 如果没找到查询按钮,尝试查找确定按钮 if (!actionButton) { actionButton = findElementInIframes('button.el-button.el-button--primary', '确定'); } // 如果找到按钮则点击 if (actionButton) { actionButton.click(); console.log(`已点击${actionButton.textContent.trim()}按钮`); // 操作后等待结果加载,再点击切换按钮 setTimeout(() => { const switchButton = findElementInIframes('button.el-button.el-button--text', '切换'); if (switchButton) { switchButton.click(); console.log('已点击切换按钮'); // 切换后可能还需要点击确定按钮 setTimeout(() => { const confirmButton = findElementInIframes('button.el-button.el-button--primary', '确定'); if (confirmButton) { confirmButton.click(); console.log('已点击确定按钮'); } }, 500); // 调整为500ms } else { console.log('未找到切换按钮'); } }, 500); // 调整为500ms } else { console.log('未找到查询或确定按钮'); } }, 500); // 调整为500ms } else { console.error('未找到纳税人识别号输入框,请检查页面元素。'); } if (isAutoFillAccount && accountInfo) { const accountInput = findElementInIframes('input[placeholder="居民身份证号码/手机号码/用户名"]') || findElementInIframes('input.el-input__inner[placeholder="居民身份证号码/手机号码/用户名"]'); const passwordInput = findElementInIframes('input[placeholder="个人用户密码"]') || findElementInIframes('input.el-input__inner[placeholder="个人用户密码"]'); if (accountInput) { accountInput.value = accountInfo.account; triggerInputEvents(accountInput); } if (passwordInput) { passwordInput.value = accountInfo.password; triggerInputEvents(passwordInput); } } clearSelection(); itemDiv.style.background = '#e0f7fa'; lastSelectedItem = content; GM_setValue('lastSelectedItem', lastSelectedItem); }, 500); // 调整为500ms }); itemDiv.addEventListener('mouseover', () => { if (itemDiv.dataset.content!== lastSelectedItem) { itemDiv.style.background = '#f9f9f9'; } }); itemDiv.addEventListener('mouseout', () => { if (itemDiv.dataset.content!== lastSelectedItem) { itemDiv.style.background = '#fff'; } }); itemDiv.dataset.content = content; container.appendChild(itemDiv); // 设置拖动相关属性和事件 setupDragItem(itemDiv, dragButton); if (content === lastSelectedItem) { itemDiv.style.background = '#e0f7fa'; } return itemDiv; } // 设置拖动功能 function setupDragItem(itemDiv, dragButton) { let isDragging = false; const dragContainer = dragButton.parentElement; let originalRect = null; let originalIndex = 0; // 拖动开始 dragButton.addEventListener('mousedown', (e) => { if (!isEditMode) return; e.stopPropagation(); // 记录初始位置和索引 originalRect = itemDiv.getBoundingClientRect(); const items = Array.from(itemList.querySelectorAll('.tax-item')); originalIndex = items.indexOf(itemDiv); isDragging = true; draggedItem = itemDiv; draggedItem.classList.add('dragging'); // 记录初始鼠标位置和偏移量 initialY = e.clientY; initialOffsetY = e.clientY - originalRect.top; // 设置拖动时的样式 draggedItem.style.opacity = '0.9'; draggedItem.style.transform = 'scale(1.02)'; draggedItem.style.zIndex = '10001'; draggedItem.style.boxShadow = '0 6px 16px rgba(0,0,0,0.15)'; draggedItem.style.background = '#f0f7ff'; draggedItem.style.borderRadius = '4px'; draggedItem.style.margin = '5px 0'; draggedItem.style.position = 'relative'; draggedItem.style.width = `${originalRect.width}px`; // 创建占位符 const placeholder = document.createElement('div'); placeholder.className = 'drag-placeholder'; placeholder.style.height = `${originalRect.height}px`; placeholder.style.opacity = '0.3'; placeholder.style.backgroundColor = '#e9ecef'; placeholder.style.borderRadius = '4px'; placeholder.style.margin = '5px 0'; placeholder.style.transition = 'all 0.2s ease'; itemDiv.parentNode.insertBefore(placeholder, itemDiv); itemDiv.placeholder = placeholder; // 隐藏原元素但保留占位空间 setTimeout(() => { draggedItem.style.opacity = '0'; }, 10); }); // 拖动结束 document.addEventListener('mouseup', () => { if (!isDragging ||!isEditMode ||!draggedItem) return; // 移除拖动状态类 draggedItem.classList.remove('dragging'); // 恢复元素样式 draggedItem.style.opacity = ''; draggedItem.style.transform = ''; draggedItem.style.zIndex = ''; draggedItem.style.boxShadow = ''; draggedItem.style.background = ''; draggedItem.style.borderRadius = ''; draggedItem.style.margin = ''; draggedItem.style.position = ''; draggedItem.style.width = ''; draggedItem.style.top = ''; // 将拖动的元素移动到占位符位置 if (draggedItem.placeholder && draggedItem.placeholder.parentNode) { draggedItem.placeholder.parentNode.insertBefore(draggedItem, draggedItem.placeholder); draggedItem.placeholder.parentNode.removeChild(draggedItem.placeholder); } // 重置拖动状态 isDragging = false; draggedItem.placeholder = null; draggedItem = null; // 更新顺序 updateItemOrder(); }); // 鼠标移动时处理拖动 document.addEventListener('mousemove', (e) => { if (!isDragging ||!isEditMode ||!draggedItem) return; e.preventDefault(); // 计算拖动元素的新位置 const listRect = itemList.getBoundingClientRect(); const newTop = e.clientY - listRect.top - initialOffsetY; // 限制拖动范围在列表内 if (newTop > 0 && newTop < listRect.height - draggedItem.offsetHeight) { draggedItem.style.top = `${newTop}px`; } // 确定新位置 const afterElement = getDragAfterElement(itemList, e.clientY); if (afterElement && afterElement!== draggedItem.placeholder) { itemList.insertBefore(draggedItem.placeholder, afterElement); } else if (!afterElement && draggedItem.placeholder.nextSibling) { itemList.appendChild(draggedItem.placeholder); } }); // 辅助函数:确定拖动元素应该放置在哪个元素后面 function getDragAfterElement(container, y) { const draggableElements = [...container.querySelectorAll('.tax-item:not(.dragging)')]; return draggableElements.reduce((closest, child) => { const box = child.getBoundingClientRect(); const offset = y - box.top - box.height / 2; if (offset < 0 && offset > closest.offset) { return { offset: offset, element: child }; } else { return closest; } }, { offset: Number.NEGATIVE_INFINITY }).element; } } // 触发输入框事件 function triggerInputEvents(input) { const inputEvent = new Event('input', { bubbles: true }); const changeEvent = new Event('change', { bubbles: true }); // 对于Vue等框架可能需要的额外事件 const blurEvent = new Event('blur', { bubbles: true }); input.dispatchEvent(inputEvent); input.dispatchEvent(changeEvent); input.dispatchEvent(blurEvent); } // 清除选中状态 function clearSelection() { const items = itemList.querySelectorAll('.tax-item'); items.forEach(item => { if (item.dataset.content!== lastSelectedItem) { item.style.background = '#fff'; } }); } // 切换编辑模式 function toggleEditMode() { isEditMode =!isEditMode; editToggleButton.textContent = isEditMode? '完成' : '编辑'; const items = document.querySelectorAll('.tax-item'); items.forEach((item, index) => { const dragContainer = item.querySelector('.drag-container'); const editBtn = item.querySelector('.edit-btn'); const deleteBtn = item.querySelector('.delete-btn'); if (isEditMode) { setTimeout(() => { dragContainer.style.display = 'flex'; editBtn.style.display = 'inline-block'; deleteBtn.style.display = 'inline-block'; dragContainer.style.opacity = '0'; editBtn.style.opacity = '0'; deleteBtn.style.opacity = '0'; setTimeout(() => { dragContainer.style.opacity = '1'; editBtn.style.opacity = '1'; deleteBtn.style.opacity = '1'; }, 50); }, index * 50); } else { dragContainer.style.opacity = '0'; editBtn.style.opacity = '0'; deleteBtn.style.opacity = '0'; setTimeout(() => { dragContainer.style.display = 'none'; editBtn.style.display = 'none'; deleteBtn.style.display = 'none'; }, 200); } const textSpan = item.querySelector('span'); textSpan.style.cursor = isEditMode? 'default' : 'pointer'; }); if (isEditMode) { importButton.style.display = 'inline-block'; exportButton.style.display = 'inline-block'; importButton.style.opacity = '0'; exportButton.style.opacity = '0'; setTimeout(() => { importButton.style.opacity = '1'; exportButton.style.opacity = '1'; }, 100); } else { importButton.style.opacity = '0'; exportButton.style.opacity = '0'; setTimeout(() => { importButton.style.display = 'none'; exportButton.style.display = 'none'; }, 200); } leftResizeHandle.style.display = isEditMode? 'block' : 'none'; } // 编辑条目 function editItem(itemDiv, oldContent, oldNote) { const modal = document.createElement('div'); Object.assign(modal.style, { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: '#fff', border: '1px solid #ccc', padding: '20px', borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)', zIndex: 10001, display: 'flex', flexDirection: 'column', gap: '10px', width: '300px', opacity: '0', transform: 'translate(-50%, -50%) scale(0.95)', transition: 'opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)' }); // 添加模态框背景遮罩 const overlay = document.createElement('div'); Object.assign(overlay.style, { position: 'fixed', top: '0', left: '0', right: '0', bottom: '0', background: 'rgba(0,0,0,0.5)', zIndex: 10000, opacity: '0', transition: 'opacity 0.3s ease' }); document.body.appendChild(overlay); // 触发动画 setTimeout(() => { modal.style.opacity = '1'; modal.style.transform = 'translate(-50%, -50%) scale(1)'; overlay.style.opacity = '0.5'; }, 10); const taxNumberInput = document.createElement('input'); taxNumberInput.value = oldContent; taxNumberInput.style.padding = '8px'; taxNumberInput.style.border = '1px solid #ccc'; taxNumberInput.style.borderRadius = '3px'; taxNumberInput.style.transition = 'border-color 0.2s ease'; taxNumberInput.addEventListener('focus', () => { taxNumberInput.style.borderColor = '#007BFF'; taxNumberInput.style.boxShadow = '0 0 0 2px rgba(0, 123, 255, 0.25)'; }); taxNumberInput.addEventListener('blur', () => { taxNumberInput.style.borderColor = '#ccc'; taxNumberInput.style.boxShadow = 'none'; }); modal.appendChild(taxNumberInput); const noteInput = document.createElement('input'); noteInput.value = oldNote; noteInput.style.padding = '8px'; noteInput.style.border = '1px solid #ccc'; noteInput.style.borderRadius = '3px'; noteInput.style.transition = 'border-color 0.2s ease'; noteInput.addEventListener('focus', () => { noteInput.style.borderColor = '#007BFF'; noteInput.style.boxShadow = '0 0 0 2px rgba(0, 123, 255, 0.25)'; }); noteInput.addEventListener('blur', () => { noteInput.style.borderColor = '#ccc'; noteInput.style.boxShadow = 'none'; }); modal.appendChild(noteInput); const confirmButton = createButton('确认', () => { const newContent = taxNumberInput.value.trim(); const newNote = noteInput.value.trim(); if (!newContent ||!newNote) { alert('税号和备注都不能为空,请重新输入。'); return; } // 编辑后的过渡动画 itemDiv.style.opacity = '0.5'; setTimeout(() => { const textSpan = itemDiv.querySelector('span'); textSpan.textContent = newNote; itemDiv.dataset.content = newContent; const items = GM_getValue('autoFillItems', []); const index = items.findIndex(item => item.content === oldContent); if (index!== -1) { items[index] = { content: newContent, note: newNote }; GM_setValue('autoFillItems', items); } itemDiv.style.opacity = '1'; // 关闭动画 modal.style.opacity = '0'; modal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(modal); document.body.removeChild(overlay); }, 300); }, 200); }); modal.appendChild(confirmButton); const cancelButton = createButton('取消', () => { // 关闭动画 modal.style.opacity = '0'; modal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(modal); document.body.removeChild(overlay); }, 300); }, true); modal.appendChild(cancelButton); // 点击遮罩关闭模态框 overlay.addEventListener('click', () => { modal.style.opacity = '0'; modal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(modal); document.body.removeChild(overlay); }, 300); }); document.body.appendChild(modal); return modal; } // 删除条目 function deleteItem(itemDiv, content) { // 删除前的淡出动画 itemDiv.style.opacity = '0'; itemDiv.style.transform = 'translateY(10px)'; itemDiv.style.height = itemDiv.offsetHeight + 'px'; itemDiv.style.overflow = 'hidden'; itemDiv.style.margin = '0'; setTimeout(() => { const items = GM_getValue('autoFillItems', []); const newItems = items.filter(item => item.content!== content); GM_setValue('autoFillItems', newItems); itemList.removeChild(itemDiv); if (content === lastSelectedItem) { lastSelectedItem = null; GM_setValue('lastSelectedItem', null); } }, 300); } // 更新条目顺序 function updateItemOrder() { const items = Array.from(document.querySelectorAll('.tax-item')); const newItemOrder = items.map(item => { const note = item.querySelector('span').textContent; const content = item.dataset.content; return { content, note }; }); GM_setValue('autoFillItems', newItemOrder); } // 导入数据 function importData() { const importModal = document.createElement('div'); Object.assign(importModal.style, { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: '#fff', border: '1px solid #ccc', padding: '20px', borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)', zIndex: 10001, display: 'flex', flexDirection: 'column', gap: '10px', width: '350px', opacity: '0', transform: 'translate(-50%, -50%) scale(0.95)', transition: 'opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)' }); // 添加模态框背景遮罩 const overlay = document.createElement('div'); Object.assign(overlay.style, { position: 'fixed', top: '0', left: '0', right: '0', bottom: '0', background: 'rgba(0,0,0,0.5)', zIndex: 10000, opacity: '0', transition: 'opacity 0.3s ease' }); document.body.appendChild(overlay); // 触发动画 setTimeout(() => { importModal.style.opacity = '1'; importModal.style.transform = 'translate(-50%, -50%) scale(1)'; overlay.style.opacity = '0.5'; }, 10); const dataTextarea = document.createElement('textarea'); dataTextarea.placeholder = '请输入要导入的数据(格式:税号,备注;税号,备注;...)'; dataTextarea.style.padding = '8px'; dataTextarea.style.border = '1px solid #ccc'; dataTextarea.style.borderRadius = '3px'; dataTextarea.style.height = '150px'; dataTextarea.style.transition = 'border-color 0.2s ease'; dataTextarea.addEventListener('focus', () => { dataTextarea.style.borderColor = '#007BFF'; dataTextarea.style.boxShadow = '0 0 0 2px rgba(0, 123, 255, 0.25)'; }); dataTextarea.addEventListener('blur', () => { dataTextarea.style.borderColor = '#ccc'; dataTextarea.style.boxShadow = 'none'; }); importModal.appendChild(dataTextarea); const confirmButton = createButton('确认导入', () => { const dataStr = dataTextarea.value.trim(); if (!dataStr) { alert('导入数据不能为空,请重新输入。'); return; } try { const lines = dataStr.split(';'); const importedItems = []; for (const line of lines) { const [content, note] = line.split(','); if (content && note) { importedItems.push({ content: content.trim(), note: note.trim() }); } else { throw new Error('导入数据格式错误,请检查。'); } } // 清空现有条目时的动画 const existingItems = document.querySelectorAll('.tax-item'); existingItems.forEach((item, index) => { setTimeout(() => { item.style.opacity = '0'; item.style.transform = 'translateY(10px)'; if (index === existingItems.length - 1) { setTimeout(() => { itemList.innerHTML = ''; GM_setValue('autoFillItems', importedItems); importedItems.forEach((item, i) => { setTimeout(() => { addItemToWindow(item.content, item.note, itemList); }, i * 80); }); alert('数据导入成功!'); // 关闭动画 importModal.style.opacity = '0'; importModal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(importModal); document.body.removeChild(overlay); }, 300); }, 300); } }, index * 50); }); } catch (error) { alert(`数据导入失败:${error.message}`); } }); importModal.appendChild(confirmButton); const cancelButton = createButton('取消', () => { // 关闭动画 importModal.style.opacity = '0'; importModal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(importModal); document.body.removeChild(overlay); }, 300); }, true); importModal.appendChild(cancelButton); // 点击遮罩关闭模态框 overlay.addEventListener('click', () => { importModal.style.opacity = '0'; importModal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(importModal); document.body.removeChild(overlay); }, 300); }); document.body.appendChild(importModal); } // 导出数据 function exportData() { const items = GM_getValue('autoFillItems', []); const dataStr = items.map(item => `${item.content},${item.note}`).join(';\n'); const exportModal = document.createElement('div'); Object.assign(exportModal.style, { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: '#fff', border: '1px solid #ccc', padding: '20px', borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)', zIndex: 10001, display: 'flex', flexDirection: 'column', gap: '10px', width: '350px', opacity: '0', transform: 'translate(-50%, -50%) scale(0.95)', transition: 'opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)' }); // 添加模态框背景遮罩 const overlay = document.createElement('div'); Object.assign(overlay.style, { position: 'fixed', top: '0', left: '0', right: '0', bottom: '0', background: 'rgba(0,0,0,0.5)', zIndex: 10000, opacity: '0', transition: 'opacity 0.3s ease' }); document.body.appendChild(overlay); // 触发动画 setTimeout(() => { exportModal.style.opacity = '1'; exportModal.style.transform = 'translate(-50%, -50%) scale(1)'; overlay.style.opacity = '0.5'; }, 10); const dataTextarea = document.createElement('textarea'); dataTextarea.value = dataStr; dataTextarea.readOnly = true; dataTextarea.style.padding = '8px'; dataTextarea.style.border = '1px solid #ccc'; dataTextarea.style.borderRadius = '3px'; dataTextarea.style.height = '150px'; exportModal.appendChild(dataTextarea); const copyButton = createButton('复制数据', () => { dataTextarea.select(); document.execCommand('copy'); // 复制成功的反馈动画 copyButton.textContent = '已复制!'; copyButton.style.background = '#28a745'; setTimeout(() => { copyButton.textContent = '复制数据'; copyButton.style.background = '#007BFF'; }, 1500); }); exportModal.appendChild(copyButton); const closeButton = createButton('关闭', () => { // 关闭动画 exportModal.style.opacity = '0'; exportModal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(exportModal); document.body.removeChild(overlay); }, 300); }, true); exportModal.appendChild(closeButton); // 点击遮罩关闭模态框 overlay.addEventListener('click', () => { exportModal.style.opacity = '0'; exportModal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(exportModal); document.body.removeChild(overlay); }, 300); }); document.body.appendChild(exportModal); } // 切换账号自动填写 function toggleAccountAutoFill() { // 添加切换按钮的动画效果 accountAutoFillSwitch.style.transform = 'scale(0.9)'; accountAutoFillSwitch.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; setTimeout(() => { accountAutoFillSwitch.style.transform = 'scale(1)'; accountAutoFillSwitch.style.boxShadow = 'none'; }, 150); isAutoFillAccount =!isAutoFillAccount; accountAutoFillSwitch.textContent = isAutoFillAccount? '自动填账号密码: 开' : '自动填账号密码: 关'; GM_setValue('isAutoFillAccount', isAutoFillAccount); if (isAutoFillAccount) { if (!accountInfo) { const modal = createAccountInfoModal(); document.body.appendChild(modal); } else { setTimeout(() => { const accountInput = findElementInIframes('input[placeholder="居民身份证号码/手机号码/用户名"]') || findElementInIframes('input.el-input__inner[placeholder="居民身份证号码/手机号码/用户名"]'); const passwordInput = findElementInIframes('input[placeholder="个人用户密码"]') || findElementInIframes('input.el-input__inner[placeholder="个人用户密码"]'); if (accountInput) { accountInput.value = accountInfo.account; triggerInputEvents(accountInput); } if (passwordInput) { passwordInput.value = accountInfo.password; triggerInputEvents(passwordInput); } }, 500); // 调整为500ms } } else { accountInfo = null; GM_setValue('accountInfo', null); const accountInput = findElementInIframes('input[placeholder="居民身份证号码/手机号码/用户名"]') || findElementInIframes('input.el-input__inner[placeholder="居民身份证号码/手机号码/用户名"]'); const passwordInput = findElementInIframes('input[placeholder="个人用户密码"]') || findElementInIframes('input.el-input__inner[placeholder="个人用户密码"]'); if (accountInput) { accountInput.value = ''; triggerInputEvents(accountInput); } if (passwordInput) { passwordInput.value = ''; triggerInputEvents(passwordInput); } } } // 创建账号信息模态框 function createAccountInfoModal() { const modal = document.createElement('div'); Object.assign(modal.style, { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: '#fff', border: '1px solid #ccc', padding: '20px', borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)', zIndex: 10001, display: 'flex', flexDirection: 'column', gap: '10px', width: '300px', opacity: '0', transform: 'translate(-50%, -50%) scale(0.95)', transition: 'opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)' }); // 添加模态框背景遮罩 const overlay = document.createElement('div'); Object.assign(overlay.style, { position: 'fixed', top: '0', left: '0', right: '0', bottom: '0', background: 'rgba(0,0,0,0.5)', zIndex: 10000, opacity: '0', transition: 'opacity 0.3s ease' }); document.body.appendChild(overlay); // 触发动画 setTimeout(() => { modal.style.opacity = '1'; modal.style.transform = 'translate(-50%, -50%) scale(1)'; overlay.style.opacity = '0.5'; }, 10); const accountInput = document.createElement('input'); accountInput.placeholder = '请输入账号'; accountInput.style.padding = '8px'; accountInput.style.border = '1px solid #ccc'; accountInput.style.borderRadius = '3px'; accountInput.style.transition = 'border-color 0.2s ease'; accountInput.addEventListener('focus', () => { accountInput.style.borderColor = '#007BFF'; accountInput.style.boxShadow = '0 0 0 2px rgba(0, 123, 255, 0.25)'; }); accountInput.addEventListener('blur', () => { accountInput.style.borderColor = '#ccc'; accountInput.style.boxShadow = 'none'; }); modal.appendChild(accountInput); const passwordInput = document.createElement('input'); passwordInput.type = 'password'; passwordInput.placeholder = '请输入密码'; passwordInput.style.padding = '8px'; passwordInput.style.border = '1px solid #ccc'; passwordInput.style.borderRadius = '3px'; passwordInput.style.transition = 'border-color 0.2s ease'; passwordInput.addEventListener('focus', () => { passwordInput.style.borderColor = '#007BFF'; passwordInput.style.boxShadow = '0 0 0 2px rgba(0, 123, 255, 0.25)'; }); passwordInput.addEventListener('blur', () => { passwordInput.style.borderColor = '#ccc'; passwordInput.style.boxShadow = 'none'; }); modal.appendChild(passwordInput); const confirmButton = createButton('确认', () => { const account = accountInput.value.trim(); const password = passwordInput.value.trim(); if (!account ||!password) { alert('账号和密码都不能为空,请重新输入。'); return; } accountInfo = { account, password }; GM_setValue('accountInfo', accountInfo); setTimeout(() => { const accountInputOnPage = findElementInIframes('input[placeholder="居民身份证号码/手机号码/用户名"]') || findElementInIframes('input.el-input__inner[placeholder="居民身份证号码/手机号码/用户名"]'); const passwordInputOnPage = findElementInIframes('input[placeholder="个人用户密码"]') || findElementInIframes('input.el-input__inner[placeholder="个人用户密码"]'); if (accountInputOnPage) { accountInputOnPage.value = account; triggerInputEvents(accountInputOnPage); } if (passwordInputOnPage) { passwordInputOnPage.value = password; triggerInputEvents(passwordInputOnPage); } }, 500); // 调整为500ms }); modal.appendChild(confirmButton); const cancelButton = createButton('取消', () => { isAutoFillAccount = false; accountAutoFillSwitch.textContent = '自动填账号密码: 关'; GM_setValue('isAutoFillAccount', isAutoFillAccount); // 关闭动画 modal.style.opacity = '0'; modal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(modal); document.body.removeChild(overlay); }, 300); }, true); modal.appendChild(cancelButton); // 点击遮罩关闭模态框 overlay.addEventListener('click', () => { modal.style.opacity = '0'; modal.style.transform = 'translate(-50%, -50%) scale(0.95)'; overlay.style.opacity = '0'; setTimeout(() => { document.body.removeChild(modal); document.body.removeChild(overlay); }, 300); }); return modal; } // 自动填充账号密码(如果开启) if (isAutoFillAccount && accountInfo) { setTimeout(() => { const accountInput = findElementInIframes('input[placeholder="居民身份证号码/手机号码/用户名"]') || findElementInIframes('input.el-input__inner[placeholder="居民身份证号码/手机号码/用户名"]'); const passwordInput = findElementInIframes('input[placeholder="个人用户密码"]') || findElementInIframes('input.el-input__inner[placeholder="个人用户密码"]'); if (accountInput) { accountInput.value = accountInfo.account; triggerInputEvents(accountInput); } if (passwordInput) { passwordInput.value = accountInfo.password; triggerInputEvents(passwordInput); } }, 500); // 调整为500ms } })();