This commit is contained in:
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.155"
|
||||
SYSTEM_VERSION = "V10.156"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -2,20 +2,139 @@
|
||||
|
||||
{% block title %}PChome 爬蟲 - EwoooC{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.pchome-tool-page {
|
||||
color: var(--momo-text-primary);
|
||||
}
|
||||
|
||||
.pchome-tool-head {
|
||||
padding: var(--momo-space-4) var(--momo-space-5);
|
||||
background: var(--momo-bg-surface);
|
||||
border: 1px solid var(--momo-border-light);
|
||||
border-radius: var(--momo-radius-md);
|
||||
}
|
||||
|
||||
.pchome-tool-head h2 {
|
||||
margin: 0;
|
||||
color: var(--momo-text-primary);
|
||||
font-family: var(--momo-font-display);
|
||||
font-size: var(--momo-text-headline);
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.pchome-tool-head p,
|
||||
.pchome-tool-page .text-muted {
|
||||
color: var(--momo-text-secondary) !important;
|
||||
}
|
||||
|
||||
.pchome-card-head {
|
||||
background: var(--momo-bg-paper);
|
||||
border-bottom: 1px solid var(--momo-border-light);
|
||||
color: var(--momo-text-primary);
|
||||
font-family: var(--momo-font-display);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.pchome-table-head th {
|
||||
background: var(--momo-bg-paper) !important;
|
||||
color: var(--momo-text-secondary) !important;
|
||||
font-family: var(--momo-font-mono, monospace);
|
||||
font-size: var(--momo-text-label);
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.pchome-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 22px;
|
||||
padding: 3px 8px;
|
||||
border: 1px solid var(--momo-border-light);
|
||||
border-radius: var(--momo-radius-sm);
|
||||
font-family: var(--momo-font-mono, monospace);
|
||||
font-size: var(--momo-text-label);
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.pchome-badge.is-danger {
|
||||
background: var(--momo-danger-bg);
|
||||
border-color: var(--momo-danger-border);
|
||||
color: var(--momo-danger-text);
|
||||
}
|
||||
|
||||
.pchome-badge.is-success {
|
||||
background: var(--momo-success-bg);
|
||||
border-color: var(--momo-success-border);
|
||||
color: var(--momo-success-text);
|
||||
}
|
||||
|
||||
.pchome-badge.is-muted {
|
||||
background: var(--momo-tag-muted-bg);
|
||||
border-color: var(--momo-tag-muted-border);
|
||||
color: var(--momo-tag-muted-text);
|
||||
}
|
||||
|
||||
.pchome-toast {
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
min-width: min(300px, calc(100vw - 40px));
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--momo-border-light);
|
||||
border-radius: var(--momo-radius-md);
|
||||
background: var(--momo-bg-elevated);
|
||||
color: var(--momo-text-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pchome-toast--success {
|
||||
background: var(--momo-success-bg);
|
||||
border-color: var(--momo-success-border);
|
||||
color: var(--momo-success-text);
|
||||
}
|
||||
|
||||
.pchome-toast--danger {
|
||||
background: var(--momo-danger-bg);
|
||||
border-color: var(--momo-danger-border);
|
||||
color: var(--momo-danger-text);
|
||||
}
|
||||
|
||||
.pchome-toast--warning {
|
||||
background: var(--momo-warning-bg);
|
||||
border-color: var(--momo-warning-border);
|
||||
color: var(--momo-warning-text);
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.pchome-tool-head {
|
||||
padding: var(--momo-space-4);
|
||||
}
|
||||
|
||||
.pchome-tool-page .card-header {
|
||||
align-items: flex-start !important;
|
||||
flex-direction: column;
|
||||
gap: var(--momo-space-2);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<div class="container-fluid py-4 pchome-tool-page">
|
||||
<header class="pchome-tool-head mb-4">
|
||||
<h2><i class="fas fa-spider me-2"></i>PChome 24h 爬蟲</h2>
|
||||
<p class="text-muted">爬取 PChome 24h 商品資料</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 爬取方式選擇 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<div class="card-header pchome-card-head">
|
||||
<i class="fas fa-folder-open me-2"></i>館別爬取
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -39,7 +158,7 @@
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white">
|
||||
<div class="card-header pchome-card-head">
|
||||
<i class="fas fa-search me-2"></i>關鍵字搜尋
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -55,7 +174,7 @@
|
||||
<option value="100">100 筆</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-success" id="searchBtn">
|
||||
<button class="btn btn-primary" id="searchBtn">
|
||||
<i class="fas fa-search me-1"></i>搜尋
|
||||
</button>
|
||||
</div>
|
||||
@@ -67,7 +186,7 @@
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header bg-info text-white">
|
||||
<div class="card-header pchome-card-head">
|
||||
<i class="fas fa-link me-2"></i>自訂 URL 爬取
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -78,7 +197,7 @@
|
||||
placeholder="https://24h.pchome.com.tw/region/DDAB">
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<button class="btn btn-info w-100" id="crawlCustomBtn">
|
||||
<button class="btn btn-primary w-100" id="crawlCustomBtn">
|
||||
<i class="fas fa-download me-1"></i>爬取
|
||||
</button>
|
||||
</div>
|
||||
@@ -125,8 +244,8 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped" id="resultTable">
|
||||
<thead class="table-dark">
|
||||
<table class="table table-hover mb-0" id="resultTable">
|
||||
<thead class="pchome-table-head">
|
||||
<tr>
|
||||
<th style="width: 80px;">圖片</th>
|
||||
<th>商品名稱</th>
|
||||
@@ -346,10 +465,10 @@
|
||||
tbody.innerHTML = '';
|
||||
|
||||
for (const p of products) {
|
||||
const discount = p.discount ? `<span class="badge bg-danger">-${p.discount}%</span>` : '-';
|
||||
const discount = p.discount ? `<span class="pchome-badge is-danger">-${p.discount}%</span>` : '-';
|
||||
const stockBadge = p.stock > 0
|
||||
? `<span class="badge bg-success">${p.stock}</span>`
|
||||
: `<span class="badge bg-secondary">缺貨</span>`;
|
||||
? `<span class="pchome-badge is-success">${p.stock}</span>`
|
||||
: `<span class="pchome-badge is-muted">缺貨</span>`;
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
@@ -431,8 +550,7 @@
|
||||
function showToast(message, type = 'info') {
|
||||
// 簡易 Toast
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `alert alert-${type} position-fixed`;
|
||||
toast.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
||||
toast.className = `pchome-toast pchome-toast--${type} position-fixed`;
|
||||
toast.innerHTML = `
|
||||
<button type="button" class="btn-close float-end" onclick="this.parentElement.remove()"></button>
|
||||
${message}
|
||||
|
||||
@@ -2,21 +2,169 @@
|
||||
|
||||
{% block title %}比價系統 - EwoooC{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.price-tool-page {
|
||||
color: var(--momo-text-primary);
|
||||
}
|
||||
|
||||
.price-tool-head {
|
||||
padding: var(--momo-space-4) var(--momo-space-5);
|
||||
background: var(--momo-bg-surface);
|
||||
border: 1px solid var(--momo-border-light);
|
||||
border-radius: var(--momo-radius-md);
|
||||
}
|
||||
|
||||
.price-tool-head h2 {
|
||||
margin: 0;
|
||||
color: var(--momo-text-primary);
|
||||
font-family: var(--momo-font-display);
|
||||
font-size: var(--momo-text-headline);
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.price-tool-head p,
|
||||
.price-tool-page .text-muted {
|
||||
color: var(--momo-text-secondary) !important;
|
||||
}
|
||||
|
||||
.price-step-head {
|
||||
background: var(--momo-bg-paper);
|
||||
border-bottom: 1px solid var(--momo-border-light);
|
||||
color: var(--momo-text-primary);
|
||||
font-family: var(--momo-font-display);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.price-count-badge,
|
||||
.price-result-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 22px;
|
||||
padding: 3px 8px;
|
||||
border: 1px solid var(--momo-border-light);
|
||||
border-radius: var(--momo-radius-sm);
|
||||
font-family: var(--momo-font-mono, monospace);
|
||||
font-size: var(--momo-text-label);
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.price-count-badge.is-muted,
|
||||
.price-result-badge.is-muted {
|
||||
background: var(--momo-tag-muted-bg);
|
||||
border-color: var(--momo-tag-muted-border);
|
||||
color: var(--momo-tag-muted-text);
|
||||
}
|
||||
|
||||
.price-count-badge.is-pchome,
|
||||
.price-result-badge.is-pchome {
|
||||
background: var(--momo-info-bg);
|
||||
border-color: var(--momo-info-border);
|
||||
color: var(--momo-info-text);
|
||||
}
|
||||
|
||||
.price-count-badge.is-momo,
|
||||
.price-result-badge.is-momo {
|
||||
background: var(--momo-warning-bg);
|
||||
border-color: var(--momo-warning-border);
|
||||
color: var(--momo-warning-text);
|
||||
}
|
||||
|
||||
.price-stat-card {
|
||||
background: var(--momo-bg-surface);
|
||||
border: 1px solid var(--momo-border-light);
|
||||
}
|
||||
|
||||
.price-stat-card h4 {
|
||||
color: var(--momo-text-primary);
|
||||
font-family: var(--momo-font-mono, monospace);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.price-stat-card.is-pchome h4 {
|
||||
color: var(--momo-info-text);
|
||||
}
|
||||
|
||||
.price-stat-card.is-momo h4 {
|
||||
color: var(--momo-warning-text);
|
||||
}
|
||||
|
||||
.price-stat-card.is-diff h4 {
|
||||
color: var(--momo-text-primary);
|
||||
}
|
||||
|
||||
.price-note {
|
||||
padding: 10px 12px;
|
||||
background: var(--momo-info-bg);
|
||||
border: 1px solid var(--momo-info-border);
|
||||
border-radius: var(--momo-radius-md);
|
||||
color: var(--momo-info-text);
|
||||
}
|
||||
|
||||
.price-table-head th {
|
||||
background: var(--momo-bg-paper) !important;
|
||||
color: var(--momo-text-secondary) !important;
|
||||
font-family: var(--momo-font-mono, monospace);
|
||||
font-size: var(--momo-text-label);
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.price-toast {
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
min-width: min(300px, calc(100vw - 40px));
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--momo-border-light);
|
||||
border-radius: var(--momo-radius-md);
|
||||
background: var(--momo-bg-elevated);
|
||||
color: var(--momo-text-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.price-toast--success {
|
||||
background: var(--momo-success-bg);
|
||||
border-color: var(--momo-success-border);
|
||||
color: var(--momo-success-text);
|
||||
}
|
||||
|
||||
.price-toast--danger {
|
||||
background: var(--momo-danger-bg);
|
||||
border-color: var(--momo-danger-border);
|
||||
color: var(--momo-danger-text);
|
||||
}
|
||||
|
||||
.price-toast--warning {
|
||||
background: var(--momo-warning-bg);
|
||||
border-color: var(--momo-warning-border);
|
||||
color: var(--momo-warning-text);
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.price-tool-head {
|
||||
padding: var(--momo-space-4);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<div class="container-fluid py-4 price-tool-page">
|
||||
<header class="price-tool-head mb-4">
|
||||
<h2><i class="fas fa-balance-scale me-2"></i>PChome vs MOMO 比價</h2>
|
||||
<p class="text-muted">比較 PChome 24h 和 MOMO 美妝商品價格</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 操作區 -->
|
||||
<div class="row mb-4">
|
||||
<!-- Step 1: 選擇品牌 -->
|
||||
<div class="col-lg-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<div class="card-header price-step-head">
|
||||
<i class="fas fa-tag me-2"></i>Step 1: 選擇品牌
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -36,11 +184,11 @@
|
||||
<!-- Step 2: 取得 PChome 商品 -->
|
||||
<div class="col-lg-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-info text-white">
|
||||
<div class="card-header price-step-head">
|
||||
<i class="fas fa-shopping-cart me-2"></i>Step 2: PChome 商品
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<button class="btn btn-info w-100 mb-2" id="fetchPchomeBtn">
|
||||
<button class="btn btn-primary w-100 mb-2" id="fetchPchomeBtn">
|
||||
<i class="fas fa-download me-1"></i>自動爬取 PChome
|
||||
</button>
|
||||
<div class="text-center my-2"><small class="text-muted">或</small></div>
|
||||
@@ -48,7 +196,7 @@
|
||||
<i class="fas fa-keyboard me-1"></i>手動輸入
|
||||
</button>
|
||||
<div class="mt-3">
|
||||
<span class="badge bg-secondary" id="pchomeCount">0 筆商品</span>
|
||||
<span class="price-count-badge is-muted" id="pchomeCount">0 筆商品</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,11 +205,11 @@
|
||||
<!-- Step 3: 取得 MOMO 商品 -->
|
||||
<div class="col-lg-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<div class="card-header price-step-head">
|
||||
<i class="fas fa-store me-2"></i>Step 3: MOMO 商品
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<button class="btn btn-warning w-100 mb-2" id="uploadMomoBtn" data-bs-toggle="modal" data-bs-target="#uploadModal">
|
||||
<button class="btn btn-primary w-100 mb-2" id="uploadMomoBtn" data-bs-toggle="modal" data-bs-target="#uploadModal">
|
||||
<i class="fas fa-file-excel me-1"></i>上傳 Excel
|
||||
</button>
|
||||
<div class="text-center my-2"><small class="text-muted">或</small></div>
|
||||
@@ -69,7 +217,7 @@
|
||||
<i class="fas fa-keyboard me-1"></i>手動輸入
|
||||
</button>
|
||||
<div class="mt-3">
|
||||
<span class="badge bg-secondary" id="momoCount">0 筆商品</span>
|
||||
<span class="price-count-badge is-muted" id="momoCount">0 筆商品</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -79,7 +227,7 @@
|
||||
<!-- 比價按鈕 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col text-center">
|
||||
<button class="btn btn-lg btn-success px-5" id="compareBtn" disabled>
|
||||
<button class="btn btn-lg btn-primary px-5" id="compareBtn" disabled>
|
||||
<i class="fas fa-balance-scale me-2"></i>開始比價
|
||||
</button>
|
||||
</div>
|
||||
@@ -108,7 +256,7 @@
|
||||
<!-- 統計卡片 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card bg-light">
|
||||
<div class="card price-stat-card">
|
||||
<div class="card-body text-center">
|
||||
<h4 class="mb-0" id="matchedCount">0</h4>
|
||||
<small class="text-muted">成功匹配</small>
|
||||
@@ -116,7 +264,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card bg-info text-white">
|
||||
<div class="card price-stat-card is-pchome">
|
||||
<div class="card-body text-center">
|
||||
<h4 class="mb-0" id="pchomeCheaperCount">0</h4>
|
||||
<small>PChome 較便宜</small>
|
||||
@@ -124,7 +272,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card bg-warning text-dark">
|
||||
<div class="card price-stat-card is-momo">
|
||||
<div class="card-body text-center">
|
||||
<h4 class="mb-0" id="momoCheaperCount">0</h4>
|
||||
<small>MOMO 較便宜</small>
|
||||
@@ -132,7 +280,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card bg-secondary text-white">
|
||||
<div class="card price-stat-card is-diff">
|
||||
<div class="card-body text-center">
|
||||
<h4 class="mb-0" id="avgPriceDiff">$0</h4>
|
||||
<small>平均價差</small>
|
||||
@@ -154,7 +302,7 @@
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped mb-0">
|
||||
<thead class="table-dark">
|
||||
<thead class="price-table-head">
|
||||
<tr>
|
||||
<th style="width: 35%;">PChome 商品</th>
|
||||
<th style="width: 35%;">MOMO 商品</th>
|
||||
@@ -188,14 +336,14 @@
|
||||
<label class="form-label">選擇 Excel 檔案</label>
|
||||
<input type="file" class="form-control" id="momoExcelFile" accept=".xlsx,.xls,.csv">
|
||||
</div>
|
||||
<div class="alert alert-info">
|
||||
<div class="price-note">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
Excel 需包含「商品名稱」和「售價」欄位。可選包含「商品編號」和「連結」。
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-warning" id="parseExcelBtn">
|
||||
<button type="button" class="btn btn-primary" id="parseExcelBtn">
|
||||
<i class="fas fa-upload me-1"></i>上傳並解析
|
||||
</button>
|
||||
</div>
|
||||
@@ -212,7 +360,7 @@
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info py-2">
|
||||
<div class="price-note py-2">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
<strong>格式說明:</strong> 每行一筆商品,用<strong>逗號</strong>或 <strong>Tab</strong> 分隔<br>
|
||||
<code>商品名稱,價格</code> 或 <code>商品名稱,價格,商品連結(選填)</code>
|
||||
@@ -330,7 +478,7 @@ La Roche-Posay 安得利防曬液 50ml,920
|
||||
if (data.success) {
|
||||
pchomeProducts = data.data.products;
|
||||
document.getElementById('pchomeCount').textContent = `${pchomeProducts.length} 筆商品`;
|
||||
document.getElementById('pchomeCount').classList.replace('bg-secondary', 'bg-info');
|
||||
document.getElementById('pchomeCount').className = 'price-count-badge is-pchome';
|
||||
updateCompareButton();
|
||||
showToast(`成功取得 ${pchomeProducts.length} 筆 PChome 商品`, 'success');
|
||||
} else {
|
||||
@@ -365,7 +513,7 @@ La Roche-Posay 安得利防曬液 50ml,920
|
||||
if (data.success) {
|
||||
momoProducts = data.data.products;
|
||||
document.getElementById('momoCount').textContent = `${momoProducts.length} 筆商品`;
|
||||
document.getElementById('momoCount').classList.replace('bg-secondary', 'bg-warning');
|
||||
document.getElementById('momoCount').className = 'price-count-badge is-momo';
|
||||
updateCompareButton();
|
||||
bootstrap.Modal.getInstance(document.getElementById('uploadModal')).hide();
|
||||
showToast(`成功解析 ${momoProducts.length} 筆 MOMO 商品`, 'success');
|
||||
@@ -424,11 +572,11 @@ La Roche-Posay 安得利防曬液 50ml,920
|
||||
if (currentManualSource === 'pchome') {
|
||||
pchomeProducts = products;
|
||||
document.getElementById('pchomeCount').textContent = `${products.length} 筆商品`;
|
||||
document.getElementById('pchomeCount').classList.replace('bg-secondary', 'bg-info');
|
||||
document.getElementById('pchomeCount').className = 'price-count-badge is-pchome';
|
||||
} else {
|
||||
momoProducts = products;
|
||||
document.getElementById('momoCount').textContent = `${products.length} 筆商品`;
|
||||
document.getElementById('momoCount').classList.replace('bg-secondary', 'bg-warning');
|
||||
document.getElementById('momoCount').className = 'price-count-badge is-momo';
|
||||
}
|
||||
|
||||
updateCompareButton();
|
||||
@@ -487,8 +635,8 @@ La Roche-Posay 安得利防曬液 50ml,920
|
||||
|
||||
const similarityClass = m.similarity >= 0.8 ? 'text-success' : (m.similarity >= 0.6 ? 'text-warning' : 'text-danger');
|
||||
const cheaperBadge = m.cheaper_at === 'pchome'
|
||||
? '<span class="badge bg-info">PChome</span>'
|
||||
: (m.cheaper_at === 'momo' ? '<span class="badge bg-warning text-dark">MOMO</span>' : '<span class="badge bg-secondary">相同</span>');
|
||||
? '<span class="price-result-badge is-pchome">PChome</span>'
|
||||
: (m.cheaper_at === 'momo' ? '<span class="price-result-badge is-momo">MOMO</span>' : '<span class="price-result-badge is-muted">相同</span>');
|
||||
|
||||
// 處理 URL (可能是 url 或 product_url)
|
||||
const pchomeUrl = m.pchome.url || m.pchome.product_url || '';
|
||||
@@ -619,8 +767,7 @@ La Roche-Posay 安得利防曬液 50ml,920
|
||||
|
||||
function showToast(message, type = 'info') {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `alert alert-${type} position-fixed`;
|
||||
toast.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
||||
toast.className = `price-toast price-toast--${type} position-fixed`;
|
||||
toast.innerHTML = `
|
||||
<button type="button" class="btn-close float-end" onclick="this.parentElement.remove()"></button>
|
||||
${message}
|
||||
|
||||
@@ -2,8 +2,47 @@
|
||||
|
||||
{% block title %}趨勢資料 - EwoooC{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.trends-page {
|
||||
color: var(--momo-text-primary);
|
||||
}
|
||||
|
||||
.trends-table-head th {
|
||||
background: var(--momo-bg-paper) !important;
|
||||
color: var(--momo-text-secondary) !important;
|
||||
font-family: var(--momo-font-mono, monospace);
|
||||
font-size: var(--momo-text-label);
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.trends-empty {
|
||||
padding: var(--momo-space-5) !important;
|
||||
color: var(--momo-text-secondary) !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trends-count-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 22px;
|
||||
padding: 3px 8px;
|
||||
background: var(--momo-tag-caramel-bg);
|
||||
border: 1px solid var(--momo-tag-caramel-border);
|
||||
border-radius: var(--momo-radius-sm);
|
||||
color: var(--momo-tag-caramel-text);
|
||||
font-family: var(--momo-font-mono, monospace);
|
||||
font-size: var(--momo-text-label);
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<div class="container-fluid py-4 trends-page">
|
||||
<div class="page-header">
|
||||
<h1><i class="fas fa-chart-line me-2"></i>趨勢資料</h1>
|
||||
<p>Google News、PTT、Dcard、YouTube 趨勢訊號</p>
|
||||
@@ -50,7 +89,7 @@
|
||||
<div class="card-header"><i class="fas fa-newspaper me-2"></i>趨勢記錄</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<thead class="trends-table-head">
|
||||
<tr>
|
||||
<th>日期</th>
|
||||
<th>來源</th>
|
||||
@@ -101,7 +140,7 @@ async function loadTrends() {
|
||||
|
||||
const recordsBody = document.getElementById('recordsBody');
|
||||
if (!records.success || !records.data.length) {
|
||||
recordsBody.innerHTML = '<tr><td colspan="5" class="text-center text-muted py-4">尚無趨勢資料</td></tr>';
|
||||
recordsBody.innerHTML = '<tr><td colspan="5" class="trends-empty">尚無趨勢資料</td></tr>';
|
||||
} else {
|
||||
recordsBody.innerHTML = records.data.map(item => `
|
||||
<tr>
|
||||
@@ -116,12 +155,12 @@ async function loadTrends() {
|
||||
|
||||
const keywordsBox = document.getElementById('keywordsBox');
|
||||
if (!keywords.success || !keywords.data.length) {
|
||||
keywordsBox.innerHTML = '<div class="text-muted text-center py-4">尚無關鍵字</div>';
|
||||
keywordsBox.innerHTML = '<div class="trends-empty">尚無關鍵字</div>';
|
||||
} else {
|
||||
keywordsBox.innerHTML = keywords.data.map(item => `
|
||||
<div class="d-flex justify-content-between align-items-center border-bottom py-2">
|
||||
<span>${escapeHtml(item.keyword)}</span>
|
||||
<span class="badge text-bg-primary">${escapeHtml(item.total_mentions || 0)}</span>
|
||||
<span class="trends-count-badge">${escapeHtml(item.total_mentions || 0)}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user