450 lines
24 KiB
HTML
450 lines
24 KiB
HTML
{% extends 'ewoooc_base.html' %}
|
||
{% block title %}AI 智慧推薦 - WOOO TECH{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='css/page-ai-recommend.css') }}">
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='css/page-ai-recommend-bem.css') }}">
|
||
{% endblock %}
|
||
|
||
{% block ewooo_content %}
|
||
{# Runtime API calls live in page-ai-recommend.js: fetch('/api/ai/generate_copy', ...), fetch('/api/ai/web_search', ...), fetch('/api/ai/product_insights', ...), fetch('/api/ai/gemini_usage?days=30'). #}
|
||
<div class="momo-app" data-page-group="ai">
|
||
<div class="ai-recommend-page">
|
||
|
||
{# ── Hero header ─────────────────────────────────── #}
|
||
<header class="ai-recommend-hero ar-hero">
|
||
<div class="ar-hero__title">
|
||
<h1 class="ai-recommend-title">
|
||
<i class="fas fa-robot"></i>AI 智慧推薦
|
||
</h1>
|
||
<small class="ar-hero__sub">根據資料庫商品分類、即時趨勢與 AI 生成紀錄,產出可追蹤的銷售文案。</small>
|
||
</div>
|
||
<div class="ar-hero__actions">
|
||
<span id="ollamaStatus" class="ar-status {{ 'ar-status--ok' if ollama_status else 'ar-status--off' }}">
|
||
<i class="fas fa-server"></i> Ollama {{ '檢查中' if ollama_status is none else ('✓' if ollama_status else '✗') }}
|
||
</span>
|
||
<span id="geminiStatus" class="ar-status {{ 'ar-status--info' if gemini_status else 'ar-status--off' }}">
|
||
<i class="fab fa-google"></i> Gemini {{ '檢查中' if gemini_status is none else ('✓' if gemini_status else '✗') }}
|
||
</span>
|
||
<button class="btn btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#helpModal" title="使用說明">
|
||
<i class="fas fa-question-circle"></i>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="row">
|
||
{# ── 左側:文案生成 ────────────────────────────── #}
|
||
<div class="col-lg-6 col-xl-5 mb-3">
|
||
<article class="card ar-card ar-card--gen">
|
||
<header class="card-header ar-card__head ar-card__head--accent">
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<h6 class="mb-0"><i class="fas fa-magic me-2"></i>文案生成</h6>
|
||
<small class="ar-card__step">第 1 步:設定參數</small>
|
||
</div>
|
||
</header>
|
||
<div class="card-body py-3">
|
||
|
||
{# 商品名稱 #}
|
||
<div class="mb-3">
|
||
<label class="form-label fw-bold mb-1">
|
||
<i class="fas fa-box me-1"></i>商品名稱 <span class="text-danger">*</span>
|
||
</label>
|
||
<div class="input-group">
|
||
<input type="text" class="form-control form-control-lg" id="productName" placeholder="輸入商品名稱...">
|
||
<button class="btn btn-outline-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" title="從商品分類快速選取">
|
||
<i class="fas fa-list"></i>
|
||
</button>
|
||
<ul class="dropdown-menu dropdown-menu-end ar-dropdown">
|
||
<li><h6 class="dropdown-header">點選分類快速填入</h6></li>
|
||
{% for category in product_categories %}
|
||
<li><a class="dropdown-item" href="#" onclick="setProduct('{{ category }}')">{{ category }}</a></li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
<div class="d-flex justify-content-between mt-1">
|
||
<small class="text-muted"><i class="fas fa-lightbulb me-1"></i>可從右側熱銷商品快速選取</small>
|
||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none" onclick="document.getElementById('productName').value=''">
|
||
<small>清除</small>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{# 風格 / 引擎 / 模型 #}
|
||
<div class="row g-2 mb-3">
|
||
<div class="col-4">
|
||
<label class="form-label small fw-bold mb-1">文案風格</label>
|
||
<select class="form-select form-select-sm" id="copyStyle">
|
||
<option value="吸睛">🎯 吸睛活潑</option>
|
||
<option value="專業">🔬 專業權威</option>
|
||
<option value="溫馨">💕 溫馨感性</option>
|
||
<option value="急迫">⚡ 限時急迫</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-4">
|
||
<label class="form-label small fw-bold mb-1">AI 引擎</label>
|
||
<select class="form-select form-select-sm" id="aiProvider" onchange="onProviderChange()">
|
||
<option value="ollama" {% if default_provider == 'ollama' %}selected{% endif %}>🖥️ Ollama (本地)</option>
|
||
<option value="gemini" {% if default_provider == 'gemini' %}selected{% endif %}>☁️ Gemini (雲端)</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-4">
|
||
<label class="form-label small fw-bold mb-1">AI 模型</label>
|
||
<select class="form-select form-select-sm" id="ollamaModelSelect">
|
||
{% for model in available_models %}
|
||
<option value="{{ model }}" {% if 'gemma3:4b' in model %}selected{% endif %}>{{ model }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
<select class="form-select form-select-sm" id="geminiModelSelect" style="display: none;">
|
||
{% for model in gemini_models %}
|
||
<option value="{{ model.id }}" {% if loop.first %}selected{% endif %}>{{ model.name }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{# Gemini 用量面板 #}
|
||
<div id="geminiUsagePanel" class="alert alert-info py-2 mb-3" style="display: none;">
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<small class="fw-bold"><i class="fab fa-google me-1"></i>Gemini 本月使用量</small>
|
||
<button type="button" class="btn btn-link btn-sm p-0" onclick="loadGeminiUsage()"><i class="fas fa-sync-alt"></i></button>
|
||
</div>
|
||
<div class="d-flex justify-content-between small mt-1">
|
||
<span>費用: <strong id="geminiMonthlyCost">$0.0000</strong></span>
|
||
<span>請求: <span id="geminiRequestCount">0</span> 次</span>
|
||
<span>Token: <span id="geminiTokenUsage">0</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
{# 關鍵字+節日 #}
|
||
<div class="accordion accordion-flush" id="advancedOptions">
|
||
<div class="accordion-item border-0">
|
||
<h2 class="accordion-header">
|
||
<button class="accordion-button collapsed py-2 px-0 bg-transparent" type="button" data-bs-toggle="collapse" data-bs-target="#keywordsCollapse">
|
||
<small class="fw-bold"><i class="fas fa-tags me-1"></i>關鍵字與節日 <span class="badge ar-selected-count ms-1" id="selectedKeywordCount">0 個已選</span></small>
|
||
</button>
|
||
</h2>
|
||
<div id="keywordsCollapse" class="accordion-collapse collapse" data-bs-parent="#advancedOptions">
|
||
<div class="accordion-body p-0 pt-2">
|
||
<div class="mb-2">
|
||
<small class="text-muted d-block mb-1">點選標籤加入/移除</small>
|
||
<div id="keywordsArea" class="ar-keywords">
|
||
{% for category in product_categories[:12] %}
|
||
<span class="badge ar-keyword-badge border me-1 mb-1 keyword-badge ar-keyword" onclick="toggleKeyword(this)">{{ category }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<small class="text-muted"><i class="fas fa-calendar-alt me-1"></i>近期節日(自動融入)</small>
|
||
<div id="upcomingHolidays" class="small mt-1"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="my-2">
|
||
<div class="d-grid">
|
||
<button class="btn btn-primary" onclick="generateCopy()" id="generateBtn">
|
||
<i class="fas fa-magic me-2"></i>生成文案
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
|
||
{# 生成結果 #}
|
||
<div id="resultArea" class="mt-3" style="display: none;">
|
||
<article class="card ar-card ar-card--result">
|
||
<header class="card-header ar-card__head ar-card__head--success d-flex justify-content-between align-items-center">
|
||
<span><i class="fas fa-sparkles me-2"></i>AI 生成文案</span>
|
||
<button class="btn btn-sm btn-light" onclick="copyCopyText()" title="複製全部文案到剪貼簿">
|
||
<i class="fas fa-copy me-1"></i>複製
|
||
</button>
|
||
</header>
|
||
<div class="card-body py-2 ar-result__body">
|
||
<div id="generatedCopy" class="ar-result__text"></div>
|
||
<hr class="my-2">
|
||
<small class="text-muted" id="copyMeta"></small>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
|
||
{# AI 智慧搜尋 #}
|
||
<article class="card ar-card mt-3 ar-card--search">
|
||
<header class="card-header ar-card__head ar-card__head--accent-soft py-2">
|
||
<div>
|
||
<h6 class="mb-0"><i class="fas fa-search-dollar me-2"></i>AI 智慧搜尋</h6>
|
||
<small class="text-muted">輸入關鍵字,AI 分析市場趨勢</small>
|
||
</div>
|
||
</header>
|
||
<div class="card-body py-2">
|
||
<div class="input-group input-group-sm mb-2">
|
||
<input type="text" class="form-control" id="webSearchQuery" placeholder="輸入搜尋關鍵字...">
|
||
<select class="form-select" id="webSearchType" style="max-width: 100px;">
|
||
<option value="general">一般</option>
|
||
<option value="shopping">商品</option>
|
||
<option value="trends">趨勢</option>
|
||
<option value="news">新聞</option>
|
||
</select>
|
||
<button class="btn btn-primary" onclick="doWebSearch()" id="webSearchBtn">
|
||
<i class="fas fa-brain"></i>
|
||
</button>
|
||
</div>
|
||
<div class="ar-quick-tags mb-2">
|
||
{% for category in product_categories[:4] %}
|
||
<span class="badge bg-light text-dark border ar-quick-tag" onclick="quickWebSearch({{ category|tojson }})">{{ category }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
<div id="webSearchResult" style="display: none;">
|
||
<hr class="my-2">
|
||
<div id="webSearchContent"></div>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
|
||
{# 商品洞察 #}
|
||
<article class="card ar-card mt-3 ar-card--insights">
|
||
<header class="card-header ar-card__head ar-card__head--warn-soft d-flex justify-content-between align-items-center py-2">
|
||
<div>
|
||
<h6 class="mb-0"><i class="fas fa-lightbulb me-2"></i>商品洞察分析</h6>
|
||
<small class="text-muted">結合網路搜尋的即時市場分析</small>
|
||
</div>
|
||
<button class="btn btn-sm btn-warning" onclick="doProductInsights()" id="insightsBtn">
|
||
<i class="fas fa-search-dollar me-1"></i>分析商品
|
||
</button>
|
||
</header>
|
||
<div id="productInsightsResult" class="card-body py-3" style="display: none;"></div>
|
||
<div id="productInsightsPlaceholder" class="card-body py-3 text-center text-muted">
|
||
<i class="fas fa-info-circle me-1"></i>輸入商品名稱後點擊「分析商品」<br>
|
||
<small>AI 會先搜尋最新網路資訊,再進行深度分析</small>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
|
||
{# ── 右側:市場資訊 tabs ──────────────────────── #}
|
||
<div class="col-lg-6 col-xl-7">
|
||
<ul class="nav nav-pills nav-fill mb-3" id="marketInfoTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active py-2" id="trends-tab" data-bs-toggle="pill" data-bs-target="#trends-panel" type="button" role="tab">
|
||
<i class="fas fa-chart-line me-1"></i><span class="d-none d-md-inline">趨勢洞察</span><span class="d-md-none">趨勢</span>
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link py-2" id="bestsellers-tab" data-bs-toggle="pill" data-bs-target="#bestsellers-panel" type="button" role="tab">
|
||
<i class="fas fa-fire-alt me-1"></i><span class="d-none d-md-inline">熱銷商品</span><span class="d-md-none">熱銷</span>
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link py-2" id="rankings-tab" data-bs-toggle="pill" data-bs-target="#rankings-panel" type="button" role="tab">
|
||
<i class="fas fa-crown me-1"></i><span class="d-none d-md-inline">排行榜</span><span class="d-md-none">排行</span>
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link py-2" id="news-tab" data-bs-toggle="pill" data-bs-target="#news-panel" type="button" role="tab">
|
||
<i class="fas fa-newspaper me-1"></i><span class="d-none d-md-inline">趨勢新聞</span><span class="d-md-none">新聞</span>
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content" id="marketInfoTabContent">
|
||
{# 趨勢洞察 #}
|
||
<div class="tab-pane fade show active" id="trends-panel" role="tabpanel">
|
||
<article class="card ar-card ar-card--trends">
|
||
<header class="card-header ar-card__head ar-card__head--soft d-flex justify-content-between align-items-center py-2">
|
||
<div>
|
||
<h6 class="mb-0"><i class="fas fa-chart-line me-2"></i>即時趨勢洞察</h6>
|
||
<small class="text-muted">來自 PTT、Dcard、Google News</small>
|
||
</div>
|
||
<div class="d-flex gap-1">
|
||
<select class="form-select form-select-sm" id="trendSource" style="width: 90px;" onchange="refreshTrends()">
|
||
<option value="">全部來源</option>
|
||
<option value="google_news">Google</option>
|
||
<option value="ptt">PTT</option>
|
||
<option value="dcard">Dcard</option>
|
||
<option value="youtube">YouTube</option>
|
||
</select>
|
||
<select class="form-select form-select-sm" id="trendCategory" style="width: 70px;" onchange="refreshTrends()">
|
||
<option value="">全部</option>
|
||
<option value="美妝">美妝</option>
|
||
<option value="3C">3C</option>
|
||
<option value="服飾">服飾</option>
|
||
<option value="居家">居家</option>
|
||
</select>
|
||
<button class="btn btn-sm btn-outline-success" onclick="refreshTrends()"><i class="fas fa-sync-alt"></i></button>
|
||
</div>
|
||
</header>
|
||
<div class="card-body py-2">
|
||
<div class="mb-2">
|
||
<small class="text-muted fw-bold">熱門關鍵字:</small>
|
||
<div id="trendKeywordCloud" class="d-flex flex-wrap gap-1 mt-1">
|
||
<span class="badge bg-light text-dark border">載入中...</span>
|
||
</div>
|
||
</div>
|
||
<div id="trendListArea" class="ar-scroll-280">
|
||
<div class="text-center py-2"><div class="spinner-border spinner-border-sm"></div></div>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
|
||
{# 熱銷商品 #}
|
||
<div class="tab-pane fade" id="bestsellers-panel" role="tabpanel">
|
||
<article class="card ar-card">
|
||
<header class="card-header ar-card__head ar-card__head--soft d-flex justify-content-between align-items-center py-2">
|
||
<div>
|
||
<h6 class="mb-0"><i class="fas fa-fire-alt me-2"></i>熱銷商品參考</h6>
|
||
<small class="text-muted">點選商品快速填入</small>
|
||
</div>
|
||
<div class="d-flex align-items-center gap-1">
|
||
<div class="btn-group btn-group-sm">
|
||
<input type="radio" class="btn-check" name="platform" id="platformPchome" value="pchome" checked>
|
||
<label class="btn btn-outline-primary btn-sm px-2" for="platformPchome">PChome</label>
|
||
<input type="radio" class="btn-check" name="platform" id="platformMomo" value="momo" disabled>
|
||
<label class="btn btn-outline-secondary btn-sm px-2 ar-disabled" for="platformMomo">MOMO</label>
|
||
</div>
|
||
<select class="form-select form-select-sm" id="bestsellersCategory" style="width: 80px;" onchange="loadBestsellers()">
|
||
<option value="面膜">面膜</option>
|
||
<option value="精華液">精華液</option>
|
||
<option value="乳液">乳液</option>
|
||
<option value="保濕">保濕</option>
|
||
<option value="美白">美白</option>
|
||
</select>
|
||
</div>
|
||
</header>
|
||
<div class="card-body py-2 ar-scroll-350" id="bestsellersCard">
|
||
<div class="text-center py-3"><div class="spinner-border spinner-border-sm"></div></div>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
|
||
{# 排行榜 #}
|
||
<div class="tab-pane fade" id="rankings-panel" role="tabpanel">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-md-6">
|
||
<article class="card ar-card h-100">
|
||
<header class="card-header ar-card__head ar-card__head--soft d-flex justify-content-between align-items-center py-2">
|
||
<div>
|
||
<h6 class="mb-0"><i class="fas fa-crown me-2"></i>COSME</h6>
|
||
<small class="text-muted">日本美妝排行</small>
|
||
</div>
|
||
<select class="form-select form-select-sm" id="cosmeCategory" style="width: 80px;" onchange="loadCosmeRankings()">
|
||
<option value="mask">面膜</option>
|
||
<option value="serum">精華液</option>
|
||
<option value="lotion">乳液</option>
|
||
<option value="sunscreen">防曬</option>
|
||
<option value="lipstick">唇彩</option>
|
||
<option value="foundation">底妝</option>
|
||
</select>
|
||
</header>
|
||
<div class="card-body py-2 ar-scroll-300" id="cosmeCard">
|
||
<div class="text-center py-3"><div class="spinner-border spinner-border-sm"></div></div>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
<div class="col-12 col-md-6">
|
||
<article class="card ar-card h-100">
|
||
<header class="card-header ar-card__head ar-card__head--soft d-flex justify-content-between align-items-center py-2">
|
||
<div>
|
||
<h6 class="mb-0"><i class="fas fa-trophy me-2"></i>mybest</h6>
|
||
<small class="text-muted">專業評測</small>
|
||
</div>
|
||
<select class="form-select form-select-sm" id="mybestCategory" style="width: 80px;" onchange="loadMybestArticles()">
|
||
<option value="skincare">保養</option>
|
||
<option value="makeup">彩妝</option>
|
||
<option value="health">健康</option>
|
||
<option value="baby">嬰兒</option>
|
||
<option value="maternity">孕婦</option>
|
||
<option value="kids">兒童</option>
|
||
</select>
|
||
</header>
|
||
<div class="card-body py-2 ar-scroll-300" id="mybestCard">
|
||
<div class="text-center py-3"><div class="spinner-border spinner-border-sm"></div></div>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{# 趨勢新聞 #}
|
||
<div class="tab-pane fade" id="news-panel" role="tabpanel">
|
||
<article class="card ar-card">
|
||
<header class="card-header ar-card__head ar-card__head--soft py-2">
|
||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||
<div>
|
||
<h6 class="mb-0"><i class="fas fa-newspaper me-2"></i>趨勢新聞</h6>
|
||
<small class="text-muted">點選新聞快速填入商品</small>
|
||
</div>
|
||
<button class="btn btn-sm btn-outline-secondary" onclick="loadTrends()"><i class="fas fa-sync-alt"></i></button>
|
||
</div>
|
||
<ul class="nav nav-tabs card-header-tabs" id="trendTabs">
|
||
<li class="nav-item">
|
||
<a class="nav-link active py-1 px-2" href="#" data-trend-type="news" onclick="switchTrendTab(this, 'news')">
|
||
<i class="fas fa-newspaper me-1"></i>新聞
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link py-1 px-2" href="#" data-trend-type="social" onclick="switchTrendTab(this, 'social')">
|
||
<i class="fas fa-hashtag me-1"></i>社群
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link py-1 px-2" href="#" data-trend-type="search" onclick="switchTrendTab(this, 'search')">
|
||
<i class="fas fa-search me-1"></i>搜尋
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</header>
|
||
<div id="newsCard" class="ar-scroll-350">
|
||
<div class="text-center py-4"><div class="spinner-border"></div><p class="mt-2 text-muted small">載入中...</p></div>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{# ── Help modal ──────────────────────────────────── #}
|
||
<div class="modal fade" id="helpModal" tabindex="-1">
|
||
<div class="modal-dialog modal-dialog-centered">
|
||
<div class="modal-content">
|
||
<div class="modal-header py-2">
|
||
<h6 class="modal-title"><i class="fas fa-question-circle me-2"></i>使用說明</h6>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body py-3">
|
||
<div class="d-flex align-items-start mb-3">
|
||
<span class="badge ar-step-badge me-2">1</span>
|
||
<div><strong>輸入商品名稱</strong><p class="text-muted small mb-0">可直接輸入或從右側熱銷商品點選</p></div>
|
||
</div>
|
||
<div class="d-flex align-items-start mb-3">
|
||
<span class="badge ar-step-badge me-2">2</span>
|
||
<div><strong>選擇文案風格</strong><p class="text-muted small mb-0">吸睛活潑、專業權威、溫馨感性、限時急迫</p></div>
|
||
</div>
|
||
<div class="d-flex align-items-start mb-3">
|
||
<span class="badge ar-step-badge me-2">3</span>
|
||
<div><strong>點擊生成文案</strong><p class="text-muted small mb-0">AI 會根據市場趨勢自動生成銷售文案</p></div>
|
||
</div>
|
||
<hr>
|
||
<p class="small text-muted mb-0">
|
||
<i class="fas fa-lightbulb me-1"></i>
|
||
<strong>小技巧:</strong>使用「AI 智慧搜尋」可獲得更多市場洞察,點選「商品洞察」可分析競品與市場定位。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{# ── Loading overlay ─────────────────────────────── #}
|
||
<div id="loadingOverlay" class="ar-loading-overlay d-none">
|
||
<div class="ar-loading-overlay__inner">
|
||
<div class="spinner-border spinner-border-lg mb-3"></div>
|
||
<h5 id="loadingText">AI 正在思考中...</h5>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
<script src="{{ url_for('static', filename='js/page-ai-recommend.js') }}"></script>
|
||
{% endblock %}
|