diff --git a/config.py b/config.py index c6c1538..51c04f4 100644 --- a/config.py +++ b/config.py @@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.170" +SYSTEM_VERSION = "V10.171" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/templates/dashboard_v2.html b/templates/dashboard_v2.html index 7284ed5..2f94f75 100644 --- a/templates/dashboard_v2.html +++ b/templates/dashboard_v2.html @@ -187,7 +187,7 @@
- {% for cat in categories %} @@ -209,10 +209,10 @@ 下架
- - @@ -222,7 +222,7 @@
- 04 + 04 {{ 'AI 挑品清單' if current_filter == 'ai_picks' else '商品列表' }} {% if current_filter == 'ai_picks' %} @@ -389,7 +389,7 @@
match {{ (competitor.match_score * 100) | round(0) | int }}%
{% endif %} {% else %} - 待比對 + 待比對 {% endif %} @@ -430,7 +430,7 @@ {% endif %}
{% else %} - 尚無建議理由 + 尚無建議理由 {% endif %} {% endif %} @@ -440,7 +440,7 @@ {% elif item.yesterday_diff < 0 %} ▼ -{{ item.yesterday_diff | abs | int | number_format }} {% else %} - -- + -- {% endif %} @@ -450,13 +450,13 @@ {% elif week_diff < 0 %} -{{ week_diff | abs | int | number_format }} {% else %} - -- + -- {% endif %} - + {{ item.record.timestamp.strftime('%m-%d %H:%M') if item.record.timestamp else '--' }} - + {{ item.safe_created_at.strftime('%m-%d %H:%M') if item.safe_created_at else '--' }} @@ -520,5 +520,6 @@ {% endblock %} {% block extra_js %} + {% endblock %} diff --git a/web/static/css/page-dashboard-v2.css b/web/static/css/page-dashboard-v2.css index fa25cc5..869a866 100644 --- a/web/static/css/page-dashboard-v2.css +++ b/web/static/css/page-dashboard-v2.css @@ -389,6 +389,13 @@ flex-wrap: wrap; } + .dashboard-section-index { + color: var(--momo-text-tertiary); + font-size: 11px; + font-weight: 800; + letter-spacing: 0.08em; + } + .dashboard-table-title { color: var(--momo-text-primary); font-size: 14px; @@ -596,6 +603,14 @@ font-size: 10px; } + .dashboard-muted { + color: var(--momo-text-tertiary); + } + + .dashboard-table-time { + color: var(--momo-text-secondary); + } + .dashboard-pchome-price { color: var(--momo-accent-strong); font-size: 16px; diff --git a/web/static/js/page-dashboard-v2.js b/web/static/js/page-dashboard-v2.js index 2f5cd1e..7af936b 100644 --- a/web/static/js/page-dashboard-v2.js +++ b/web/static/js/page-dashboard-v2.js @@ -30,17 +30,27 @@ let priceChartInstance = null; } function ensureDashboardChart() { + if (window.EwoooCChartTheme && window.EwoooCChartTheme.loadChartJs) { + return window.EwoooCChartTheme.loadChartJs(); + } if (typeof Chart !== 'undefined') { - return Promise.resolve(); + return Promise.resolve(window.Chart); } if (dashboardChartLoader) { return dashboardChartLoader; } dashboardChartLoader = new Promise((resolve, reject) => { + const existing = document.querySelector('script[data-chartjs-loader="dashboard"]'); + if (existing) { + existing.addEventListener('load', () => resolve(window.Chart), { once: true }); + existing.addEventListener('error', () => reject(new Error('Chart.js 載入失敗')), { once: true }); + return; + } const script = document.createElement('script'); - script.src = 'https://cdn.jsdelivr.net/npm/chart.js'; + script.src = 'https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js'; script.async = true; - script.onload = resolve; + script.dataset.chartjsLoader = 'dashboard'; + script.onload = () => resolve(window.Chart); script.onerror = () => reject(new Error('Chart.js 載入失敗')); document.head.appendChild(script); }); @@ -235,29 +245,40 @@ let priceChartInstance = null; }); }); - function triggerTask() { - if (confirm('確定要手動執行全站爬蟲嗎?可能需要一段時間。')) { - fetch('/api/run_task', { - method: 'POST', - headers: { 'X-CSRFToken': getCSRFToken() } - }) - .then(response => response.json()) - .then(data => alert(data.message)) - .catch(error => alert('錯誤: ' + error)); + document.querySelectorAll('[data-dashboard-auto-submit]').forEach(select => { + select.addEventListener('change', () => { + if (select.form) { + select.form.submit(); + } + }); + }); + + const dashboardTaskMap = { + crawler: { + confirmText: '確定要手動執行全站爬蟲嗎?可能需要一段時間。', + url: '/api/run_task' + }, + notification: { + confirmText: '確定要發送今日商品異動通知嗎?', + url: '/api/trigger_momo_notification' } + }; + + function runDashboardTask(taskName) { + const task = dashboardTaskMap[taskName]; + if (!task || !confirm(task.confirmText)) return; + fetch(task.url, { + method: 'POST', + headers: { 'X-CSRFToken': getCSRFToken() } + }) + .then(response => response.json()) + .then(data => alert(data.message)) + .catch(error => alert('錯誤: ' + error)); } - function triggerNotification() { - if (confirm('確定要發送今日商品異動通知嗎?')) { - fetch('/api/trigger_momo_notification', { - method: 'POST', - headers: { 'X-CSRFToken': getCSRFToken() } - }) - .then(response => response.json()) - .then(data => alert(data.message)) - .catch(error => alert('錯誤: ' + error)); - } - } + document.querySelectorAll('[data-dashboard-task]').forEach(button => { + button.addEventListener('click', () => runDashboardTask(button.dataset.dashboardTask)); + }); function trackMomoLinkClick(event) { const link = event.target.closest('.momo-tracked-link');