批处理完成后保留固定值设置,新增重试和清空设置按钮
- _on_finished 不再清空产品/客户输入框及缓存选择 - 每行结果保存原始数据 _row 以支持重试 - 新增"重试"按钮:重跑所有非成功记录,原地更新结果表格 - 新增"清空设置"按钮:一键清空所有固定值字段和缓存
This commit is contained in:
@@ -208,6 +208,10 @@ class App:
|
||||
btn_frame, text="⏹ 停止", command=self._stop, width=10, state=DISABLED
|
||||
)
|
||||
self.stop_btn.pack(side="left", padx=2)
|
||||
self.retry_btn = Button(
|
||||
btn_frame, text="重试", command=self._retry, width=10, state=DISABLED
|
||||
)
|
||||
self.retry_btn.pack(side="left", padx=2)
|
||||
|
||||
# 状态
|
||||
Label(
|
||||
@@ -262,6 +266,11 @@ class App:
|
||||
Entry(r3, textvariable=self.fv_sender_addr, width=18).pack(side="left", padx=5)
|
||||
Label(r3, text="(有值即强制覆盖)", fg="gray", font=("", 8)).pack(side="left")
|
||||
|
||||
# 清空设置按钮
|
||||
r4 = Frame(fixed_frame)
|
||||
r4.pack(fill=X, pady=2)
|
||||
Button(r4, text="清空设置", command=self._clear_fixed_values, width=10).pack(side="right", padx=5)
|
||||
|
||||
# ── 进度条 ──
|
||||
progress_frame = Frame(self.root)
|
||||
progress_frame.pack(fill=X, padx=10, pady=2)
|
||||
@@ -876,6 +885,7 @@ class App:
|
||||
break
|
||||
|
||||
result = await _engine.process_single_row(row)
|
||||
result["_row"] = row # 保留原始行数据以便重试
|
||||
self.results.append(result)
|
||||
|
||||
# 更新结果表格
|
||||
@@ -914,6 +924,24 @@ class App:
|
||||
)
|
||||
self.status_var.set(f"处理中: {current}/{total}")
|
||||
|
||||
def _clear_fixed_values(self):
|
||||
"""清空所有固定值设置和缓存的选择。"""
|
||||
self.fv_product.set("")
|
||||
self.fv_customer.set("")
|
||||
self.fv_destination.set("")
|
||||
self.fv_district_code.set("")
|
||||
self.fv_sender_name.set("")
|
||||
self.fv_sender_addr.set("")
|
||||
self.fv_transport_type.set("")
|
||||
self.fv_weight.set("")
|
||||
self._product_cached_choice = None
|
||||
self._product_cached_detail = None
|
||||
self._customer_cached_choice = None
|
||||
self._customer_cached_detail = None
|
||||
self._sender_realname_cached = None
|
||||
self._transmode_map = {}
|
||||
self.fv_transport_combo["values"] = []
|
||||
|
||||
def _on_finished(self):
|
||||
global _engine
|
||||
self.running = False
|
||||
@@ -921,18 +949,18 @@ class App:
|
||||
_engine = None
|
||||
self._set_controls_state("stopped")
|
||||
self.status_var.set("就绪")
|
||||
# 清空产品/客户输入框(batch 结束后重置)
|
||||
self.fv_product.set("")
|
||||
self.fv_customer.set("")
|
||||
# 清空缓存的选择
|
||||
self._product_cached_choice = None
|
||||
self._product_cached_detail = None
|
||||
self._customer_cached_choice = None
|
||||
self._customer_cached_detail = None
|
||||
self._sender_realname_cached = None
|
||||
self.fv_transport_type.set("")
|
||||
self.fv_weight.set("")
|
||||
self._transmode_map = {}
|
||||
# 清空产品/客户输入框(batch 结束后重置)—— 已注释,保留设置以便重试
|
||||
# self.fv_product.set("")
|
||||
# self.fv_customer.set("")
|
||||
# # 清空缓存的选择
|
||||
# self._product_cached_choice = None
|
||||
# self._product_cached_detail = None
|
||||
# self._customer_cached_choice = None
|
||||
# self._customer_cached_detail = None
|
||||
# self._sender_realname_cached = None
|
||||
# self.fv_transport_type.set("")
|
||||
# self.fv_weight.set("")
|
||||
# self._transmode_map = {}
|
||||
|
||||
def _pause(self):
|
||||
global _engine
|
||||
@@ -962,15 +990,159 @@ class App:
|
||||
self.status_var.set("正在停止...")
|
||||
self._log("用户停止")
|
||||
|
||||
def _retry(self):
|
||||
"""重试所有非成功的记录。"""
|
||||
if self.running:
|
||||
return
|
||||
failed_indices = []
|
||||
failed_rows = []
|
||||
for i, r in enumerate(self.results):
|
||||
if r.get("status") != "成功" and "_row" in r:
|
||||
failed_indices.append(i)
|
||||
failed_rows.append(r["_row"])
|
||||
if not failed_rows:
|
||||
messagebox.showinfo("提示", "没有需要重试的记录")
|
||||
return
|
||||
self._save_fixed_from_entries()
|
||||
self.running = True
|
||||
self._set_controls_state("running")
|
||||
self.status_var.set(f"重试 {len(failed_rows)} 条...")
|
||||
self._log(f"开始重试 {len(failed_rows)} 条失败记录...")
|
||||
self.progress["maximum"] = len(failed_rows)
|
||||
self.progress["value"] = 0
|
||||
self.progress_label.configure(text=f"成功 0 | 失败 0 | 剩余 {len(failed_rows)}")
|
||||
_run_in_thread(self._run_retry(failed_indices, failed_rows))
|
||||
|
||||
async def _run_retry(self, indices, rows):
|
||||
"""后台重试指定的行。"""
|
||||
global _engine
|
||||
try:
|
||||
from engine import PickupEngine
|
||||
|
||||
field_mapping = get_field_mapping(self.config)
|
||||
defaults = get_defaults(self.config)
|
||||
timing = get_timing_config(self.config)
|
||||
fixed_values = get_fixed_values(self.config)
|
||||
|
||||
_engine = PickupEngine(
|
||||
url="https://10.4.188.1/pickup-web/a/postwaybill/main",
|
||||
field_mapping=field_mapping,
|
||||
defaults=defaults,
|
||||
timing=timing,
|
||||
log_callback=self._log_async,
|
||||
fixed_values=fixed_values,
|
||||
choose_callback=self._choose_callback,
|
||||
confirm_callback=self._confirm_callback,
|
||||
)
|
||||
|
||||
if self._search_api is not None:
|
||||
_engine.inject_infrastructure(self._search_sess, self._search_bridge, self._search_api)
|
||||
if self._product_cached_choice is not None:
|
||||
_engine._product_choice = self._product_cached_choice
|
||||
if self._product_cached_detail is not None:
|
||||
_engine._product_detail = self._product_cached_detail
|
||||
if self._customer_cached_choice is not None:
|
||||
_engine._customer_choice = self._customer_cached_choice
|
||||
if self._customer_cached_detail is not None:
|
||||
_engine._customer_detail = self._customer_cached_detail
|
||||
if self._sender_realname_cached is not None:
|
||||
_engine._sender_realname = self._sender_realname_cached
|
||||
transport_name = self.fv_transport_type.get()
|
||||
if transport_name and self._transmode_map:
|
||||
_engine._transport_type = self._transmode_map.get(transport_name, "")
|
||||
|
||||
self._update_status("启动中...")
|
||||
try:
|
||||
await _engine.start()
|
||||
except Exception as e:
|
||||
self._log_async(f"[error] 启动失败: {e}")
|
||||
self._update_status("启动失败")
|
||||
await _engine.stop()
|
||||
return
|
||||
|
||||
if not _engine._sess or not _engine._sess._http.cookies:
|
||||
self._log_async("[提示] 未找到登录会话,请点击'登录'按钮完成登录后再开始处理")
|
||||
self._update_status("未登录")
|
||||
await _engine.stop()
|
||||
return
|
||||
cookie_names = {c.name for c in _engine._sess._http.cookies}
|
||||
missing = [n for n in ("JSESSIONID", "jdpt.session.id") if n not in cookie_names]
|
||||
if missing:
|
||||
self._log_async(f"[提示] 缺少关键 cookie: {', '.join(missing)},请重新登录并手动填写")
|
||||
self._update_status("未登录")
|
||||
await _engine.stop()
|
||||
return
|
||||
|
||||
try:
|
||||
await _engine.begin_processing()
|
||||
self._update_status("后台运行中")
|
||||
self._log_async("HTTP API 引擎已就绪")
|
||||
except Exception as e:
|
||||
self._log_async(f"[error] 初始化失败: {e}")
|
||||
self._update_status("启动失败")
|
||||
await _engine.stop()
|
||||
return
|
||||
|
||||
children = self.result_tree.get_children()
|
||||
success = 0
|
||||
fail = 0
|
||||
for j, (idx, row) in enumerate(zip(indices, rows)):
|
||||
if _engine._stop_requested:
|
||||
break
|
||||
|
||||
result = await _engine.process_single_row(row)
|
||||
result["_row"] = row
|
||||
self.results[idx] = result
|
||||
|
||||
values = (
|
||||
result.get("单号", ""),
|
||||
result.get("status", ""),
|
||||
result.get("重量", ""),
|
||||
result.get("产品", ""),
|
||||
result.get("客户", ""),
|
||||
result.get("运输类型", ""),
|
||||
result.get("寄达地区划代码", ""),
|
||||
result.get("寄件人姓名", ""),
|
||||
result.get("寄件地址", ""),
|
||||
)
|
||||
# 更新对应 treeview 行
|
||||
if idx < len(children):
|
||||
self.root.after(0, lambda v=values, iid=children[idx]: self.result_tree.item(iid, values=v))
|
||||
|
||||
if result.get("status") == "成功":
|
||||
success += 1
|
||||
else:
|
||||
fail += 1
|
||||
|
||||
self.root.after(0, lambda cur=j+1, total=len(rows), s=success, f=fail: self._update_progress(cur, total, s, f))
|
||||
|
||||
delay = timing.get("delay_between_rows_ms", 3000) / 1000
|
||||
if j < len(rows) - 1:
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
self._log_async(f"\n重试完成!成功 {success},失败 {fail}")
|
||||
|
||||
except Exception as e:
|
||||
self._log_async(f"[error] 重试异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
if _engine:
|
||||
await _engine.stop()
|
||||
self.root.after(0, self._on_finished)
|
||||
|
||||
def _set_controls_state(self, state):
|
||||
if state == "running":
|
||||
self.start_btn.configure(state=DISABLED)
|
||||
self.pause_btn.configure(state=NORMAL)
|
||||
self.stop_btn.configure(state=NORMAL)
|
||||
self.retry_btn.configure(state=DISABLED)
|
||||
else:
|
||||
self.start_btn.configure(state=NORMAL)
|
||||
self.pause_btn.configure(state=DISABLED, text="⏸ 暂停")
|
||||
self.stop_btn.configure(state=DISABLED)
|
||||
has_failed = any(r.get("status") != "成功" for r in self.results)
|
||||
self.retry_btn.configure(state=NORMAL if has_failed else DISABLED)
|
||||
|
||||
# ─── 日志 ───────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user