// ==UserScript==
// @name 淘宝买家订单导出插件增强版
// @namespace http://tampermonkey.net/
// @version 0.7
// @description 基于淘宝买家订单导出插件做的增强版,添加自动记录、订单计数等功能
// @author kubrick
// @include https://buyertrade.taobao.com/trade/itemlist*
// @include https://buyertrade.taobao.com/trade/itemlist*
// @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 创建顶部控制栏容器
function createControlBar() {
const existingBar = document.getElementById('orderExportControlBar');
if (existingBar) return existingBar;
const controlBar = document.createElement('div');
controlBar.id = 'orderExportControlBar';
controlBar.style.position = 'fixed';
controlBar.style.top = '10px';
controlBar.style.right = '10px';
controlBar.style.zIndex = '9999';
controlBar.style.backgroundColor = 'rgba(255, 255, 255, 0.9)';
controlBar.style.padding = '10px';
controlBar.style.borderRadius = '10px';
controlBar.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.2)';
controlBar.style.display = 'flex';
controlBar.style.flexDirection = 'column';
controlBar.style.gap = '10px';
document.body.appendChild(controlBar);
return controlBar;
}
// 添加圆角按钮
function addButton(container, onclickFunc, color, value = "按钮", width = "120px", height = "40px") {
const button = document.createElement("button");
button.textContent = value;
button.style.height = height;
button.style.width = width;
button.style.color = "white";
button.style.background = color;
button.style.border = "none";
button.style.borderRadius = "20px";
button.style.cursor = "pointer";
button.style.fontWeight = "bold";
button.style.fontSize = "14px";
button.style.transition = "all 0.3s";
button.onmouseover = function() {
button.style.opacity = "0.8";
button.style.transform = "scale(1.05)";
};
button.onmouseout = function() {
button.style.opacity = "1";
button.style.transform = "scale(1)";
};
button.onclick = function() {
onclickFunc();
};
container.appendChild(button);
return button;
}
// 添加订单计数显示
function addOrderCountDisplay(container) {
const countDisplay = document.createElement("div");
countDisplay.id = "orderCountDisplay";
countDisplay.style.padding = "8px 15px";
countDisplay.style.backgroundColor = "#f5f5f5";
countDisplay.style.borderRadius = "20px";
countDisplay.style.textAlign = "center";
countDisplay.style.fontWeight = "bold";
countDisplay.textContent = "当前订单数: 0";
container.appendChild(countDisplay);
return countDisplay;
}
// 更新订单计数显示
function updateOrderCount() {
const countDisplay = document.getElementById("orderCountDisplay");
if (countDisplay) {
countDisplay.textContent = `当前订单数: ${Object.keys(orderList).length}`;
}
}
// 主逻辑
const orderListPage = /(http|https):\/\/buyertrade\.taobao.*?\/trade/g;
if (orderListPage.test(window.location.href)) {
// 创建控制栏
const controlBar = createControlBar();
// 添加订单计数显示
addOrderCountDisplay(controlBar);
// 添加按钮
addButton(controlBar, autoRecordAllOrders, "#FFA500", "自动记录所有订单");
addButton(controlBar, stopRecording, "#FF5252", "停止记录"); // 新增停止按钮
addButton(controlBar, addCurrentPageOrdersToList, "#1E90FF", "添加当页订单");
addButton(controlBar, exportOrders, "#4CAF50", "下载订单数据");
addButton(controlBar, cleanOrderList, "#FF5252", "清除订单");
// 添加状态提示
const statusDisplay = document.createElement("div");
statusDisplay.id = "recordStatus";
statusDisplay.style.padding = "5px";
statusDisplay.style.fontSize = "12px";
statusDisplay.style.color = "#666";
controlBar.appendChild(statusDisplay);
}
let isAutoRecording = false;
let stopRecordingFlag = false; // 新增停止标志
let orderList = {};
// 自动记录所有订单
function autoRecordAllOrders() {
if (isAutoRecording) {
updateStatus("已经在自动记录中...");
return;
}
isAutoRecording = true;
stopRecordingFlag = false; // 重置停止标志
updateStatus("开始自动记录所有订单...");
// 开始记录第一页
recordCurrentPageAndContinue();
}
// 新增:停止记录功能
function stopRecording() {
if (!isAutoRecording) {
updateStatus("当前没有进行中的记录任务");
return;
}
stopRecordingFlag = true;
updateStatus("正在停止记录...");
}
// 更新状态显示
function updateStatus(message) {
const statusDisplay = document.getElementById("recordStatus");
if (statusDisplay) {
statusDisplay.textContent = message;
}
console.log(message);
}
// 记录当前页并继续下一页
function recordCurrentPageAndContinue() {
// 检查是否收到停止信号
if (stopRecordingFlag) {
finishAutoRecording();
updateStatus("已手动停止记录");
return;
}
const orders = document.getElementsByClassName("js-order-container");
// 检查是否有订单数据
if (!orders || orders.length === 0) {
finishAutoRecording();
updateStatus("没有找到订单数据");
return;
}
let hasValidData = false;
let currentPageCount = 0;
for (let order of orders) {
let items = processOrder(order, null);
if (!items) {
continue;
}
hasValidData = true;
currentPageCount += Object.keys(items).length;
_.forEach(items, (value, key) => {
orderList[key] = value;
})
}
updateOrderCount();
updateStatus(`已记录当前页 ${currentPageCount} 条订单,总计 ${Object.keys(orderList).length} 条`);
// 检查是否有下一页
const nextPageBtn = document.querySelector(".pagination-next:not(.disabled)");
if (nextPageBtn && hasValidData && !stopRecordingFlag) {
updateStatus("正在翻到下一页...");
// 保存当前滚动位置
const scrollY = window.scrollY;
// 点击下一页按钮
nextPageBtn.click();
// 等待页面加载完成后继续记录
setTimeout(() => {
// 恢复滚动位置
window.scrollTo(0, scrollY);
recordCurrentPageAndContinue();
}, 2000);
} else {
finishAutoRecording();
if (!hasValidData) {
updateStatus("没有有效订单数据,自动停止");
} else if (!nextPageBtn) {
updateStatus("已到达最后一页,记录完成");
}
}
}
// 完成自动记录
function finishAutoRecording() {
isAutoRecording = false;
stopRecordingFlag = false;
updateStatus(`记录完成! 共记录 ${Object.keys(orderList).length} 条订单`);
}
// 添加当前页面的订单到导出列表
function addCurrentPageOrdersToList() {
const orders = document.getElementsByClassName("js-order-container");
let count = 0;
for (let order of orders) {
let items = processOrder(order, null);
if (!items) {
continue;
}
count += Object.keys(items).length;
_.forEach(items, (value, key) => {
orderList[key] = value;
})
}
updateOrderCount();
updateStatus(`已添加 ${count} 条订单,总计 ${Object.keys(orderList).length} 条`);
}
// 导出csv
function toCsv(header, data, filename) {
if (Object.keys(data).length === 0) {
updateStatus("没有订单数据可导出");
return;
}
let rows = "";
let row = header.join(",");
rows += row + "\n";
_.forEach(data, value => {
rows += _.replace(value.join(","), '#', '@') + "\n";
})
let blob = new Blob(["\ufeff" + rows], {
type: 'text/csv;charset=utf-8;'
});
let encodedUrl = URL.createObjectURL(blob);
let url = document.createElement("a");
url.setAttribute("href", encodedUrl);
url.setAttribute("download", filename + ".csv");
document.body.appendChild(url);
url.click();
document.body.removeChild(url);
updateStatus(`已导出 ${Object.keys(data).length} 条订单数据`);
}
function exportOrders() {
const header = ["订单号", "下单日期", "商品明细", "商品链接", "单价", "数量", "实付款", "状态"];
toCsv(header, orderList, "淘宝订单导出_" + new Date().toLocaleDateString());
}
// 清空订单
function cleanOrderList() {
let count = Object.keys(orderList).length;
orderList = {};
updateOrderCount();
updateStatus(`已清除 ${count} 条订单记录`);
}
// 处理订单-获取订单的详细信息
function processOrder(order, time) {
let outputData = {};
let textContent = order.textContent;
let pattern = /(\d{4}-\d{2}-\d{2})订单号: ()/;
let isExist = pattern.exec(textContent);
let insuranceText = "保险服务";
if (!isExist) {
return null;
}
const date = isExist[1];
const id = order.querySelector("div[data-id]")?.getAttribute("data-id");
if (!id) return null;
let index = 0;
if (time != undefined && time != null) {
if (date != time) {
if (date < time) {
return null;
}
return null;
}
}
let productQuery = order.querySelector("span[data-reactid='.0.7:$order-" + id + ".$" + id + ".0.1:1:0.$" + index + ".$0.0.1.0.0.1']");
let priceQuery = order.querySelector("span[data-reactid='.0.7:$order-" + id + ".$" + id + ".0.1:1:0.$" + index + ".$1.0.1.1']");
let countQuery = order.querySelector("p[data-reactid='.0.7:$order-" + id + ".$" + id + ".0.1:1:0.$" + index + ".$2.0.0']");
let actualPayQuery = order.querySelector("span[data-reactid='.0.7:$order-" + id + ".$" + id + ".0.1:1:0.$" + index + ".$4.0.0.2.0.1']");
let itemUrlQuery = order.querySelector("a[href]");
if (!productQuery || productQuery.textContent == insuranceText) {
return null;
}
let price = priceQuery?.textContent || "0";
let count = countQuery?.textContent || "1";
let actualPay = actualPayQuery?.textContent || "0";
let orderStatus = "";
if (index === 0) {
let statusQuery = order.querySelector("span[data-reactid='.0.7:$order-" + id + ".$" + id + ".0.1:1:0.$" + index + ".$5.0.0.0']");
orderStatus = statusQuery?.textContent || "";
}
let itemUrl = itemUrlQuery?.href || "";
outputData[id + index] = [
id,
date,
productQuery.textContent.replace(/,/g, ","),
itemUrl,
parseFloat(price),
count,
actualPay,
orderStatus,
];
return outputData;
}
})();