888 lines
47 KiB
HTML
888 lines
47 KiB
HTML
{% extends 'ewoooc_base.html' %}
|
||
{% block title %}業績分析 - EwoooC{% endblock %}
|
||
|
||
{#
|
||
v3 改寫重點:
|
||
- 從 inline <style> 519 行 → 抽出到 page-sales-analysis.css
|
||
- 移除控制面板 header 的紫色漸層背景
|
||
- 移除引導訊息區的 #0d6efd 藍色佔比硬編碼
|
||
- 6 個 KPI 卡:bg-primary / bg-danger / bg-success / bg-info / bg-warning / #20c997
|
||
→ 5 個語意 KPI 變體(.kpi--revenue / .kpi--cost / .kpi--profit / .kpi--qty / .kpi--sku)
|
||
- ABC 三類卡:text-danger / text-warning / text-success → P0/P1/P2 token
|
||
- YoY 成長率:#e8f5e9 綠底硬編 → 用 olive accent token
|
||
- Marketing chart titles:text-primary 藍 / text-success 綠 → page-accent / olive
|
||
#}
|
||
|
||
{% block extra_css %}
|
||
<link rel="stylesheet" href="https://cdn.datatables.net/1.11.5/css/dataTables.bootstrap5.min.css">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css">
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='css/page-sales-analysis.css') }}">
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='css/page-sales-analysis-bem.css') }}">
|
||
{% endblock %}
|
||
|
||
{% block ewooo_content %}
|
||
|
||
<!-- ============ Loading Overlay (warm reskin) ============ -->
|
||
<div id="loadingOverlay" data-screen-label="loading">
|
||
<div class="loading-logo-container">
|
||
<div class="logo-pulse"></div>
|
||
<div class="logo-ring"></div>
|
||
<div class="logo-ring-inner"></div>
|
||
<div class="orbit-particles">
|
||
<div class="orbit-particle"></div>
|
||
<div class="orbit-particle"></div>
|
||
<div class="orbit-particle"></div>
|
||
<div class="orbit-particle"></div>
|
||
</div>
|
||
<div class="sparkles">
|
||
{% for _ in range(6) %}<div class="sparkle"></div>{% endfor %}
|
||
</div>
|
||
<div class="loading-logo">EwoooC</div>
|
||
</div>
|
||
<div class="loading-text" id="loadingText">
|
||
<i class="fas fa-chart-bar me-2" aria-hidden="true"></i>正在載入數據...
|
||
</div>
|
||
<div class="loading-progress"><div class="loading-progress-bar"></div></div>
|
||
<div class="loading-hint" id="loadingHint">大量資料可能需要較長時間,請稍候</div>
|
||
</div>
|
||
|
||
<div class="sales-analysis-page" data-page-group="analytics" data-screen-label="sales-analysis">
|
||
{% include 'components/_analysis_report_tabs.html' %}
|
||
|
||
<!-- ============ Page header ============ -->
|
||
<header class="sa-page-head">
|
||
<div class="sa-page-head__lead">
|
||
<h4 class="sa-page-head__title">
|
||
<i class="fas fa-chart-pie" aria-hidden="true"></i>
|
||
業績分析儀表板
|
||
</h4>
|
||
<p class="sa-page-head__brief">用分類、品牌與毛利找出 PChome 成長槓桿。</p>
|
||
{% if db_data_range %}
|
||
<span class="sa-tag sa-tag--neutral">
|
||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||
資料期間:{{ db_data_range }}
|
||
</span>
|
||
{% endif %}
|
||
</div>
|
||
{% if not no_filter %}
|
||
<div class="sa-page-head__meta">
|
||
<span class="sa-tag sa-tag--accent">
|
||
<i class="fas fa-database" aria-hidden="true"></i>
|
||
{% if start_date or end_date %}
|
||
{% if start_date and end_date %}{{ start_date }} ~ {{ end_date }}
|
||
{% elif start_date %}{{ start_date }} 起
|
||
{% else %}{{ end_date }} 止{% endif %}
|
||
{% elif data_range_months == 0 %}全部資料
|
||
{% else %}最近 {{ data_range_months }} 個月{% endif %}
|
||
<span class="sa-tag__sep">·</span>
|
||
{{ "{:,}".format(total_records) }} 筆
|
||
</span>
|
||
</div>
|
||
{% endif %}
|
||
</header>
|
||
|
||
{% if error %}
|
||
<div class="alert alert-warning sa-alert">
|
||
<i class="fas fa-exclamation-triangle me-2" aria-hidden="true"></i>{{ error }}
|
||
<div class="mt-2">
|
||
<a href="/settings" class="btn btn-sm btn-outline-dark">前往匯入資料</a>
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
|
||
<!-- ============ 控制面板 (篩選器) ============ -->
|
||
<section class="card sa-filter-card sa-filter-card--sticky">
|
||
<div class="card-header sa-filter-head">
|
||
<h5 class="sa-filter-head__title">
|
||
<i class="fas fa-sliders-h" aria-hidden="true"></i>進階篩選與分析
|
||
</h5>
|
||
<div class="btn-group sa-metric-switch" role="group" aria-label="分析維度切換">
|
||
{% set metric_opts = [
|
||
{'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'}) %}
|
||
{% endif %}
|
||
{% for m in metric_opts %}
|
||
<input type="radio" class="btn-check" name="metric" id="metric{{ m.v|capitalize }}"
|
||
value="{{ m.v }}" autocomplete="off"
|
||
onchange="setFilter('metric', '{{ m.v }}')"
|
||
{% if selected_metric == m.v %}checked{% endif %}>
|
||
<label class="btn btn-sm sa-metric-switch__btn" for="metric{{ m.v|capitalize }}">
|
||
<i class="fas fa-{{ m.icon }} me-1" aria-hidden="true"></i>{{ m.label }}
|
||
</label>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card-body sa-filter-body">
|
||
<form method="GET" action="/sales_analysis" class="row g-3">
|
||
<input type="hidden" name="metric" value="{{ selected_metric }}">
|
||
|
||
<!-- ─── 第一區:資料範圍 ─── -->
|
||
<div class="col-12">
|
||
<div class="sa-filter-group">
|
||
<h6 class="sa-filter-group__title">
|
||
<i class="fas fa-calendar-check sa-filter-group__icon sa-filter-group__icon--accent" aria-hidden="true"></i>資料範圍設定
|
||
</h6>
|
||
<div class="row g-2">
|
||
<div class="col-md-3">
|
||
<label class="form-label">
|
||
<i class="fas fa-database me-1" aria-hidden="true"></i>資料載入範圍
|
||
</label>
|
||
<select name="data_range" class="form-select" onchange="handleDataRangeChange(this)">
|
||
<option value="" {% if not request.args.get('data_range') %}selected{% endif %}>-- 請選擇 --</option>
|
||
{% for opt in [(1,'最近 1 個月 (推薦)'), (3,'最近 3 個月'), (6,'最近 6 個月'), (12,'最近 12 個月'), (0,'全部資料')] %}
|
||
<option value="{{ opt.0 }}" {% if request.args.get('data_range') == opt.0|string %}selected{% endif %}>{{ opt.1 }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">
|
||
<i class="fas fa-calendar-alt me-1" aria-hidden="true"></i>開始日期
|
||
<small class="text-muted">(選填)</small>
|
||
</label>
|
||
<input type="text" id="start_date" name="start_date"
|
||
class="form-control flatpickr-input"
|
||
placeholder="選擇開始日期"
|
||
value="{{ request.args.get('start_date', '') }}"
|
||
readonly="readonly">
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">
|
||
<i class="fas fa-calendar-check me-1" aria-hidden="true"></i>結束日期
|
||
<small class="text-muted">(選填)</small>
|
||
</label>
|
||
<div class="input-group">
|
||
<input type="text" id="end_date" name="end_date"
|
||
class="form-control flatpickr-input"
|
||
placeholder="選擇結束日期"
|
||
value="{{ request.args.get('end_date', '') }}"
|
||
readonly="readonly">
|
||
{% if request.args.get('start_date') or request.args.get('end_date') %}
|
||
<button type="button" class="btn btn-outline-danger" onclick="clearDateRange()" title="清除日期範圍">
|
||
<i class="fas fa-times" aria-hidden="true"></i>
|
||
</button>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3 d-flex align-items-end gap-2">
|
||
<a href="/sales_analysis" class="btn btn-outline-secondary flex-fill">
|
||
<i class="fas fa-redo me-1" aria-hidden="true"></i>重置
|
||
</a>
|
||
<button type="submit" class="btn btn-primary flex-fill">
|
||
<i class="fas fa-search me-1" aria-hidden="true"></i>查詢
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 第二區:商品屬性 ─── -->
|
||
<div class="col-12">
|
||
<div class="sa-filter-group">
|
||
<h6 class="sa-filter-group__title">
|
||
<i class="fas fa-tags sa-filter-group__icon sa-filter-group__icon--info" aria-hidden="true"></i>商品屬性篩選
|
||
</h6>
|
||
<div class="row g-2">
|
||
{% set attr_filters = [
|
||
{'key':'category', 'label':'商品分類', 'icon':'layer-group', 'all_label':'全部分類', 'options': all_categories, 'selected': selected_category, 'show': true},
|
||
{'key':'brand', 'label':'品牌', 'icon':'tag', 'all_label':'全部品牌', 'options': all_brands, 'selected': selected_brand, 'show': all_brands},
|
||
{'key':'vendor', 'label':'供應商', 'icon':'truck', 'all_label':'全部廠商', 'options': all_vendors, 'selected': selected_vendor, 'show': all_vendors}
|
||
] %}
|
||
{% for f in attr_filters %}
|
||
{% if f.show %}
|
||
<div class="col-md-4">
|
||
<label class="form-label"><i class="fas fa-{{ f.icon }} me-1" aria-hidden="true"></i>{{ f.label }}</label>
|
||
<div class="btn-group w-100 sa-dropdown-filter">
|
||
<button type="button" class="btn btn-outline-secondary dropdown-toggle w-100 text-start text-truncate" data-bs-toggle="dropdown" aria-expanded="false">
|
||
<i class="fas fa-{{ f.icon }} me-2 text-muted" aria-hidden="true"></i>
|
||
{{ f.selected if f.selected != 'all' else f.all_label }}
|
||
</button>
|
||
<ul class="dropdown-menu w-100 sa-dropdown-menu">
|
||
<li class="px-2 py-1">
|
||
<input type="text" class="form-control form-control-sm" placeholder="搜尋..." onkeyup="filterDropdown(this)">
|
||
</li>
|
||
<li><a class="dropdown-item" href="javascript:setFilter('{{ f.key }}', 'all')">{{ f.all_label }}</a></li>
|
||
<li><hr class="dropdown-divider"></li>
|
||
{% for opt in f.options %}
|
||
<li><a class="dropdown-item" href="javascript:setFilter('{{ f.key }}', '{{ opt }}')">{{ opt }}</a></li>
|
||
{% endfor %}
|
||
</ul>
|
||
<input type="hidden" name="{{ f.key }}" value="{{ f.selected }}">
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 第三區:行銷與付款 ─── -->
|
||
{% if all_activities or all_payments %}
|
||
<div class="col-12">
|
||
<div class="sa-filter-group">
|
||
<h6 class="sa-filter-group__title">
|
||
<i class="fas fa-bullhorn sa-filter-group__icon sa-filter-group__icon--warning" aria-hidden="true"></i>行銷與付款
|
||
</h6>
|
||
<div class="row g-2">
|
||
{% set mkt_filters = [
|
||
{'key':'activity', 'label':'行銷活動', 'icon':'bullhorn', 'all_label':'全部活動', 'options': all_activities, 'selected': selected_activity, 'show': all_activities, 'col':'col-md-6'},
|
||
{'key':'payment', 'label':'付款方式', 'icon':'credit-card', 'all_label':'全部付款方式', 'options': all_payments, 'selected': selected_payment, 'show': all_payments, 'col':'col-md-6'}
|
||
] %}
|
||
{% for f in mkt_filters %}
|
||
{% if f.show %}
|
||
<div class="{{ f.col }}">
|
||
<label class="form-label"><i class="fas fa-{{ f.icon }} me-1" aria-hidden="true"></i>{{ f.label }}</label>
|
||
<div class="btn-group w-100 sa-dropdown-filter">
|
||
<button type="button" class="btn btn-outline-secondary dropdown-toggle w-100 text-start text-truncate" data-bs-toggle="dropdown" aria-expanded="false">
|
||
<i class="fas fa-{{ f.icon }} me-2 text-muted" aria-hidden="true"></i>
|
||
{{ f.selected if f.selected != 'all' else f.all_label }}
|
||
</button>
|
||
<ul class="dropdown-menu w-100 sa-dropdown-menu">
|
||
<li class="px-2 py-1">
|
||
<input type="text" class="form-control form-control-sm" placeholder="搜尋..." onkeyup="filterDropdown(this)">
|
||
</li>
|
||
<li><a class="dropdown-item" href="javascript:setFilter('{{ f.key }}', 'all')">{{ f.all_label }}</a></li>
|
||
<li><hr class="dropdown-divider"></li>
|
||
{% for opt in f.options %}
|
||
<li><a class="dropdown-item" href="javascript:setFilter('{{ f.key }}', '{{ opt }}')">{{ opt }}</a></li>
|
||
{% endfor %}
|
||
</ul>
|
||
<input type="hidden" name="{{ f.key }}" value="{{ f.selected }}">
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- ─── 第四區:時間維度 ─── -->
|
||
{% if cols.date %}
|
||
<div class="col-12">
|
||
<div class="sa-filter-group">
|
||
<h6 class="sa-filter-group__title">
|
||
<i class="fas fa-clock sa-filter-group__icon sa-filter-group__icon--danger" aria-hidden="true"></i>時間維度篩選
|
||
</h6>
|
||
<div class="row g-2">
|
||
<!-- 月份 -->
|
||
<div class="col-md-4">
|
||
<label class="form-label"><i class="fas fa-calendar-alt me-1" aria-hidden="true"></i>月份</label>
|
||
<div class="btn-group w-100 sa-dropdown-filter">
|
||
<button type="button" class="btn btn-outline-secondary dropdown-toggle w-100 text-start text-truncate" data-bs-toggle="dropdown" aria-expanded="false">
|
||
<i class="fas fa-calendar-alt me-2 text-muted" aria-hidden="true"></i>
|
||
{{ selected_month if selected_month != 'all' else '全部月份' }}
|
||
</button>
|
||
<ul class="dropdown-menu w-100 sa-dropdown-menu">
|
||
<li><a class="dropdown-item" href="javascript:setFilter('month', 'all')">全部月份</a></li>
|
||
<li><hr class="dropdown-divider"></li>
|
||
{% for m in all_months %}
|
||
<li><a class="dropdown-item" href="javascript:setFilter('month', '{{ m }}')">{{ m }}</a></li>
|
||
{% endfor %}
|
||
</ul>
|
||
<input type="hidden" name="month" value="{{ selected_month }}">
|
||
</div>
|
||
</div>
|
||
<!-- 星期 -->
|
||
{% set dow_labels = {'all':'全部星期','0':'週一','1':'週二','2':'週三','3':'週四','4':'週五','5':'週六','6':'週日'} %}
|
||
<div class="col-md-4">
|
||
<label class="form-label"><i class="fas fa-calendar-day me-1" aria-hidden="true"></i>星期</label>
|
||
<div class="btn-group w-100 sa-dropdown-filter">
|
||
<button type="button" class="btn btn-outline-secondary dropdown-toggle w-100 text-start text-truncate" data-bs-toggle="dropdown" aria-expanded="false">
|
||
<i class="fas fa-calendar-day me-2 text-muted" aria-hidden="true"></i>
|
||
{{ dow_labels[selected_dow|string] }}
|
||
</button>
|
||
<ul class="dropdown-menu w-100 sa-dropdown-menu">
|
||
{% for k, label in [('all','全部星期'),('0','週一'),('1','週二'),('2','週三'),('3','週四'),('4','週五'),('5','週六'),('6','週日')] %}
|
||
<li><a class="dropdown-item" href="javascript:setFilter('dow', '{{ k }}')">{{ label }}</a></li>
|
||
{% if loop.first %}<li><hr class="dropdown-divider"></li>{% endif %}
|
||
{% endfor %}
|
||
</ul>
|
||
<input type="hidden" name="dow" value="{{ selected_dow }}">
|
||
</div>
|
||
</div>
|
||
<!-- 時段 -->
|
||
<div class="col-md-4">
|
||
<label class="form-label"><i class="fas fa-clock me-1" aria-hidden="true"></i>時段</label>
|
||
<div class="btn-group w-100 sa-dropdown-filter">
|
||
<button type="button" class="btn btn-outline-secondary dropdown-toggle w-100 text-start text-truncate" data-bs-toggle="dropdown" aria-expanded="false">
|
||
<i class="fas fa-clock me-2 text-muted" aria-hidden="true"></i>
|
||
{{ selected_hour + ':00' if selected_hour != 'all' else '全部時段' }}
|
||
</button>
|
||
<ul class="dropdown-menu w-100 sa-dropdown-menu">
|
||
<li><a class="dropdown-item" href="javascript:setFilter('hour', 'all')">全部時段</a></li>
|
||
<li><hr class="dropdown-divider"></li>
|
||
{% for h in range(24) %}
|
||
<li><a class="dropdown-item" href="javascript:setFilter('hour', '{{ h }}')">{{ "%02d"|format(h) }}:00</a></li>
|
||
{% endfor %}
|
||
</ul>
|
||
<input type="hidden" name="hour" value="{{ selected_hour }}">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- ─── 第五區:數值與關鍵字 ─── -->
|
||
<div class="col-12">
|
||
<div class="sa-filter-group">
|
||
<h6 class="sa-filter-group__title">
|
||
<i class="fas fa-search sa-filter-group__icon sa-filter-group__icon--success" aria-hidden="true"></i>數值與關鍵字篩選
|
||
</h6>
|
||
<div class="row g-2">
|
||
<div class="col-md-4">
|
||
<label class="form-label"><i class="fas fa-dollar-sign me-1" aria-hidden="true"></i>單價區間 ($)</label>
|
||
<div class="input-group">
|
||
<input type="number" name="min_price" class="form-control" placeholder="Min" value="{{ min_price }}">
|
||
<span class="input-group-text">~</span>
|
||
<input type="number" name="max_price" class="form-control" placeholder="Max" value="{{ max_price }}">
|
||
</div>
|
||
</div>
|
||
{% if cols.cost or cols.profit %}
|
||
<div class="col-md-4">
|
||
<label class="form-label"><i class="fas fa-percentage me-1" aria-hidden="true"></i>毛利率區間 (%)</label>
|
||
<div class="input-group">
|
||
<input type="number" name="min_margin" class="form-control" placeholder="Min" value="{{ min_margin }}">
|
||
<span class="input-group-text">~</span>
|
||
<input type="number" name="max_margin" class="form-control" placeholder="Max" value="{{ max_margin }}">
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
<div class="col-md-4">
|
||
<label class="form-label"><i class="fas fa-search me-1" aria-hidden="true"></i>關鍵字搜尋</label>
|
||
<div class="input-group">
|
||
<span class="input-group-text"><i class="fas fa-search" aria-hidden="true"></i></span>
|
||
<input type="text" name="keyword" class="form-control" placeholder="輸入商品名稱..." value="{{ keyword }}">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ 引導訊息(首次進入) ============ -->
|
||
{% if no_filter %}
|
||
<section class="sa-empty">
|
||
<div class="sa-empty__inner">
|
||
<div class="sa-empty__icon">
|
||
<i class="fas fa-chart-line" aria-hidden="true"></i>
|
||
</div>
|
||
<h3 class="sa-empty__title">
|
||
<i class="fas fa-hand-point-up me-2" aria-hidden="true"></i>開始分析您的業績數據
|
||
</h3>
|
||
<p class="sa-empty__lead">
|
||
請在上方<strong>「進階篩選」</strong>區域選擇以下任一條件開始分析:
|
||
</p>
|
||
<div class="row g-3 sa-empty__hints">
|
||
<div class="col-md-6">
|
||
<div class="sa-empty__hint sa-empty__hint--accent">
|
||
<h5><i class="fas fa-database me-2" aria-hidden="true"></i>資料載入範圍</h5>
|
||
<p>快速選擇最近 1/3/6/12 個月或全部資料</p>
|
||
<p class="sa-empty__hint-foot"><i class="fas fa-star me-1" aria-hidden="true"></i>推薦新手使用</p>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="sa-empty__hint sa-empty__hint--olive">
|
||
<h5><i class="fas fa-calendar-alt me-2" aria-hidden="true"></i>自訂日期區間</h5>
|
||
<p>鎖定活動或檔期,評估業績變化</p>
|
||
<p class="sa-empty__hint-foot"><i class="fas fa-clock me-1" aria-hidden="true"></i>適合檔期與活動回顧</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert alert-info sa-empty__tip">
|
||
<i class="fas fa-info-circle me-2" aria-hidden="true"></i>
|
||
<strong>分析下一步:</strong>選擇條件後,直接看影響業績的圖表、分類與商品明細。
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{% else %}
|
||
|
||
<!-- ============ KPI 區塊(v3:語意 variant 取代 Bootstrap bg-*) ============ -->
|
||
{% 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}
|
||
] %}
|
||
<div class="row mb-4 sa-kpi-row">
|
||
{% for k in kpi_cards %}{% if k.show %}
|
||
<div class="col-md-2">
|
||
<article class="card kpi-card sa-kpi sa-kpi--{{ k.variant }}">
|
||
<div class="card-body p-3">
|
||
<div class="kpi-label">{{ k.label }}</div>
|
||
<div class="kpi-value">{{ k.value }}</div>
|
||
<i class="fas fa-{{ k.icon }} icon-bg" aria-hidden="true"></i>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
{% endif %}{% endfor %}
|
||
</div>
|
||
|
||
<!-- ============ ABC 分析 (Pareto) ============ -->
|
||
{% if abc_stats %}
|
||
<section class="row mb-4">
|
||
<div class="col-12">
|
||
<div class="card sa-panel sa-panel--abc">
|
||
<div class="card-body py-3">
|
||
<div class="d-flex align-items-center justify-content-between">
|
||
<h6 class="sa-panel__title">
|
||
<i class="fas fa-sort-amount-down me-2" aria-hidden="true"></i>ABC 分析 (80/20 法則)
|
||
</h6>
|
||
<span class="sa-tag sa-tag--neutral">選類別看貢獻</span>
|
||
</div>
|
||
<div class="row mt-3 sa-abc">
|
||
{% set abc_classes = [
|
||
{'k':'A','tier':'p0','title':'A 類 (核心)'},
|
||
{'k':'B','tier':'p1','title':'B 類 (次要)'},
|
||
{'k':'C','tier':'p2','title':'C 類 (長尾)'}
|
||
] %}
|
||
{% for c in abc_classes %}
|
||
<div class="col-md-4 sa-abc__cell sa-abc__cell--{{ c.tier }}"
|
||
onclick="window.open('/abc_analysis/detail?class={{ c.k }}&{{ request.query_string.decode() }}', '_blank')">
|
||
<h5 class="sa-abc__heading">{{ c.title }}</h5>
|
||
<div class="sa-abc__metric">營收佔比:{{ "{:.1f}%".format(abc_stats[c.k]['pct_rev']) }}</div>
|
||
<div class="sa-abc__sku">{{ abc_stats[c.k]['count'] }} SKU ({{ "{:.1f}%".format(abc_stats[c.k]['pct_sku']) }})</div>
|
||
<div class="sa-abc__bar">
|
||
<div class="sa-abc__bar-fill" style="width: {{ abc_stats[c.k]['pct_rev'] }}%"></div>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
{% endif %}
|
||
|
||
<!-- ============ 年度對比 (YoY) ============ -->
|
||
<section class="row mb-4">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-body">
|
||
<header class="sa-yoy-head">
|
||
<h6 class="sa-panel__title">
|
||
<i class="fas fa-chart-line me-2" aria-hidden="true"></i>年度對比 (YoY Comparison)
|
||
</h6>
|
||
<div class="sa-yoy-controls">
|
||
<select id="yoy-year1" class="form-select form-select-sm" aria-label="基準年">
|
||
<option value="2025" selected>2025</option>
|
||
<option value="2024">2024</option>
|
||
<option value="2023">2023</option>
|
||
</select>
|
||
<span class="sa-yoy-controls__sep">vs</span>
|
||
<select id="yoy-year2" class="form-select form-select-sm" aria-label="對比年">
|
||
<option value="2026" selected>2026</option>
|
||
<option value="2025">2025</option>
|
||
<option value="2024">2024</option>
|
||
</select>
|
||
<select id="yoy-metric" class="form-select form-select-sm" aria-label="指標">
|
||
<option value="revenue" selected>銷售金額</option>
|
||
<option value="qty">銷售數量</option>
|
||
<option value="profit">毛利金額</option>
|
||
</select>
|
||
<button class="btn btn-sm btn-outline-primary" onclick="loadYoYData()" title="重新載入">
|
||
<i class="fas fa-sync-alt" aria-hidden="true"></i>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="row mb-3 sa-yoy-summary" id="yoy-summary">
|
||
<div class="col-md-4">
|
||
<div class="card sa-yoy-card">
|
||
<div class="card-body text-center py-3">
|
||
<small id="yoy-year1-label">2024年</small>
|
||
<h4 class="sa-yoy-card__value sa-yoy-card__value--neutral" id="yoy-year1-value">$0</h4>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<div class="card sa-yoy-card">
|
||
<div class="card-body text-center py-3">
|
||
<small id="yoy-year2-label">2025年</small>
|
||
<h4 class="sa-yoy-card__value sa-yoy-card__value--accent" id="yoy-year2-value">$0</h4>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<div class="card sa-yoy-card sa-yoy-card--growth" id="yoy-growth-card">
|
||
<div class="card-body text-center py-3">
|
||
<small>成長率</small>
|
||
<h4 class="sa-yoy-card__value" id="yoy-growth-value">
|
||
<i class="fas fa-arrow-up" aria-hidden="true"></i> 0%
|
||
</h4>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sa-chart-shell" style="height: 250px;">
|
||
<canvas id="yoy-chart"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ 廠商獲利能力排行 ============ -->
|
||
{% if vendor_stats %}
|
||
<section class="row mb-4">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<span><i class="fas fa-industry me-2" aria-hidden="true"></i>廠商獲利能力排行 (Top 100)</span>
|
||
<a href="/api/export/excel/vendor?{{ request.query_string.decode() }}" class="btn btn-sm btn-outline-success">
|
||
<i class="fas fa-file-excel me-1" aria-hidden="true"></i>匯出報表
|
||
</a>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="table-responsive sa-vendor-table">
|
||
<table class="table table-hover table-sm align-middle mb-0">
|
||
<thead>
|
||
<tr>
|
||
<th class="text-center" style="width: 60px;">排名</th>
|
||
<th>廠商名稱</th>
|
||
<th class="text-end">總業績</th>
|
||
<th class="text-end">佔比 (%)</th>
|
||
<th class="text-end">總銷量</th>
|
||
<th class="text-end">平均客單 (ASP)</th>
|
||
{% if cols.cost or cols.profit %}<th class="text-end">毛利額</th>{% endif %}
|
||
{% if cols.cost or cols.profit %}<th class="text-end">毛利率</th>{% endif %}
|
||
<th class="text-end">商品數 (SKU)</th>
|
||
<th class="text-end">平均單品產值</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for v in vendor_stats %}
|
||
<tr>
|
||
<td class="text-center text-muted">{{ loop.index }}</td>
|
||
<td class="fw-bold">
|
||
<a href="javascript:setFilter('vendor', '{{ v.name }}')" class="sa-vendor-link" title="篩選此廠商商品">
|
||
{{ v.name }}
|
||
<i class="fas fa-filter ms-1 sa-vendor-link__icon" aria-hidden="true"></i>
|
||
</a>
|
||
</td>
|
||
<td class="text-end">${{ "{:,.0f}".format(v.revenue) }}</td>
|
||
<td class="text-end small text-muted">{{ "{:.1f}%".format(v.share) }}</td>
|
||
<td class="text-end">{{ "{:,.0f}".format(v.qty) }}</td>
|
||
<td class="text-end">${{ "{:,.0f}".format(v.asp) }}</td>
|
||
{% if cols.cost or cols.profit %}
|
||
<td class="text-end sa-vendor-table__profit">${{ "{:,.0f}".format(v.profit) }}</td>
|
||
<td class="text-end">
|
||
{% set tier = 'p2' if v.margin_rate >= 30 else ('p1' if v.margin_rate >= 15 else 'p0') %}
|
||
<span class="sa-margin-badge sa-margin-badge--{{ tier }}">{{ "{:.1f}%".format(v.margin_rate) }}</span>
|
||
</td>
|
||
{% endif %}
|
||
<td class="text-end">{{ v.sku_count }}</td>
|
||
<td class="text-end">${{ "{:,.0f}".format(v.revenue / v.sku_count if v.sku_count > 0 else 0) }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
{% endif %}
|
||
|
||
<!-- ============ 商業洞察 (Business Insights) ============ -->
|
||
<section class="row mb-4">
|
||
<div class="col-12">
|
||
<div class="card sa-insights">
|
||
<div class="card-header sa-insights__head">
|
||
<i class="fas fa-lightbulb me-2" aria-hidden="true"></i>商業洞察 (Top 3 Highlights)
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row sa-insights__row">
|
||
<!-- 業績 Top 3 -->
|
||
<div class="col-md-4 sa-insights__col">
|
||
<div class="sa-insights__col-head">
|
||
<h6 class="sa-insights__col-title sa-insights__col-title--accent">🏆 業績貢獻王 (Revenue)</h6>
|
||
<button class="btn btn-sm btn-outline-primary" onclick="showTopDetail('revenue', 'amount')">
|
||
<i class="fas fa-list me-1" aria-hidden="true"></i>詳細
|
||
</button>
|
||
</div>
|
||
<small class="sa-insights__sub">分類 Top 3:</small>
|
||
<ul class="sa-insights__list">
|
||
{% for item in insights.rev_cats %}
|
||
<li><span class="sa-insights__rank sa-insights__rank--accent">{{ loop.index }}</span>{{ item.name }}<span class="sa-insights__value">${{ "{:,.0f}".format(item.value) }}</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
<small class="sa-insights__sub">商品 Top 3:</small>
|
||
<ul class="sa-insights__list">
|
||
{% for item in insights.rev_prods %}
|
||
<li class="text-truncate"><span class="sa-insights__rank sa-insights__rank--accent">{{ loop.index }}</span>{{ item.name }}<span class="sa-insights__value">${{ "{:,.0f}".format(item.value) }}</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- 毛利 Top 3 -->
|
||
{% if cols.cost or cols.profit %}
|
||
<div class="col-md-4 sa-insights__col">
|
||
<div class="sa-insights__col-head">
|
||
<h6 class="sa-insights__col-title sa-insights__col-title--olive">💰 獲利金雞母 (Gross Margin)</h6>
|
||
<button class="btn btn-sm btn-outline-success" onclick="showTopDetail('margin', 'profit')">
|
||
<i class="fas fa-list me-1" aria-hidden="true"></i>詳細
|
||
</button>
|
||
</div>
|
||
<small class="sa-insights__sub">分類 Top 3:</small>
|
||
<ul class="sa-insights__list">
|
||
{% for item in insights.margin_cats %}
|
||
<li><span class="sa-insights__rank sa-insights__rank--olive">{{ loop.index }}</span>{{ item.name }}<span class="sa-insights__value">${{ "{:,.0f}".format(item.value) }}</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
<small class="sa-insights__sub">商品 Top 3:</small>
|
||
<ul class="sa-insights__list">
|
||
{% for item in insights.margin_prods %}
|
||
<li class="text-truncate"><span class="sa-insights__rank sa-insights__rank--olive">{{ loop.index }}</span>{{ item.name }}<span class="sa-insights__value">${{ "{:,.0f}".format(item.value) }}</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
{% else %}
|
||
<div class="col-md-4 sa-insights__col sa-insights__col--empty">
|
||
<i class="fas fa-info-circle me-2" aria-hidden="true"></i>無成本/毛利資料
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- 銷量 Top 3 -->
|
||
<div class="col-md-4 sa-insights__col sa-insights__col--last">
|
||
<div class="sa-insights__col-head">
|
||
<h6 class="sa-insights__col-title sa-insights__col-title--mahogany">📦 人氣引流款 (Sales Qty)</h6>
|
||
<button class="btn btn-sm btn-outline-primary" onclick="showTopDetail('quantity', 'qty')">
|
||
<i class="fas fa-list me-1" aria-hidden="true"></i>詳細
|
||
</button>
|
||
</div>
|
||
<small class="sa-insights__sub">分類 Top 3:</small>
|
||
<ul class="sa-insights__list">
|
||
{% for item in insights.qty_cats %}
|
||
<li><span class="sa-insights__rank sa-insights__rank--mahogany">{{ loop.index }}</span>{{ item.name }}<span class="sa-insights__value">{{ "{:,.0f}".format(item.value) }}</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
<small class="sa-insights__sub">商品 Top 3:</small>
|
||
<ul class="sa-insights__list">
|
||
{% for item in insights.qty_prods %}
|
||
<li class="text-truncate"><span class="sa-insights__rank sa-insights__rank--mahogany">{{ loop.index }}</span>{{ item.name }}<span class="sa-insights__value">{{ "{:,.0f}".format(item.value) }}</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ 圖表區塊 ============ -->
|
||
{# Turn C:圖表 + DataTable,配色由 analysis-chart-theme.js 統一注入 #}
|
||
<div class="row">
|
||
<div class="col-lg-8">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-chart-bar me-2" aria-hidden="true"></i>Top 20 熱銷排行 ({{ '銷售金額' if selected_metric == 'amount' else '銷售數量' }})</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 600px;"><canvas id="barChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-4">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-chart-pie me-2" aria-hidden="true"></i>全站類別分佈 (Top 12)</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 400px;"><canvas id="categoryChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-4">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-th-large me-2" aria-hidden="true"></i>業績板塊分佈 (分類 → 商品)</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 400px;"><canvas id="treemapChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-4">
|
||
<div class="col-lg-6">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-chart-column me-2" aria-hidden="true"></i>價格帶業績貢獻 (Price Range)</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 350px;"><canvas id="priceDistChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-6">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-braille me-2" aria-hidden="true"></i>價格 vs 銷量分佈 (Scatter Plot)</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 350px;"><canvas id="scatterChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{% if cols.cost or cols.profit %}
|
||
<div class="row mt-4">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-chess-board me-2" aria-hidden="true"></i>商品策略 BCG 矩陣 (波士頓矩陣)</div>
|
||
<div class="card-body">
|
||
<p class="text-muted small mb-2"><i class="fas fa-info-circle me-1" aria-hidden="true"></i> X軸:銷量 (市場份額) | Y軸:毛利率 (獲利能力) | 十字線:中位數閾值</p>
|
||
<div class="sa-chart-shell" style="height: 500px;"><canvas id="bcgChart"></canvas></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if seasonality_data %}
|
||
<div class="row mt-4">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-sun me-2" aria-hidden="true"></i>淡旺季熱力圖 (Seasonality Heatmap) — Top 10 分類</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 400px;"><canvas id="seasonalityChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if marketing_data %}
|
||
<div class="row mt-4">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<span><i class="fas fa-bullhorn me-2" aria-hidden="true"></i>行銷活動業績貢獻 (Marketing Campaign Contribution)</span>
|
||
<button class="btn btn-sm btn-outline-success" onclick="exportMarketingExcel('all')">
|
||
<i class="fas fa-file-excel me-1" aria-hidden="true"></i>匯出全部
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row">
|
||
<div class="col-lg-6 mb-4">
|
||
<h6 class="sa-mkt-title sa-mkt-title--accent"><i class="fas fa-tags me-2" aria-hidden="true"></i>折扣活動排行 (Discount Campaigns)</h6>
|
||
<div class="sa-chart-shell" style="height: 350px;"><canvas id="mktDiscountChart"></canvas></div>
|
||
</div>
|
||
<div class="col-lg-6 mb-4">
|
||
<h6 class="sa-mkt-title sa-mkt-title--olive"><i class="fas fa-ticket-alt me-2" aria-hidden="true"></i>折價券活動排行 (Coupon Campaigns)</h6>
|
||
<div class="sa-chart-shell" style="height: 350px;"><canvas id="mktCouponChart"></canvas></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if cols.date %}
|
||
<div class="row mt-4">
|
||
<div class="col-lg-6">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-calendar-alt me-2" aria-hidden="true"></i>每月業績趨勢 (Monthly Trend)</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 300px;"><canvas id="monthlyChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-6">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-chart-line me-2" aria-hidden="true"></i>每週業績趨勢 (全年度)</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 300px;"><canvas id="weeklyChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-4">
|
||
<div class="col-lg-4">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-calendar-week me-2" aria-hidden="true"></i>每日業績趨勢 (週一至週日)</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 300px;"><canvas id="dowChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-4">
|
||
<div class="card">
|
||
<div class="card-header"><i class="fas fa-clock me-2" aria-hidden="true"></i>每小時業績熱點 (00:00 — 23:00)</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 300px;"><canvas id="hourlyChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-4">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<i class="fas fa-th me-2" aria-hidden="true"></i>多維度熱點 (星期 × 小時)
|
||
<i class="fas fa-info-circle text-muted ms-1" data-bs-toggle="tooltip" title="此圖表用於識別每週的「黃金銷售時段」。顏色越深代表該時段業績越好。" aria-hidden="true"></i>
|
||
</div>
|
||
<div class="card-body"><div class="sa-chart-shell" style="height: 300px;"><canvas id="heatmapChart"></canvas></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- ============ 詳細資料表 (DataTable) ============ -->
|
||
<div class="card mt-4">
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<span><i class="fas fa-list-ol me-2" aria-hidden="true"></i>詳細數據列表 (Top 1000)</span>
|
||
</div>
|
||
<div class="card-body">
|
||
<table id="dataTable" class="table table-hover align-middle mb-0" style="width:100%">
|
||
<thead>
|
||
<tr>
|
||
<th class="text-center" style="width: 60px;">排名</th>
|
||
<th style="width: 100px;">商品 ID</th>
|
||
<th style="width: 25%;">商品名稱</th>
|
||
{% if cols.brand %}<th>品牌</th>{% endif %}
|
||
{% if cols.vendor %}<th>廠商名稱</th>{% endif %}
|
||
{% if cols.cat %}<th>商品館 (分類)</th>{% endif %}
|
||
{% if cols.qty %}<th>平均單價</th>{% endif %}
|
||
{% if cols.cost or cols.profit %}<th>毛利率</th>{% endif %}
|
||
{% if cols.return_qty %}<th>退貨率</th>{% endif %}
|
||
{% if cols.date %}<th>銷售月份</th>{% endif %}
|
||
{% if cols.qty %}<th class="text-end">銷售數量</th>{% endif %}
|
||
<th class="text-end">銷售金額</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{% endif %}{# /no_filter else #}
|
||
</div>{# /sales-analysis-page #}
|
||
{% endif %}{# /error #}
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
{% if not no_filter %}
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/chartjs-chart-treemap@2.0.2/dist/chartjs-chart-treemap.min.js"></script>
|
||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
|
||
<script src="https://cdn.datatables.net/1.11.5/js/dataTables.bootstrap5.min.js"></script>
|
||
{% endif %}
|
||
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/l10n/zh-tw.js"></script>
|
||
|
||
<!-- Chart 全站主題(暖色 palette)— 必須在所有 chart 初始化之前 -->
|
||
<script src="{{ url_for('static', filename='js/analysis-chart-theme.js') }}"></script>
|
||
|
||
{% if not error %}
|
||
{% set empty_chart_data = {'labels': [], 'values': [], 'chart_values': [], 'metric_label': ''} %}
|
||
<script id="sales-data" type="application/json">
|
||
{
|
||
"barData": {{ bar_data | default(empty_chart_data, true) | tojson | safe }},
|
||
"categoryData": {{ category_data | default(cat_data | default(empty_chart_data, true), true) | tojson | safe }},
|
||
"treemapData": {{ treemap_data | default([], true) | tojson | safe }},
|
||
"monthlyData": {{ monthly_data | default(empty_chart_data, true) | tojson | safe }},
|
||
"weeklyData": {{ weekly_data | default(empty_chart_data, true) | tojson | safe }},
|
||
"dowData": {{ dow_data | default(empty_chart_data, true) | tojson | safe }},
|
||
"hourlyData": {{ hourly_data | default(empty_chart_data, true) | tojson | safe }},
|
||
"heatmapData": {{ heatmap_data | default({}, true) | tojson | safe }},
|
||
"scatterData": {{ scatter_data | default([], true) | tojson | safe }},
|
||
"priceDistData": {{ price_dist_data | default(empty_chart_data, true) | tojson | safe }},
|
||
"bcgData": {{ bcg_data | default({}, true) | tojson | safe }},
|
||
"seasonalityData": {{ seasonality_data | default({}, true) | tojson | safe }},
|
||
"marketingData": {{ marketing_data | default({}, true) | tojson | safe }}
|
||
}
|
||
</script>
|
||
{% endif %}
|
||
|
||
<!-- Page logic:filters / dropdown search / charts / DataTable / Flatpickr 初始化 -->
|
||
<script src="{{ url_for('static', filename='js/page-sales-analysis.js') }}"></script>
|
||
{% endblock %}
|