-
文案生成
- 第 1 步:設定參數 +銷售動作生成
+ 設定商品與目的
- {# 商品名稱 #}
- {# 風格 / 引擎 / 模型 #}
- 可從右側熱銷商品快速選取
+ 可從右側 PChome 熱銷商品帶入
清除
-
-
+
+
-
-
+
- {# Gemini 用量面板 #}
-
-
-
-
-
@@ -145,17 +136,16 @@
- {# 生成結果 #}
- AI 生成文案
+ 可用銷售文案
複製
@@ -168,12 +158,11 @@
- {# AI 智慧搜尋 #}
- {# 商品洞察 #}
- 分析商品
+ 判斷下一步
@@ -220,7 +208,6 @@
- {# ── 右側:市場資訊 tabs ──────────────────────── #}
- Gemini 備援本月使用量
-
-
-
- 費用:$0.0000
- 請求:0 次
- 用量:0
-
+
+ $0.0000
+ 0
+ 0
- {# 關鍵字+節日 #}
@@ -145,17 +136,16 @@
- 生成文案
+ 產生銷售建議
-
AI 智慧搜尋
- 輸入關鍵字,AI 分析市場趨勢 +市場訊號快查
+ 輸入關鍵字,整理趨勢與可用行動
@@ -201,7 +190,6 @@
@@ -209,7 +197,7 @@
把外部訊號轉成可追蹤的銷售動作
- @@ -246,13 +233,12 @@
- {# 趨勢洞察 #}
- {# 熱銷商品 #}
@@ -316,7 +301,6 @@
- {# 排行榜 #}
- {# 趨勢新聞 #}
@@ -404,12 +387,11 @@
-{# ── Help modal ──────────────────────────────────── #}
-{# ── Loading overlay ─────────────────────────────── #}
{% endblock %}
diff --git a/templates/monthly_summary_analysis.html b/templates/monthly_summary_analysis.html
index f8ab808..1e55b51 100644
--- a/templates/monthly_summary_analysis.html
+++ b/templates/monthly_summary_analysis.html
@@ -1,5 +1,5 @@
{% extends 'ewoooc_base.html' %}
-{% block title %}月份總表數據分析 - EwoooC{% endblock %}
+{% block title %}PChome 月結作戰分析 - EwoooC{% endblock %}
{#
Turn B — extends ewoooc_base + BEM 結構化
@@ -17,10 +17,9 @@
{% block ewooo_content %}
{# Runtime data source lives in page-monthly-summary.js: /api/monthly_summary_data #}
-
-
+
+ 決策路徑
+ 先看成長缺口
+ 再看毛利貢獻
+ 最後調整品類結構
+
+
即時趨勢洞察
- 來自 PTT、Dcard、Google News + 可用來挑主推與文案角度
@@ -364,7 +348,6 @@
-
銷售動作
+銷售決策流程
@@ -435,11 +417,10 @@
-
AI 正在思考中...
+正在整理銷售建議...
- 資料載入中…
+ 月結業績整理中…
- 月份總表數據分析 + PChome 月結作戰分析
用月結資料判斷成長、毛利與品類結構。 @@ -42,13 +40,19 @@
-
- {{ system_version }}
+
+ 月結分析
@@ -83,15 +87,15 @@
@@ -103,7 +107,7 @@
- 所有及廠商
+ 全部廠商
-
@@ -116,7 +120,7 @@
-
- 刷新 + 更新 - 業績貢獻王 (Top 3 Brands)
+業績主推品牌
@@ -154,7 +157,7 @@
- 獲利金雞母 (Top 3 Brands)
+高毛利守價品牌
@@ -166,7 +169,7 @@
- 人氣引流款 (Top 3 Brands)
+人氣引流品牌
@@ -177,12 +180,11 @@ -
- 年度 YoY 業績對比分析(本期 vs 去年同期)
+年度業績對照(本期 / 去年同期)
@@ -196,12 +198,11 @@-- Top 50 廠商獲利能力排行
+Top 50 廠商毛利優先序
@@ -215,12 +216,11 @@-- 全站類別業績分佈(Top 12)
+全站類別業績佔比
@@ -235,7 +235,7 @@- 價格帶業績貢獻比例
+價格帶業績佔比
@@ -249,12 +249,11 @@-- 年度月份業績對比趨勢
+年度月趨勢
@@ -270,12 +269,11 @@-- 品牌策略 BCG 矩陣(銷量 vs 毛利率)
+品牌策略矩陣(銷量 / 毛利率)
@@ -285,7 +283,7 @@- 價格 vs 銷售量分佈散佈圖
+價格與銷售量分佈
@@ -294,12 +292,11 @@-- 分類與月份淡旺季業績熱力圖
+分類淡旺季熱力圖
@@ -308,12 +305,11 @@-- 區名稱全站銷售排行(Area Ranking)
+區域銷售排行
@@ -327,12 +323,11 @@-- 開架保養 & 臉部清潔(合併業績)
+開架保養與臉部清潔
@@ -352,7 +347,7 @@- 身體保養(年度業績分析)
+身體保養年度表現
@@ -372,7 +367,7 @@- 彩妝/指彩 & 精油擴香(合併業績)
+彩妝、指彩與精油擴香
@@ -392,7 +387,7 @@- 私密保養 & 嬰幼洗沐(合併業績)
+私密保養與嬰幼洗沐
@@ -408,12 +403,11 @@-- 資料明細
+可追蹤清單
- 匯入數據 + 補匯入資料@@ -423,11 +417,11 @@ diff --git a/templates/sales_analysis.html b/templates/sales_analysis.html index 48e705d..69521be 100644 --- a/templates/sales_analysis.html +++ b/templates/sales_analysis.html @@ -1,5 +1,5 @@ {% extends 'ewoooc_base.html' %} -{% block title %}業績分析 - EwoooC{% endblock %} +{% block title %}PChome 業績作戰分析 - EwoooC{% endblock %} {# v3 改寫重點: @@ -22,7 +22,6 @@ {% block ewooo_content %} -年/月 處別 -區名稱 +區域 PM 品牌 廠商 -交易 +交易型態 銷售額(本月) YoY @@ -40,23 +39,22 @@EwoooC- 正在載入數據... + 正在整理業績...-大量資料可能需要較長時間,請稍候+先整理可主推、需守價與待補資料的商品{% include 'components/_analysis_report_tabs.html' %} -- 業績分析儀表板 + PChome 業績作戰分析
-用分類、品牌與毛利找出 PChome 成長槓桿。
+用分類、品牌與毛利找出主推、守價與補資料順序。
{% if db_data_range %} @@ -67,7 +65,7 @@ {% if not no_filter %}- + {% if start_date or end_date %} {% if start_date and end_date %}{{ start_date }} ~ {{ end_date }} {% elif start_date %}{{ start_date }} 起 @@ -75,12 +73,20 @@ {% elif data_range_months == 0 %}全部資料 {% else %}最近 {{ data_range_months }} 個月{% endif %} · - {{ "{:,}".format(total_records) }} 筆 + {{ "{:,}".format(total_records) }} 筆商品紀錄{% endif %} ++ 決策順序 + 主推高業績 + 守住高毛利 + 補齊價差證據 + 回今日作戰 + + {% if error %}{{ error }} @@ -90,19 +96,18 @@{% else %} -- 進階篩選與分析 + 設定分析視角
{% set metric_opts = [ - {'v':'amount', 'label':'依金額分析', 'icon':'dollar-sign'}, - {'v':'qty', 'label':'依銷售量分析', 'icon':'box'} + {'v':'amount', 'label':'看業績', 'icon':'dollar-sign'}, + {'v':'qty', 'label':'看銷量', 'icon':'box'} ] %} {% if cols.cost or cols.profit %} - {% set _ = metric_opts.append({'v':'profit', 'label':'依毛利分析', 'icon':'chart-line'}) %} + {% set _ = metric_opts.append({'v':'profit', 'label':'看毛利', 'icon':'chart-line'}) %} {% endif %} {% for m in metric_opts %} -- 資料範圍設定 + 分析期間
{% else %} - {% set kpi_cards = [ - {'variant':'revenue','label':'總業績 (Revenue)', 'value': '${:,.0f}'.format(kpi.revenue), 'icon':'coins', 'show': true}, - {'variant':'cost', 'label':'總成本 (Cost)', 'value': '${:,.0f}'.format(kpi.cost), 'icon':'file-invoice-dollar', 'show': cols.cost or cols.profit}, - {'variant':'profit', 'label':'毛利額 (Profit)', 'value': '${:,.0f}'.format(kpi.gross_margin), 'icon':'hand-holding-usd', 'show': cols.cost or cols.profit}, - {'variant':'rate', 'label':'毛利率 (%)', 'value': '{:.1f}%'.format(kpi.gross_margin_rate),'icon':'percentage', 'show': cols.cost or cols.profit}, - {'variant':'qty', 'label':'總銷量 (Qty)', 'value': '{:,.0f}'.format(kpi.qty), 'icon':'boxes', 'show': true}, - {'variant':'sku', 'label':'商品數 (SKU)', 'value': '{:,}'.format(kpi.sku_count|default(kpi.count, true)), 'icon':'tags', 'show': true} + {'variant':'revenue','label':'總業績', 'value': '${:,.0f}'.format(kpi.revenue), 'icon':'coins', 'show': true}, + {'variant':'cost', 'label':'總成本', 'value': '${:,.0f}'.format(kpi.cost), 'icon':'file-invoice-dollar', 'show': cols.cost or cols.profit}, + {'variant':'profit', 'label':'毛利額', 'value': '${:,.0f}'.format(kpi.gross_margin), 'icon':'hand-holding-usd', 'show': cols.cost or cols.profit}, + {'variant':'rate', 'label':'毛利率', 'value': '{:.1f}%'.format(kpi.gross_margin_rate),'icon':'percentage', 'show': cols.cost or cols.profit}, + {'variant':'qty', 'label':'總銷量', 'value': '{:,.0f}'.format(kpi.qty), 'icon':'boxes', 'show': true}, + {'variant':'sku', 'label':'商品數', 'value': '{:,}'.format(kpi.sku_count|default(kpi.count, true)), 'icon':'tags', 'show': true} ] %}- {% if no_filter %}@@ -374,44 +377,43 @@- 開始分析您的業績數據 + 先選期間,再看作戰順序
- 請在上方「進階篩選」區域選擇以下任一條件開始分析: + 選好期間後,直接看主推、守毛利與補比價的商品。
-資料載入範圍
-快速選擇最近 1/3/6/12 個月或全部資料
-推薦新手使用
+最近資料
+快速看近 1/3/6/12 個月的主推與守價商品
+適合每日追蹤
-自訂日期區間
-鎖定活動或檔期,評估業績變化
+檔期區間
+鎖定活動或檔期,評估業績、毛利與品類變化
適合檔期與活動回顧
- 分析下一步:選擇條件後,直接看影響業績的圖表、分類與商品明細。 + 分析下一步:選擇條件後,直接看影響業績的圖表、分類與商品明細,再決定曝光、調價或補比價。{% for k in kpi_cards %}{% if k.show %} @@ -427,7 +429,6 @@ {% endif %}{% endfor %}- {% if abc_stats %}@@ -435,22 +436,22 @@- ABC 分析 (80/20 法則) + 主推分層
選類別看貢獻{% set abc_classes = [ - {'k':'A','tier':'p0','title':'A 類 (核心)'}, - {'k':'B','tier':'p1','title':'B 類 (次要)'}, - {'k':'C','tier':'p2','title':'C 類 (長尾)'} + {'k':'A','tier':'p0','title':'A 類:核心主推'}, + {'k':'B','tier':'p1','title':'B 類:加強曝光'}, + {'k':'C','tier':'p2','title':'C 類:長尾觀察'} ] %} {% for c in abc_classes %}{{ c.title }}
營收佔比:{{ "{:.1f}%".format(abc_stats[c.k]['pct_rev']) }}-{{ abc_stats[c.k]['count'] }} SKU ({{ "{:.1f}%".format(abc_stats[c.k]['pct_sku']) }})+商品數:{{ abc_stats[c.k]['count'] }}({{ "{:.1f}%".format(abc_stats[c.k]['pct_sku']) }})@@ -463,14 +464,13 @@ {% endif %} -- 年度對比 (YoY Comparison) + 年度成長對照
- {% if vendor_stats %}@@ -551,12 +550,12 @@排名 廠商名稱 總業績 -佔比 (%) +佔比 總銷量 -平均客單 (ASP) +平均客單 {% if cols.cost or cols.profit %}毛利額 {% endif %} {% if cols.cost or cols.profit %}毛利率 {% endif %} -商品數 (SKU) +商品數 平均單品產值 @@ -594,19 +593,17 @@ {% endif %} -- 商業洞察 (Top 3 Highlights) + 今日決策 Top 3--- {% if cols.cost or cols.profit %}🏆 業績貢獻王 (Revenue)
+業績主推
詳細 @@ -625,11 +622,10 @@-{% endif %} -💰 獲利金雞母 (Gross Margin)
+高毛利守價
詳細 @@ -653,10 +649,9 @@-- - {# Turn C:圖表 + DataTable,配色由 analysis-chart-theme.js 統一注入 #}📦 人氣引流款 (Sales Qty)
+人氣引流
詳細 @@ -680,18 +675,16 @@-Top 20 熱銷排行 ({{ '銷售金額' if selected_metric == 'amount' else '銷售數量' }})+Top 20 熱銷排行({{ '銷售金額' if selected_metric == 'amount' else '銷售數量' }})@@ -700,7 +693,7 @@-全站類別分佈 (Top 12)+全站類別佔比@@ -709,13 +702,13 @@-業績板塊分佈 (分類 → 商品)+業績板塊分佈:分類到商品-價格帶業績貢獻 (Price Range)+價格帶業績貢獻@@ -725,9 +718,9 @@-價格 vs 銷量分佈 (Scatter Plot)+價格與銷量分佈-@@ -739,7 +732,7 @@商品策略 BCG 矩陣 (波士頓矩陣)+商品策略矩陣-X軸:銷量 (市場份額) | Y軸:毛利率 (獲利能力) | 十字線:中位數閾值
+右上優先主推;右下先守價或調整組合。
@@ -751,7 +744,7 @@-淡旺季熱力圖 (Seasonality Heatmap) — Top 10 分類+淡旺季熱力圖:Top 10 分類- 行銷活動業績貢獻 (Marketing Campaign Contribution) + 活動業績貢獻匯出全部 @@ -759,11 +752,11 @@{% endif %} -@@ -777,7 +770,7 @@-折扣活動排行 (Discount Campaigns)
+折扣活動排行
-折價券活動排行 (Coupon Campaigns)
+折價券活動排行
@@ -798,7 +791,7 @@-每月業績趨勢 (Monthly Trend)+每月業績趨勢@@ -814,21 +807,20 @@-每小時業績熱點 (00:00 — 23:00)+每小時業績熱點- 詳細數據列表 (Top 1000) + 商品作戰清單 Top 1000排名 -商品 ID +商品編號 商品名稱 {% if cols.brand %}品牌 {% endif %} {% if cols.vendor %}廠商名稱 {% endif %} - {% if cols.cat %}商品館 (分類) {% endif %} + {% if cols.cat %}分類 {% endif %} {% if cols.qty %}平均單價 {% endif %} {% if cols.cost or cols.profit %}毛利率 {% endif %} {% if cols.return_qty %}退貨率 {% endif %} @@ -882,6 +874,6 @@ {% endif %} - + {% endblock %} diff --git a/tests/test_frontend_v2_assets.py b/tests/test_frontend_v2_assets.py index 590cff7..5476274 100644 --- a/tests/test_frontend_v2_assets.py +++ b/tests/test_frontend_v2_assets.py @@ -633,34 +633,47 @@ def test_ai_recommend_uses_v2_shell_and_runtime_category_data(): assert "{% for category in product_categories[:4] %}" in template assert "quickWebSearch({{ category|tojson }})" in template assert "quickWebSearch('保濕面膜')" not in template - assert "fetch('/api/ai/generate_copy'" in template - assert "fetch('/api/ai/web_search'" in template - assert "fetch('/api/ai/product_insights'" in template - assert "fetch('/api/ai/gemini_usage?days=30')" in template + assert "fetch('/api/ai/generate_copy'" in page_js + assert "fetch('/api/ai/web_search'" in page_js + assert "fetch('/api/ai/product_insights'" in page_js + assert "fetch('/api/ai/gemini_usage?days=30')" in page_js assert "mock" not in template.lower() assert "假商品" not in template - assert "Ollama 主路徑" in template - assert "Gemini 備援" in template - assert "Gemini 備援(系統自動,不可手動選)" in template - assert "disabled>☁️ Gemini 備援" in template - assert "權杖:" in template - assert "Ollama 主路徑" in page_js - assert "Gemini 備援" in page_js - assert "搜尋失敗:" in page_js - assert "分析失敗:" in page_js + assert "PChome 銷售建議" in template + assert "銷售動作生成" in template + assert "建議目的" in template + assert "處理順序" in template + assert "建議引擎" in template + assert "備援守門" in template + assert "ar-engine-settings" in template + assert "整理訊號" in page_js + assert "商品判斷暫時不可用" in page_js forbidden_visible_text = [ "🖥️ Ollama (本地)", "☁️ Gemini (雲端)", + "Ollama 主路徑", + "Gemini 備援", + "Gemini 備援(系統自動,不可手動選)", + "disabled>☁️ Gemini 備援", + "AI 模型主路徑", + "AI 路徑", + "分析模型", "Web Search 功能", "渲染 Web Search", "整合 Web Search", "Token:", + "權杖:", "費用:", + "費用:", "生成失敗:", + "生成失敗:", "發生錯誤:", + "發生錯誤:", "搜尋失敗:", + "搜尋失敗:", "分析失敗:", + "分析失敗:", ] combined = template + "\n" + page_js for marker in forbidden_visible_text: diff --git a/tests/test_pchome_revenue_growth_service.py b/tests/test_pchome_revenue_growth_service.py index 8bada2d..d09e3d2 100644 --- a/tests/test_pchome_revenue_growth_service.py +++ b/tests/test_pchome_revenue_growth_service.py @@ -684,14 +684,14 @@ def test_primary_pages_use_growth_outcome_copy_instead_of_feature_explaining(): expected = { "templates/daily_sales.html": "找出下滑與價差壓力", - "templates/ai_recommend.html": "把價差、商品證據與趨勢轉成可追蹤的銷售建議", + "templates/ai_recommend.html": "把價差、商品證據與趨勢轉成主推、調價、補比價動作", "templates/auto_import_index.html": "保持 PChome 業績新鮮", "templates/price_comparison.html": "確認同款、判斷價差、決定下一步", "templates/vendor_stockout_index_v2.html": "避免主推商品斷貨", "templates/monthly_summary_analysis.html": "判斷成長、毛利與品類結構", "templates/dashboard_v2.html": "先看業績,再決定調價、曝光與組合", "templates/edm_dashboard_v2.html": "用活動價格異動找主推、補貨與曝光機會", - "templates/sales_analysis.html": "用分類、品牌與毛利找出 PChome 成長槓桿", + "templates/sales_analysis.html": "用分類、品牌與毛利找出主推、守價與補資料順序", "templates/growth_analysis.html": "用月趨勢評估成長缺口、價差壓力與毛利品質", "templates/vendor_stockout_import_v2.html": "補齊缺貨資料,先保住主推商品供貨", "templates/vendor_stockout_list_v2.html": "先看待發送與失敗,避免主推商品斷貨拖累業績", @@ -957,7 +957,7 @@ def test_visible_operations_pages_hide_internal_runtime_terms(): from pathlib import Path expected = { - "templates/ai_recommend.html": ["分析模型", "用量"], + "templates/ai_recommend.html": ["銷售動作生成", "建議目的", "處理順序"], "templates/vendor_stockout_index_v2.html": ["先匯入缺貨批次", "供貨風險"], "templates/dashboard_v2.html": ["尚無挑品建議", "先累積 PChome 比價與挑品資料"], "templates/daily_sales.html": ["左右滑動看業績趨勢", "左右滑動看分類明細"], @@ -971,7 +971,7 @@ def test_visible_operations_pages_hide_internal_runtime_terms(): "templates/cicd_dashboard.html": ["部署流程", "部署歷史", "修復部署", "查看部署紀錄"], } forbidden_by_path = { - "templates/ai_recommend.html": ["權杖:", "AI 模型"], + "templates/ai_recommend.html": ["權杖:", "AI 模型", "分析模型", "AI 路徑", "Gemini 備援", "Ollama 主路徑"], "templates/vendor_stockout_index_v2.html": ["資料庫目前沒有缺貨資料"], "templates/system_settings.html": ["資料表:", "資料表:", "自動建表", "匯入並建立通用資料表"], "templates/notification_templates.html": ["模板代碼", "${t.code}", "CI/CD Pipeline SUCCESS"], diff --git a/web/static/css/page-ai-recommend-bem.css b/web/static/css/page-ai-recommend-bem.css index 3a35afa..50a1065 100644 --- a/web/static/css/page-ai-recommend-bem.css +++ b/web/static/css/page-ai-recommend-bem.css @@ -23,6 +23,46 @@ display: flex; align-items: center; gap: var(--momo-space-1, 8px); } +/* ── Decision strip ────────────────────────────────── */ +.ai-recommend-page .ar-command-strip { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.55rem; + margin: var(--momo-space-3, 16px) 0; + padding: 0.65rem 0.8rem; + border: 1px solid var(--momo-border-subtle); + border-radius: var(--momo-radius-md, 8px); + background: color-mix(in srgb, var(--momo-page-accent) 7%, var(--momo-surface)); +} +.ai-recommend-page .ar-command-strip__label { + color: var(--momo-text-tertiary); + font-size: 0.76rem; + font-weight: 800; +} +.ai-recommend-page .ar-command-strip strong { + display: inline-flex; + align-items: center; + gap: 0.38rem; + min-height: 2rem; + padding: 0.34rem 0.58rem; + border: 1px solid color-mix(in srgb, var(--momo-page-accent) 22%, var(--momo-border-subtle)); + border-radius: 999px; + background: var(--momo-surface); + color: var(--momo-text-strong); + font-size: 0.82rem; + font-weight: 800; +} +.ai-recommend-page .ar-command-strip strong i { color: var(--momo-page-accent); } +.ai-recommend-page .ar-command-strip__link { + margin-left: auto; + color: var(--momo-page-accent); + font-size: 0.82rem; + font-weight: 800; + text-decoration: none; +} +.ai-recommend-page .ar-command-strip__link:hover { text-decoration: underline; } + /* ── Status pills ──────────────────────────────────── */ .ai-recommend-page .ar-status { display: inline-flex; align-items: center; gap: 4px; @@ -72,6 +112,10 @@ } .ai-recommend-page .ar-card__step { opacity: 0.85; } +.ai-recommend-page .ar-engine-settings { + display: none !important; +} + /* ── Card variant accent borders ──────────────────── */ .ai-recommend-page .ar-card--gen { border-color: var(--momo-text-strong) !important; } .ai-recommend-page .ar-card--result { border-color: var(--momo-warm-olive, #6f7a4a) !important; } @@ -179,3 +223,16 @@ .ar-loading-overlay .spinner-border-lg { width: 3rem; height: 3rem; border-width: 0.3em; } + +@media (max-width: 767.98px) { + .ai-recommend-page .ar-hero__actions { + width: 100%; + justify-content: flex-start; + flex-wrap: wrap; + } + + .ai-recommend-page .ar-command-strip__link { + width: 100%; + margin-left: 0; + } +} diff --git a/web/static/css/page-monthly-summary-bem.css b/web/static/css/page-monthly-summary-bem.css index 7c45168..65ba39b 100644 --- a/web/static/css/page-monthly-summary-bem.css +++ b/web/static/css/page-monthly-summary-bem.css @@ -73,6 +73,39 @@ } .ms-page-head__sub code { background: transparent; color: inherit; } +/* ── 2.1 Decision strip ─────────────────────────────── */ +.ms-action-strip { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.55rem; + margin: var(--momo-space-3, 16px) 0; + padding: 0.65rem 0.8rem; + border: 1px solid var(--momo-border-subtle); + border-radius: var(--ms-card-radius); + background: var(--momo-surface-raised); + box-shadow: var(--momo-shadow-soft); +} +.ms-action-strip__label { + color: var(--momo-text-secondary); + font-size: 0.76rem; + font-weight: 800; +} +.ms-action-strip strong { + display: inline-flex; + align-items: center; + gap: 0.38rem; + min-height: 2rem; + padding: 0.34rem 0.58rem; + border: 1px solid color-mix(in srgb, var(--momo-warm-caramel) 24%, var(--momo-border-subtle)); + border-radius: 999px; + background: rgba(250, 246, 238, 0.78); + color: var(--momo-text-primary); + font-size: 0.82rem; + font-weight: 800; +} +.ms-action-strip strong i { color: var(--momo-warm-caramel); } + /* ── 3. Tag / pill ────────────────────────────────────── */ .ms-tag { display: inline-flex; align-items: center; gap: 6px; @@ -306,6 +339,11 @@ gap: var(--momo-space-3); } + .ms-action-strip strong { + width: 100%; + justify-content: flex-start; + } + .ms-page-head { grid-template-columns: 1fr; padding: var(--momo-space-4); diff --git a/web/static/css/page-sales-analysis-bem.css b/web/static/css/page-sales-analysis-bem.css index eb8699e..984c692 100644 --- a/web/static/css/page-sales-analysis-bem.css +++ b/web/static/css/page-sales-analysis-bem.css @@ -42,6 +42,45 @@ letter-spacing: 0; } +.sa-command-strip { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.55rem; + margin: -0.35rem 0 1.1rem; + padding: 0.65rem 0.8rem; + border: 1px solid var(--momo-border-subtle); + border-radius: 8px; + background: color-mix(in srgb, var(--momo-page-accent) 7%, var(--momo-surface)); +} +.sa-command-strip__label { + color: var(--momo-text-muted); + font-size: 0.76rem; + font-weight: 800; +} +.sa-command-strip strong { + display: inline-flex; + align-items: center; + gap: 0.38rem; + min-height: 2rem; + padding: 0.34rem 0.58rem; + border: 1px solid color-mix(in srgb, var(--momo-page-accent) 22%, var(--momo-border-subtle)); + border-radius: 999px; + background: var(--momo-surface); + color: var(--momo-text-strong); + font-size: 0.82rem; + font-weight: 800; +} +.sa-command-strip strong i { color: var(--momo-page-accent); } +.sa-command-strip__link { + margin-left: auto; + color: var(--momo-page-accent); + font-size: 0.82rem; + font-weight: 800; + text-decoration: none; +} +.sa-command-strip__link:hover { text-decoration: underline; } + .sa-tag { display: inline-flex; align-items: center; @@ -536,4 +575,5 @@ .sa-insights__col { border-right: none; border-bottom: 1px dashed var(--momo-border-subtle); } .sa-insights__col:last-child { border-bottom: none; } .sa-filter-card--sticky { position: static; } + .sa-command-strip__link { width: 100%; margin-left: 0; } } diff --git a/web/static/js/page-ai-recommend.js b/web/static/js/page-ai-recommend.js index ee2b1e8..01045de 100644 --- a/web/static/js/page-ai-recommend.js +++ b/web/static/js/page-ai-recommend.js @@ -236,21 +236,18 @@ } catch { return ''; } } - // ====== AI 引擎切換相關 ====== + // ====== 建議引擎切換相關 ====== - // AI 引擎切換處理 + // 建議引擎切換處理 function onProviderChange() { const provider = document.getElementById('aiProvider').value; const isGemini = provider === 'gemini'; - // 切換模型選擇器顯示 document.getElementById('ollamaModelSelect').style.display = isGemini ? 'none' : 'block'; document.getElementById('geminiModelSelect').style.display = isGemini ? 'block' : 'none'; - // 顯示/隱藏 Gemini 使用量面板 document.getElementById('geminiUsagePanel').style.display = isGemini ? 'block' : 'none'; - // 如果選擇 Gemini,載入使用量 if (isGemini) { loadGeminiUsage(); } @@ -263,7 +260,7 @@ .then(data => { if (data.success) { const summary = data.data.summary; - document.getElementById('geminiMonthlyCost').textContent = '$' + summary.total_cost_usd.toFixed(4); + document.getElementById('fallbackMonthlySpend').textContent = '$' + summary.total_cost_usd.toFixed(4); document.getElementById('geminiRequestCount').textContent = summary.total_requests.toLocaleString(); document.getElementById('geminiTokenUsage').textContent = summary.total_tokens.toLocaleString(); } @@ -273,7 +270,7 @@ }); } - // 首屏先渲染,AI 狀態載入後再更新,避免健康檢查阻塞頁面 TTFB + // 首屏先渲染,建議引擎狀態載入後再更新,避免健康檢查阻塞頁面 TTFB function refreshAIStatus() { fetch('/api/ai/status') .then(r => { @@ -283,11 +280,11 @@ .then(data => { if (!data.success || !data.data) return; const status = data.data; - updateAIStatusBadge('ollamaStatus', 'fas fa-server', 'AI 模型主路徑', status.ollama?.connected, 'ar-status--ok'); - updateAIStatusBadge('geminiStatus', 'fab fa-google', 'Gemini 備援', status.gemini?.connected, 'ar-status--info'); + updateAIStatusBadge('ollamaStatus', 'fas fa-wand-magic-sparkles', '建議引擎', status.ollama?.connected, 'ar-status--ok'); + updateAIStatusBadge('geminiStatus', 'fas fa-shield-alt', '備援守門', status.gemini?.connected, 'ar-status--info'); updateOllamaModels(status.ollama?.available_models || []); }) - .catch(e => console.warn('AI 狀態刷新失敗:', e)); + .catch(e => console.warn('建議引擎狀態刷新未完成:', e)); } function updateAIStatusBadge(id, iconClass, label, connected, okClass) { @@ -295,7 +292,7 @@ if (!el) return; el.classList.remove('ar-status--ok', 'ar-status--info', 'ar-status--off'); el.classList.add(connected ? okClass : 'ar-status--off'); - el.innerHTML = ` ${label} ${connected ? '✓' : '✗'}`; + el.innerHTML = ` ${label} ${connected ? '可用' : '待確認'}`; } function updateOllamaModels(models) { @@ -309,9 +306,8 @@ }).join(''); } - // 初始化 AI 引擎選擇(頁面載入時) + // 初始化建議引擎選擇(頁面載入時) function initAIProvider() { - // 確保 Ollama 為預設 const provider = document.getElementById('aiProvider').value; onProviderChange(); } @@ -374,14 +370,14 @@ const btn = document.getElementById('generateBtn'); const originalBtnText = btn.innerHTML; btn.disabled = true; - btn.innerHTML = 'AI 正在生成中...'; + btn.innerHTML = '正在整理建議...'; btn.classList.add('btn-secondary'); btn.classList.remove('btn-primary'); // 顯示全螢幕載入動畫 - showLoading('AI 正在生成完整文案套組...'); + showLoading('正在產生銷售建議...'); - // 取得 AI 設定 + // 取得幕後建議設定 const provider = document.getElementById('aiProvider').value; const model = provider === 'gemini' ? document.getElementById('geminiModelSelect').value @@ -422,14 +418,10 @@ document.getElementById('generatedCopy').innerHTML = formattedCopy; // 組合元資料顯示 - let metaHtml = `${data.data.provider === 'gemini' ? 'Gemini 備援' : 'AI 模型主路徑'}:${data.data.model}`; - metaHtml += ` | 耗時:${data.data.duration}秒`; + let metaHtml = `建議已完成`; + metaHtml += ` | 分析耗時:${data.data.duration}秒`; - // 如果是 Gemini,顯示費用 if (data.data.provider === 'gemini' && data.data.cost) { - metaHtml += ` | 費用:$${data.data.cost.total.toFixed(4)}`; - metaHtml += ` | 權杖:${data.data.tokens.total}`; - // 刷新使用量面板 loadGeminiUsage(); } @@ -437,7 +429,7 @@ document.getElementById('resultArea').style.display = 'block'; document.getElementById('resultArea').scrollIntoView({ behavior: 'smooth' }); } else { - alert('生成失敗:' + data.error); + alert('建議沒有完成:' + data.error); } }) .catch(e => { @@ -447,7 +439,7 @@ btn.innerHTML = originalBtnText; btn.classList.remove('btn-secondary'); btn.classList.add('btn-primary'); - alert('發生錯誤:' + e.message); + alert('建議暫時無法產生:' + e.message); }); } @@ -516,7 +508,7 @@ doWebSearch(); } - // AI 網路搜尋 + // 市場訊號搜尋 function doWebSearch() { const query = document.getElementById('webSearchQuery').value.trim(); if (!query) { @@ -532,11 +524,11 @@ // 更新按鈕狀態 btn.disabled = true; - btn.innerHTML = '搜尋中...'; + btn.innerHTML = '整理中...'; // 顯示結果區 resultArea.style.display = 'block'; - contentArea.innerHTML = ''; + contentArea.innerHTML = '
AI 正在分析市場資訊...(可能需要 30-60 秒)'; // 設定前端超時 (3 分鐘) const controller = new AbortController(); @@ -565,27 +557,27 @@ }) .then(data => { btn.disabled = false; - btn.innerHTML = 'AI 搜尋'; + btn.innerHTML = '整理訊號'; if (data.success) { renderWebSearchResult(data.data); } else { - contentArea.innerHTML = `
正在整理市場訊號...約需 30-60 秒${data.error}`; + contentArea.innerHTML = `市場訊號暫時不可用,請稍後重試。`; } }) .catch(e => { clearTimeout(timeoutId); btn.disabled = false; - btn.innerHTML = 'AI 搜尋'; + btn.innerHTML = '整理訊號'; if (e.name === 'AbortError') { - contentArea.innerHTML = `AI 伺服器回應較慢,請稍後再試。`; + contentArea.innerHTML = `市場訊號整理較慢,請稍後再試。`; } else { - contentArea.innerHTML = `搜尋失敗:${e.message}`; + contentArea.innerHTML = `市場訊號暫時不可用,請稍後重試。`; } }); } - // 渲染網路搜尋結果 - 卡片式顯示 + // 渲染市場訊號結果 function renderWebSearchResult(data) { const contentArea = document.getElementById('webSearchContent'); let html = ''; @@ -594,12 +586,11 @@ if (data.parsed) { const p = data.parsed; - // 摘要卡片 if (p.summary) { html += `-AI 分析摘要
+市場摘要
`; } - // 分析結果卡片 if (p.results && p.results.length > 0) { html += `${escapeHtml(p.summary)}
@@ -607,12 +598,11 @@-分析結果 (${p.results.length})
+可用線索 (${p.results.length})
`; @@ -634,11 +624,9 @@`; } - // 洞察與建議並排顯示 if ((p.insights && p.insights.length > 0) || (p.recommended_actions && p.recommended_actions.length > 0)) { html += ''; - // 洞察卡片 if (p.insights && p.insights.length > 0) { const colClass = (p.recommended_actions && p.recommended_actions.length > 0) ? 'col-md-6' : 'col-12'; html += ` @@ -656,14 +644,13 @@`; } - // 建議行動卡片 if (p.recommended_actions && p.recommended_actions.length > 0) { const colClass = (p.insights && p.insights.length > 0) ? 'col-md-6' : 'col-12'; html += `-建議行動
+可採取動作
@@ -677,11 +664,10 @@ html += ''; } } else { - // 顯示原始內容 html = ``; // 設定前端超時 (4 分鐘,因為需要兩步) @@ -739,7 +723,7 @@ method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ - query: `${productName} 市場分析 競品比較 價格 評價 2024`, + query: `${productName} 市場分析 競品比較 價格 評價 ${new Date().getFullYear()}`, search_type: 'shopping', num_results: 5 }), @@ -756,23 +740,23 @@ .then(searchData => { // 更新進度 document.getElementById('insightProgress').style.width = '60%'; - document.getElementById('insightStatus').textContent = '步驟 2/2:AI 深度分析中...'; - btn.innerHTML = '分析中...'; + document.getElementById('insightStatus').textContent = '步驟 2/2:產生下一步...'; + btn.innerHTML = '判斷中...'; - // 準備搜尋結果摘要 + // 準備外部訊號摘要 let webContext = ''; if (searchData.success && searchData.data) { const parsed = searchData.data.parsed; if (parsed) { - webContext = `\n\n【網路搜尋結果摘要】\n${parsed.summary || ''}\n`; + webContext = `\n\n【外部訊號摘要】\n${parsed.summary || ''}\n`; if (parsed.results && parsed.results.length > 0) { - webContext += '\n相關資訊:\n'; + webContext += '\n相關訊號:\n'; parsed.results.slice(0, 3).forEach((r, i) => { webContext += `${i+1}. ${r.title}: ${r.description}\n`; }); } } else if (searchData.data.raw_content) { - webContext = `\n\n【網路搜尋結果】\n${searchData.data.raw_content.substring(0, 500)}`; + webContext = `\n\n【外部訊號】\n${searchData.data.raw_content.substring(0, 500)}`; } } @@ -784,7 +768,7 @@ product_name: productName, include_competitors: true, include_trends: true, - web_context: webContext // 傳入網路搜尋結果 + web_context: webContext }), signal: controller.signal }); @@ -796,47 +780,46 @@ }) .then(data => { btn.disabled = false; - btn.innerHTML = '分析商品'; + btn.innerHTML = '判斷下一步'; if (data.success) { renderProductInsights(data.data, productName); } else { - resultArea.innerHTML = `-搜尋結果
+市場訊號
`; } - // 底部資訊 html += `${escapeHtml(data.raw_content)}@@ -689,12 +675,10 @@- ${data.model || 'AI'} | - ${data.duration || '?'}秒 + 完成於 ${data.duration || '?'} 秒`; contentArea.innerHTML = html; @@ -715,7 +699,7 @@ // 更新按鈕狀態 btn.disabled = true; - btn.innerHTML = '搜尋中...'; + btn.innerHTML = '整理中...'; // 顯示結果區 placeholder.style.display = 'none'; @@ -726,8 +710,8 @@-步驟 1/2:搜尋網路最新資訊...
- (整體約需 60-90 秒) +步驟 1/2:整理外部訊號...
+ 約需 60-90 秒${data.error}`; + resultArea.innerHTML = `商品判斷暫時不可用,請稍後重試。`; } }) .catch(e => { clearTimeout(timeoutId); btn.disabled = false; - btn.innerHTML = '分析商品'; + btn.innerHTML = '判斷下一步'; if (e.name === 'AbortError') { - resultArea.innerHTML = `AI 伺服器回應較慢,請稍後再試。`; + resultArea.innerHTML = `商品判斷較慢,請稍後再試。`; } else { - resultArea.innerHTML = `分析失敗:${e.message}`; + resultArea.innerHTML = `商品判斷暫時不可用,請稍後重試。`; } }); } - // 渲染商品洞察結果 - 完整顯示版本 + // 渲染商品洞察結果 function renderProductInsights(data, productName) { const resultArea = document.getElementById('productInsightsResult'); let html = ''; // 標題 html += `-`; if (data.insights) { const ins = data.insights; - // 市場定位 - 卡片樣式 if (ins.market_position) { const mp = ins.market_position; html += `${escapeHtml(productName || '商品')} 市場分析
- 含網路即時資訊 +${escapeHtml(productName || '商品')} 下一步判斷
+ 含外部訊號`; } - // 競品分析 - 表格樣式 if (ins.competitors && ins.competitors.length > 0) { html += `- 市場定位 + 商品定位 ${mp.price_range ? `${escapeHtml(mp.price_range)}` : ''}${escapeHtml(mp.positioning || mp.target_audience || mp.description || '')}
@@ -844,16 +827,15 @@`; } - // 市場趨勢 if (ins.trends) { html += `- 競品分析 + 競品重點-
+競品 優勢 劣勢 `; ins.competitors.slice(0, 5).forEach(c => { html += `競品 可借鏡 風險 @@ -865,7 +847,6 @@ html += ` @@ -876,11 +857,10 @@`; } - // 銷售建議 if (ins.recommendations && ins.recommendations.length > 0) { html += ``; } - // 行銷關鍵字 - 可點擊加入 if (ins.keywords && ins.keywords.length > 0) { html += `- 銷售建議 + 下一步- `;
ins.recommendations.forEach(r => {
html += `
- ${escapeHtml(r)} `; @@ -888,10 +868,9 @@ html += `
- 行銷關鍵字 + 可用關鍵字 點擊可加入文案`; } } else if (data.raw_content) { - // 原始內容顯示 html += ``; ins.keywords.forEach(k => { @@ -900,7 +879,6 @@ html += ``; } - // 底部資訊 html += `${escapeHtml(data.raw_content)}@@ -908,14 +886,12 @@- ${data.model || 'AI'} | - ${data.duration || '?'}秒 + 完成於 ${data.duration || '?'} 秒`; @@ -1005,7 +981,7 @@ 'dcard': 'Dcard', 'google_news': '新聞', 'youtube': 'YT', - 'ollama_web_search': 'AI' + 'ollama_web_search': '搜尋' }; const html = records.map(r => ` @@ -1033,7 +1009,7 @@ // 初始化 document.addEventListener('DOMContentLoaded', function() { - initAIProvider(); // 初始化 AI 引擎選擇 + initAIProvider(); refreshAIStatus(); renderUpcomingHolidays(); refreshTrends(); // 載入即時趨勢(預設頁籤)- 重新分析 + 重新判斷