PUGE il y a 1 mois
Parent
commit
78418dcb18
5 fichiers modifiés avec 843 ajouts et 326 suppressions
  1. 3 3
      按照数据搜索整理文件.py
  2. 228 144
      整理美团数据.py
  3. 424 176
      爱回收.py
  4. 3 3
      米画师.py
  5. 185 0
      飞书提交数据.py

+ 3 - 3
按照数据搜索整理文件.py

@@ -335,7 +335,7 @@ SCSQ202605260219	POCY2605260123778S01	S25122500884400102	S251225008844	80*210cm
     def copy_files(self, files, output_dir, order_number):
         """复制文件到目标目录"""
         # 创建订单号目录
-        target_dir = Path(output_dir) / order_number
+        target_dir = Path(output_dir)
         target_dir.mkdir(parents=True, exist_ok=True)
         
         copied_count = 0
@@ -451,9 +451,9 @@ SCSQ202605260219	POCY2605260123778S01	S25122500884400102	S251225008844	80*210cm
                 
 
                 # 转换为字符串
-                copied_count = self.copy_files(found_files, output_dir + "/" + date_str, 送仓申请单号[order_number])
+                copied_count = self.copy_files(found_files, output_dir, 送仓申请单号[order_number])
                 total_files_copied += copied_count
-                self.log_message(f"✓ 成功复制 {copied_count} 个文件到 {output_dir}/{date_str}/{送仓申请单号[order_number]}")
+                self.log_message(f"✓ 成功复制 {copied_count} 个文件到 {output_dir}/{送仓申请单号[order_number]}")
                 processed_orders += 1
             else:
                 self.log_message(f"✗ 警告:未找到包含 {order_number} 的文件", True)

+ 228 - 144
整理美团数据.py

@@ -13,7 +13,7 @@ class ProductDownloaderGUI:
     def __init__(self, root):
         self.root = root
         self.root.title("商品资源下载器")
-        self.root.geometry("900x700")
+        self.root.geometry("900x650")
         self.root.resizable(True, True)
         
         # 设置样式
@@ -29,7 +29,7 @@ class ProductDownloaderGUI:
         input_frame.pack(fill=BOTH, expand=True, pady=(0, 10))
         
         # 输入框
-        self.input_text = scrolledtext.ScrolledText(input_frame, height=15, font=("Consolas", 10))
+        self.input_text = scrolledtext.ScrolledText(input_frame, height=18, font=("Consolas", 10))
         self.input_text.pack(fill=BOTH, expand=True)
         
         # 输出目录选择
@@ -46,13 +46,6 @@ class ProductDownloaderGUI:
         browse_btn = ttk.Button(dir_select_frame, text="浏览...", command=self.browse_output_dir)
         browse_btn.pack(side=RIGHT)
         
-        # 信息显示区域
-        info_frame = ttk.LabelFrame(main_frame, text="商品信息", padding="10")
-        info_frame.pack(fill=BOTH, expand=True, pady=(0, 10))
-        
-        self.info_text = scrolledtext.ScrolledText(info_frame, height=8, font=("微软雅黑", 10))
-        self.info_text.pack(fill=BOTH, expand=True)
-        
         # 进度条
         self.progress_var = DoubleVar()
         self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)
@@ -93,21 +86,8 @@ class ProductDownloaderGUI:
         """清空输入框"""
         self.input_text.delete(1.0, END)
         
-    def update_info_display(self, product_info):
-        """更新商品信息显示"""
-        self.info_text.delete(1.0, END)
-        info_str = f"""
-【商品名称】{product_info.get('name', '未知')}
-【商品描述】{product_info.get('description', '无')}
-【价格】¥{product_info.get('min_price', '未知')}
-【规格】{product_info.get('spec', '无')}
-【品牌】{product_info.get('brand', '未知')}
-【月销量】{product_info.get('month_saled', '未知')}
-        """
-        self.info_text.insert(1.0, info_str.strip())
-        
     def extract_urls(self, data):
