已点菜品栏更新
This commit is contained in:
parent
93fb61e140
commit
1bef9631eb
|
|
@ -144,6 +144,24 @@ class MealScheduleStore:
|
|||
return False
|
||||
return name in self.today_dish_names()
|
||||
|
||||
def lookup_price(self, dish_name):
|
||||
"""按菜名查今日排餐价格;未命中返回 None。"""
|
||||
name = (dish_name or '').strip()
|
||||
if not name:
|
||||
return None
|
||||
today = datetime.now().strftime('%Y-%m-%d')
|
||||
with self._lock:
|
||||
raw = self._raw
|
||||
date = self._date
|
||||
if not raw or date != today:
|
||||
return None
|
||||
for base in raw.get('bases') or []:
|
||||
for meal in base.get('meals') or []:
|
||||
for dish in meal.get('dishes') or []:
|
||||
if (dish.get('name') or '').strip() == name:
|
||||
return dish.get('price')
|
||||
return None
|
||||
|
||||
def current_period(self, now=None):
|
||||
"""
|
||||
根据当前时间匹配餐段。
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
self._order_locked = False # 报过一单后锁定,清场后才允许下一单
|
||||
self._order_clear_since = None # 无盘连续计时起点
|
||||
self._order_clear_hold = 1.5 # 无盘持续多久才解锁(秒)
|
||||
self._cart_hold = False # 本盘清单已展示:清场前不刷新
|
||||
self._last_detect_fp = None # 上一帧指纹(静止画面跳过检测)
|
||||
self._last_live_dishes = None # 上一轮识别结果(跳过检测时推进稳定计时)
|
||||
self._frame_sim_threshold = 0.992 # 帧相似度阈值:非常相似则跳过 YOLO/分类
|
||||
|
|
@ -304,11 +305,15 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
|
||||
def _on_wheel_all(self, event):
|
||||
alive = []
|
||||
menu_cv = getattr(self, "_menu_canvas", None)
|
||||
for canvas in list(getattr(self, "_scroll_canvases", [])):
|
||||
try:
|
||||
if not canvas.winfo_exists():
|
||||
continue
|
||||
alive.append(canvas)
|
||||
# 今日菜单为空时禁止滚动
|
||||
if menu_cv is not None and canvas is menu_cv and not getattr(self, "_menu_scroll_enabled", True):
|
||||
continue
|
||||
x, y = canvas.winfo_rootx(), canvas.winfo_rooty()
|
||||
if x <= event.x_root <= x + canvas.winfo_width() and \
|
||||
y <= event.y_root <= y + canvas.winfo_height():
|
||||
|
|
@ -1297,38 +1302,120 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
self._sync_dialog = None
|
||||
tk.Frame(self.root, bg=c["border"], height=1).pack(fill=tk.X)
|
||||
|
||||
def _build_today_menu(self, parent, c):
|
||||
"""左侧:第三方接口拉取的今日排餐菜单。"""
|
||||
panel = tk.Frame(
|
||||
def _build_left_panel(self, parent, c):
|
||||
"""左侧大框:已点菜品 / 今日菜单 同级页签切换。"""
|
||||
shell = tk.Frame(
|
||||
parent, bg=c["surface"],
|
||||
highlightbackground=c["border"], highlightthickness=1,
|
||||
)
|
||||
panel.pack(fill=tk.X, pady=(0, 8))
|
||||
shell.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
head = tk.Frame(panel, bg=c["surface"])
|
||||
head.pack(fill=tk.X, padx=12, pady=(10, 4))
|
||||
tk.Label(
|
||||
head, text="今日菜单", bg=c["surface"], fg=c["text"], font=FONTS["h1"],
|
||||
).pack(side=tk.LEFT)
|
||||
PillButton(
|
||||
head, text="刷新", variant="ghost", size="sm",
|
||||
tab_row = tk.Frame(shell, bg=c["surface"])
|
||||
tab_row.pack(fill=tk.X, padx=10, pady=(10, 6))
|
||||
|
||||
tabs = tk.Frame(tab_row, bg=c["surface"])
|
||||
tabs.pack(side=tk.LEFT)
|
||||
self._left_tab = "cart"
|
||||
self._left_tab_btns = {}
|
||||
self._left_tab_cart_btn = PillButton(
|
||||
tabs, text="已点菜品", variant="primary", size="sm",
|
||||
command=lambda: self._switch_left_tab("cart"),
|
||||
)
|
||||
self._left_tab_cart_btn.pack(side=tk.LEFT, padx=(0, 6))
|
||||
self._left_tab_menu_btn = PillButton(
|
||||
tabs, text="今日菜单", variant="ghost", size="sm",
|
||||
command=lambda: self._switch_left_tab("menu"),
|
||||
)
|
||||
self._left_tab_menu_btn.pack(side=tk.LEFT)
|
||||
self._left_tab_btns = {
|
||||
"cart": self._left_tab_cart_btn,
|
||||
"menu": self._left_tab_menu_btn,
|
||||
}
|
||||
|
||||
# 右侧:随页签切换的操作区
|
||||
self._left_tab_actions = tk.Frame(tab_row, bg=c["surface"])
|
||||
self._left_tab_actions.pack(side=tk.RIGHT)
|
||||
self.cart_count_label = tk.Label(
|
||||
self._left_tab_actions, text="0 项", bg=c["surface"], fg=c["muted"],
|
||||
font=FONTS["small"],
|
||||
)
|
||||
self._menu_refresh_btn = PillButton(
|
||||
self._left_tab_actions, text="刷新", variant="ghost", size="sm",
|
||||
command=self.refresh_today_menu_async,
|
||||
tooltip="重新从管理系统拉取今日排餐",
|
||||
).pack(side=tk.RIGHT)
|
||||
)
|
||||
|
||||
body = tk.Frame(shell, bg=c["surface"])
|
||||
body.pack(fill=tk.BOTH, expand=True)
|
||||
self._left_body = body
|
||||
|
||||
self._cart_page = tk.Frame(body, bg=c["surface"])
|
||||
self._menu_page = tk.Frame(body, bg=c["surface"])
|
||||
self._build_pos_cart(self._cart_page, c)
|
||||
self._build_today_menu(self._menu_page, c)
|
||||
self._switch_left_tab("cart")
|
||||
return shell
|
||||
|
||||
def _switch_left_tab(self, which):
|
||||
"""切换左侧页签:cart=已点菜品,menu=今日菜单。"""
|
||||
which = "menu" if which == "menu" else "cart"
|
||||
self._left_tab = which
|
||||
cart_page = getattr(self, "_cart_page", None)
|
||||
menu_page = getattr(self, "_menu_page", None)
|
||||
if cart_page is None or menu_page is None:
|
||||
return
|
||||
if which == "cart":
|
||||
menu_page.pack_forget()
|
||||
cart_page.pack(fill=tk.BOTH, expand=True)
|
||||
else:
|
||||
cart_page.pack_forget()
|
||||
menu_page.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
for key, btn in (getattr(self, "_left_tab_btns", {}) or {}).items():
|
||||
try:
|
||||
btn.set_variant("primary" if key == which else "ghost")
|
||||
except Exception:
|
||||
try:
|
||||
btn.configure(variant="primary" if key == which else "ghost")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 操作区:已点显示数量,菜单显示刷新
|
||||
try:
|
||||
self.cart_count_label.pack_forget()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self._menu_refresh_btn.pack_forget()
|
||||
except Exception:
|
||||
pass
|
||||
if which == "cart":
|
||||
try:
|
||||
self.cart_count_label.pack(side=tk.RIGHT)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
self._menu_refresh_btn.pack(side=tk.RIGHT)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _build_today_menu(self, parent, c):
|
||||
"""今日排餐内容(嵌入左侧页签页,不再单独外框)。"""
|
||||
panel = parent
|
||||
|
||||
self.menu_meta_label = tk.Label(
|
||||
panel, text="正在加载今日菜单…", bg=c["surface"], fg=c["muted"],
|
||||
font=FONTS["small"], anchor="w",
|
||||
)
|
||||
self.menu_meta_label.pack(fill=tk.X, padx=12, pady=(0, 4))
|
||||
self.menu_meta_label.pack(fill=tk.X, padx=12, pady=(4, 4))
|
||||
|
||||
# 餐段切换
|
||||
self.menu_period_bar = tk.Frame(panel, bg=c["surface"])
|
||||
self.menu_period_bar.pack(fill=tk.X, padx=8, pady=(0, 4))
|
||||
|
||||
list_host = tk.Frame(panel, bg=c["surface"], height=148)
|
||||
list_host.pack(fill=tk.X, padx=4, pady=(0, 8))
|
||||
list_host.pack_propagate(False)
|
||||
list_host = tk.Frame(panel, bg=c["surface"])
|
||||
list_host.pack(fill=tk.BOTH, expand=True, padx=4, pady=(0, 8))
|
||||
wrap, self.menu_dish_frame, self._menu_canvas = self._make_scroll_area(
|
||||
list_host, c["surface"],
|
||||
)
|
||||
|
|
@ -1338,6 +1425,7 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
self._menu_selected_period = None # None=自动当前餐段
|
||||
self._menu_period_btns = []
|
||||
self._menu_refresh_job = None
|
||||
self._menu_scroll_enabled = False
|
||||
return panel
|
||||
|
||||
def refresh_today_menu_async(self):
|
||||
|
|
@ -1442,7 +1530,7 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
if not periods:
|
||||
EmptyState(
|
||||
self.menu_dish_frame, text="暂无今日菜单",
|
||||
hint="启动后会自动从管理系统拉取;也可点右上角「刷新」",
|
||||
hint="启动后会自动从管理系统拉取;也可点页签旁「刷新」",
|
||||
glyph="◎", bg=c["surface"], pady=16,
|
||||
).pack(fill=tk.X)
|
||||
elif not dishes:
|
||||
|
|
@ -1478,16 +1566,24 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
row, text=name, bg=bg, fg=c["text"], font=FONTS["ui"],
|
||||
anchor="w",
|
||||
).pack(side=tk.LEFT, padx=(8, 4), pady=4)
|
||||
if price_txt:
|
||||
tk.Label(
|
||||
row, text=price_txt, bg=bg, fg=c["muted"], font=FONTS["small"],
|
||||
).pack(side=tk.RIGHT, padx=8)
|
||||
tk.Label(
|
||||
row, text=price_txt or "—", bg=bg,
|
||||
fg=c["text"] if price_txt else c["muted"],
|
||||
font=FONTS["small"],
|
||||
).pack(side=tk.RIGHT, padx=8)
|
||||
|
||||
# 有菜才允许滚动;空菜单锁死滚动区域,避免空转
|
||||
try:
|
||||
self._menu_canvas.update_idletasks()
|
||||
self._menu_canvas.configure(scrollregion=self._menu_canvas.bbox("all"))
|
||||
if dishes:
|
||||
self._menu_canvas.configure(scrollregion=self._menu_canvas.bbox("all"))
|
||||
self._menu_scroll_enabled = True
|
||||
else:
|
||||
self._menu_canvas.yview_moveto(0)
|
||||
self._menu_canvas.configure(scrollregion=(0, 0, 0, 0))
|
||||
self._menu_scroll_enabled = False
|
||||
except Exception:
|
||||
pass
|
||||
self._menu_scroll_enabled = bool(dishes)
|
||||
|
||||
# 每分钟刷新一次(餐段切换 / 元信息)
|
||||
job = getattr(self, "_menu_refresh_job", None)
|
||||
|
|
@ -1506,25 +1602,25 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
self.refresh_today_menu()
|
||||
|
||||
def _build_pos_cart(self, parent, c):
|
||||
panel = tk.Frame(parent, bg=c["surface"], highlightbackground=c["border"], highlightthickness=1)
|
||||
panel.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
head = tk.Frame(panel, bg=c["surface"])
|
||||
head.pack(fill=tk.X, padx=12, pady=(12, 8))
|
||||
tk.Label(head, text="已点菜品", bg=c["surface"], fg=c["text"],
|
||||
font=FONTS["h1"]).pack(side=tk.LEFT)
|
||||
self.cart_count_label = tk.Label(head, text="0 项", bg=c["surface"], fg=c["muted"],
|
||||
font=FONTS["small"])
|
||||
self.cart_count_label.pack(side=tk.RIGHT)
|
||||
"""已点菜品内容(嵌入左侧页签页,标题由页签承担)。"""
|
||||
panel = parent
|
||||
|
||||
cols = tk.Frame(panel, bg=c["pos_row_alt"])
|
||||
cols.pack(fill=tk.X)
|
||||
for text, w in (("菜品", 22), ("数量", 10), ("", 4)):
|
||||
for text, w in (("菜品", 14), ("金额", 8), ("数量", 8), ("", 4)):
|
||||
tk.Label(cols, text=text, bg=c["pos_row_alt"], fg=c["muted"],
|
||||
font=FONTS["small"], width=w, anchor="w").pack(side=tk.LEFT, padx=4, pady=6)
|
||||
|
||||
wrap, self.cart_frame, self._cart_canvas = self._make_scroll_area(panel, c["surface"])
|
||||
wrap.pack(fill=tk.BOTH, expand=True, padx=4, pady=4)
|
||||
|
||||
foot = tk.Frame(panel, bg=c["surface"])
|
||||
foot.pack(fill=tk.X, padx=12, pady=(0, 10))
|
||||
self.cart_total_label = tk.Label(
|
||||
foot, text="合计 ¥0", bg=c["surface"], fg=c["text"],
|
||||
font=FONTS["ui_bold"], anchor="e",
|
||||
)
|
||||
self.cart_total_label.pack(side=tk.RIGHT)
|
||||
return panel
|
||||
|
||||
def _build_pos_preview(self, parent, c):
|
||||
|
|
@ -1696,8 +1792,7 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
left = tk.Frame(body, bg=c["bg"], width=SIZES.get("cart_w", 320))
|
||||
left.grid(row=0, column=0, sticky="nsew", padx=(0, 8))
|
||||
left.grid_propagate(False)
|
||||
self._build_today_menu(left, c)
|
||||
self._build_pos_cart(left, c)
|
||||
self._build_left_panel(left, c)
|
||||
|
||||
right = tk.Frame(body, bg=c["bg"])
|
||||
right.grid(row=0, column=1, sticky="nsew")
|
||||
|
|
@ -1975,16 +2070,15 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
self._order_locked = False
|
||||
self._order_clear_since = None
|
||||
self._last_reported_sig = None
|
||||
self._cart_hold = False
|
||||
|
||||
def _note_order_scene_busy(self):
|
||||
"""画面上仍有托盘/已识别菜:清场计时清零。"""
|
||||
self._order_clear_since = None
|
||||
|
||||
def _note_order_scene_clear(self):
|
||||
"""无餐盘:累计清场时间,满 hold 后解除订单锁定。"""
|
||||
if not self._order_locked:
|
||||
self._order_clear_since = None
|
||||
return False
|
||||
"""无餐盘:累计清场时间,满 hold 后解除订单锁定,并允许下一盘刷新清单。"""
|
||||
# 无上报锁定时也要能「松开清单」:无盘持续后允许下一盘覆盖已点菜品
|
||||
now = time.time()
|
||||
if self._order_clear_since is None:
|
||||
self._order_clear_since = now
|
||||
|
|
@ -1992,10 +2086,15 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
hold = float(getattr(self, "_order_clear_hold", 1.5) or 1.5)
|
||||
if now - self._order_clear_since < hold:
|
||||
return False
|
||||
was_locked = bool(self._order_locked)
|
||||
self._order_locked = False
|
||||
self._order_clear_since = None
|
||||
self._last_reported_sig = None
|
||||
print(f"[order] 清场解锁(无盘持续 ≥ {hold:g}s,允许下一单)")
|
||||
self._cart_hold = False # 下一盘识别到目标时可更换清单
|
||||
if was_locked:
|
||||
print(f"[order] 清场解锁(无盘持续 ≥ {hold:g}s,允许下一单)")
|
||||
else:
|
||||
print(f"[order] 清场,已点清单可被下一盘覆盖(无盘 ≥ {hold:g}s)")
|
||||
return True
|
||||
|
||||
def _try_live_report(self, save_path, known_dishes, preview):
|
||||
|
|
@ -2451,15 +2550,15 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
|
||||
def _no_plate(did_unlock=unlocked):
|
||||
self._live_overlay_dishes = []
|
||||
self.render_result_dock([], None)
|
||||
tip = "扫描中:未检测到餐盘"
|
||||
# 无盘不刷空清单:保留上一盘已点菜品,直到下一盘识别到目标
|
||||
tip = "扫描中:未检测到餐盘|左侧保留上一盘"
|
||||
if did_unlock:
|
||||
tip += "|已清场,可报下一单"
|
||||
self._set_result_text(tip, "warn")
|
||||
if hasattr(self, "scan_tip_label"):
|
||||
try:
|
||||
self.scan_tip_label.config(
|
||||
text="扫描中 · 请将餐盘放入画面中央"
|
||||
text="扫描中 · 请放入下一盘(已点清单暂留)"
|
||||
+ (" · 已解锁下一单" if did_unlock else "")
|
||||
)
|
||||
except Exception:
|
||||
|
|
@ -2518,9 +2617,9 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
written=pending_written, elapsed=stable_elapsed,
|
||||
committed=already_committed, hold_s=hold,
|
||||
):
|
||||
# 只更新叠加框 + 左侧清单;预览继续刷实时帧
|
||||
# 只更新叠加框;清单:本盘展示后冻结,清场后下一盘再换
|
||||
self._live_overlay_dishes = list(ds or [])
|
||||
self.render_result_dock(ds, path)
|
||||
self.render_result_dock(ds, path, live_hold=True)
|
||||
try:
|
||||
self.update_pending_btn()
|
||||
except Exception:
|
||||
|
|
@ -2529,13 +2628,14 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
known = [d for d in ds if not d.get("is_unknown")]
|
||||
unknown = [d for d in ds if d.get("is_unknown")]
|
||||
|
||||
if known:
|
||||
top = max(known, key=lambda d: d.get("score") or 0)
|
||||
name = top.get("label") or "菜品"
|
||||
msg = f"扫描中|已识别 {len(known)}|{name}"
|
||||
if known or unknown:
|
||||
msg = f"扫描中|清单 {len(ds)} 项"
|
||||
if known:
|
||||
top = max(known, key=lambda d: d.get("score") or 0)
|
||||
msg += f"|{top.get('label') or '菜品'}"
|
||||
if unknown:
|
||||
msg += f"|未知 {len(unknown)}(不入清单)"
|
||||
self._set_result_text(msg, "ok" if not unknown else "warn")
|
||||
msg += f"|未知 {len(unknown)}"
|
||||
self._set_result_text(msg, "ok" if known else "warn")
|
||||
else:
|
||||
self._set_result_text(
|
||||
txt or f"扫描中|检出 {len(ds)} 盘,尚无达到阈值的菜品",
|
||||
|
|
@ -2571,7 +2671,7 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
)
|
||||
else:
|
||||
self.scan_tip_label.config(
|
||||
text="实时扫描中 · 仅已识别菜计入左侧清单"
|
||||
text="实时扫描中 · 下一盘识别后刷新已点清单"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
|
@ -2590,6 +2690,7 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
self._cart = []
|
||||
self._last_dishes = []
|
||||
self._live_overlay_dishes = []
|
||||
self._cart_hold = False
|
||||
self.render_cart()
|
||||
if not self._preview_enabled and not self._live_enabled and hasattr(self, "img_label"):
|
||||
self.img_label.config(
|
||||
|
|
@ -2599,23 +2700,40 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
self.img_label.image = None
|
||||
self._set_result_text("已清空本次已点菜品", "ok")
|
||||
|
||||
def _format_price_text(self, price):
|
||||
if isinstance(price, (int, float)):
|
||||
return f"¥{price:g}"
|
||||
if price not in (None, ""):
|
||||
return f"¥{price}"
|
||||
return "—"
|
||||
|
||||
def fill_cart_from_dishes(self, dishes):
|
||||
"""左侧清单只放已识别菜;未知仅留在预览标注 / 待学习流程。"""
|
||||
"""写入左侧清单:已识别 + 未知;金额按今日菜单价查询。"""
|
||||
cart = []
|
||||
for d in dishes or []:
|
||||
if d.get("is_unknown"):
|
||||
continue
|
||||
name = d.get("label") or d.get("name") or "菜品"
|
||||
is_unk = bool(d.get("is_unknown"))
|
||||
if is_unk:
|
||||
sug = (d.get("suggested_category") or "").strip()
|
||||
name = f"未知·{sug}" if sug else "未知"
|
||||
price = None
|
||||
else:
|
||||
name = d.get("label") or d.get("name") or "菜品"
|
||||
try:
|
||||
price = self.meal_schedule.lookup_price(name)
|
||||
except Exception:
|
||||
price = None
|
||||
cart.append({
|
||||
"index": d.get("index"),
|
||||
"name": name,
|
||||
"label": d.get("label"),
|
||||
"score": d.get("score"),
|
||||
"is_unknown": False,
|
||||
"is_unknown": is_unk,
|
||||
"qty": 1,
|
||||
"price": price,
|
||||
"crop_image": d.get("crop_image"),
|
||||
"pending_id": d.get("pending_id"),
|
||||
"suggested_category": d.get("suggested_category"),
|
||||
"in_today_menu": bool(d.get("in_today_menu")),
|
||||
})
|
||||
self._cart = cart
|
||||
self._last_dishes = dishes or []
|
||||
|
|
@ -2641,10 +2759,9 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
self._cart_thumbs = []
|
||||
|
||||
if not self._cart:
|
||||
hold_s = float(getattr(self, "_live_stable_seconds", DISH_LIVE_PENDING_STABLE_SECONDS) or 1.0)
|
||||
EmptyState(
|
||||
self.cart_frame, text="暂无已识别菜品",
|
||||
hint=f"未知菜不入清单;画面稳定约 {hold_s:g} 秒后记入待学习",
|
||||
self.cart_frame, text="暂无已点菜品",
|
||||
hint="下一盘出现并识别到目标后刷新;未知菜也会显示",
|
||||
glyph="◍", bg=c["surface"], pady=28,
|
||||
).pack(fill=tk.X)
|
||||
else:
|
||||
|
|
@ -2669,7 +2786,8 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
|
||||
info = tk.Frame(pad, bg=bg)
|
||||
info.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||
tk.Label(info, text=item.get("name") or "菜品", bg=bg, fg=c["text"],
|
||||
name_fg = c["warn"] if item.get("is_unknown") else c["text"]
|
||||
tk.Label(info, text=item.get("name") or "菜品", bg=bg, fg=name_fg,
|
||||
font=FONTS["ui_bold"], anchor="w").pack(fill=tk.X)
|
||||
score = item.get("score")
|
||||
meta = f"#{item.get('index') or i + 1}"
|
||||
|
|
@ -2677,9 +2795,18 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
meta += f" · {score:.2f}"
|
||||
if item.get("is_unknown"):
|
||||
meta += " · 待确认"
|
||||
elif item.get("in_today_menu"):
|
||||
meta += " · 今日菜单"
|
||||
tk.Label(info, text=meta, bg=bg, fg=c["muted"],
|
||||
font=FONTS["small"], anchor="w").pack(fill=tk.X)
|
||||
|
||||
price_txt = self._format_price_text(item.get("price"))
|
||||
tk.Label(
|
||||
pad, text=price_txt, bg=bg,
|
||||
fg=c["text"] if price_txt != "—" else c["muted"],
|
||||
font=FONTS["ui"], width=6, anchor="e",
|
||||
).pack(side=tk.LEFT, padx=(4, 2))
|
||||
|
||||
qty_box = tk.Frame(pad, bg=bg)
|
||||
qty_box.pack(side=tk.LEFT, padx=6)
|
||||
PillButton(qty_box, text="−", variant="ghost", size="sm",
|
||||
|
|
@ -2693,11 +2820,45 @@ class BaseDishApp(PendingLearningUIMixin):
|
|||
|
||||
if hasattr(self, "cart_count_label"):
|
||||
self.cart_count_label.config(text=f"{len(self._cart)} 项")
|
||||
if hasattr(self, "cart_total_label"):
|
||||
total = 0.0
|
||||
has_price = False
|
||||
for item in self._cart:
|
||||
p = item.get("price")
|
||||
q = int(item.get("qty") or 1)
|
||||
if isinstance(p, (int, float)):
|
||||
total += float(p) * q
|
||||
has_price = True
|
||||
self.cart_total_label.config(
|
||||
text=f"合计 ¥{total:g}" if has_price else "合计 —"
|
||||
)
|
||||
|
||||
def render_result_dock(self, dishes, labeled_path=None):
|
||||
"""兼容旧调用:识别结果写入购物车。"""
|
||||
def render_result_dock(self, dishes, labeled_path=None, *, live_hold=False):
|
||||
"""识别结果写入购物车。
|
||||
|
||||
live_hold=True(实时扫描):
|
||||
- 无目标:不改清单
|
||||
- 本盘已展示过(_cart_hold):不因标签抖动刷新
|
||||
- 清场后下一盘识别到目标:才更换清单
|
||||
"""
|
||||
if live_hold:
|
||||
if not dishes:
|
||||
return
|
||||
if getattr(self, "_cart_hold", False):
|
||||
return
|
||||
self.fill_cart_from_dishes(dishes)
|
||||
self.render_cart()
|
||||
self._cart_hold = True
|
||||
# 新一盘写入清单时自动切到「已点菜品」
|
||||
try:
|
||||
if getattr(self, "_left_tab", None) != "cart":
|
||||
self._switch_left_tab("cart")
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
self.fill_cart_from_dishes(dishes)
|
||||
self.render_cart()
|
||||
self._cart_hold = False
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# 模板详情 / 删除
|
||||
|
|
|
|||
Loading…
Reference in New Issue