From bf11957f7ca41afc55d1198fe273dcc92153f9e2 Mon Sep 17 00:00:00 2001 From: SkyLi Date: Fri, 15 May 2026 15:14:55 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E7=95=8C=E9=9D=A2=EF=BC=9A?= =?UTF-8?q?=E5=9B=BA=E5=AE=9A=E5=80=BC=E6=94=B9=E7=94=A8=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=A1=86+=E5=A4=8D=E9=80=89=E6=A1=86=EF=BC=8C=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=8C=BA=E6=9B=BF=E6=8D=A2=E4=B8=BA=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E8=A1=A8=E6=A0=BC=EF=BC=8C=E9=97=B4=E9=9A=94?= =?UTF-8?q?=E5=8D=95=E4=BD=8D=E6=94=B9=E4=B8=BA=E6=AF=AB=E7=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.json | 2 +- engine.py | 11 ++- log.py | 5 +- main.py | 256 +++++++++++++++++++++++----------------------------- uv.lock | 2 +- 5 files changed, 127 insertions(+), 149 deletions(-) diff --git a/config.json b/config.json index 73a7213..15dfd91 100644 --- a/config.json +++ b/config.json @@ -3,7 +3,7 @@ "url": "https://10.4.188.1/pickup-web/a/postwaybill/main" }, "timing": { - "delay_between_rows_ms": 3000 + "delay_between_rows_ms": 500 }, "field_mapping": { "_comment": "仅需在此配置与内置映射不同的自定义列名,默认使用内置映射" diff --git a/engine.py b/engine.py index 5fc05a2..13ce31c 100644 --- a/engine.py +++ b/engine.py @@ -462,7 +462,9 @@ class PickupEngine: async def process_single_row(self, row: dict[str, str]) -> dict: start_time = datetime.now() - result = {"status": "失败", "message": "", "postage": "", "time": "", "寄达地": ""} + result = {"status": "失败", "message": "", "postage": "", "time": "", "寄达地": "", + "单号": row.get("单号", "").strip(), "重量": row.get("重量", "").strip(), + "产品": "", "客户": "", "寄达地区划代码": "", "寄件人姓名": "", "寄件地址": ""} try: await self._check_pause() @@ -515,6 +517,13 @@ class PickupEngine: self._dlog(" 提交中...") submit_result = await self._submit_form(form_data) result["寄达地"] = form_data.get("receiverArriveOrgName", "") + result["单号"] = row.get("单号", "").strip() + result["重量"] = form_data.get("realWeight", "") + result["产品"] = self._product_choice.get("productName", "") if self._product_choice else "" + result["客户"] = self._customer_choice.get("customerName", "") if self._customer_choice else "" + result["寄达地区划代码"] = form_data.get("receiverDistrictNo", "") + result["寄件人姓名"] = form_data.get("senderLinker", "") + result["寄件地址"] = form_data.get("senderAddr", "") elapsed = (datetime.now() - start_time).total_seconds() if submit_result.get("success"): diff --git a/log.py b/log.py index cbbd546..c77afca 100644 --- a/log.py +++ b/log.py @@ -9,7 +9,7 @@ import sys from datetime import datetime -LEVEL = 2 # 编译时修改此值 +LEVEL = -1 # 编译时修改此值 _gui_callback = None # GUI 注册的回调: (msg: str) -> None @@ -31,7 +31,8 @@ def _emit(msg: str): def user(msg: str): - _emit(msg) + if LEVEL >= 0: + _emit(msg) def debug(msg: str): diff --git a/main.py b/main.py index 455413a..ded602a 100644 --- a/main.py +++ b/main.py @@ -23,6 +23,7 @@ from tkinter import ( Y, BooleanVar, Button, + Checkbutton, Entry, Frame, Label, @@ -108,13 +109,25 @@ class App: self.config = {} self.excel_path = StringVar() - self.delay_var = StringVar(value="3") + self.delay_var = StringVar(value="500") self.status_var = StringVar(value="就绪") self.engine_status_var = StringVar(value="未启动") self.running = False self.paused = False self.results = [] + # 固定值编辑框变量 + self.fv_product = StringVar() + self.fv_customer = StringVar() + self.fv_destination = StringVar() + self.fv_force_destination = BooleanVar() + self.fv_district_code = StringVar() + self.fv_force_district = BooleanVar() + self.fv_sender_name = StringVar() + self.fv_force_sender_name = BooleanVar() + self.fv_sender_addr = StringVar() + self.fv_force_sender_addr = BooleanVar() + self._build_ui() # 关闭窗口时清理 @@ -152,7 +165,7 @@ class App: # 延时设置 row3 = Frame(config_frame) row3.pack(fill=X, pady=2) - Label(row3, text="间隔(秒):", width=10, anchor="e").pack(side="left") + Label(row3, text="间隔(毫秒):", width=10, anchor="e").pack(side="left") Entry(row3, textvariable=self.delay_var, width=8).pack(side="left", padx=5) Label(row3, text="(每单提交之间的等待时间)", fg="gray").pack(side="left") @@ -176,10 +189,6 @@ class App: btn_frame, text="⏹ 停止", command=self._stop, width=10, state=DISABLED ) self.stop_btn.pack(side="left", padx=2) - self.config_btn = Button( - btn_frame, text="编辑配置", command=self._edit_config, width=10 - ) - self.config_btn.pack(side="right", padx=2) # 状态 Label( @@ -187,31 +196,46 @@ class App: ).pack(side="right", padx=10) # ── 固定值设置 ── - fixed_frame = LabelFrame(self.root, text="固定值设置(优先级:强制覆盖 > API获取值 > 固定值 > 表格 > 默认值)", padx=5, pady=5) + fixed_frame = LabelFrame(self.root, text="固定值设置", padx=10, pady=5) fixed_frame.pack(fill=X, padx=10, pady=5) - tree_f = Frame(fixed_frame) - tree_f.pack(fill=X) + # Row 0: 产品 | 客户 + r0 = Frame(fixed_frame) + r0.pack(fill=X, pady=2) + Label(r0, text="产品:", width=10, anchor="e").pack(side="left") + Entry(r0, textvariable=self.fv_product, width=20).pack(side="left", padx=5) + Label(r0, text="客户:", width=10, anchor="e").pack(side="left", padx=(30, 0)) + Entry(r0, textvariable=self.fv_customer, width=20).pack(side="left", padx=5) - columns = ("field", "value", "force") - self.fixed_tree = ttk.Treeview(tree_f, columns=columns, show="headings", height=4) - self.fixed_tree.heading("field", text="字段名") - self.fixed_tree.heading("value", text="固定值") - self.fixed_tree.heading("force", text="强制覆盖") - self.fixed_tree.column("field", width=100) - self.fixed_tree.column("value", width=260) - self.fixed_tree.column("force", width=70, anchor="center") - self.fixed_tree.pack(side="left", fill=X, expand=True) + # Row 1: 寄达地 | 无影响 + r1 = Frame(fixed_frame) + r1.pack(fill=X, pady=2) + Label(r1, text="寄达地:", width=10, anchor="e").pack(side="left") + Entry(r1, textvariable=self.fv_destination, width=20).pack(side="left", padx=5) + Checkbutton(r1, text="强制覆盖", variable=self.fv_force_destination).pack(side="left", padx=5) + Label(r1, text="无影响", fg="gray", font=("", 8)).pack(side="left") - btn_f = Frame(fixed_frame) - btn_f.pack(fill=X, pady=2) - Button(btn_f, text="添加", command=self._add_fixed_value, width=6).pack(side="left", padx=2) - Button(btn_f, text="删除", command=lambda: [self.fixed_tree.delete(i) for i in self.fixed_tree.selection()], width=6).pack(side="left", padx=2) - Button(btn_f, text="保存", command=self._save_fixed_values, width=6, bg="#4CAF50", fg="white").pack(side="left", padx=2) - Label(btn_f, text="双击编辑值 | 单击'强制覆盖'列切换 | 留空则不生效", fg="gray").pack(side="left", padx=5) + # Row 2: 寄达地区划代码 | 强制覆盖 | 直接决定寄达地 + r2 = Frame(fixed_frame) + r2.pack(fill=X, pady=2) + Label(r2, text="寄达地区划代码:", width=12, anchor="e").pack(side="left") + Entry(r2, textvariable=self.fv_district_code, width=20).pack(side="left", padx=5) + Checkbutton(r2, text="强制覆盖", variable=self.fv_force_district).pack(side="left", padx=5) + Label(r2, text="(直接决定寄达地)", fg="gray", font=("", 8)).pack(side="left") - self.fixed_tree.bind("", self._edit_fixed_cell) - self.fixed_tree.bind("", self._toggle_force) + # Row 3: 寄件人姓名 | 强制覆盖 + r3 = Frame(fixed_frame) + r3.pack(fill=X, pady=2) + Label(r3, text="寄件人姓名:", width=12, anchor="e").pack(side="left") + Entry(r3, textvariable=self.fv_sender_name, width=20).pack(side="left", padx=5) + Checkbutton(r3, text="强制覆盖", variable=self.fv_force_sender_name).pack(side="left", padx=5) + + # Row 4: 寄件地址 | 强制覆盖 + r4 = Frame(fixed_frame) + r4.pack(fill=X, pady=2) + Label(r4, text="寄件地址:", width=12, anchor="e").pack(side="left") + Entry(r4, textvariable=self.fv_sender_addr, width=20).pack(side="left", padx=5) + Checkbutton(r4, text="强制覆盖", variable=self.fv_force_sender_addr).pack(side="left", padx=5) # ── 进度条 ── progress_frame = Frame(self.root) @@ -223,16 +247,21 @@ class App: self.progress_label = Label(progress_frame, text="", fg="gray") self.progress_label.pack() - # ── 日志区 ── - log_frame = LabelFrame(self.root, text="运行日志", padx=10, pady=5) - log_frame.pack(fill="both", expand=True, padx=10, pady=5) + # ── 提交结果表格 ── + result_frame = LabelFrame(self.root, text="提交结果", padx=10, pady=5) + result_frame.pack(fill="both", expand=True, padx=10, pady=5) - self.log_text = Text(log_frame, wrap="word", state=DISABLED, font=("Consolas", 9)) - self.log_text.pack(side="left", fill="both", expand=True) + columns = ("单号", "处理状态", "重量", "产品", "客户", "寄达地区划代码", "寄件人姓名", "寄件地址") + self.result_tree = ttk.Treeview(result_frame, columns=columns, show="headings", height=8) + col_widths = [140, 80, 60, 110, 110, 110, 80, 160] + for col, width in zip(columns, col_widths): + self.result_tree.heading(col, text=col) + self.result_tree.column(col, width=width, anchor="center") + self.result_tree.pack(side="left", fill="both", expand=True) - scroll_y = Scrollbar(log_frame, orient=VERTICAL, command=self.log_text.yview) + scroll_y = Scrollbar(result_frame, orient=VERTICAL, command=self.result_tree.yview) scroll_y.pack(side=RIGHT, fill=Y) - self.log_text.configure(yscrollcommand=scroll_y.set) + self.result_tree.configure(yscrollcommand=scroll_y.set) # ─── 配置管理 ─────────────────────────────────────────── @@ -248,18 +277,21 @@ class App: shutil.copy2(bundled, config_path) self.config = load_config(config_path) timing = get_timing_config(self.config) - delay_sec = timing.get("delay_between_rows_ms", 3000) // 1000 - self.delay_var.set(str(delay_sec)) + delay_ms = timing.get("delay_between_rows_ms", 500) + self.delay_var.set(str(delay_ms)) - # 加载固定值到表格 - for item in self.fixed_tree.get_children(): - self.fixed_tree.delete(item) - for field, cfg in self.config.get("fixed_values", {}).items(): - val = cfg.get("value", "") - force = "✓" if cfg.get("force") else "" - self.fixed_tree.insert("", "end", values=(field, val, force)) - - self._log("配置加载成功") + # 加载固定值到编辑框 + fv = self.config.get("fixed_values", {}) + self.fv_product.set(fv.get("产品名称", {}).get("value", "")) + self.fv_customer.set(fv.get("客户名称", {}).get("value", "")) + self.fv_destination.set(fv.get("寄达地", {}).get("value", "")) + self.fv_force_destination.set(fv.get("寄达地", {}).get("force", False)) + self.fv_district_code.set(fv.get("寄达地区划", {}).get("value", "")) + self.fv_force_district.set(fv.get("寄达地区划", {}).get("force", False)) + self.fv_sender_name.set(fv.get("寄件人", {}).get("value", "")) + self.fv_force_sender_name.set(fv.get("寄件人", {}).get("force", False)) + self.fv_sender_addr.set(fv.get("寄件地址", {}).get("value", "")) + self.fv_force_sender_addr.set(fv.get("寄件地址", {}).get("force", False)) # 检查已保存的登录会话 self._check_session_status() @@ -282,99 +314,18 @@ class App: self.engine_status_var.set("未登录") self.status_label.configure(fg="orange") - def _edit_config(self): - """打开配置编辑窗口。""" - config_path = _get_config_path() - # os.startfile(config_path) if sys.platform == "win32" else None + # ─── 固定值保存 ──────────────────────────────────────── - win = Toplevel(self.root) - win.title("编辑配置") - win.geometry("700x500") - - text = Text(win, wrap="none", font=("Consolas", 10)) - text.pack(fill="both", expand=True, padx=5, pady=5) - - with open(config_path, "r", encoding="utf-8") as f: - text.insert("1.0", f.read()) - - def save(): - new_config = text.get("1.0", "end-1c") - try: - json.loads(new_config) # 验证 JSON - with open(config_path, "w", encoding="utf-8") as f: - f.write(new_config) - self._load_config() - win.destroy() - self._log("配置已保存并重新加载") - except json.JSONDecodeError as e: - messagebox.showerror("JSON 错误", f"配置格式错误:\n{e}") - - Button(win, text="保存", command=save, bg="#4CAF50", fg="white").pack( - side="bottom", pady=5 - ) - - # ─── 固定值设置 ──────────────────────────────────────── - - def _edit_fixed_cell(self, event): - """双击编辑固定值单元格(跳过强制覆盖列)。""" - region = self.fixed_tree.identify_region(event.x, event.y) - if region != "cell": - return - column = self.fixed_tree.identify_column(event.x) - if column == "#3": # 强制覆盖列不双击编辑,单击切换 - return - item = self.fixed_tree.identify_row(event.y) - if not item: - return - col_idx = int(column.replace("#", "")) - 1 - old_val = self.fixed_tree.item(item, "values")[col_idx] - - edit = Entry(self.root) - edit.insert(0, old_val) - edit.select_range(0, "end") - bbox = self.fixed_tree.bbox(item, column) - if bbox: - edit.place(x=bbox[0] + 185, y=bbox[1] + 310, width=bbox[2], height=bbox[3]) - - def save_edit(e=None): - vals = list(self.fixed_tree.item(item, "values")) - vals[col_idx] = edit.get() - self.fixed_tree.item(item, values=vals) - edit.destroy() - edit.bind("", save_edit) - edit.bind("", lambda e: edit.destroy()) - edit.focus_set() - - def _add_fixed_value(self): - """添加新的固定值行(默认不强制覆盖)。""" - self.fixed_tree.insert("", "end", values=("新固定值", "", "")) - - def _toggle_force(self, event): - """单击'强制覆盖'列切换开关状态。""" - region = self.fixed_tree.identify_region(event.x, event.y) - if region != "cell": - return - column = self.fixed_tree.identify_column(event.x) - if column != "#3": # force 是第3列 - return - item = self.fixed_tree.identify_row(event.y) - if not item: - return - vals = list(self.fixed_tree.item(item, "values")) - vals[2] = "" if vals[2] == "✓" else "✓" - self.fixed_tree.item(item, values=vals) - - def _save_fixed_values(self): - """保存固定值到 config.json(新格式:{value, force})。""" - new_fixed = {} - for item in self.fixed_tree.get_children(): - vals = self.fixed_tree.item(item, "values") - field_name = vals[0].strip() - if field_name: - new_fixed[field_name] = { - "value": vals[1].strip() if len(vals) > 1 else "", - "force": vals[2] == "✓" if len(vals) > 2 else False, - } + def _save_fixed_from_entries(self): + """从编辑框保存固定值到 config.json。""" + new_fixed = { + "产品名称": {"value": self.fv_product.get().strip(), "force": False}, + "客户名称": {"value": self.fv_customer.get().strip(), "force": False}, + "寄达地": {"value": self.fv_destination.get().strip(), "force": self.fv_force_destination.get()}, + "寄达地区划": {"value": self.fv_district_code.get().strip(), "force": self.fv_force_district.get()}, + "寄件人": {"value": self.fv_sender_name.get().strip(), "force": self.fv_force_sender_name.get()}, + "寄件地址": {"value": self.fv_sender_addr.get().strip(), "force": self.fv_force_sender_addr.get()}, + } self.config["fixed_values"] = new_fixed config_path = _get_config_path() @@ -384,7 +335,19 @@ class App: with open(config_path, "w", encoding="utf-8") as f: json.dump(cfg, f, ensure_ascii=False, indent=4) - self._log(f"固定值已保存: {len(new_fixed)} 项") + def _add_result_row(self, result): + """添加一行到结果表格。""" + values = ( + result.get("单号", ""), + result.get("status", ""), + result.get("重量", ""), + result.get("产品", ""), + result.get("客户", ""), + result.get("寄达地区划代码", ""), + result.get("寄件人姓名", ""), + result.get("寄件地址", ""), + ) + self.result_tree.insert("", "end", values=values) # ─── 文件选择 ─────────────────────────────────────────── @@ -478,12 +441,19 @@ class App: self.paused = False self._set_controls_state("running") + # 保存固定值到配置文件 + self._save_fixed_from_entries() + + # 清空结果表格 + for item in self.result_tree.get_children(): + self.result_tree.delete(item) + # 创建结果工作簿 self.results = [] # 更新配置中的延时 try: - delay_ms = int(self.delay_var.get()) * 1000 + delay_ms = int(self.delay_var.get()) self.config.setdefault("timing", {})["delay_between_rows_ms"] = delay_ms except ValueError: pass @@ -560,6 +530,9 @@ class App: result = await _engine.process_single_row(row) self.results.append(result) + # 更新结果表格 + self.root.after(0, lambda r=result: self._add_result_row(r)) + # 更新进度 self.root.after(0, lambda cur=i+1, total=len(rows): self._update_progress(cur, total)) @@ -796,12 +769,7 @@ class App: return await future def _log_sync(self, msg): - timestamp = datetime.now().strftime("%H:%M:%S") - line = f"[{timestamp}] {msg}\n" - self.log_text.configure(state=NORMAL) - self.log_text.insert(END, line) - self.log_text.see(END) - self.log_text.configure(state=DISABLED) + pass # ─── 入口 ────────────────────────────────────────────────── diff --git a/uv.lock b/uv.lock index 1d950b2..33892f6 100644 --- a/uv.lock +++ b/uv.lock @@ -187,7 +187,7 @@ wheels = [ [[package]] name = "pickup-auto" -version = "0.1.0" +version = "2.0.0" source = { virtual = "." } dependencies = [ { name = "openpyxl" },