-        """从JSON数据中提取图片和视频URL"""
+        """从JSON数据中提取图片和视频URL - 适配多种数据结构"""
         urls = {
             'main_images': [],      # 主图
             'detail_images': [],    # 详情图
@@ -128,58 +108,45 @@ class ProductDownloaderGUI:
                     urls['videos'].append(url)
         
         try:
-            # 从common_info中提取SKU主图
-            if 'data' in data and 'ext' in data['data']:
-                common_info = data['data']['ext'].get('common_info', {})
-                
-                # 提取商品名称和描述
-                if 'skus' in common_info:
-                    for sku in common_info['skus']:
-                        if 'picture' in sku and sku['picture']:
-                            add_url(sku['picture'], 'main')
-                
-                # 从blocks中提取
-                if 'blocks' in data['data']['ext']:
-                    blocks = data['data']['ext']['blocks']
-                    
-                    # 提取主图(头图)
-                    for block in blocks.get('banner', []):
-                        s_type = block.get('s_type', '')
-                        
-                        # 头图模块
-                        if s_type == 'sm_type_detail_pop_head_photo':
-                            head_info = block.get('data', {}).get('head_info', {})
-                            pictures = head_info.get('pictures', [])
-                            for pic in pictures:
-                                add_url(pic, 'main')
-                        
-                        # 商品详情图模块
-                        elif s_type == 'sm_type_goods_detail_describe_non_food':
-                            json_data = block.get('data', {}).get('json_data', {})
-                            pic_content = json_data.get('pic_content', {})
-                            contents = pic_content.get('contents', [])
-                            for content in contents:
-                                if content.startswith('http') and ('jpg' in content or 'png' in content or 'jpeg' in content):
-                                    add_url(content, 'detail')
-                        
-                        # 推荐搭配模块中的图片
-                        elif s_type == 'sm_type_goods_detail_match_product':
-                            json_data = block.get('data', {}).get('json_data', {})
-                            collocate = json_data.get('collocate_recommend', {})
-                            spus = collocate.get('spus', [])
-                            for spu in spus:
-                                if 'picture' in spu and spu['picture']:
-                                    add_url(spu['picture'], 'detail')
-                    
-                    # 从float模块中提取购物车相关图片
-                    for block in blocks.get('float', []):
-                        if block.get('s_type') == 'sm_type_cart_info':
-                            data_json = block.get('data', {})
-                            shopping_cart = data_json.get('shopping_cart', {})
-                            if shopping_cart.get('shopping_cart_icon'):
-                                add_url(shopping_cart['shopping_cart_icon'], 'detail')
-            
-            # 使用正则表达式提取所有图片URL作为补充
+            # 获取data部分
+            data_content = data.get('data', {})
+            
+            # 1. 提取主图 - pictures字段
+            pictures = data_content.get('pictures', [])
+            for pic in pictures:
+                add_url(pic, 'main')
+            
+            # 2. 提取主图 - opt_pictures字段
+            opt_pictures = data_content.get('opt_pictures', [])
+            for pic in opt_pictures:
+                add_url(pic, 'main')
+            
+            # 3. 从SKU中提取主图
+            skus = data_content.get('skus', [])
+            for sku in skus:
+                sku_pic = sku.get('picture')
+                if sku_pic:
+                    add_url(sku_pic, 'main')
+            
+            # 4. 提取详情图 - pic_content.contents
+            pic_content = data_content.get('pic_content', {})
+            contents = pic_content.get('contents', [])
+            for content in contents:
+                if content and isinstance(content, str):
+                    add_url(content, 'detail')
+            
+            # 5. 提取视频
+            video_infos = data_content.get('product_video_infos', [])
+            for video_info in video_infos:
+                video_url = video_info.get('url')
+                if video_url:
+                    add_url(video_url, 'video')
+                # 视频封面图作为主图
+                main_picture = video_info.get('mainPicture')
+                if main_picture:
+                    add_url(main_picture, 'main')
+            
+            # 6. 使用正则表达式补充提取所有图片URL
             data_str = json.dumps(data, ensure_ascii=False)
             
             # 匹配图片URL
@@ -194,74 +161,207 @@ class ProductDownloaderGUI:
             for img_url in all_images:
                 if img_url in seen_urls:
                     continue
-                seen_urls.add(img_url)
-                
                 # 根据URL特征分类
                 if 'head_photo' in img_url or 'main' in img_url.lower() or 'sku' in img_url.lower():
-                    urls['main_images'].append(img_url)
-                else:
-                    urls['detail_images'].append(img_url)
+                    add_url(img_url, 'main')
             
-            # 添加视频
+            # 添加正则匹配到的视频
             for video_url in all_videos:
                 if video_url not in seen_urls:
-                    seen_urls.add(video_url)
-                    urls['videos'].append(video_url)
+                    add_url(video_url, 'video')
                     
         except Exception as e:
             print(f"提取URL时出错: {e}")
         
         # 限制数量
-        urls['main_images'] = urls['main_images'][:10]
-        urls['detail_images'] = urls['detail_images'][:30]
+        urls['main_images'] = urls['main_images'][:15]
+        urls['detail_images'] = urls['detail_images'][:40]
         urls['videos'] = urls['videos'][:5]
         
         return urls
     
     def extract_product_info(self, data):
-        """提取商品信息"""
+        """提取商品简要信息"""
         product_info = {
+            'id': '未知',
             'name': '未命名商品',
-            'description': '无',
             'min_price': '未知',
-            'spec': '无',
+            'origin_price': '未知',
+            'month_saled': '未知',
+            'month_saled_content': '未知',
+            'sku_label': '无',
             'brand': '未知',
-            'month_saled': '未知'
+            'sku_count': 0,
+            'image_count': 0,
+            'video_count': 0,
+            'sku_list': [],
+            'standard_params': [],
+            'delivery_info': '',
+            'activity_infos': []
         }
         
         try:
-            if 'data' in data and 'ext' in data['data']:
-                common_info = data['data']['ext'].get('common_info', {})
-                
-                product_info['name'] = common_info.get('name', '未命名商品')
-                product_info['description'] = common_info.get('description', '无')
-                product_info['min_price'] = common_info.get('min_price', '未知')
-                product_info['spec'] = common_info.get('sku_label', '无')
-                
-                # 提取月销量
-                if 'skus' in common_info and common_info['skus']:
-                    sku = common_info['skus'][0]
-                    if 'promotion_info' in sku:
-                        match = re.search(r'月售(\d+)', sku.get('promotion_info', ''))
-                        if match:
-                            product_info['month_saled'] = match.group(1)
+            data_content = data.get('data', {})
+            
+            # 基础信息
+            product_info['id'] = data_content.get('id', '未知')
+            product_info['name'] = data_content.get('name', '未命名商品')
+            product_info['min_price'] = data_content.get('min_price', '未知')
+            product_info['month_saled_content'] = data_content.get('month_saled_content', '未知')
+            
+            # 提取划线价(从unify_price中获取)
+            unify_price = data_content.get('unify_price', {})
+            product_info['origin_price'] = unify_price.get('underlined_price', '未知')
+            if product_info['origin_price'] == '未知':
+                # 如果unify_price中没有,从skus中获取
+                skus = data_content.get('skus', [])
+                if skus:
+                    product_info['origin_price'] = skus[0].get('origin_price', '未知')
+            
+            # SKU信息
+            skus = data_content.get('skus', [])
+            product_info['sku_count'] = len(skus)
+            product_info['sku_label'] = data_content.get('sku_label', '规格')
+            
+            # 提取每个SKU的详细信息
+            for sku in skus:
+                sku_info = {
+                    'spec': sku.get('spec', ''),
+                    'price': sku.get('price', ''),
+                    'origin_price': sku.get('origin_price', ''),
+                    'stock': sku.get('stock', ''),
+                    'upccode': sku.get('upccode', ''),
+                    'picture': sku.get('picture', '')
+                }
+                product_info['sku_list'].append(sku_info)
+            
+            # 提取标准参数
+            standard_list = data_content.get('standard_productinfo_list', [])
+            for item in standard_list:
+                product_info['standard_params'].append({
+                    'fieldName': item.get('fieldName', ''),
+                    'value': item.get('value', '')
+                })
+            
+            # 提取品牌(从标准参数中获取)
+            for param in product_info['standard_params']:
+                if param['fieldName'] == '品牌':
+                    product_info['brand'] = param['value']
+                    break
+            if product_info['brand'] == '未知':
+                # 如果标准参数中没有,尝试从名称中提取
+                name = product_info['name']
+                brand_keywords = ['农夫山泉', '可口可乐', '百事', '康师傅', '统一', '伊利', '蒙牛', '华为', '小米', '苹果']
+                for brand in brand_keywords:
+                    if brand in name:
+                        product_info['brand'] = brand
+                        break
+            
+            # 图片数量
+            pictures = data_content.get('pictures', [])
+            product_info['image_count'] = len(pictures)
+            
+            # 视频数量
+            video_infos = data_content.get('product_video_infos', [])
+            product_info['video_count'] = len(video_infos)
+            
+            # 配送信息
+            delivery_info = data_content.get('delivery_info', {})
+            if delivery_info:
+                product_info['delivery_info'] = delivery_info.get('delivery_content', '')
+            
+            # 活动信息
+            activity_infos = data_content.get('activity_infos', [])
+            for act in activity_infos:
+                product_info['activity_infos'].append(act.get('activity_text', ''))
                 
-                # 提取品牌
-                if 'name' in product_info and '农夫山泉' in product_info['name']:
-                    product_info['brand'] = '农夫山泉'
-                elif 'name' in product_info and '可口可乐' in product_info['name']:
-                    product_info['brand'] = '可口可乐'
-                elif 'name' in product_info and '百事' in product_info['name']:
-                    product_info['brand'] = '百事'
-                    
         except Exception as e:
             print(f"提取商品信息时出错: {e}")
         
         return product_info
+
+    def save_product_info(self, product_info, output_path, urls):
+        """保存商品简要信息到data.txt"""
+        try:
+            txt_path = output_path / 'data.txt'
+            with open(txt_path, 'w', encoding='utf-8') as f:
+                # 商品名称
+                f.write(f"商品: {product_info.get('name', '未命名商品')}\n")
+                
+                # 价格信息
+                min_price = product_info.get('min_price', '未知')
+                origin_price = product_info.get('origin_price', '未知')
+                
+                if min_price != '未知' and origin_price != '未知':
+                    f.write(f"价格: {min_price}元起 (划线价 {origin_price}元)\n")
+                    # 计算折扣
+                    try:
+                        discount = int((1 - float(min_price) / float(origin_price)) * 100)
+                        if discount > 0:
+                            f.write(f"折扣: {discount}% OFF\n")
+                    except:
+                        pass
+                elif min_price != '未知':
+                    f.write(f"价格: {min_price}元起\n")
+                else:
+                    f.write(f"价格: 未知\n")
+                
+                # 销量
+                month_saled = product_info.get('month_saled_content', '未知')
+                f.write(f"销量: {month_saled}\n")
+                f.write("\n")
+                
+                # SKU信息
+                sku_list = product_info.get('sku_list', [])
+                sku_label = product_info.get('sku_label', '规格')
+                if sku_list:
+                    f.write(f"可选{sku_label} (共{len(sku_list)}种):\n")
+                    for i, sku in enumerate(sku_list, 1):
+                        spec = sku.get('spec', '')
+                        price = sku.get('price', '')
+                        stock = sku.get('stock', '')
+                        upccode = sku.get('upccode', '')
+                        
+                        f.write(f"  {i}. {spec} - {price}元")
+                        if stock and stock != '未知' and stock != '':
+                            f.write(f" (库存{stock})")
+                        if upccode:
+                            f.write(f" 代码:{upccode}")
+                        f.write("\n")
+                else:
+                    f.write(f"无{sku_label}信息\n")
+                
+                f.write("\n")
+                
+                # 参数信息
+                standard_params = product_info.get('standard_params', [])
+                if standard_params:
+                    params_str = " | ".join([f"{p['fieldName']}:{p['value']}" for p in standard_params])
+                    f.write(f"参数: {params_str}\n")
+                
+                f.write("\n")
+                
+                # 配送信息
+                delivery_info = product_info.get('delivery_info', '')
+                if delivery_info:
+                    f.write(f"配送: {delivery_info}\n")
+                
+                # 活动信息
+                activity_infos = product_info.get('activity_infos', [])
+                for act in activity_infos:
+                    if act:
+                        f.write(f"优惠: {act}\n")
+                        
+            return True
+        except Exception as e:
+            print(f"保存data.txt失败: {e}")
+            return False
     
     def download_file(self, url, filepath, timeout=30):
         """下载文件"""
         try:
+            print(url)
+            print(filepath)
             response = self.session.get(url, timeout=timeout, stream=True)
             if response.status_code == 200:
                 with open(filepath, 'wb') as f:
@@ -277,9 +377,7 @@ class ProductDownloaderGUI:
     
     def sanitize_filename(self, name):
         """清理文件名中的非法字符"""
-        # 移除非法字符
         name = re.sub(r'[<>:"/\\|?*]', '_', name)
-        # 限制长度
         if len(name) > 100:
             name = name[:100]
         return name.strip()
@@ -293,30 +391,20 @@ class ProductDownloaderGUI:
             'errors': []
         }
         
