// ==UserScript== // @name 麦芽田备注图片下载器 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 一键下载麦芽田订单备注中的所有图片 // @author @Cland // @match *://*.maiyatian.com/* // @grant GM_download // ==/UserScript== (function() { 'use strict'; // 修改监听逻辑,不再在找到元素后立即断开观察 const observer = new MutationObserver((mutations, obs) => { checkAndAddButton(); }); // 开始观察 observer.observe(document.body, { childList: true, subtree: true }); function checkAndAddButton() { // 修改选择器以匹配实际的 DOM 结构 const remarkElements = document.querySelectorAll('.order-goods .order-remark'); remarkElements.forEach(remarkElement => { // 只处理包含"用户备注"的 order-remark 元素 const titleElement = remarkElement.querySelector('.order-remark-title'); if (titleElement && titleElement.textContent.trim() === '用户备注') { const contentElement = remarkElement.querySelector('.order-remark-content'); // 添加下载按钮 if (titleElement && !titleElement.querySelector('.download-remark-images')) { addDownloadButton(titleElement); } // 添加图片展示区域 if (contentElement && !remarkElement.querySelector('.remark-preview-container')) { addImagePreviewArea(remarkElement, contentElement); } // 添加文本着色 if (contentElement) { colorizeRemarkText(contentElement); } // 修改悬浮预览窗口逻辑 if (contentElement) { updateFloatingPreview(contentElement); } } }); // 添加隐藏打印贺卡按钮的功能 hideCardPrintButton(); } function addDownloadButton(titleElement) { // 创建下载按钮 const downloadBtn = document.createElement('button'); downloadBtn.innerHTML = '下载所有备注图片'; downloadBtn.className = 'download-remark-images'; downloadBtn.style.cssText = ` margin-left: 10px; padding: 2px 8px; color: #2DAED6; border: 1px solid #2DAED6; border-radius: 3px; background: white; cursor: pointer; `; // 添加点击事件 downloadBtn.addEventListener('click', function() { const remarkElement = titleElement.closest('.order-remark'); downloadAllImages(remarkElement); }); // 将按钮添加到标题元素后面 titleElement.appendChild(downloadBtn); } function addImagePreviewArea(remarkElement, contentElement) { // 创建整个预览区域的容器 const previewContainer = document.createElement('div'); previewContainer.className = 'remark-preview-container'; previewContainer.style.cssText = ` margin: 10px 0; background-color: #fff; border-radius: 4px; padding: 15px; `; // ���建图片预览区域 const previewArea = document.createElement('div'); previewArea.className = 'remark-images-preview'; previewArea.style.cssText = ` display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 15px; `; // 解析备注内容 const text = contentElement.textContent; const imageMatches = text.matchAll(/\[(.*?)\](https?:\/\/[^\s]+?\.(jpg|jpeg|png|gif))/g); // 处理图片 const images = Array.from(imageMatches); images.forEach(([_, label, url]) => { const imageContainer = document.createElement('div'); imageContainer.style.cssText = ` aspect-ratio: 1; position: relative; `; const image = document.createElement('img'); image.src = url; image.style.cssText = ` width: 100%; height: 100%; object-fit: cover; border-radius: 4px; cursor: pointer; border: 1px solid #E7E7E7; `; const labelDiv = document.createElement('div'); labelDiv.textContent = label; labelDiv.style.cssText = ` position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.6); color: white; font-size: 12px; padding: 4px; text-align: center; border-radius: 0 0 4px 4px; `; // 为定制花束图片和其他定制要求图片添加下载按钮 if (label.includes('定制花束图片') || label.includes('其他定制要求图片')) { const downloadBtn = document.createElement('button'); downloadBtn.textContent = '⬇️'; downloadBtn.style.cssText = ` position: absolute; top: 8px; right: 8px; padding: 4px 8px; background: rgba(45, 174, 214, 0.9); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; z-index: 2; transition: background 0.2s; `; // 悬停效果 downloadBtn.addEventListener('mouseenter', () => { downloadBtn.style.background = 'rgba(45, 174, 214, 1)'; }); downloadBtn.addEventListener('mouseleave', () => { downloadBtn.style.background = 'rgba(45, 174, 214, 0.9)'; }); // 添加点击事件 downloadBtn.addEventListener('click', (e) => { e.stopPropagation(); // 防止触发图片预览 const filename = `${label}.${url.split('.').pop()}`; GM_download({ url: url, name: filename, saveAs: false, onerror: (error) => console.error('下载失败:', error) }); }); imageContainer.appendChild(downloadBtn); } imageContainer.appendChild(image); imageContainer.appendChild(labelDiv); previewArea.appendChild(imageContainer); // 点击图片放大预览 image.addEventListener('click', () => { const modal = createImageModal(url, label); document.body.appendChild(modal); }); }); // 组装所有元素 previewContainer.appendChild(previewArea); contentElement.parentNode.insertBefore(previewContainer, contentElement.nextSibling); } function createImageModal(url, label) { const modal = document.createElement('div'); modal.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 9999; cursor: pointer; `; const img = document.createElement('img'); img.src = url; img.style.cssText = ` max-width: 90vw; max-height: 80vh; object-fit: contain; margin-bottom: 15px; `; const downloadBtn = document.createElement('button'); downloadBtn.textContent = '下载图片'; downloadBtn.style.cssText = ` padding: 8px 16px; background: #2DAED6; color: white; border: none; border-radius: 4px; cursor: pointer; margin-top: 10px; `; downloadBtn.addEventListener('click', (e) => { e.stopPropagation(); const filename = `${label}.${url.split('.').pop()}`; GM_download({ url: url, name: filename, saveAs: false, onerror: (error) => console.error('下载失败:', error) }); }); modal.appendChild(img); modal.appendChild(downloadBtn); modal.addEventListener('click', () => modal.remove()); return modal; } function downloadAllImages(remarkElement) { if (!remarkElement) return; const remarkContent = remarkElement.querySelector('.order-remark-content'); if (!remarkContent) return; // 使用新的正则表达式匹配带标签的图片URL const text = remarkContent.textContent; const matches = text.matchAll(/\[(.*?)\](https?:\/\/[^\s]+?\.(jpg|jpeg|png|gif))/g); const urlsWithLabels = Array.from(matches, match => ({ label: match[1], url: match[2] })); if (!urlsWithLabels.length) { alert('未找到可下载的图片链接'); return; } // 下载所有找到的图片 urlsWithLabels.forEach(({label, url}, index) => { const filename = `${label}_${index + 1}.${url.split('.').pop()}`; GM_download({ url: url, name: filename, saveAs: false, onerror: (error) => console.error('下载失败:', error) }); }); } // 修改文本着色函数 function colorizeRemarkText(contentElement) { let text = contentElement.textContent; // 创建一个容器来包装所有内容 const container = document.createElement('div'); container.style.cssText = ` display: flex; flex-direction: column; gap: 8px; `; // 匹配所有[标签]内容格式的文本 const matches = text.matchAll(/\[(.*?)\]([^\[]*)/g); for (const match of matches) { const [_, label, content] = match; // 创建每行的容器 const lineContainer = document.createElement('div'); lineContainer.style.cssText = ` display: flex; align-items: flex-start; gap: 8px; `; // 创建标签元素(不可复制) const labelElement = document.createElement('div'); labelElement.style.cssText = ` user-select: none; background: #f5f5f5; padding: 2px 8px; border-radius: 4px; font-size: 12px; white-space: nowrap; `; // 根据标签类型设置颜��� if (label.includes('图片') || label.includes('定制花束') || label.includes('其他定制要求')) { labelElement.style.color = '#999999'; } else if (label.includes('其他定制备注') || label.includes('贺卡')) { labelElement.style.color = '#000000'; } labelElement.textContent = `[${label}]`; // 创建内容元素 const contentElement = document.createElement('div'); contentElement.style.cssText = ` flex: 1; word-break: break-all; `; contentElement.textContent = content.trim(); // 根据标签类型设置内容颜色 if (label.includes('图片') || label.includes('定制花束') || label.includes('其他定制要求')) { contentElement.style.color = '#999999'; } else if (label.includes('其他定制备注') || label.includes('贺卡')) { contentElement.style.color = '#000000'; } // 组装每行的元素 lineContainer.appendChild(labelElement); lineContainer.appendChild(contentElement); container.appendChild(lineContainer); } // 清空原有内容并添加新的格式化内容 contentElement.textContent = ''; contentElement.appendChild(container); } // 新增更新预览内容的函数 function updateFloatingPreview(contentElement) { let floatingWindow = document.querySelector('.floating-remark-preview'); let toggleBtn = document.querySelector('.floating-preview-toggle'); // 如果预览窗口不存在,则创建 if (!floatingWindow) { const { window: newWindow, button: newButton } = createFloatingPreview(); floatingWindow = newWindow; toggleBtn = newButton; } // 更新预览内容 const content = floatingWindow.querySelector('.preview-content'); if (content) { updatePreviewContent(content, contentElement); } } // 修改创建预览窗口的函数 function createFloatingPreview() { // 创建悬浮窗触发按钮 const toggleBtn = document.createElement('button'); toggleBtn.className = 'floating-preview-toggle'; toggleBtn.innerHTML = '📝 备注预览'; toggleBtn.style.cssText = ` position: fixed; right: 20px; top: 80px; z-index: 9998; padding: 8px 16px; background: #2DAED6; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); user-select: none; `; // 创建悬浮预览窗口 const floatingWindow = document.createElement('div'); floatingWindow.className = 'floating-remark-preview'; floatingWindow.style.cssText = ` position: fixed; right: 20px; top: 130px; width: 400px; max-height: 70vh; background: white; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9997; overflow: hidden; display: none; transition: all 0.3s ease; `; // 创建预览窗口头部 const header = document.createElement('div'); header.style.cssText = ` padding: 12px 16px; background: #f5f5f5; border-bottom: 1px solid #e8e8e8; font-weight: bold; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none; `; header.innerHTML = '备注预览'; // 修改内容容器,添加类名以便后续更新 const content = document.createElement('div'); content.className = 'preview-content'; content.style.cssText = ` padding: 16px; overflow-y: auto; max-height: calc(70vh - 45px); `; // 组装窗口 floatingWindow.appendChild(header); floatingWindow.appendChild(content); // 添加展开/折叠功能 let isExpanded = false; toggleBtn.addEventListener('click', () => { isExpanded = !isExpanded; floatingWindow.style.display = isExpanded ? 'block' : 'none'; toggleBtn.innerHTML = isExpanded ? '📝 关闭预览' : '📝 备注预览'; }); // 添加到页面 document.body.appendChild(toggleBtn); document.body.appendChild(floatingWindow); // 添加拖拽功能(同时支持按钮和窗口的拖拽) let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; let dragTarget = null; function dragStart(e, target) { initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; dragTarget = target; isDragging = true; } function drag(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; setTranslate(currentX, currentY, dragTarget); // 如果拖动的是按钮,同时更新窗口位置 if (dragTarget === toggleBtn) { const windowX = currentX + (parseFloat(floatingWindow.style.right) || 20); const windowY = currentY + (parseFloat(floatingWindow.style.top) || 130); setTranslate(windowX, windowY, floatingWindow); } } } function dragEnd() { initialX = currentX; initialY = currentY; isDragging = false; dragTarget = null; } function setTranslate(xPos, yPos, el) { el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`; } // 为按钮添加拖拽事件 toggleBtn.addEventListener('mousedown', (e) => dragStart(e, toggleBtn)); // 为窗口头部添加拖拽事件 header.addEventListener('mousedown', (e) => dragStart(e, floatingWindow)); // 添加全局拖拽事件监听 document.addEventListener('mousemove', drag); document.addEventListener('mouseup', dragEnd); // 返回窗口和按钮引用 return { window: floatingWindow, button: toggleBtn }; } // 新增更新预览内容的函数 function updatePreviewContent(previewContent, originalContent) { // 清空现有内容 previewContent.textContent = ''; // 创建预览容器 const previewContainer = document.createElement('div'); previewContainer.className = 'remark-preview-container'; previewContainer.style.cssText = ` background-color: #fff; border-radius: 4px; `; // 创建图片预览区域 const previewArea = document.createElement('div'); previewArea.className = 'remark-images-preview'; previewArea.style.cssText = ` display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-bottom: 12px; `; // 解析备注内容并添加图片 const text = originalContent.textContent; // 修改正则表达式以匹配所有类型的图片 const imageMatches = text.matchAll(/\[(.*?(?:图片|定制花束|其他定制要求).*?)\](https?:\/\/[^\s]+?\.(jpg|jpeg|png|gif))/g); const images = Array.from(imageMatches); images.forEach(([_, label, url]) => { const imageContainer = document.createElement('div'); imageContainer.style.cssText = ` aspect-ratio: 1; position: relative; `; const image = document.createElement('img'); image.src = url; image.style.cssText = ` width: 100%; height: 100%; object-fit: cover; border-radius: 4px; cursor: pointer; border: 1px solid #E7E7E7; `; const labelDiv = document.createElement('div'); labelDiv.textContent = label; labelDiv.style.cssText = ` position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.6); color: white; font-size: 12px; padding: 4px; text-align: center; border-radius: 0 0 4px 4px; `; // 为定制花束图片和其他定制要求图片添加下载按钮 if (label.includes('定制花束图片') || label.includes('其他定制要求图片')) { const downloadBtn = document.createElement('button'); downloadBtn.textContent = '⬇️'; downloadBtn.style.cssText = ` position: absolute; top: 8px; right: 8px; padding: 4px 8px; background: rgba(45, 174, 214, 0.9); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; z-index: 2; transition: background 0.2s; `; // 悬停效果 downloadBtn.addEventListener('mouseenter', () => { downloadBtn.style.background = 'rgba(45, 174, 214, 1)'; }); downloadBtn.addEventListener('mouseleave', () => { downloadBtn.style.background = 'rgba(45, 174, 214, 0.9)'; }); // 添加点击事件 downloadBtn.addEventListener('click', (e) => { e.stopPropagation(); // 防止触发图片预览 const filename = `${label}.${url.split('.').pop()}`; GM_download({ url: url, name: filename, saveAs: false, onerror: (error) => console.error('下载失败:', error) }); }); imageContainer.appendChild(downloadBtn); } imageContainer.appendChild(image); imageContainer.appendChild(labelDiv); previewArea.appendChild(imageContainer); // 点击图片放大预览 image.addEventListener('click', () => { const modal = createImageModal(url, label); document.body.appendChild(modal); }); }); // 添加备注���本内容 const remarkText = document.createElement('div'); remarkText.style.cssText = ` margin-top: 12px; line-height: 1.5; `; remarkText.textContent = text; colorizeRemarkText(remarkText); // 组装预览窗口 previewContainer.appendChild(previewArea); previewContainer.appendChild(remarkText); previewContent.appendChild(previewContainer); } // 新增隐藏打印贺卡按钮的函数 function hideCardPrintButton() { // 添加样式到页面 const style = document.createElement('style'); style.textContent = ` .order-remark-print { display: none !important; } `; // 避免重复添加样式 if (!document.querySelector('#hide-print-button-style')) { style.id = 'hide-print-button-style'; document.head.appendChild(style); } } // 初始检查 checkAndAddButton(); // 监听 PJAX 事件 document.addEventListener('pjax:complete', checkAndAddButton); window.addEventListener('popstate', checkAndAddButton); window.addEventListener('load', checkAndAddButton); })();