From d410677e5ecafee10159ab0cdfd3db1c3c8fb285 Mon Sep 17 00:00:00 2001 From: OoO Date: Wed, 13 May 2026 11:00:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E9=80=9F=E6=B4=BB=E5=8B=95=E7=9C=8B?= =?UTF-8?q?=E6=9D=BF=E6=99=82=E6=AE=B5=E8=BC=89=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.py | 2 +- routes/edm_routes.py | 48 ++++++++++++++++++++------------- templates/edm_dashboard_v2.html | 19 ++++++------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/config.py b/config.py index 122e6cc..cac39c6 100644 --- a/config.py +++ b/config.py @@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.101" +SYSTEM_VERSION = "V10.102" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/routes/edm_routes.py b/routes/edm_routes.py index fb1240c..c88a182 100644 --- a/routes/edm_routes.py +++ b/routes/edm_routes.py @@ -62,7 +62,7 @@ def load_scheduler_stats(): return {} -def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): +def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order, requested_slot=None): """ 通用的促銷儀表板數據建構函數 用於 edm 和 festival 兩種頁面類型 @@ -135,20 +135,23 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): return f"{current_slot_hour:02d}:00" active_tab = get_current_time_slot() + if requested_slot in sorted_grouped_items: + active_tab = requested_slot if active_tab not in sorted_grouped_items and sorted_grouped_items: active_tab = next(iter(sorted_grouped_items)) - # 7. 計算在架天數與總銷量 - all_icodes_in_batch = [item.i_code for item in items_in_batch] + # 7. 僅為首屏目前時段補齊列級資訊;其他時段以連結切換後再載入。 + visible_items = sorted_grouped_items.get(active_tab, []) + visible_icodes = [item.i_code for item in visible_items] product_categories = {} days_on_shelf_map = {} total_sold_map = {} history_map = {} - if all_icodes_in_batch: + if visible_icodes: # 從主商品表查詢分類 main_products = session.query(Product.i_code, Product.category).filter( - Product.i_code.in_(all_icodes_in_batch) + Product.i_code.in_(visible_icodes) ).all() product_categories = {p.i_code: p.category for p in main_products} @@ -159,7 +162,7 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): PromoProduct.i_code, func.count(func.distinct(func.date(PromoProduct.crawled_at))) ).filter( - PromoProduct.i_code.in_(all_icodes_in_batch), + PromoProduct.i_code.in_(visible_icodes), PromoProduct.page_type == page_type ).group_by(PromoProduct.i_code).all() else: @@ -168,7 +171,7 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): PromoProduct.i_code, func.count(func.distinct(func.strftime('%Y-%m-%d', PromoProduct.crawled_at))) ).filter( - PromoProduct.i_code.in_(all_icodes_in_batch), + PromoProduct.i_code.in_(visible_icodes), PromoProduct.page_type == page_type ).group_by(PromoProduct.i_code).all() days_on_shelf_map = {r[0]: r[1] for r in days_on_shelf_q} @@ -180,7 +183,7 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): PromoProduct.i_code, func.min(PromoProduct.id).label('min_id') ).filter( - PromoProduct.i_code.in_(all_icodes_in_batch), + PromoProduct.i_code.in_(visible_icodes), PromoProduct.remain_qty.isnot(None), PromoProduct.page_type == 'edm' ).group_by(PromoProduct.i_code).subquery() @@ -190,7 +193,7 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): ).join(first_qty_subq, PromoProduct.id == first_qty_subq.c.min_id).all() first_qty_map = {r[0]: r[1] for r in first_qty_records} - for item in items_in_batch: + for item in visible_items: if item.i_code in first_qty_map and item.remain_qty is not None: initial_qty = first_qty_map[item.i_code] current_qty = item.remain_qty @@ -204,7 +207,7 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): PromoProduct.remain_qty, PromoProduct.crawled_at ).filter( - PromoProduct.i_code.in_(all_icodes_in_batch), + PromoProduct.i_code.in_(visible_icodes), PromoProduct.crawled_at >= today_start ).order_by(PromoProduct.crawled_at).all() @@ -216,8 +219,8 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): if not history_map[key] or (history_map[key] and history_map[key][-1]['qty'] != rec.remain_qty): history_map[key].append({'time': rec.crawled_at.strftime('%H:%M'), 'qty': rec.remain_qty}) - # 8. 附加分類資訊到每個 item - for item in items_in_batch: + # 8. 附加分類資訊到首屏可見 item + for item in visible_items: item.safe_product_url = normalize_momo_product_url(item.url, item.i_code) or build_momo_product_url(item.i_code) item.main_category = product_categories.get(item.i_code) if item.main_category: @@ -226,9 +229,11 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order): item.total_sold = total_sold_map.get(item.i_code, 0) item.qty_history = history_map.get((item.i_code, item.time_slot), []) - # 9. 排序邏輯 + # 9. 排序邏輯:首屏只需要排序目前時段 reverse = (order == 'desc') - for time_slot in sorted_grouped_items: + for time_slot in [active_tab]: + if time_slot not in sorted_grouped_items: + continue if sort_by == 'name': sorted_grouped_items[time_slot].sort(key=lambda x: x.name or '', reverse=reverse) elif sort_by == 'remain_qty': @@ -307,9 +312,10 @@ def edm_dashboard(): sort_by = request.args.get('sort_by', 'default') order = request.args.get('order', 'desc') + requested_slot = request.args.get('slot') try: - data = _build_promo_dashboard_data(session, 'edm', '限時搶購', sort_by, order) + data = _build_promo_dashboard_data(session, 'edm', '限時搶購', sort_by, order, requested_slot) # 建立儀表板頁籤 promo_pages = [ @@ -362,9 +368,10 @@ def festival_dashboard(): sort_by = request.args.get('sort_by', 'default') order = request.args.get('order', 'desc') + requested_slot = request.args.get('slot') try: - data = _build_promo_dashboard_data(session, PAGE_TYPE, PAGE_NAME, sort_by, order) + data = _build_promo_dashboard_data(session, PAGE_TYPE, PAGE_NAME, sort_by, order, requested_slot) # 建立儀表板頁籤 promo_pages = [ @@ -414,9 +421,10 @@ def mothers_day_dashboard(): sort_by = request.args.get('sort_by', 'default') order = request.args.get('order', 'desc') + requested_slot = request.args.get('slot') try: - data = _build_promo_dashboard_data(session, PAGE_TYPE, PAGE_NAME, sort_by, order) + data = _build_promo_dashboard_data(session, PAGE_TYPE, PAGE_NAME, sort_by, order, requested_slot) # 建立儀表板頁籤 promo_pages = [ @@ -466,9 +474,10 @@ def valentine_520_dashboard(): sort_by = request.args.get('sort_by', 'default') order = request.args.get('order', 'desc') + requested_slot = request.args.get('slot') try: - data = _build_promo_dashboard_data(session, PAGE_TYPE, PAGE_NAME, sort_by, order) + data = _build_promo_dashboard_data(session, PAGE_TYPE, PAGE_NAME, sort_by, order, requested_slot) # 建立儀表板頁籤 promo_pages = [ @@ -518,9 +527,10 @@ def labor_day_dashboard(): sort_by = request.args.get('sort_by', 'default') order = request.args.get('order', 'desc') + requested_slot = request.args.get('slot') try: - data = _build_promo_dashboard_data(session, PAGE_TYPE, PAGE_NAME, sort_by, order) + data = _build_promo_dashboard_data(session, PAGE_TYPE, PAGE_NAME, sort_by, order, requested_slot) # 建立儀表板頁籤 promo_pages = [ diff --git a/templates/edm_dashboard_v2.html b/templates/edm_dashboard_v2.html index d3dac62..8c6f423 100644 --- a/templates/edm_dashboard_v2.html +++ b/templates/edm_dashboard_v2.html @@ -333,6 +333,7 @@ border-radius: 6px; font-size: 12px; font-weight: 800; + text-decoration: none; white-space: nowrap; } @@ -824,10 +825,10 @@
{% for slot, stats in slot_stats.items() %} {% set slot_id = slugify(slot) %} - + {% endfor %}
{% else %} @@ -839,9 +840,10 @@ {% if slot_stats %}
- {% for slot, stats in slot_stats.items() %} - {% set slot_id = slugify(slot) %} - {% set items = grouped_items.get(slot, []) %} + {% set slot = active_tab %} + {% set stats = slot_stats.get(slot, {'new': 0, 'up': 0, 'down': 0, 'delisted_last_run': 0, 'on_shelf': 0}) %} + {% set slot_id = slugify(slot) %} + {% set items = grouped_items.get(slot, []) %}
@@ -863,16 +865,16 @@ 分類 / 狀態 {% set next_order_name = 'asc' if current_sort == 'name' and current_order == 'desc' else 'desc' %} - 商品資訊 + 商品資訊 {% set next_order_price = 'asc' if current_sort == 'price' and current_order == 'desc' else 'desc' %} - 價格 + 價格 {% if current_promo_page == 'edm' %} {% set next_order_qty = 'asc' if current_sort == 'remain_qty' and current_order == 'desc' else 'desc' %} - 銷售 / 庫存 + 銷售 / 庫存 {% else %} 狀態 {% endif %} @@ -1027,7 +1029,6 @@
- {% endfor %}
{% endif %}