-        # 创建子目录
-        main_img_dir = output_path / "主图"
-        detail_img_dir = output_path / "详情图"
-        video_dir = output_path / "视频"
-        
-        main_img_dir.mkdir(exist_ok=True)
-        detail_img_dir.mkdir(exist_ok=True)
-        video_dir.mkdir(exist_ok=True)
-        
         total_items = len(urls['main_images']) + len(urls['detail_images']) + len(urls['videos'])
         
         # 下载主图
         for i, url in enumerate(urls['main_images'], 1):
             results['total'] += 1
-            # 获取文件扩展名
             path = urlparse(url).path
             ext = os.path.splitext(path)[1] or '.jpg'
             if ext not in ['.jpg', '.jpeg', '.png', '.gif', '.webp']:
                 ext = '.jpg'
             filename = f"zhutu_{i}{ext}"
-            filepath = main_img_dir / filename
+            filepath = output_path / filename
             
             progress_callback(f"下载主图 {i}/{len(urls['main_images'])}...", 
-                            int(results['total'] / total_items * 100))
+                            int(results['total'] / total_items * 100) if total_items > 0 else 0)
             
             if self.download_file(url, filepath):
                 results['downloaded'] += 1
@@ -331,10 +419,10 @@ class ProductDownloaderGUI:
             if ext not in ['.jpg', '.jpeg', '.png', '.gif', '.webp']:
                 ext = '.jpg'
             filename = f"xiangqing_{i}{ext}"
-            filepath = detail_img_dir / filename
+            filepath = output_path / filename
             
             progress_callback(f"下载详情图 {i}/{len(urls['detail_images'])}...",
-                            int(results['total'] / total_items * 100))
+                            int(results['total'] / total_items * 100) if total_items > 0 else 0)
             
             if self.download_file(url, filepath):
                 results['downloaded'] += 1
@@ -349,10 +437,10 @@ class ProductDownloaderGUI:
             if ext not in ['.mp4', '.mov', '.avi', '.webm']:
                 ext = '.mp4'
             filename = f"shipin_{i}{ext}"
-            filepath = video_dir / filename
+            filepath = output_path / filename
             
             progress_callback(f"下载视频 {i}/{len(urls['videos'])}...",
-                            int(results['total'] / total_items * 100))
+                            int(results['total'] / total_items * 100) if total_items > 0 else 0)
             
             if self.download_file(url, filepath):
                 results['downloaded'] += 1
@@ -363,23 +451,19 @@ class ProductDownloaderGUI:
     
     def start_download(self):
         """启动下载任务"""
-        # 获取输入数据
         input_data = self.input_text.get(1.0, END).strip()
         if not input_data:
             messagebox.showerror("错误", "请输入商品数据")
             return
         
-        # 检查输出目录
         output_dir = self.output_dir.get()
         if not output_dir:
             messagebox.showerror("错误", "请选择输出目录")
             return
         
-        # 禁用启动按钮
         self.start_btn.config(state=DISABLED, text="⏳ 下载中...")
         self.progress_var.set(0)
         
-        # 在新线程中执行下载
         thread = threading.Thread(target=self.download_task, args=(input_data, output_dir))
         thread.daemon = True
         thread.start()
@@ -393,9 +477,6 @@ class ProductDownloaderGUI:
             # 提取商品信息
             product_info = self.extract_product_info(data)
             
-            # 更新信息显示
-            self.root.after(0, self.update_info_display, product_info)
-            
             # 清理产品名作为文件夹名
             safe_name = self.sanitize_filename(product_info['name'])
             
@@ -407,36 +488,39 @@ class ProductDownloaderGUI:
             self.update_status_thread("正在分析数据,提取资源链接...")
             urls = self.extract_urls(data)
             
+            # 保存商品简要信息到data.txt
+            self.update_status_thread("正在保存商品信息...")
+            if self.save_product_info(product_info, product_path, urls):
+                self.update_status_thread("商品信息已保存")
+            else:
+                self.update_status_thread("保存商品信息失败")
+            
             total_urls = len(urls['main_images']) + len(urls['detail_images']) + len(urls['videos'])
             
             if total_urls == 0:
-                self.root.after(0, messagebox.showwarning, "警告", "未找到任何图片或视频资源")
+                self.root.after(0, messagebox.showwarning, "警告", "未找到任何图片或视频资源\n但data.txt已保存")
                 self.update_status_thread("未找到资源")
                 self.enable_button_thread()
                 return
             
             self.update_status_thread(f"发现 {len(urls['main_images'])} 张主图, {len(urls['detail_images'])} 张详情图, {len(urls['videos'])} 个视频")
             
-            # 定义进度回调
             def update_progress(msg, progress):
                 self.update_status_thread(msg)
                 self.root.after(0, self.progress_var.set, progress)
             
-            # 下载资源
             results = self.download_resources(safe_name, product_path, urls, update_progress)
             
-            # 更新进度到100%
             self.root.after(0, self.progress_var.set, 100)
             
-            # 显示结果
             if results['downloaded'] > 0:
-                msg = f"✅ 下载完成!\n\n成功下载 {results['downloaded']}/{results['total']} 个文件\n保存路径: {product_path}"
+                msg = f"✅ 下载完成!\n\n成功下载 {results['downloaded']}/{results['total']} 个文件\n保存路径: {product_path}\n\n商品信息已保存为 data.txt"
                 if results['errors']:
                     msg += f"\n\n⚠️ 失败 {len(results['errors'])} 个文件"
                 self.root.after(0, messagebox.showinfo, "完成", msg)
                 self.update_status_thread(f"下载完成 - 成功 {results['downloaded']}/{results['total']}")
             else:
-                self.root.after(0, messagebox.showerror, "错误", "下载失败,没有成功下载任何文件")
+                self.root.after(0, messagebox.showerror, "错误", "下载失败,没有成功下载任何文件\n但data.txt已保存")
                 self.update_status_thread("下载失败")
                 
         except json.JSONDecodeError as e:

