重构界面:固定值改用编辑框+复选框,日志区替换为提交结果表格,间隔单位改为毫秒
This commit is contained in:
+1
-1
@@ -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": "仅需在此配置与内置映射不同的自定义列名,默认使用内置映射"
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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("<Double-1>", self._edit_fixed_cell)
|
||||
self.fixed_tree.bind("<ButtonRelease-1>", 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("<Return>", save_edit)
|
||||
edit.bind("<FocusOut>", 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
|
||||
|
||||
# ─── 入口 ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user