您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
导出拼多多订单并生成详细分析报告,包含商品图片、消费统计与购买趋势分析 改版自 @[seeker](https://greasyfork.org/zh-CN/scripts/534938-%E5%AF%BC%E5%87%BA%E6%8B%BC%E5%A4%9A%E5%A4%9A%E8%AE%A2%E5%8D%95)
// ==UserScript== // @name 拼多多订单导出与分析 // @namespace win.somereason.web.utils // @version 1.0.1 // @description 导出拼多多订单并生成详细分析报告,包含商品图片、消费统计与购买趋势分析 改版自 @[seeker](https://greasyfork.org/zh-CN/scripts/534938-%E5%AF%BC%E5%87%BA%E6%8B%BC%E5%A4%9A%E5%A4%9A%E8%AE%A2%E5%8D%95) // @author wenmoux // @match *://mobile.pinduoduo.com/orders.html* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // ===== 数据结构 ===== let orderData = { items: [], summary: { totalOrders: 0, totalAmount: 0, averagePrice: 0, frequentCategories: {}, frequentShops: {}, repurchasedItems: [], monthlyTrends: {} } }; // ===== 插入控制面板和日志浮窗 ===== (function setupUI() { const controlPanel = document.createElement("div"); controlPanel.id = "pdd-analysis-panel"; controlPanel.innerHTML = ` <style> /* Material Design 3 风格 */ #pdd-analysis-panel { position: fixed; bottom: 20px; right: 20px; width: 360px; background: #ffffff; border-radius: 16px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); padding: 16px; z-index: 9999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; display: flex; flex-direction: column; gap: 12px; transition: all 0.3s ease; max-height: 80vh; overflow-y: auto; } #pdd-control-buttons { display: flex; gap: 8px; } .pdd-btn { flex: 1; background-color: #e2231a; color: white; border: none; padding: 12px; border-radius: 8px; font-size: 14px; cursor: pointer; transition: all 0.2s ease; font-weight: 600; text-align: center; } .pdd-btn:hover { background-color: #c41c14; transform: translateY(-1px); } .pdd-btn-secondary { background-color: #f5f5f5; color: #333; } .pdd-btn-secondary:hover { background-color: #e0e0e0; } #pdd-log-container { max-height: 200px; overflow-y: auto; padding: 10px; background: #f5f5f5; border-radius: 8px; font-size: 13px; color: #333; } .log-entry { margin-bottom: 6px; padding-bottom: 6px; border-bottom: 1px solid #e0e0e0; } .log-time { font-size: 11px; color: #888; margin-right: 5px; } .log-message { font-weight: 500; } .progress-container { width: 100%; height: 6px; background-color: #e0e0e0; border-radius: 3px; margin-top: 8px; } .progress-bar { height: 100%; width: 0%; background-color: #e2231a; border-radius: 3px; transition: width 0.3s ease; } .panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; } .panel-title { font-size: 16px; font-weight: 700; color: #333; } .panel-actions { display: flex; gap: 8px; } .panel-action { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; background: #f5f5f5; border-radius: 50%; cursor: pointer; } .panel-action:hover { background: #e0e0e0; } </style> <div class="panel-header"> <div class="panel-title">拼多多订单分析器</div> <div class="panel-actions"> <div class="panel-action" id="pdd-panel-minimize" title="最小化">−</div> </div> </div> <div id="pdd-control-buttons"> <button id="exportOrdersBtn" class="pdd-btn">导出订单数据</button> <button id="generateReportBtn" class="pdd-btn pdd-btn-secondary">生成分析报告</button> </div> <div id="pdd-log-container"></div> <div class="progress-container"> <div class="progress-bar" id="progress-indicator"></div> </div>`; document.body.appendChild(controlPanel); // 事件绑定 document.getElementById('pdd-panel-minimize').addEventListener('click', function() { const panel = document.getElementById('pdd-analysis-panel'); if (panel.style.height === '42px') { panel.style.height = ''; } else { panel.style.height = '42px'; setTimeout(() => { panel.querySelector('.panel-actions').style.display = 'flex'; panel.querySelector('#pdd-control-buttons').style.display = 'none'; panel.querySelector('#pdd-log-container').style.display = 'none'; panel.querySelector('.progress-container').style.display = 'none'; }, 300); } }); })(); // ===== 日志记录函数 ===== window.logMessage = function(msg, type = 'info') { const logContainer = document.getElementById('pdd-log-container'); if (!logContainer) return; const logEntry = document.createElement("div"); logEntry.className = `log-entry log-${type}`; const timeSpan = document.createElement("span"); timeSpan.className = "log-time"; timeSpan.textContent = `[${new Date().toLocaleTimeString()}]`; const messageSpan = document.createElement("span"); messageSpan.className = "log-message"; messageSpan.textContent = msg; logEntry.appendChild(timeSpan); logEntry.appendChild(messageSpan); logContainer.appendChild(logEntry); logContainer.scrollTop = logContainer.scrollHeight; }; // ===== 进度条更新函数 ===== function updateProgress(percent) { const progressBar = document.getElementById('progress-indicator'); if (progressBar) { progressBar.style.width = `${percent}%`; } } // ===== 自动滚动到底部后调用 callback ===== function autoScrollUntilDone(callback) { let lastHeight = document.body.scrollHeight; let noChangeCount = 0; let progress = 0; logMessage("🚀 开始自动滚动加载更多订单..."); updateProgress(5); const interval = setInterval(() => { const doneText = document.querySelector('.loading-text'); if (doneText && doneText.innerText.includes('您已经没有更多的订单了')) { logMessage("✅ 已滚动到底部,加载完成!"); updateProgress(100); clearInterval(interval); setTimeout(callback, 1000); return; } const currentHeight = document.body.scrollHeight; if (currentHeight === lastHeight) { noChangeCount++; if (noChangeCount > 5) { window.scrollBy({ top: 1500, behavior: 'smooth' }); noChangeCount = 0; } } else { noChangeCount = 0; progress = Math.min(95, progress + 5); updateProgress(progress); } lastHeight = currentHeight; window.scrollBy({ top: 800, behavior: 'smooth' }); logMessage("⬇️ 正在加载更多订单..."); }, 800); } // ===== 提取订单数据 ===== function extractOrderData() { logMessage("📦 正在提取订单商品信息..."); orderData.items = []; const orderItems = document.querySelectorAll(".U6SAh0Eo"); if (!orderItems || orderItems.length === 0) { logMessage("⚠️ 未找到订单项,请确认页面加载完成", "error"); return false; }orderItems.forEach((item, index) => { try { // 基本信息提取 const shopName = item.querySelector('[data-test="店铺名称"]')?.innerText.trim() || "未知店铺"; const productName = item.querySelector('[data-test="商品名称"]')?.innerText.trim() || "未知商品"; const productModel = item.querySelector(".bJrhQPD0")?.innerText.trim() || ""; const price = parseFloat((item.querySelector('[data-test="商品价格"]')?.innerText.replace("¥", "").trim() || "0")); const status = item.querySelector('[data-test="订单状态"]')?.innerText.trim() || ""; const paid = parseFloat((item.querySelector(".pdcOje4N")?.innerText.replace("¥", "").trim() || "0")); // 提取图片 const imgElement = item.querySelector('[data-test="商品图片"] img'); const imgUrl = imgElement ? imgElement.getAttribute('src') : "";// 提取订单时间 const orderTimeElement = item.parentNode.querySelector('.MZwI5r1b'); const orderTime = orderTimeElement ? orderTimeElement.innerText.replace('下单时间:', '').trim() : ""; // 提取订单号 const orderIdElement = item.parentNode.querySelector('.TQ8iHK1y'); const orderId = orderIdElement ? orderIdElement.innerText.replace('订单号:', '').trim() : "";// 商品分类推断(简单基于关键词) let category = "其他"; const keywordCategories = { "食品生鲜": [ // 基础食品类 "食品", "零食", "饼干", "糖果", "巧克力", "坚果", "水果", "蔬菜", "肉", "海鲜", "调料", "酒", "茶", "咖啡", // 中国特色零食 "鲜花饼", "玫瑰花饼", "月饼", "辣条", "休闲食品", "小吃", "批发", "整箱", "网红食品", "麻辣", "手工", "老式", // 地方特产 "土特产", "云南特产", "东北特产", "新疆特产", "广式月饼", "苏式月饼", "粽子", "腊肉", "香肠", // 休闲食品细分 "薯片", "膨化食品", "瓜子", "开心果", "碧根果", "夏威夷果", "腰果", "话梅", "鱿鱼丝", "牛肉干", "猪肉脯", // 方便速食 "方便面", "自热火锅", "自热米饭", "即食", "速食", "冲泡", "螺蛳粉", "酸辣粉", "米线", // 生鲜 "水产", "三文鱼", "龙虾", "大闸蟹", "咸鸭蛋", "松花蛋", "皮蛋", "咸鸭蛋黄", "腊肠" ], "服饰穿搭": [ // 基础服装类 "衣", "裤", "鞋", "袜", "帽", "围巾", "包", "服", "裙", "外套", "内衣", "T恤", "西装", "牛仔", // 时尚服装 "男鞋", "运动鞋", "板鞋", "潮鞋", "小白鞋", "休闲鞋", "古着", "vintage", "宽松", "直筒", "牛仔裤", "男装", "女装", "百搭", // 中国风服装 "汉服", "旗袍", "唐装", "中式服装", "中国风", "复古风", "中式婚礼", "盘扣", "团扇", "香囊", // 季节性服装 "羽绒服", "棉衣", "保暖内衣", "秋裤", "毛衣", "卫衣", "雪地靴", "泳装", "沙滩裤", "防晒衣", "防晒霜", // 配饰 "项链", "耳环", "手镯", "戒指", "发饰", "胸针", "丝巾", "皮带", "钱包", "手表", "墨镜", // 家纺 "干衣机", "烘干机", "风干机", "床单", "四件套", "被罩", "枕套", "毛毯", "凉席", "蚊帐" ], "家居家装": [ // 基础家居类 "床", "沙发", "桌", "椅", "柜", "灯", "窗帘", "地毯", "家具", "装饰", "摆件", "餐具", "厨具", // 新增家居 "香薰机", "香氛", "杯子", "塑料杯", "水杯", "一次性杯", "智能家居", "全屋智能", "小米智能", "排插", "电源板", "开关", "灯具", "空调", "智能开关", "干衣柜", // 家装细分 "床垫", "席梦思", "乳胶床垫", "床头柜", "衣柜", "电视柜", "书柜", "鞋柜", "餐桌", "茶几", "梳妆台", "电脑桌", // 厨房用品 "电饭煲", "电压力锅", "豆浆机", "破壁机", "料理机", "空气炸锅", "微波炉", "电磁炉", "蒸锅", "炒锅", "菜刀", "砧板", // 中式家居 "紫砂壶", "茶具", "茶盘", "香道", "瓷器", "青花瓷", "景德镇", "陶瓷", "刺绣", "十字绣", "福字", "春联", "中国结" ], "数码电器": [ // 基础电子类 "手机", "电脑", "平板", "相机", "耳机", "音箱", "充电器", "电视", "显示器", "键盘", "鼠标", // 智能设备 "智能设备", "米家", "APP智能", "远程控制", "USB", "电量统计", "遥控", "定时开关", "接线端子", "电源板", "数码配件", "分控排插", // 手机品牌 "华为", "小米", "OPPO", "vivo", "苹果", "三星", "荣耀", "红米", "realme", "iQOO", // 电脑配件 "CPU", "显卡", "主板", "内存", "硬盘", "SSD", "机械硬盘", "散热器", "电源", "机箱", "显示器", "路由器", // 游戏设备 "游戏本", "游戏台式机", "游戏显卡", "游戏鼠标", "游戏键盘", "机械键盘", "耳麦", "游戏耳机", "游戏手柄", "RGB灯效", // 家用电器 "冰箱", "洗衣机", "油烟机", "燃气灶", "热水器", "净水器", "空气净化器", "扫地机器人", "加湿器", "吸尘器", "除螨仪" ], "美妆个护": [ // 基础美妆类 "化妆品", "护肤", "面膜", "口红", "粉底", "眼影", "香水", "洗面奶", "护发", "美妆工具", // 护肤成分 "烟酰胺", "珍珠", "海盐", "手工皂", "美白", "黑色素", "改善肤质", "精油", "玻尿酸", "胶原蛋白", "维生素C", // 国货美妆 "花西子", "完美日记", "colorkey", "橘朵", "稚优泉", "美康粉黛", "大宝", "百雀羚", "相宜本草", "百草味", // 个人护理 "洗发水", "护发素", "沐浴露", "身体乳", "香体", "防晒霜", "卫生巾", "洗手液", "消毒液", "漱口水", "牙膏", "牙刷", // 男士护理 "男士护肤", "剃须刀", "刮胡刀", "须后水", "男士面霜", "男士洗面奶", "控油", "祛痘", "美容仪", "脱毛器" ], "母婴亲子": [ // 基础母婴类 "奶粉", "尿布", "婴儿", "童装", "玩具", "孕妇", "奶瓶", "推车", "儿童", "宝宝", // 婴幼儿用品 "婴儿车", "婴儿床", "学步车", "婴儿餐椅", "婴儿洗澡盆", "婴儿背带", "婴儿睡袋", "婴儿枕头", "婴儿床垫", // 婴幼儿食品 "辅食", "婴儿米粉", "婴儿面条", "婴儿饼干", "DHA", "钙铁锌", "益生菌", "鱼肝油", "婴儿营养品", // 孕产妇用品 "月子服", "哺乳内衣", "孕妇裤", "孕妇枕", "孕妇护肤", "防辐射服", "产后修复", "催奶", "吸奶器", // 儿童教育 "早教", "绘本", "识字卡", "拼图", "积木", "乐高", "画板", "学习桌", "书包", "文具盒" ], "日用百货": [ // 基础日用品 "纸巾", "洗发水", "沐浴露", "牙膏", "洗衣液", "清洁", "卫生纸", "毛巾", "卫生巾", // 纸品细分 "抽纸", "原木纸巾", "便携装", "原生木浆", "面巾纸", "餐巾纸", "批发整箱", "家庭装", "湿厕纸", "湿巾", // 清洁用品 "洗衣粉", "消毒剂", "洁厕灵", "管道疏通", "地板清洁", "玻璃清洁", "厨房清洁", "除湿剂", "除味剂", "垃圾袋", // 居家日用 "衣架", "晾衣架", "收纳盒", "收纳箱", "防尘罩", "防潮垫", "防滑垫", "门垫", "蚊香", "电蚊香", "灭蚊灯" ], "办公文具": [ // 基础办公用品 "美工刀", "壁纸刀", "介刀", "刀片", "工具刀", "拆箱刀", "快递刀", "物流刀", "电商用品", "五金工具", // 文具 "笔记本", "签字笔", "中性笔", "钢笔", "铅笔", "橡皮", "文件夹", "档案袋", "订书机", "打孔器", "计算器", // 办公耗材 "复印纸", "打印纸", "彩色纸", "照片纸", "标签纸", "墨盒", "硒鼓", "碳粉", "打印机", "复印机", "扫描仪", // 学生文具 "课本保护套", "作业本", "日记本", "练习册", "书皮", "书套", "尺子", "圆规", "三角板", "涂改液", "荧光笔" ], "医药健康": [ // 基础医疗保健 "口罩", "医用口罩", "外科口罩", "防疫", "防护", "防尘", "抗病毒", "防细菌", "三层防护", // 中医药品 "中药", "中成药", "膏药", "创可贴", "云南白药", "正红花油", "板蓝根", "清热解毒", "感冒灵", "退烧药", // 营养保健 "维生素", "蛋白粉", "钙片", "鱼油", "蜂王浆", "蜂蜜", "阿胶", "燕窝", "人参", "灵芝", "西洋参", // 医疗器械 "血压计", "血糖仪", "体温计", "电子体温计", "雾化器", "制氧机", "助听器", "轮椅", "拐杖", "医用手套", // 消毒用品 "酒精", "碘伏", "过氧化氢", "84消毒液", "免洗洗手液", "消毒湿巾", "紫外线消毒灯" ], "运动户外": [ // 基础运动户外 "运动装备", "户外用品", "运动服装", "运动鞋", "休闲运动", "健身器材", // 体育用品 "篮球", "足球", "排球", "乒乓球", "羽毛球", "网球", "高尔夫", "游泳", "瑜伽垫", "瑜伽服", "健身手套", // 户外装备 "帐篷", "睡袋", "登山包", "户外服装", "户外鞋", "望远镜", "指南针", "登山杖", "野餐垫", "防潮垫", "野营灯", // 运动智能 "智能手环", "运动手表", "心率带", "计步器", "GPS导航", "运动相机", "骑行码表", "骑行灯", "头戴式耳机" ], "宠物用品": [ // 基础宠物用品 "宠物食品", "宠物玩具", "宠物床", "宠物清洁", "宠物医疗", "猫砂", "狗粮", "猫粮", // 猫咪用品 "猫抓板", "猫爬架", "猫窝", "猫厕所", "猫砂盆", "猫粮盆", "猫玩具", "逗猫棒", "猫咪零食", "猫草", // 狗狗用品 "狗窝", "狗链", "狗厕所", "狗尿垫", "狗狗玩具", "狗粮盆", "狗狗衣服", "狗狗鞋", "狗狗零食", "狗狗磨牙", // 宠物医疗 "宠物驱虫", "宠物体外驱虫", "宠物体内驱虫", "宠物疫苗", "宠物消毒", "宠物医院", "宠物保健", "宠物美容" ], "汽车用品": [ // 基础汽车用品 "汽车配件", "车载设备", "汽车装饰", "汽车清洁", "汽车维修", "车载香薰", // 车内装饰 "座垫", "方向盘套", "头枕", "腰靠", "脚垫", "后备箱垫", "香水", "车载挂饰", "车贴", "车衣", "遮阳挡", // 车载电子 "行车记录仪", "车载充电器", "车载蓝牙", "导航仪", "倒车雷达", "胎压监测", "车载净化器", "车载吸尘器", // 汽车维护 "机油", "防冻液", "玻璃水", "轮胎", "电瓶", "雨刷", "灯泡", "刹车片", "火花塞", "汽车贴膜", "洗车工具" ], "图书文娱": [ // 新增类别 "图书", "小说", "文学", "教育", "历史", "科学", "艺术", "杂志", "漫画", "绘本", // 教育类 "教材", "教辅", "考试", "中小学教辅", "大学教材", "考研", "公务员", "英语", "字帖", "课外读物", // 文艺类 "武侠小说", "言情小说", "科幻小说", "悬疑推理", "青春文学", "历史小说", "诗歌", "散文", "传记", "艺术欣赏", // 娱乐产品 "CD", "DVD", "蓝光", "黑胶唱片", "电影", "音乐", "乐器", "吉他", "钢琴", "电子琴", "口琴" ], "数字虚拟": [ // 新增类别 "游戏点卡", "充值卡", "Q币", "视频会员", "音乐会员", "电子书", "虚拟资产", "主题", "皮肤", "表情包", // 游戏类 "网游充值", "手游充值", "王者荣耀", "和平精英", "阴阳师", "LOL", "DNF", "梦幻西游", "游戏装备", "游戏代练", // 服务类 "腾讯视频会员", "爱奇艺会员", "优酷会员", "芒果TV会员", "网易云音乐", "QQ音乐", "酷狗音乐", "喜马拉雅", // 电商服务 "淘宝会员", "京东PLUS", "拼多多会员", "美团会员", "饿了么会员", "滴滴打车券" ], "礼品鲜花": [ // 新增类别 "鲜花", "绿植", "干花", "永生花", "礼盒", "贺卡", "创意礼品", "纪念品", "定制礼品", "生日礼物", // 节日礼品 "春节礼品", "元宵礼品", "情人节礼物", "母亲节礼物", "父亲节礼物", "儿童节礼物", "教师节礼物", "中秋礼品", "圣诞礼物", // 鲜花类别 "玫瑰", "康乃馨", "百合", "向日葵", "郁金香", "满天星", "扶郎花", "马蹄莲", "绣球花", "风信子" ], "特产区域": [ // 新增类别 "华北特产", "东北特产", "华东特产", "华中特产", "华南特产", "西南特产", "西北特产", "港澳台特产", "进口商品", // 地方特产 "新疆特产", "内蒙古特产", "云南特产", "四川特产", "广东特产", "福建特产", "浙江特产", "山东特产", "东北特产", // 特产细分 "大闸蟹", "西湖龙井", "金华火腿", "临安山核桃", "安溪铁观音", "武夷岩茶", "陈皮", "东阿阿胶", "枸杞", "红枣" ] }; for (const [cat, keywords] of Object.entries(keywordCategories)) { if (keywords.some(word => productName.includes(word))) { category = cat; break; } } // 构造订单数据对象 const orderItem = { orderId, shopName, productName, productModel, price, status, paid, imgUrl, orderTime, category, timeStamp: new Date(orderTime).getTime() || Date.now() }; orderData.items.push(orderItem); } catch (e) { logMessage(`⚠️ 订单项${index+1} 提取失败,已跳过: ${e.message}`, "error");} }); logMessage(`✅ 成功提取 ${orderData.items.length} 条订单信息`); return orderData.items.length > 0; } // ===== 计算数据统计和分析 ===== function calculateStatistics() { logMessage("📊 正在生成数据统计和购买趋势分析..."); const items = orderData.items; if (!items || items.length === 0) { logMessage("⚠️ 没有订单数据可供分析", "error"); return false; } // 基本统计 const totalOrders = items.length; const totalAmount = items.reduce((sum, item) => sum + item.paid, 0); const averagePrice = totalAmount / totalOrders; // 分类统计 const categoryCounts = {}; const shopCounts = {}; items.forEach(item => { // 品类统计 categoryCounts[item.category] = (categoryCounts[item.category] || 0) + 1; // 店铺统计 shopCounts[item.shopName] = (shopCounts[item.shopName] || 0) + 1; }); // 常购商品分析(找出相同或相似名称的商品) const productNameMap = {}; items.forEach(item => { // 简化商品名称用于匹配(取前10个字符作为基础名称) const simpleName = item.productName.substring(0, 10); if (!productNameMap[simpleName]) { productNameMap[simpleName] = []; } productNameMap[simpleName].push(item); }); // 找出购买次数超过1次的商品 const repurchasedItems = []; for (const [name, products] of Object.entries(productNameMap)) { if (products.length > 1) { repurchasedItems.push({ name: name, count: products.length, totalSpent: products.reduce((sum, p) => sum + p.paid, 0), items: products }); } } // 按月消费趋势 const monthlyTrends = {}; items.forEach(item => { if (item.orderTime) { const date = new Date(item.orderTime); if (!isNaN(date.getTime())) { const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!monthlyTrends[monthKey]) { monthlyTrends[monthKey] = { count: 0, amount: 0, items: [] }; } monthlyTrends[monthKey].count++; monthlyTrends[monthKey].amount += item.paid; monthlyTrends[monthKey].items.push(item); } } }); // 保存统计结果 orderData.summary = { totalOrders, totalAmount, averagePrice, frequentCategories: categoryCounts, frequentShops: shopCounts, repurchasedItems, monthlyTrends }; logMessage("✅ 数据统计分析完成!"); return true; } // ===== 下载 CSV 文件 ===== function downloadCSV() { logMessage("📄 正在生成 CSV 文件...");let csv = `订单号,店铺名称,商品名称,商品型号,商品价格,订单状态,实付价格,订单时间,商品分类,图片链接\n`; orderData.items.forEach(item => { csv += `"${item.orderId}","${item.shopName}","${item.productName}","${item.productModel}","${item.price}","${item.status}","${item.paid}","${item.orderTime}","${item.category}","${item.imgUrl}"\n`; });const blob = new Blob(['\uFEFF' + csv], { type: "text/csv;charset=utf-8;" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `PDD_订单数据_${new Date().toISOString().slice(0,10)}.csv`; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100); logMessage("✅ CSV 文件导出成功!"); } // ===== 生成HTML报告 ===== function generateHTMLReport() { logMessage("📊 正在生成详细分析报告..."); // 格式化货币 const formatCurrency = num => `¥${num.toFixed(2)}`; // 准备分类数据 const categoryLabels = Object.keys(orderData.summary.frequentCategories); const categoryData = categoryLabels.map(cat => orderData.summary.frequentCategories[cat]); // 准备店铺数据 const shopLabels = Object.keys(orderData.summary.frequentShops).filter((_, i) => i < 10); // 最多显示前10个店铺 const shopData = shopLabels.map(shop => orderData.summary.frequentShops[shop]); // 准备月度趋势数据 const trendLabels = Object.keys(orderData.summary.monthlyTrends).sort(); const trendAmounts = trendLabels.map(month => orderData.summary.monthlyTrends[month].amount); const trendCounts = trendLabels.map(month => orderData.summary.monthlyTrends[month].count); // 构建报告HTML const reportHTML = ` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>拼多多订单分析报告</title> <style> :root { --primary-color: #e2231a; --secondary-color: #f5762a; --background-color: #f5f5f5; --card-background: #ffffff; --text-color: #333333; --text-secondary: #757575; --border-radius: 16px; } * { box-sizing: border-box;margin: 0; padding: 0; }body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; background-color: var(--background-color); color: var(--text-color); line-height: 1.6; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; }h1, h2, h3 { color: var(--text-color); margin-bottom: 16px; } .header { text-align: center; margin-bottom: 40px; padding: 24px; background: var(--card-background); border-radius: var(--border-radius); box-shadow: 0 4px 20px rgba(0,0,0,0.08); }.header h1 { color: var(--primary-color); font-size: 32px; margin-bottom: 8px; } .header p { color: var(--text-secondary); font-size: 16px; }.summary-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 40px; } .card { background: var(--card-background); border-radius: var(--border-radius); padding: 24px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }.card-title { font-size: 16px; color: var(--text-secondary); margin-bottom: 8px; } .card-value { font-size: 28px; font-weight: bold; color: var(--primary-color); }.tab-container { margin-bottom: 40px; } .tab-nav { display: flex; margin-bottom: 20px; background: var(--card-background); border-radius: var(--border-radius); padding: 4px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.08); } .tab-btn { padding: 12px 20px; cursor: pointer; border: none; background: transparent; font-size: 16px; font-weight: 500; color: var(--text-secondary); flex-grow: 1; text-align: center; transition: all 0.3s;border-radius: 12px; }.tab-btn.active { color: var(--card-background); background: var(--primary-color); }.tab-content { display: none; background: var(--card-background); border-radius: var(--border-radius); padding: 24px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); } .tab-content.active { display: block; }.chart-container { margin-bottom: 40px;height: 400px; }.items-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-bottom: 40px; } .item-card { background: var(--card-background); border-radius: var(--border-radius); overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.08); transition: transform 0.3s ease; }.item-card:hover { transform: translateY(-5px); }.item-image { height: 200px; overflow: hidden; }.item-image img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease; }.item-card:hover .item-image img { transform: scale(1.05); }.item-details { padding: 16px; } .item-name { font-weight: bold; margin-bottom: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .item-shop { color: var(--text-secondary); margin-bottom: 8px; font-size: 14px; } .item-meta { display: flex; justify-content: space-between; color: var(--text-secondary); font-size: 14px; }.item-price { color: var(--primary-color); font-weight: bold; }.footer { text-align: center; margin-top: 60px; padding: 20px; color: var(--text-secondary); } table { width: 100%; border-collapse: collapse; margin-bottom: 20px; } table th, table td { padding: 12px; text-align: left; border-bottom: 1px solid #eeeeee; } table th { font-weight: 600; background-color: #f9f9f9; }.repurchase-stats { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; flex-wrap: wrap; } .repurchase-count, .repurchase-total { background: #f3f3f3; border-radius: 20px; padding: 4px 12px; font-size: 14px; font-weight: 500; }.repurchase-count { color: var(--secondary-color); } .repurchase-total { color: var(--primary-color); }.repurchase-item { margin-bottom: 24px; } @media (max-width: 768px) { .summary-cards { grid-template-columns: 1fr; } .items-grid { grid-template-columns: 1fr; } }</style> </head> <body> <div class="container"> <div class="header"><h1>拼多多订单分析报告</h1> <p>生成时间: ${new Date().toLocaleString('zh-CN')}</p> </div> <div class="summary-cards"> <div class="card"> <div class="card-title">总订单数</div> <div class="card-value">${orderData.summary.totalOrders}</div> </div> <div class="card"> <div class="card-title">总消费金额</div><div class="card-value">${formatCurrency(orderData.summary.totalAmount)}</div> </div><div class="card"> <div class="card-title">平均每单</div> <div class="card-value">${formatCurrency(orderData.summary.averagePrice)}</div></div> <div class="card"> <div class="card-title">常购商品数</div> <div class="card-value">${orderData.summary.repurchasedItems.length}</div></div> </div><div class="tab-container"> <div class="tab-nav"> <button class="tab-btn active" data-tab="overview">消费概览</button><button class="tab-btn" data-tab="category">品类分析</button><button class="tab-btn" data-tab="products">商品分析</button><button class="tab-btn" data-tab="orders">订单明细</button></div> <div id="overview" class="tab-content active"> <h2>近期购买的商品</h2> <div class="items-grid"> ${orderData.items.slice(0, 6).map(item => ` <div class="item-card"> <div class="item-image"> <img src="${item.imgUrl || 'https://img.pddpic.com/mms-material-img/2023-07-19/2c0b7d3b-a63e-42c9-8426-44ea5ed7e84d.png'}" alt="${item.productName}"></div> <div class="item-details"> <div class="item-name">${item.productName}</div> <div class="item-shop">${item.shopName}</div> <div class="item-meta"> <div>${item.orderTime}</div><div class="item-price">${formatCurrency(item.paid)}</div></div></div> </div> `).join('')}</div> </div> <div id="category" class="tab-content"> <h2>品类占比</h2> <div class="chart-container"> <canvas id="categoryChart"></canvas> </div><h2>店铺分布</h2><div class="chart-container"> <canvas id="shopChart"></canvas> </div> </div> <div id="products" class="tab-content"> <h2>经常复购的商品</h2> <div class="items-grid"> ${orderData.summary.repurchasedItems.map(item => { // 获取该商品的最新一条订单项 const latestItem = item.items.sort((a, b) => { return new Date(b.orderTime).getTime() - new Date(a.orderTime).getTime(); })[0]; return ` <div class="item-card repurchase-item"> <div class="item-image"> <img src="${latestItem.imgUrl || 'https://img.pddpic.com/mms-material-img/2023-07-19/2c0b7d3b-a63e-42c9-8426-44ea5ed7e84d.png'}" alt="${item.name}"> </div> <div class="item-details"> <div class="item-name">${latestItem.productName}</div><div class="item-shop">${latestItem.shopName}</div> <div class="repurchase-stats"> <div class="repurchase-count">购买${item.count}次</div><div class="repurchase-total">共${formatCurrency(item.totalSpent)}</div></div> <div class="item-meta"><div>最近: ${new Date(Math.max(...item.items.map(i => new Date(i.orderTime).getTime()))).toLocaleDateString()}</div> <div class="item-price">均价: ${formatCurrency(item.totalSpent / item.count)}</div> </div> </div></div> `; }).join('')} </div></div> <div id="orders" class="tab-content"> <h2>订单明细</h2> <table> <thead> <tr> <th>商品</th><th>店铺</th> <th>价格</th> <th>订单时间</th> <th>状态</th></tr> </thead><tbody> ${orderData.items.map(item => `<tr><td>${item.productName}</td><td>${item.shopName}</td> <td>${formatCurrency(item.paid)}</td><td>${item.orderTime}</td> <td>${item.status}</td></tr> `).join('')} </tbody></table> </div> </div> <div class="footer"> <p>数据来源:拼多多订单 · 共${orderData.summary.totalOrders}条记录</p></div> </div> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js"></script> <script> // 标签切换 document.querySelectorAll('.tab-btn').forEach(button => { button.addEventListener('click', () => { document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); button.classList.add('active'); document.getElementById(button.getAttribute('data-tab')).classList.add('active'); }); }); // 消费趋势图 const trendCtx = document.getElementById('trendChart').getContext('2d'); new Chart(trendCtx, { type: 'line', data: { labels: ${JSON.stringify(trendLabels)}, datasets: [ { label: '消费金额', data: ${JSON.stringify(trendAmounts)}, backgroundColor: 'rgba(226, 35, 26, 0.2)', borderColor: 'rgba(226, 35, 26, 1)', borderWidth: 2,tension: 0.3, fill: true }, { label: '订单数量', data: ${JSON.stringify(trendCounts)}, backgroundColor: 'rgba(245, 118, 42, 0.2)', borderColor: 'rgba(245, 118, 42, 1)', borderWidth: 2, tension: 0.3, yAxisID: 'y1' } ] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, title: { display: true, text: '金额 (元)' } }, y1: { beginAtZero: true, position: 'right', title: { display: true, text: '订单数' }, grid: { drawOnChartArea: false } } } } }); // 品类占比图 const categoryCtx = document.getElementById('categoryChart').getContext('2d'); new Chart(categoryCtx, { type: 'doughnut', data: { labels: ${JSON.stringify(categoryLabels)}, datasets: [{ data: ${JSON.stringify(categoryData)}, backgroundColor: [ 'rgba(226, 35, 26, 0.8)', 'rgba(245, 118, 42, 0.8)', 'rgba(255, 193, 7, 0.8)', 'rgba(76, 175, 80, 0.8)', 'rgba(33, 150, 243, 0.8)', 'rgba(156, 39, 176, 0.8)', 'rgba(233, 30, 99, 0.8)' ], borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right' } } } }); // 店铺分布图 const shopCtx = document.getElementById('shopChart').getContext('2d'); new Chart(shopCtx, { type: 'bar', data: { labels: ${JSON.stringify(shopLabels)}, datasets: [{ label: '订单数量', data: ${JSON.stringify(shopData)}, backgroundColor: 'rgba(226, 35, 26, 0.7)', borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true } } } }); </script> </body> </html> `; // 创建下载 const blob = new Blob([reportHTML], { type: "text/html;charset=utf-8;" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `PDD_分析报告_${new Date().toISOString().slice(0,10)}.html`; document.body.appendChild(a); a.click(); // 自动打开报告 setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); window.open(url, '_blank'); }, 100); logMessage("✅ 分析报告生成成功!"); } // ===== 绑定点击事件 ===== document.getElementById("exportOrdersBtn").addEventListener("click", function() { autoScrollUntilDone(() => { if (extractOrderData()) { calculateStatistics(); downloadCSV(); } else { logMessage("⚠️ 未找到有效订单数据", "error"); } }); }); document.getElementById("generateReportBtn").addEventListener("click", function() { if (orderData.items.length > 0) { calculateStatistics(); generateHTMLReport(); } else { logMessage("⚠️ 请先导出订单数据", "error"); autoScrollUntilDone(() => { if (extractOrderData()) { calculateStatistics(); generateHTMLReport(); } }); } }); // ===== 初始化提示 ===== setTimeout(() => { logMessage("🛒 拼多多订单分析工具已加载,点击按钮开始导出/分析"); }, 1000); })();