+ 424 - 176
爱回收.py

@@ -7,19 +7,46 @@ from datetime import datetime
 import uuid
 import time
 import os
+import configparser
 
 class PhoneQueryApp:
     def __init__(self, root):
         self.root = root
         self.root.title("手机数据查询系统")
-        self.root.geometry("1200x800")
+        self.root.geometry("1300x850")
+        
+        # 配置文件
+        self.config_file = "config.ini"
+        self.token = self.load_token()
         
         # 商品数据
         self.current_products = []
         self.is_loading_cycles = False
-        self.collected_items = []
+        self.current_page = 0
+        self.total_pages = 0
+        self.page_size = 30
+        self.current_keyword = ""
+        
+        # 筛选选项映射
+        self.memory_map = {
+            "1T": 187,
+            "512G": 192,
+            "256G": 209,
+            "128G": 210
+        }
+        
+        self.battery_map = {
+            "100%": 1680,
+            "95%-99%": 5699,
+            "90%-95%": 5700,
+            "85%-90%": 5697,
+            "80%-85%": 5698,
+            "70%-80%": 6159,
+            "70%以下": 6160,
+            "不支持": 1627
+        }
         
-        # 状况标签映射(tagId -> 名称)
+        # 状况标签映射
         self.condition_tags = {
             10: "屏幕完美",
             11: "机身无痕",
@@ -33,27 +60,37 @@ class PhoneQueryApp:
             21: "原厂部件"
         }
         
-        # 从文件读取cookie
-        self.cookie = self.load_cookie()
-        
-        # 创建界面
         self.create_widgets()
+        self.result_tree.bind("<Button-3>", self.show_context_menu)
         
-    def load_cookie(self):
-        """从cookie.txt文件读取cookie"""
-        cookie_file = "cookie.txt"
-        if os.path.exists(cookie_file):
+    def load_token(self):
+        """从配置文件加载Token"""
+        if os.path.exists(self.config_file):
             try:
-                with open(cookie_file, 'r', encoding='utf-8') as f:
-                    cookie = f.read().strip()
-                    print(f"已加载cookie: {cookie[:50]}...")
-                    return cookie
-            except Exception as e:
-                print(f"读取cookie文件失败: {e}")
-                return ""
-        else:
-            print("cookie.txt文件不存在,请创建并放入cookie")
-            return ""
+                config = configparser.ConfigParser()
+                config.read(self.config_file)
+                return config.get('Settings', 'token', fallback='dccf18b052ef46d78d4feb796182d5df')
+            except:
+                return 'dccf18b052ef46d78d4feb796182d5df'
+        return 'dccf18b052ef46d78d4feb796182d5df'
+        
+    def save_token(self):
+        """保存Token到配置文件"""
+        try:
+            config = configparser.ConfigParser()
+            if os.path.exists(self.config_file):
+                config.read(self.config_file)
+            if not config.has_section('Settings'):
+                config.add_section('Settings')
+            config.set('Settings', 'token', self.token_var.get())
+            with open(self.config_file, 'w') as f:
+                config.write(f)
+            self.token = self.token_var.get()
+            messagebox.showinfo("成功", "Token已保存")
+            return True
+        except Exception as e:
+            messagebox.showerror("错误", f"保存Token失败: {e}")
+            return False
         
     def get_headers(self):
         """获取请求头"""
@@ -62,7 +99,7 @@ class PhoneQueryApp:
             "content-type": "application/json;charset=UTF-8",
             "ahs-app-version": "7.49.3",
             "Ahs-Timestamp": str(int(datetime.now().timestamp())),
-            "Ahs-Token": "dccf18b052ef46d78d4feb796182d5df",
+            "Ahs-Token": self.token,
             "Ahs-Session-Id": str(uuid.uuid4()),
             "Ahs-App-Id": "10007",
             "Ahs-Device-Id": "3166e003-4ba7-4b57-8158-30859d81c7d5",
@@ -70,8 +107,7 @@ class PhoneQueryApp:
             "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 26_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.74(0x18004a2c) NetType/WIFI Language/zh_CN",
             "Referer": "https://servicewechat.com/wx7e490492b4c23e98/1055/page-frame.html",
             "Accept": "*/*",
-            "Accept-Encoding": "gzip, deflate, br",
-            # "Cookie": self.cookie
+            "Accept-Encoding": "gzip, deflate, br"
         }
         
     def create_widgets(self):
@@ -82,11 +118,24 @@ class PhoneQueryApp:
         self.root.columnconfigure(0, weight=1)
         self.root.rowconfigure(0, weight=1)
         main_frame.columnconfigure(0, weight=1)
-        main_frame.rowconfigure(2, weight=1)
+        main_frame.rowconfigure(5, weight=1)
+        
+        # Token设置区域
+        token_frame = ttk.LabelFrame(main_frame, text="Token设置", padding="10")
+        token_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
+        token_frame.columnconfigure(1, weight=1)
+        
+        ttk.Label(token_frame, text="Ahs-Token:", font=("Arial", 10)).grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
+        self.token_var = tk.StringVar(value=self.token)
+        self.token_entry = ttk.Entry(token_frame, textvariable=self.token_var, width=60, font=("Arial", 10))
+        self.token_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
+        
+        self.save_token_btn = ttk.Button(token_frame, text="保存Token", command=self.save_token, width=12)
+        self.save_token_btn.grid(row=0, column=2)
         
         # 查询区域
         query_frame = ttk.LabelFrame(main_frame, text="查询条件", padding="10")
-        query_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
+        query_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
         query_frame.columnconfigure(1, weight=1)
         
         ttk.Label(query_frame, text="机型:", font=("Arial", 11)).grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
@@ -97,26 +146,48 @@ class PhoneQueryApp:
         self.search_btn = ttk.Button(query_frame, text="搜索", command=self.search_products, width=15)
         self.search_btn.grid(row=0, column=2)
         
-        # 筛选区域
-        filter_frame = ttk.LabelFrame(main_frame, text="筛选条件", padding="10")
-        filter_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
+        # ==================== 筛选区域 ====================
+        # 第一行:内存 + 电池效率(各占一半)
+        filter_row1 = ttk.Frame(main_frame)
+        filter_row1.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
+        filter_row1.columnconfigure(0, weight=1)
+        filter_row1.columnconfigure(1, weight=1)
         
-        # 第一行:价格区间 和 成色
-        # 价格区间
-        price_frame = ttk.LabelFrame(filter_frame, text="价格区间", padding="5")
-        price_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=5, pady=5)
+        # 内存筛选
+        memory_frame = ttk.LabelFrame(filter_row1, text="内存(多选)", padding="5")
+        memory_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
         
-        self.price_vars = {}
-        price_options = ["0-1499", "1500-1999", "2000-2999", "3000-3999", "4000以上"]
-        for i, option in enumerate(price_options):
+        self.memory_vars = {}
+        memory_options = ["1T", "512G", "256G", "128G"]
+        for i, option in enumerate(memory_options):
             var = tk.BooleanVar()
-            self.price_vars[option] = var
-            cb = ttk.Checkbutton(price_frame, text=option, variable=var)
-            cb.grid(row=0, column=i, sticky=tk.W, padx=10, pady=2)
+            self.memory_vars[option] = var
+            cb = ttk.Checkbutton(memory_frame, text=option, variable=var)
+            cb.grid(row=0, column=i, sticky=tk.W, padx=15, pady=3)
         
