// ==UserScript==
// @name TEMU商品卡定位
// @namespace http://tampermonkey.net/
// @version 5.1
// @description tfind + 商品图片一键下载
// @author binning
// @match https://www.temu.com/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const style = document.createElement('style');
style.textContent = `
* { margin: 0; padding: 0; box-sizing: border-box; }
/* 悬浮触发小圆点 */
.tool-float-btn {
position: fixed;
left: 15px;
top: 15px;
width: 44px;
height: 44px;
border-radius: 50%;
background: #409eff;
color: #fff;
font-size: 18px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 100000;
box-shadow: 0 4px 15px rgba(64,158,255,0.35);
user-select: none;
transition: all 0.25s ease;
}
.tool-float-btn:hover {
transform: scale(1.08);
background: #66b1ff;
}
/* 主工具面板 */
.goods-tool-panel {
position: fixed;
left: 15px;
top: 15px;
z-index: 99999;
background: #ffffff;
width:600px;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
overflow: hidden;
border: 1px solid #ebeef5;
font-family: "Microsoft Yahei", sans-serif;
display: none;
}
.tool-header {
padding: 14px 18px;
background: #f7f8fa;
border-bottom: 1px solid #ebeef5;
display: flex;
align-items: center;
gap: 12px;
}
.tool-header .refresh-btn,
.tool-header .export-btn,
.tool-header .downloadAll-btn {
padding: 7px 16px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 13px;
transition: all 0.2s ease;
}
.refresh-btn { background: #409eff; color: #fff; }
.refresh-btn:hover { background: #66b1ff; }
.export-btn { background: #67c23a; color: #fff; }
.export-btn:hover { background: #85ce61; }
.downloadAll-btn { background: #e6a23c; color: #fff; }
.downloadAll-btn:hover { background: #ebb563; }
.tool-header label {
font-size: 13px;
color: #606266;
user-select: none;
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
}
.tool-header input[type="checkbox"] {
width: 15px;
height: 15px;
cursor: pointer;
}
table td:nth(2)-child{
width:100px
}
table td:last-child{
width:190px
}
.table-body {
max-height: 550px;
overflow-y: auto;
}
.table-body::-webkit-scrollbar { width: 6px; }
.table-body::-webkit-scrollbar-thumb { background: #dcdfe6; border-radius: 3px; }
.table-body::-webkit-scrollbar-track { background: #f5f7fa; }
#goodsTable {
width: 100%;
border-collapse: collapse;
font-size: 12.5px;
color: #303133;
}
#goodsTable th {
position: sticky;
top: 0;
background: #f5f7fa;
z-index: 10;
font-weight: 500;
color: #606266;
}
#goodsTable th, #goodsTable td {
border: 1px solid #ebeef5;
padding: 9px 6px;
text-align: center;
}
#goodsTable th {
cursor: pointer;
user-select: none;
transition: background 0.2s;
}
#goodsTable th:hover { background: #ecf5ff; }
#goodsTable tbody tr:hover { background: #fafafa; }
#goodsTable .title {
cursor: pointer;
max-width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #303133;
}
#goodsTable .title:hover { color: #409eff; }
#goodsTable .preview-img {
width: 52px;
height: 52px;
object-fit: cover;
cursor: pointer;
border-radius: 6px;
transition: transform 0.2s;
}
#goodsTable .preview-img:hover { transform: scale(1.08); }
.pos-btn {
background: #409eff;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.pos-btn:hover { background: #66b1ff; }
.download-btn {
background: #67c23a;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
margin-left: 5px;
transition: all 0.2s;
}
.download-btn:hover { background: #85ce61; }
.del-btn {
background: #f56c6c;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
margin-left: 5px;
transition: all 0.2s;
}
.del-btn:hover { background: #f78989; }
.img-modal {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.85); display: none;
justify-content: center; align-items: center;
z-index: 100000; cursor: zoom-out;
}
.img-modal img {
max-width: 90%; max-height: 90%;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
}
.highlight-goods {
animation: highlight 1.4s ease;
border: 3px solid #409eff !important;
border-radius: 8px;
box-sizing: border-box;
}
@keyframes highlight {
0% { background: rgba(64,158,255,0.1); }
50% { background: rgba64,158,255,0.2); }
100% { background: transparent; }
}
`;
document.head.appendChild(style);
// 悬浮圆点
const floatBtn = document.createElement('div');
floatBtn.className = 'tool-float-btn';
floatBtn.innerText = 'T';
document.body.appendChild(floatBtn);
// 主面板
const panel = document.createElement('div');
panel.className = 'goods-tool-panel';
panel.innerHTML = `
`;
document.body.appendChild(panel);
// 展开/收缩切换
let panelShow = false;
floatBtn.onclick = () => {
panelShow = !panelShow;
panel.style.display = panelShow ? 'block' : 'none';
};
// 图片放大弹窗
const imgModal = document.createElement('div');
imgModal.className = 'img-modal';
const modalImg = document.createElement('img');
imgModal.appendChild(modalImg);
document.body.appendChild(imgModal);
imgModal.onclick = () => imgModal.style.display = 'none';
let currentData = [];
let sortField = 'index';
let sortOrder = 'asc';
let onlyHasSales = false;
// 清理文件名非法字符
function safeFileName(name) {
return name.replace(/[\\/:*?"<>|]/g, '_').trim();
}
function cleanImgSrc(src) {
if (!src) return '';
return src.split('?')[0];
}
function parseSales(text) {
if (!text) return 0;
let lower = text.toLowerCase().trim();
if (lower.includes('万')) {
let num = lower.replace(/[^\d.]/g, '');
return parseFloat(num) * 10000 || 0;
}
let numStr = lower.replace(/[^\d]/g, '');
return parseInt(numStr) || 0;
}
function getSalesNumber(text) {
return parseSales(text);
}
function escapeHtml(str) {
return str?.replace(/"/g, '"').replace(//g, '>') || '';
}
// 下载图片:文件名 = 销量 标题
async function downloadImage(url, salesText, title) {
if (!url) return;
// 拼接:销量 标题
let fileName = `${salesText} ${title}`;
fileName = safeFileName(fileName);
try {
const response = await fetch(url, { mode: 'cors' });
const blob = await response.blob();
const blobUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = fileName + '.jpg';
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(blobUrl);
}, 100);
} catch (err) {
const a = document.createElement('a');
a.href = url;
a.download = fileName + '.jpg';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
}
// 下载全部图片
async function downloadAllImages() {
const rows = document.querySelectorAll('#goodsTable tr:not(:first-child)');
if (!rows.length) {
alert('暂无图片可下载');
return;
}
let count = 0;
for (const tr of rows) {
const img = tr.cells[3].querySelector('img');
const src = img?.dataset?.fullsrc;
const salesText = tr.cells[2].textContent.trim();
const title = tr.cells[1].dataset.full || '无标题';
if (src) {
await downloadImage(src, salesText, title);
count++;
await new Promise(r => setTimeout(r, 300));
}
}
alert(`批量下载完成!\n成功下载 ${count} 张图片`);
}
function fetchData() {
const goodsList = [...document.querySelectorAll('.EKDT7a3v'), ...document.querySelectorAll('._2gR7cTnt')];
const data = [];
goodsList.forEach((item, idx) => {
const titleEl = item.querySelector('._2D9RBAXL');
let salesText = '暂无销量';
const wrapNd = item.querySelector('._7LLqXcNd');
if (wrapNd) {
const saleItems = wrapNd.querySelectorAll('._2XgTiMJi');
if (saleItems.length) {
const lastSale = saleItems[saleItems.length - 1];
salesText = lastSale.textContent.trim();
}
}
const imgEl = item.querySelector('.goods-img-external');
const rawSrc = imgEl?.src || '';
const cleanSrc = cleanImgSrc(rawSrc);
data.push({
index: idx,
title: titleEl ? titleEl.textContent.trim() : '无标题',
salesText: salesText,
sales: parseSales(salesText),
rawSrc: rawSrc,
cleanSrc: cleanSrc,
element: item
});
});
currentData = data;
}
function sortData() {
currentData.sort((a, b) => {
if (sortField === 'index') return a.index - b.index;
if (sortField === 'sales') return sortOrder === 'asc' ? a.sales - b.sales : b.sales - a.sales;
if (sortField === 'title') return sortOrder === 'asc' ? a.title.localeCompare(b.title) : b.title.localeCompare(a.title);
return 0;
});
}
function renderTable() {
fetchData();
sortData();
const showData = onlyHasSales ? currentData.filter(i => i.sales > 0) : currentData;
let html = `
| 序号 |
标题 |
销量 |
预览图 |
操作 |
`;
showData.forEach((item, i) => {
html += `
| ${i + 1} |
${item.title} |
${item.salesText} |
 |
|
`;
});
html += '
';
document.querySelector('.table-body').innerHTML = html;
const rows = document.querySelectorAll('#goodsTable tbody tr');
showData.forEach((item, i) => {
rows[i].dataItem = item;
});
bindEvents();
}
function bindEvents() {
document.querySelectorAll('#goodsTable .title').forEach(el => {
el.onclick = () => navigator.clipboard.writeText(el.dataset.full)
.then(() => alert('已复制标题'))
.catch(() => alert('复制失败'));
});
document.querySelectorAll('.preview-img').forEach(el => {
el.onclick = () => {
modalImg.src = el.dataset.fullsrc;
imgModal.style.display = 'flex';
};
});
document.querySelectorAll('.pos-btn').forEach(btn => {
btn.onclick = () => {
const row = btn.closest('tr');
const item = row.dataItem;
if (!item || !item.element) return;
item.element.scrollIntoView({ behavior: 'smooth', block: 'center' });
item.element.classList.add('highlight-goods');
setTimeout(() => item.element.classList.remove('highlight-goods'), 1400);
};
});
// 单个下载:传销量+标题
document.querySelectorAll('.download-btn').forEach(btn => {
btn.onclick = () => {
const row = btn.closest('tr');
const item = row.dataItem;
const salesText = row.cells[2].textContent.trim();
if (!item || !item.cleanSrc) {
alert('暂无图片可下载');
return;
}
downloadImage(item.cleanSrc, salesText, item.title);
};
});
document.querySelectorAll('.del-btn').forEach(btn => {
btn.onclick = () => btn.closest('tr').remove();
});
document.querySelectorAll('#goodsTable th[data-sort]').forEach(th => {
th.onclick = () => {
if (sortField === th.dataset.sort) {
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
} else {
sortField = th.dataset.sort;
sortOrder = 'asc';
}
renderTable();
};
});
}
function exportCSV() {
const rows = document.querySelectorAll('#goodsTable tr:not(:first-child)');
if (!rows.length) { alert('暂无数据可导出'); return; }
let csv = '\ufeff序号,商品标题,销量,图片地址\r\n';
rows.forEach(tr => {
const seq = tr.cells[0].textContent.trim();
const title = `"${tr.cells[1].dataset.full.replace(/"/g, '""')}"`;
const salesText = tr.cells[2].textContent.trim();
const salesNum = getSalesNumber(salesText);
const img = tr.cells[3].querySelector('img').dataset.fullsrc;
csv += `${seq},${title},${salesNum},${img}\r\n`;
});
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = '商品数据.csv';
a.click();
URL.revokeObjectURL(a.href);
}
// 绑定按钮事件
document.querySelector('.refresh-btn').onclick = renderTable;
document.querySelector('.export-btn').onclick = exportCSV;
document.querySelector('.downloadAll-btn').onclick = downloadAllImages;
document.getElementById('onlySalesCheck').addEventListener('change', e => {
onlyHasSales = e.target.checked;
renderTable();
});
// 初始渲染
window.addEventListener('load', () => {
renderTable();
});
})();