296 lines
12 KiB
HTML
296 lines
12 KiB
HTML
{% extends 'ewoooc_base.html' %}
|
|
{% block title %}營運成長報表 - EwoooC{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.growth-analysis-page {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 18px;
|
|
}
|
|
|
|
.growth-analysis-page .card {
|
|
border: 1px solid var(--momo-border-strong);
|
|
border-radius: 8px;
|
|
box-shadow: var(--momo-shadow-soft);
|
|
margin-bottom: 1.5rem;
|
|
background: rgba(255, 253, 248, 0.94);
|
|
}
|
|
|
|
.growth-analysis-page .card-header {
|
|
background: rgba(250, 247, 240, 0.9);
|
|
border-bottom: 1px solid var(--momo-border-subtle);
|
|
font-weight: 800;
|
|
color: var(--momo-text-strong);
|
|
padding: 1rem 1.25rem;
|
|
}
|
|
|
|
.kpi-card { position: relative; overflow: hidden; border: none; }
|
|
.kpi-card .icon-bg { position: absolute; right: -15px; bottom: -15px; font-size: 6rem; opacity: 0.15; transform: rotate(-15deg); pointer-events: none; }
|
|
.kpi-value { font-size: 2rem; font-weight: 800; letter-spacing: -0.5px; margin-bottom: 0.2rem; }
|
|
.kpi-label { font-size: 0.85rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; opacity: 0.9; }
|
|
|
|
.growth-analysis-page .bg-primary,
|
|
.growth-analysis-page .bg-success,
|
|
.growth-analysis-page .bg-info {
|
|
background: linear-gradient(135deg, var(--momo-page-accent-dark), var(--momo-page-accent)) !important;
|
|
}
|
|
|
|
.growth-analysis-page .bg-success {
|
|
background: linear-gradient(135deg, var(--momo-warm-earth), var(--momo-warm-honey)) !important;
|
|
}
|
|
|
|
.growth-analysis-page .bg-info {
|
|
background: linear-gradient(135deg, var(--momo-warm-rust), var(--momo-warm-caramel)) !important;
|
|
}
|
|
|
|
.growth-analysis-page .text-success,
|
|
.growth-analysis-page .trend-up {
|
|
color: var(--momo-warm-honey) !important;
|
|
}
|
|
|
|
.growth-analysis-page .trend-down {
|
|
color: var(--momo-warm-rust) !important;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block ewooo_content %}
|
|
<div class="growth-analysis-page">
|
|
{% include 'components/_analysis_report_tabs.html' %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4 mt-4">
|
|
<h4 class="mb-0 fw-bold text-dark"><i class="fas fa-rocket me-2 text-success"></i>營運成長策略報表</h4>
|
|
<span class="text-muted small">數據更新至: {{ chart_data.labels[-1] if chart_data.labels else '-' }}</span>
|
|
</div>
|
|
|
|
<!-- KPI Cards -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-4">
|
|
<div class="card kpi-card bg-primary text-white h-100 shadow-sm">
|
|
<div class="card-body p-4">
|
|
<div class="kpi-label text-white-50">YTD 本年度累計業績 ({{ kpi.current_year }})</div>
|
|
<div class="kpi-value">${{ "{:,.0f}".format(kpi.ytd_revenue) }}</div>
|
|
<div class="mt-2">
|
|
<span class="badge bg-white text-primary me-2">YoY Growth</span>
|
|
<span class="fw-bold {{ 'text-white' if kpi.ytd_growth >= 0 else 'text-warning' }}">
|
|
<i class="fas fa-{{ 'arrow-up' if kpi.ytd_growth >= 0 else 'arrow-down' }} me-1"></i>
|
|
{{ "{:+.1f}%".format(kpi.ytd_growth) }}
|
|
</span>
|
|
<span class="small text-white-50 ms-1">vs 去年同期</span>
|
|
</div>
|
|
<i class="fas fa-chart-line icon-bg"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card kpi-card bg-success text-white h-100 shadow-sm">
|
|
<div class="card-body p-4">
|
|
<div class="kpi-label text-white-50">近30天平均客單價 (AOV)</div>
|
|
<div class="kpi-value">${{ "{:,.0f}".format(kpi.recent_aov) }}</div>
|
|
<div class="mt-2 small text-white-50">
|
|
真實訂單基礎 (Unique Order ID)
|
|
</div>
|
|
<i class="fas fa-shopping-cart icon-bg"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card kpi-card bg-info text-white h-100 shadow-sm">
|
|
<div class="card-body p-4">
|
|
<div class="kpi-label text-white-50">總訂單數 (Total Orders)</div>
|
|
<div class="kpi-value">{{ "{:,.0f}".format(kpi.total_orders) }}</div>
|
|
<div class="mt-2 small text-white-50">
|
|
全時期累計
|
|
</div>
|
|
<i class="fas fa-receipt icon-bg"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts Row 1: Revenue & Growth -->
|
|
<div class="row mb-4">
|
|
<div class="col-lg-8">
|
|
<div class="card h-100">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span><i class="fas fa-chart-bar me-2"></i>月營收與年增率 (Revenue & YoY)</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div style="height: 350px;">
|
|
<canvas id="revenueChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-4">
|
|
<div class="card h-100">
|
|
<div class="card-header">
|
|
<i class="fas fa-percentage me-2"></i>月增率分析 (MoM)
|
|
</div>
|
|
<div class="card-body">
|
|
<div style="height: 350px;">
|
|
<canvas id="momChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts Row 2: AOV & Profit -->
|
|
<div class="row mb-4">
|
|
<div class="col-lg-6">
|
|
<div class="card h-100">
|
|
<div class="card-header">
|
|
<i class="fas fa-wallet me-2"></i>客單價趨勢 (AOV Trend)
|
|
</div>
|
|
<div class="card-body">
|
|
<div style="height: 300px;">
|
|
<canvas id="aovChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-6">
|
|
<div class="card h-100">
|
|
<div class="card-header">
|
|
<i class="fas fa-hand-holding-usd me-2"></i>獲利能力分析 (Gross Margin %)
|
|
</div>
|
|
<div class="card-body">
|
|
<div style="height: 300px;">
|
|
<canvas id="marginChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script>
|
|
|
|
<!-- Data Injection -->
|
|
<script id="chart-data" type="application/json">
|
|
{{ chart_data | tojson }}
|
|
</script>
|
|
|
|
<script>
|
|
const data = JSON.parse(document.getElementById('chart-data').textContent);
|
|
const chartPalette = {
|
|
caramel: 'rgba(201, 100, 66, 1)',
|
|
caramelSoft: 'rgba(201, 100, 66, 0.58)',
|
|
honey: 'rgba(184, 132, 22, 1)',
|
|
honeySoft: 'rgba(184, 132, 22, 0.58)',
|
|
rust: 'rgba(181, 52, 47, 1)',
|
|
rustSoft: 'rgba(181, 52, 47, 0.48)',
|
|
mahogany: 'rgba(143, 69, 48, 1)',
|
|
mahoganySoft: 'rgba(143, 69, 48, 0.12)'
|
|
};
|
|
|
|
Chart.defaults.color = '#6f665a';
|
|
Chart.defaults.borderColor = 'rgba(126, 111, 92, 0.18)';
|
|
Chart.defaults.font.family = "'Noto Sans TC', 'Inter', system-ui, sans-serif";
|
|
|
|
// 1. Revenue & YoY Chart (Mixed)
|
|
new Chart(document.getElementById('revenueChart'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: data.labels,
|
|
datasets: [
|
|
{
|
|
label: '月營收 ($)',
|
|
data: data.revenue,
|
|
backgroundColor: chartPalette.caramelSoft,
|
|
order: 2
|
|
},
|
|
{
|
|
label: 'YoY 年增率 (%)',
|
|
data: data.yoy,
|
|
type: 'line',
|
|
borderColor: chartPalette.rust,
|
|
borderWidth: 2,
|
|
yAxisID: 'y1',
|
|
order: 1,
|
|
tension: 0.3
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: { beginAtZero: true, title: {display: true, text: '金額 ($)'} },
|
|
y1: {
|
|
position: 'right',
|
|
grid: { drawOnChartArea: false },
|
|
title: {display: true, text: '成長率 (%)'}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// 2. MoM Chart
|
|
new Chart(document.getElementById('momChart'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: data.labels,
|
|
datasets: [{
|
|
label: 'MoM 月增率 (%)',
|
|
data: data.mom,
|
|
backgroundColor: (ctx) => {
|
|
const val = ctx.raw;
|
|
return val >= 0 ? chartPalette.honeySoft : chartPalette.rustSoft;
|
|
}
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { display: false } }
|
|
}
|
|
});
|
|
|
|
// 3. AOV Chart
|
|
new Chart(document.getElementById('aovChart'), {
|
|
type: 'line',
|
|
data: {
|
|
labels: data.labels,
|
|
datasets: [{
|
|
label: '平均客單價 ($)',
|
|
data: data.aov,
|
|
borderColor: chartPalette.caramel,
|
|
backgroundColor: 'rgba(201, 100, 66, 0.12)',
|
|
fill: true,
|
|
tension: 0.4
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: { y: { beginAtZero: true } }
|
|
}
|
|
});
|
|
|
|
// 4. Margin Rate Chart
|
|
new Chart(document.getElementById('marginChart'), {
|
|
type: 'line',
|
|
data: {
|
|
labels: data.labels,
|
|
datasets: [{
|
|
label: '毛利率 (%)',
|
|
data: data.margin_rate,
|
|
borderColor: chartPalette.honey,
|
|
backgroundColor: 'rgba(184, 132, 22, 0.12)',
|
|
fill: true,
|
|
tension: 0.4
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: { y: { beginAtZero: true } }
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|