-        # 成色
-        fineness_frame = ttk.LabelFrame(filter_frame, text="成色", padding="5")
-        fineness_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5, pady=5)
+        # 电池效率筛选
+        battery_frame = ttk.LabelFrame(filter_row1, text="电池效率(多选)", padding="5")
+        battery_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0))
+        
+        self.battery_vars = {}
+        battery_options = ["100%", "95%-99%", "90%-95%", "85%-90%", "80%-85%", "70%-80%", "70%以下", "不支持"]
+        for i, option in enumerate(battery_options):
+            var = tk.BooleanVar()
+            self.battery_vars[option] = var
+            row = i // 4
+            col = i % 4
+            cb = ttk.Checkbutton(battery_frame, text=option, variable=var)
+            cb.grid(row=row, column=col, sticky=tk.W, padx=10, pady=3)
+        
+        # 第二行:成色 + 状况标签(各占一半)
+        filter_row2 = ttk.Frame(main_frame)
+        filter_row2.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
+        filter_row2.columnconfigure(0, weight=1)
+        filter_row2.columnconfigure(1, weight=1)
+        
+        # 成色筛选
+        fineness_frame = ttk.LabelFrame(filter_row2, text="成色(多选)", padding="5")
+        fineness_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
         
         self.fineness_vars = {}
         fineness_options = ["全新仅拆封", "准新机", "99新", "95新", "9成新", "8成新", "7成新"]
@@ -124,95 +195,118 @@ class PhoneQueryApp:
             var = tk.BooleanVar()
             self.fineness_vars[option] = var
             cb = ttk.Checkbutton(fineness_frame, text=option, variable=var)
-            cb.grid(row=0, column=i, sticky=tk.W, padx=10, pady=2)
+            cb.grid(row=0, column=i, sticky=tk.W, padx=10, pady=3)
         
-        # 第二行:状况标签(占满整行)
-        condition_frame = ttk.LabelFrame(filter_frame, text="状况标签", padding="5")
-        condition_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=5)
+        # 状况标签筛选
+        condition_frame = ttk.LabelFrame(filter_row2, text="状况标签(多选)", padding="5")
+        condition_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0))
         
         self.condition_vars = {}
-        # 将状况标签分成两行显示,每行5个
         condition_items = list(self.condition_tags.items())
         for i, (tag_id, tag_name) in enumerate(condition_items):
             var = tk.BooleanVar()
             self.condition_vars[tag_id] = var
-            row = i // 5  # 每行5个
+            row = i // 5
             col = i % 5
             cb = ttk.Checkbutton(condition_frame, text=tag_name, variable=var)
-            cb.grid(row=row, column=col, sticky=tk.W, padx=10, pady=2)
-        
-        filter_frame.columnconfigure(0, weight=1)
-        filter_frame.columnconfigure(1, weight=1)
+            cb.grid(row=row, column=col, sticky=tk.W, padx=10, pady=3)
         
-        # 结果显示区域
-        result_frame = ttk.LabelFrame(main_frame, text="查询结果", padding="10")
-        result_frame.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
+        # ==================== 结果显示区域 ====================
+        result_frame = ttk.LabelFrame(main_frame, text="查询结果(可多选商品,点击批量收藏)", padding="10")
+        result_frame.grid(row=4, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
         result_frame.columnconfigure(0, weight=1)
         result_frame.rowconfigure(0, weight=1)
         
-        columns = ("名称", "价格", "循环次数", "成色", "内存", "颜色", "网络制式")
-        self.result_tree = ttk.Treeview(result_frame, columns=columns, show="headings", height=15)
+        # 添加电池效率列
+        columns = ("名称", "价格", "电池效率", "循环次数", "成色", "内存", "颜色", "网络制式")
+        self.result_tree = ttk.Treeview(result_frame, columns=columns, show="headings", height=15, selectmode="extended")
         
         self.result_tree.heading("名称", text="名称")
         self.result_tree.heading("价格", text="价格")
+        self.result_tree.heading("电池效率", text="电池效率")
         self.result_tree.heading("循环次数", text="循环次数")
         self.result_tree.heading("成色", text="成色")
         self.result_tree.heading("内存", text="内存")
         self.result_tree.heading("颜色", text="颜色")
         self.result_tree.heading("网络制式", text="网络制式")
         
-        self.result_tree.column("名称", width=400)
-        self.result_tree.column("价格", width=100)
-        self.result_tree.column("循环次数", width=100)
-        self.result_tree.column("成色", width=100)
-        self.result_tree.column("内存", width=80)
-        self.result_tree.column("颜色", width=100)
-        self.result_tree.column("网络制式", width=100)
+        self.result_tree.column("名称", width=350)
+        self.result_tree.column("价格", width=90)
+        self.result_tree.column("电池效率", width=100)
+        self.result_tree.column("循环次数", width=90)
+        self.result_tree.column("成色", width=90)
+        self.result_tree.column("内存", width=70)
+        self.result_tree.column("颜色", width=90)
+        self.result_tree.column("网络制式", width=90)
         
         scrollbar = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=self.result_tree.yview)
         self.result_tree.configure(yscrollcommand=scrollbar.set)
         self.result_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
         scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
         
-        # 底部收藏栏
+        # ==================== 底部按钮栏 ====================
         bottom_frame = ttk.Frame(main_frame)
-        bottom_frame.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
+        bottom_frame.grid(row=5, column=0, sticky=(tk.W, tk.E), pady=(10, 5))
         
-        ttk.Label(bottom_frame, text="循环次数小于:", font=("Arial", 10)).pack(side=tk.LEFT, padx=(0, 5))
-        self.cycle_threshold = ttk.Entry(bottom_frame, width=10, font=("Arial", 10))
-        self.cycle_threshold.pack(side=tk.LEFT, padx=(0, 5))
-        self.cycle_threshold.insert(0, "600")
-        ttk.Label(bottom_frame, text="次的机型自动收藏", font=("Arial", 10)).pack(side=tk.LEFT, padx=(0, 20))
+        self.check_price_btn = ttk.Button(bottom_frame, text="获取循环次数", command=self.check_prices, width=18)
+        self.check_price_btn.pack(side=tk.LEFT, padx=(0, 10))
         
-        self.collect_btn = ttk.Button(bottom_frame, text="启动", command=self.start_collect, width=10)
-        self.collect_btn.pack(side=tk.LEFT)
+        self.batch_collect_btn = ttk.Button(bottom_frame, text="批量收藏所选商品", command=self.batch_collect, width=20)
+        self.batch_collect_btn.pack(side=tk.LEFT, padx=(0, 20))
         
         self.progress_var = tk.StringVar()
         self.progress_var.set("")
         ttk.Label(bottom_frame, textvariable=self.progress_var, font=("Arial", 9)).pack(side=tk.LEFT, padx=(20, 0))
         
-        self.collect_count_var = tk.StringVar()
-        self.collect_count_var.set("")
-        ttk.Label(bottom_frame, textvariable=self.collect_count_var, font=("Arial", 9), foreground="green").pack(side=tk.LEFT, padx=(10, 0))
+        # ==================== 分页栏 ====================
+        pagination_frame = ttk.Frame(main_frame)
+        pagination_frame.grid(row=6, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
+        
+        self.prev_btn = ttk.Button(pagination_frame, text="上一页", command=self.prev_page, width=10)
+        self.prev_btn.pack(side=tk.LEFT, padx=(0, 10))
+        
+        self.next_btn = ttk.Button(pagination_frame, text="下一页", command=self.next_page, width=10)
+        self.next_btn.pack(side=tk.LEFT, padx=(0, 20))
+        
+        ttk.Label(pagination_frame, text="第").pack(side=tk.LEFT)
+        self.page_entry = ttk.Entry(pagination_frame, width=8)
+        self.page_entry.pack(side=tk.LEFT, padx=(5, 5))
+        ttk.Label(pagination_frame, text="页").pack(side=tk.LEFT)
+        
+        self.jump_btn = ttk.Button(pagination_frame, text="跳转", command=self.jump_to_page, width=8)
+        self.jump_btn.pack(side=tk.LEFT, padx=(10, 20))
+        
+        self.page_info_var = tk.StringVar()
+        self.page_info_var.set("第 0 / 0 页")
+        page_label = ttk.Label(pagination_frame, textvariable=self.page_info_var, font=("Arial", 9))
+        page_label.pack(side=tk.LEFT)
+        
+        # ==================== 状态栏 ====================
+        status_frame = ttk.Frame(main_frame)
+        status_frame.grid(row=7, column=0, sticky=(tk.W, tk.E), pady=(5, 0))
         
-        # 状态栏
         self.status_var = tk.StringVar()
         self.status_var.set("就绪")
-        status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN)
-        status_bar.grid(row=4, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-        
-    def get_selected_prices(self):
-        price_map = {
-            "0-1499": (0, 1499),
-            "1500-1999": (1500, 1999),
-            "2000-2999": (2000, 2999),
-            "3000-3999": (3000, 3999),
-            "4000以上": (4000, 20000)
-        }
-        for option, var in self.price_vars.items():
+        status_bar = ttk.Label(status_frame, textvariable=self.status_var, relief=tk.SUNKEN)
+        status_bar.pack(side=tk.LEFT, fill=tk.X, expand=True)
+        
+        self.update_pagination_buttons()
+        
+    def get_selected_memory(self):
+        """获取选中的内存PPV ID列表"""
+        selected = []
+        for option, var in self.memory_vars.items():
+            if var.get():
+                selected.append(self.memory_map[option])
+        return selected
+        
+    def get_selected_battery(self):
+        """获取选中的电池效率PPV ID列表"""
+        selected = []
+        for option, var in self.battery_vars.items():
             if var.get():
-                return price_map[option]
-        return (0, 20000)
+                selected.append(self.battery_map[option])
+        return selected
         
     def get_selected_fineness(self):
         fineness_map = {
@@ -228,63 +322,94 @@ class PhoneQueryApp:
         for option, var in self.fineness_vars.items():
             if var.get():
                 selected.append(fineness_map[option])
-        return selected if selected else [99, 100, 111, 95, 90, 80, 70]
+        return selected
         
     def get_selected_condition_tags(self):
-        """获取选中的状况标签ID列表"""
         selected = []
         for tag_id, var in self.condition_vars.items():
             if var.get():
                 selected.append(tag_id)
         return selected
         
-    def search_products(self):
+    def get_battery_efficiency(self, condition_labels):
+        """从conditionLabels中提取电池效率"""
+        if not condition_labels:
+            return ""
+        
+        for label in condition_labels:
+            if label.get('group') in ['原厂电池', '第三方电池']:
+                return label.get('label', '')
+        return ""
+        
+    def search_products(self, page=0):
         keyword = self.model_entry.get().strip()
         if not keyword:
             messagebox.showwarning("警告", "请输入机型")
             return
             
+        self.current_keyword = keyword
+        self.current_page = page
+        
         for item in self.result_tree.get_children():
             self.result_tree.delete(item)
         self.current_products.clear()
-        self.collected_items.clear()
         self.progress_var.set("")
-        self.collect_count_var.set("")
-        self.collect_btn.config(state="normal")
+        self.check_price_btn.config(state="normal")
+        self.batch_collect_btn.config(state="normal")
             
         self.status_var.set("正在搜索...")
         self.search_btn.config(state="disabled")
+        self.prev_btn.config(state="disabled")
+        self.next_btn.config(state="disabled")
+        self.jump_btn.config(state="disabled")
         
-        thread = threading.Thread(target=self.do_search, args=(keyword,))
+        thread = threading.Thread(target=self.do_search, args=(keyword, page))
         thread.daemon = True
         thread.start()
         
-    def do_search(self, keyword):
+    def do_search(self, keyword, page):
         try:
-            min_price, max_price = self.get_selected_prices()
+            # 获取筛选条件
+            memory_ids = self.get_selected_memory()
+            battery_ids = self.get_selected_battery()
             fineness_ids = self.get_selected_fineness()
             condition_tags = self.get_selected_condition_tags()
             
             request_data = {
-                "minPrice": min_price,
-                "maxPrice": max_price,
                 "sortValue": "sort_composite",
-                "gaeaPricePpvIds": {},
-                "gaeaFinenessIds": fineness_ids,
-                "conditionTagList": condition_tags,  # 使用选中的状况标签
                 "scene": "search",
                 "keyword": keyword,
                 "sirReq": False,
                 "cityId": 324,
-                "pageIndex": 0,
-                "pageSize": 50
+                "pageIndex": page,
+                "pageSize": self.page_size
             }
             
+            # 添加内存筛选(使用 gaeaSkuPpvIds)
+            if memory_ids:
+                request_data["gaeaSkuPpvIds"] = {
+                    "22": memory_ids  # 22是内存的ppnId
+                }
+            
+            # 添加电池效率筛选(使用 gaeaPricePpvIds)
+            if battery_ids:
+                request_data["gaeaPricePpvIds"] = {
+                    "473": battery_ids  # 473是电池效率的ppnId
+                }
+            
+            # 添加成色筛选
+            if fineness_ids:
+                request_data["gaeaFinenessIds"] = fineness_ids
+            
+            # 添加状况标签筛选
+            if condition_tags:
+                request_data["conditionTagList"] = condition_tags
+            
             url = "https://dubai-mp.aihuishou.com/ahs-yanxuan-service/products/search-goods-v2"
             response = requests.post(url, json=request_data, headers=self.get_headers(), timeout=30)
             result = response.json()
-            
-            self.root.after(0, self.display_results, result)
+            print("请求参数:", json.dumps(request_data, ensure_ascii=False))
+            self.root.after(0, self.display_results, result, page)
             
         except Exception as e:
             self.root.after(0, self.show_error, str(e))
@@ -294,32 +419,42 @@ class PhoneQueryApp:
         sale_goods_no = product.get('saleGoodsNo', '')
         product_no = product.get('productNo', '')
         
+        # 提取颜色
         color = ""
-        memory = product.get('memoryDesc', '')
-        
-        if '黑色' in name:
-            color = "黑色"
-        elif '白色' in name:
-            color = "白色"
-        elif '蓝色' in name:
-            color = "蓝色"
-        elif '原色' in name:
-            color = "原色"
-            
+        color_keywords = ['黑色', '白色', '蓝色', '原色', '金色', '银色', '石墨色', '紫色', '绿色', '粉色', '红色', '黄色', '暗紫色', '深空黑色']
+        for keyword in color_keywords:
+            if keyword in name:
+                color = keyword
+                break
+        
+        # 获取电池效率
+        condition_labels = product.get('conditionLabels', [])
+        battery_efficiency = self.get_battery_efficiency(condition_labels)
+        
+        # 如果conditionLabels中没有,从productTag中获取
+        if not battery_efficiency:
+            product_tags = product.get('productTag', [])
+            for tag in product_tags:
+                if '电池' in tag:
+                    battery_efficiency = tag
+                    break
+        
         return {
             'name': name[:50],
             'price': product.get('price', 0),
+            'battery_efficiency': battery_efficiency,
             'cycle_count': '',
             'fineness': product.get('gaeaFinenessName', ''),
-            'memory': memory,
+            'memory': product.get('memoryDesc', ''),
             'color': color,
             'network': "全网通",
             'saleGoodsNo': sale_goods_no,
             'productNo': product_no
         }
         
-    def display_results(self, result):
+    def display_results(self, result, page):
         self.search_btn.config(state="normal")
+        self.jump_btn.config(state="normal")
         
         if result.get('code') != 0:
             self.status_var.set(f"查询失败: {result.get('resultMessage', '未知错误')}")
@@ -327,16 +462,33 @@ class PhoneQueryApp:
             return
             
         data = result.get('data', [])
+        total_count = result.get('totalCount', 0)
+        
+        if total_count > 0:
+            self.total_pages = (total_count + self.page_size - 1) // self.page_size
+        else:
+            if len(data) < self.page_size:
+                self.total_pages = page + 1
+            else:
+                self.total_pages = page + 2
+        
         if not data:
             self.status_var.set("未找到相关产品")
             messagebox.showinfo("提示", "未找到相关产品")
+            self.update_pagination_buttons()
             return
             
         for product in data:
             info = self.parse_product_info(product)
             item_id = self.result_tree.insert("", tk.END, values=(
-                info['name'], f"¥{info['price']}", info['cycle_count'],
-                info['fineness'], info['memory'], info['color'], info['network']
+                info['name'], 
+                f"¥{info['price']}", 
+                info['battery_efficiency'],
+                info['cycle_count'],
+                info['fineness'], 
+                info['memory'], 
+                info['color'], 
+                info['network']
             ))
             self.current_products.append({
                 'item_id': item_id,
@@ -345,8 +497,48 @@ class PhoneQueryApp:
                 'info': info
             })
             
-        self.status_var.set(f"共找到 {len(data)} 条结果")
+        self.status_var.set(f"第 {page + 1} 页,共找到 {len(data)} 条结果")
+        self.update_pagination_buttons()
         
+    def update_pagination_buttons(self):
+        self.page_info_var.set(f"第 {self.current_page + 1} / {self.total_pages if self.total_pages > 0 else 1} 页")
+        
+        if self.current_page <= 0:
+            self.prev_btn.config(state="disabled")
+        else:
+            self.prev_btn.config(state="normal")
+            self.next_btn.config(state="normal")
+            
+        if self.total_pages > 0 and self.current_page >= self.total_pages - 1:
+            self.next_btn.config(state="disabled")
+        else:
+            if len(self.current_products) >= self.page_size:
+                self.next_btn.config(state="normal")
+            else:
+                self.next_btn.config(state="disabled")
+                
+    def prev_page(self):
+        if self.current_page > 0:
+            self.search_products(self.current_page - 1)
+            
+    def next_page(self):
+        if self.total_pages == 0 or self.current_page < self.total_pages - 1:
+            self.search_products(self.current_page + 1)
+            
+    def jump_to_page(self):
+        try:
+            page_num = int(self.page_entry.get().strip())
+            if page_num < 1:
+                messagebox.showwarning("警告", "页码必须大于0")
+                return
+            if self.total_pages > 0 and page_num > self.total_pages:
+                messagebox.showwarning("警告", f"页码不能超过 {self.total_pages}")
+                return
+            self.search_products(page_num - 1)
+            self.page_entry.delete(0, tk.END)
+        except ValueError:
+            messagebox.showwarning("警告", "请输入有效的页码")
+            
     def get_cycle_count(self, sale_goods_no):
         """获取商品的电池循环次数"""
         try:
@@ -372,7 +564,6 @@ class PhoneQueryApp:
             return ''
             
     def add_to_collection(self, item_no):
-        """调用收藏接口添加收藏"""
         try:
             url = "https://dubai-mp.aihuishou.com/ahs-yanxuan-service/collect/save"
             payload = json.dumps({"itemNo": item_no, "type": 1})
@@ -381,7 +572,6 @@ class PhoneQueryApp:
             response = requests.post(url, data=payload, headers=headers, timeout=10)
             result = response.json()
             
-            # 根据实际返回判断:code为200且data为True表示成功
             if result.get('code') == 200 and result.get('data') == True:
                 return True, "收藏成功"
             else:
@@ -389,84 +579,142 @@ class PhoneQueryApp:
         except Exception as e:
             return False, str(e)
             
-    def start_collect(self):
-        if not self.current_products:
-            messagebox.showwarning("警告", "请先搜索商品")
+    def batch_collect(self):
+        selected_items = self.result_tree.selection()
+        if not selected_items:
+            messagebox.showwarning("警告", "请先选择要收藏的商品(可多选)")
             return
-        if self.is_loading_cycles:
-            messagebox.showinfo("提示", "正在处理中,请稍候...")
+            
+        result = messagebox.askyesno("确认收藏", f"确定要收藏选中的 {len(selected_items)} 个商品吗?")
+        if not result:
             return
             
-        if not self.cookie:
-            result = messagebox.askyesno("提示", "未找到cookie.txt文件或cookie为空,是否继续?")
-            if not result:
-                return
+        thread = threading.Thread(target=self.process_batch_collect, args=(selected_items,))
+        thread.daemon = True
+        thread.start()
+        
+    def process_batch_collect(self, selected_items):
+        self.batch_collect_btn.config(state="disabled")
+        self.check_price_btn.config(state="disabled")
+        
+        success_count = 0
+        fail_count = 0
+        total = len(selected_items)
+        
+        for i, item_id in enumerate(selected_items):
+            product = None
+            for p in self.current_products:
+                if p['item_id'] == item_id:
+                    product = p
+                    break
+                    
+            if product:
+                item_no = product['productNo'] or product['saleGoodsNo']
+                self.root.after(0, lambda txt=f"收藏中: {i+1}/{total}": self.progress_var.set(txt))
+                
+                success, msg = self.add_to_collection(item_no)
+                
+                if success:
+                    success_count += 1
+                else:
+                    fail_count += 1
+                    print(f"收藏失败: {product['info']['name']} - {msg}")
+                
+                time.sleep(0.2)
+        
+        self.root.after(0, lambda: self.finish_batch_collect(success_count, fail_count, total))
+        
+    def finish_batch_collect(self, success_count, fail_count, total):
+        self.batch_collect_btn.config(state="normal")
+        self.check_price_btn.config(state="normal")
+        self.progress_var.set("")
+        
+        if fail_count == 0:
+            self.status_var.set(f"批量收藏完成!成功收藏 {success_count} 个商品")
+            messagebox.showinfo("批量收藏完成", f"成功收藏 {success_count} 个商品!")
+        else:
+            self.status_var.set(f"批量收藏完成:成功 {success_count},失败 {fail_count}")
+            messagebox.showwarning("批量收藏完成", f"成功收藏 {success_count} 个商品\n失败 {fail_count} 个商品")
             
-        try:
-            threshold = int(self.cycle_threshold.get().strip())
-        except ValueError:
-            messagebox.showerror("错误", "请输入有效的循环次数")
+    def check_prices(self):
+        if not self.current_products:
+            messagebox.showwarning("警告", "请先搜索商品")
+            return
+        if self.is_loading_cycles:
+            messagebox.showinfo("提示", "正在获取中,请稍候...")
             return
             
-        thread = threading.Thread(target=self.process_collect, args=(threshold,))
+        thread = threading.Thread(target=self.process_check_prices)
         thread.daemon = True
         thread.start()
         
-    def process_collect(self, threshold):
-        """处理收藏:获取循环次数并收藏符合条件的商品"""
+    def process_check_prices(self):
         self.is_loading_cycles = True
-        self.collect_btn.config(state="disabled")
-        self.collected_items.clear()
+        self.check_price_btn.config(state="disabled")
+        self.batch_collect_btn.config(state="disabled")
         
         total = len(self.current_products)
-        collected_count = 0
         
         for i, product in enumerate(self.current_products):
-            self.root.after(0, lambda txt=f"处理: {i+1}/{total}": self.progress_var.set(txt))
-            self.root.after(0, lambda i=i: self.status_var.set(f"正在获取循环次数 ({i+1}/{total})..."))
+            self.root.after(0, lambda txt=f"获取中: {i+1}/{total}": self.progress_var.set(txt))
             
             cycle_count = self.get_cycle_count(product['saleGoodsNo'])
             
             if cycle_count:
                 values = list(self.result_tree.item(product['item_id'], 'values'))
-                values[2] = cycle_count
+                values[3] = cycle_count
                 self.root.after(0, lambda pid=product['item_id'], vals=values: self.result_tree.item(pid, values=vals))
-                
-                try:
-                    cycle_num = int(cycle_count)
-                    
-                    if cycle_num < threshold:
-                        item_no = product['productNo'] or product['saleGoodsNo']
-                        success, msg = self.add_to_collection(item_no)
-                        
-                        if success:
-                            collected_count += 1
-                            self.collected_items.append(f"{product['info']['name']} ({cycle_num}次)")
-                            self.root.after(0, lambda: self.status_var.set(f"✓ 已收藏: {product['info']['name'][:30]}"))
-                except ValueError:
-                    pass
             
             time.sleep(0.3)
             
-        self.root.after(0, lambda: self.finish_collect(collected_count, threshold))
+        self.root.after(0, self.finish_check_prices)
         
-    def finish_collect(self, collected_count, threshold):
+    def finish_check_prices(self):
         self.is_loading_cycles = False
-        self.collect_btn.config(state="normal")
+        self.check_price_btn.config(state="normal")
+        self.batch_collect_btn.config(state="normal")
         self.progress_var.set("")
-        self.collect_count_var.set(f"已收藏: {collected_count} 件")
-        self.status_var.set(f"完成!处理 {len(self.current_products)} 条,收藏 {collected_count} 件")
-        
-        if collected_count > 0:
-            detail = "\n".join(self.collected_items[:5])
-            if len(self.collected_items) > 5:
-                detail += f"\n... 等共{collected_count}件"
-            messagebox.showinfo("收藏完成", f"已成功收藏 {collected_count} 件商品\n\n{detail}")
-        else:
-            messagebox.showinfo("完成", f"没有找到循环次数小于 {threshold} 的商品")
+        self.status_var.set(f"完成!已获取 {len(self.current_products)} 条商品的循环次数")
+        messagebox.showinfo("完成", f"已成功获取 {len(self.current_products)} 条商品的循环次数")
+        
+    def show_context_menu(self, event):
+        item = self.result_tree.identify_row(event.y)
+        if not item:
+            return
+        
+        self.result_tree.selection_set(item)
+        values = self.result_tree.item(item, 'values')
+        product_name = values[0]
+        battery = values[2]
+        cycle_count = values[3]
+        
+        menu = tk.Menu(self.root, tearoff=0)
+        menu.add_command(label=f"收藏: {product_name[:30]}", command=lambda: self.collect_single_item(item))
+        if battery:
+            menu.add_command(label=f"电池: {battery}", command=None, state="disabled")
+        if cycle_count:
+            menu.add_command(label=f"循环次数: {cycle_count}", command=None, state="disabled")
+        menu.add_separator()
+        menu.add_command(label="取消", command=None)
+        menu.post(event.x_root, event.y_root)
+        
+    def collect_single_item(self, item):
+        for product in self.current_products:
+            if product['item_id'] == item:
+                item_no = product['productNo'] or product['saleGoodsNo']
+                success, msg = self.add_to_collection(item_no)
+                
+                if success:
+                    self.status_var.set(f"✓ 收藏成功: {product['info']['name'][:30]}")
+                    messagebox.showinfo("收藏成功", f"已成功收藏\n{product['info']['name']}")
+                else:
+                    self.status_var.set(f"✗ 收藏失败: {msg}")
+                    messagebox.showerror("收藏失败", f"收藏失败\n{product['info']['name']}\n错误: {msg}")
+                return
         
     def show_error(self, error_msg):
         self.search_btn.config(state="normal")
+        self.jump_btn.config(state="normal")
         self.status_var.set(f"查询失败: {error_msg}")
         messagebox.showerror("错误", f"查询失败: {error_msg}")
 

+ 3 - 3
米画师.py

@@ -66,11 +66,11 @@ class PurchaseApp:
         url = f"https://www.mihuashi.com/api/v1/manufactures/{product_id}/purchase"
         
         # 构建请求数据
-        payload = f"pay_password={pay_password}&remember_token=xxrohSdEFqQSt0fNY1mh4k&specification_ids%5B%5D=3931"
+        payload = f"pay_password={pay_password}&remember_token=xxrohSwRvhkMzftVNTb2SQ&specification_ids%5B%5D=3931"
         
         headers = {
             "content-type": "application/x-www-form-urlencoded",
-            "baggage": "sentry-environment=production,sentry-public_key=afa52c0802b4441ba513fb25cb6bc20a,sentry-release=com.mihuashi.iOS%408.7.1%2B231,sentry-trace_id=a0ac9f45d3f74b51b387a345b0d95389",
+            "baggage": "sentry-environment=production,sentry-public_key=afa52c0802b4441ba513fb25cb6bc20a,sentry-release=com.mihuashi.iOS%408.8.1%2B245,sentry-trace_id=c576b096547343d790462dbe3600ce7e",
             "x-track-trans-data": "CiQ4N2Q4NTRmOS02ZTQ4LTQ3YmMtOGIwYy1mMzRhY2U2Yzg4YTUSFQoMaG90XzE2NzY0X3YyFQDAr0QYHRISCggxNjc3OF92MhVgU9G+GNcBGhAKCTE1NzU4OmN0ch0A0l09GhAKCTE1NzU4OmN2ch0AyLg5IgAq1wIhC3L415YlBiR6vAJ7ImFwaV9yZXFfdGltZSI6MTc4MTI1MDQ2NCwic3BtXzMiOiLmjqjojZAiLCJzcG1fMiI6Iuapseeqly3mnI3orr4m5rC05Y2wIiwicGFnZV9udW0iOjEsImFiX3ZlcnNpb24iOiIiLCJzcG0iOiLnsbPnlLvluIgkIyMk5qmx56qXLeacjeiuvibmsLTljbAkIyMk5o6o6I2QIiwiY2xvc2VfcGVyc29uYWxpemVkX3JlYyI6ZmFsc2UsImJodl90aW1lIjoiMTc4MTI1MDQ2NCIsImJodl90aW1lX2hvdXIiOiIxNSIsImJodl90aW1lX21vbnRoZGF5IjoiMTIiLCJiaHZfdGltZV93ZWVrZGF5IjoiNSIsImZha2VfY29udGV4dF9pZCI6ImZha2VfY29udGV4dF9pZCJ9sAEOygEGNzY3NDY00AEy",
             "sensor-id": "96F047C1-773B-4596-BA8F-EB8E3D08D404",
             "authorization": "Bearer xxrohSdEFqQSt0fNY1mh4k",
@@ -82,7 +82,7 @@ class PurchaseApp:
             "sentry-trace": "a0ac9f45d3f74b51b387a345b0d95389-2b3cdabdb84741e4-0",
             "x-network-type": "wifi",
             "user-agent": "MHSIPhoneApp/8.7.1 (iPhone; iOS 26.5; Scale/3.00; iPhone15,4)",
-            "cookie": "aliyungf_tc=c3e9536bffb022b57059d7436f69e970c3ced15034d620e006be4582617a4e9e",
+            "cookie": "aliyungf_tc=5db016b76e04464209e124368fc4945f6be91f55436d0e145e708503abaa3602",
             "Accept-Encoding": "gzip, deflate, br",
             "Connection": "keep-alive"
         }

Fichier diff supprimé car celui-ci est trop grand
+ 185 - 0
飞书提交数据.py


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff