This commit is contained in:
@@ -37,8 +37,8 @@
|
||||
color: var(--momo-text-primary, #2a2520);
|
||||
}
|
||||
.analysis-report-tab.is-active {
|
||||
border-color: var(--momo-page-accent-dark, #65411f);
|
||||
background: linear-gradient(135deg, var(--momo-page-accent, #8a5a2b), var(--momo-page-accent-dark, #65411f));
|
||||
border-color: var(--momo-page-accent-dark, #a95846);
|
||||
background: linear-gradient(135deg, var(--momo-page-accent, #d96f52), var(--momo-page-accent-dark, #a95846));
|
||||
color: var(--momo-page-inverse, #fff8ee);
|
||||
}
|
||||
.analysis-report-tab.is-external {
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
--momo-legacy-paper: #f3eee2;
|
||||
--momo-legacy-ink: #2a2520;
|
||||
--momo-legacy-muted: #645c52;
|
||||
--momo-legacy-accent: #c96442;
|
||||
--momo-legacy-accent-dark: #8f4530;
|
||||
--momo-legacy-accent: #d96f52;
|
||||
--momo-legacy-accent-dark: #a95846;
|
||||
--momo-legacy-accent-soft: rgba(201, 100, 66, 0.12);
|
||||
--momo-legacy-line: rgba(42, 37, 32, 0.16);
|
||||
--momo-legacy-inverse: #fff8ee;
|
||||
@@ -101,7 +101,7 @@
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
color: var(--momo-legacy-inverse) !important;
|
||||
background: linear-gradient(135deg, #b1543a, #7a3520) !important;
|
||||
background: linear-gradient(135deg, #c65f45, #9f4f3e) !important;
|
||||
border-color: #7a3520 !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,16 +130,8 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.trend-up {
|
||||
color: #2ecc71;
|
||||
}
|
||||
|
||||
.trend-down {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.bg-purple {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: linear-gradient(135deg, var(--momo-warm-rust), var(--momo-warm-mahogany));
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
@@ -174,7 +166,7 @@
|
||||
padding: 2rem;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||||
border: 1px solid var(--momo-border-strong);
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
@@ -196,12 +188,12 @@
|
||||
}
|
||||
|
||||
.calendar-header h5:hover {
|
||||
color: #667eea;
|
||||
color: var(--momo-warm-caramel);
|
||||
transform: translateX(-4px);
|
||||
}
|
||||
|
||||
.calendar-header h5:hover i {
|
||||
color: #667eea;
|
||||
color: var(--momo-warm-caramel);
|
||||
transform: rotate(360deg);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
@@ -212,7 +204,7 @@
|
||||
}
|
||||
|
||||
.calendar-nav button {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: linear-gradient(135deg, var(--momo-warm-caramel), var(--momo-warm-mahogany));
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
padding: 0.6rem 1.2rem;
|
||||
@@ -221,12 +213,12 @@
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
box-shadow: 0 4px 12px rgba(217, 111, 82, 0.26);
|
||||
}
|
||||
|
||||
.calendar-nav button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
|
||||
box-shadow: 0 6px 16px rgba(217, 111, 82, 0.32);
|
||||
}
|
||||
|
||||
.calendar-nav button:active {
|
||||
@@ -261,19 +253,19 @@
|
||||
|
||||
.calendar-day:hover {
|
||||
transform: translateY(-3px) scale(1.02);
|
||||
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.15);
|
||||
border-color: #667eea;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);
|
||||
box-shadow: 0 8px 20px rgba(217, 111, 82, 0.16);
|
||||
border-color: var(--momo-warm-caramel);
|
||||
background: linear-gradient(135deg, #ffffff 0%, var(--momo-warm-peach-soft) 100%);
|
||||
}
|
||||
|
||||
.calendar-day.has-data {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafbff 100%);
|
||||
background: linear-gradient(135deg, #ffffff 0%, rgba(255, 248, 238, 0.9) 100%);
|
||||
border-color: #d0d5e0;
|
||||
}
|
||||
|
||||
.calendar-day.has-data:hover {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.2);
|
||||
border-color: var(--momo-warm-caramel);
|
||||
box-shadow: 0 8px 24px rgba(217, 111, 82, 0.2);
|
||||
}
|
||||
|
||||
.calendar-day.other-month {
|
||||
@@ -282,10 +274,10 @@
|
||||
}
|
||||
|
||||
.calendar-day.selected {
|
||||
border-color: #667eea !important;
|
||||
border-color: var(--momo-warm-caramel) !important;
|
||||
border-width: 5px !important;
|
||||
box-shadow: 0 0 0 6px rgba(102, 126, 234, 0.25), 0 12px 32px rgba(102, 126, 234, 0.4), inset 0 2px 10px rgba(102, 126, 234, 0.15) !important;
|
||||
background: linear-gradient(135deg, #e8edff 0%, #d0d9ff 100%) !important;
|
||||
box-shadow: 0 0 0 6px rgba(217, 111, 82, 0.22), 0 12px 32px rgba(217, 111, 82, 0.28), inset 0 2px 10px rgba(217, 111, 82, 0.12) !important;
|
||||
background: linear-gradient(135deg, var(--momo-warm-peach-soft) 0%, var(--momo-warm-caramel-soft) 100%) !important;
|
||||
transform: scale(1.08) !important;
|
||||
position: relative;
|
||||
animation: pulseGlow 2s ease-in-out infinite;
|
||||
@@ -298,7 +290,7 @@
|
||||
right: -8px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: linear-gradient(135deg, var(--momo-warm-caramel), var(--momo-warm-mahogany));
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
display: flex;
|
||||
@@ -306,7 +298,7 @@
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 900;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5);
|
||||
box-shadow: 0 4px 12px rgba(217, 111, 82, 0.36);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@@ -319,11 +311,11 @@
|
||||
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 6px rgba(102, 126, 234, 0.25), 0 12px 32px rgba(102, 126, 234, 0.4), inset 0 2px 10px rgba(102, 126, 234, 0.15);
|
||||
box-shadow: 0 0 0 6px rgba(217, 111, 82, 0.22), 0 12px 32px rgba(217, 111, 82, 0.28), inset 0 2px 10px rgba(217, 111, 82, 0.12);
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: 0 0 0 8px rgba(102, 126, 234, 0.35), 0 16px 40px rgba(102, 126, 234, 0.5), inset 0 2px 10px rgba(102, 126, 234, 0.2);
|
||||
box-shadow: 0 0 0 8px rgba(217, 111, 82, 0.28), 0 16px 40px rgba(217, 111, 82, 0.34), inset 0 2px 10px rgba(217, 111, 82, 0.16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,17 +416,17 @@
|
||||
|
||||
.date-selector:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
border-color: var(--momo-warm-caramel);
|
||||
box-shadow: 0 0 0 3px rgba(217, 111, 82, 0.12);
|
||||
}
|
||||
|
||||
/* 頁面標題樣式 */
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: linear-gradient(135deg, var(--momo-warm-caramel), var(--momo-warm-mahogany));
|
||||
padding: 2rem;
|
||||
border-radius: 16px;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.25);
|
||||
box-shadow: 0 8px 24px rgba(217, 111, 82, 0.22);
|
||||
}
|
||||
|
||||
.page-header h4 {
|
||||
@@ -462,19 +454,19 @@
|
||||
|
||||
/* 按鈕樣式優化 */
|
||||
.btn-success {
|
||||
background: linear-gradient(135deg, #51cf66 0%, #37b24d 100%);
|
||||
background: linear-gradient(135deg, var(--momo-warm-earth), var(--momo-warm-honey));
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
padding: 0.6rem 1.5rem;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 4px 12px rgba(81, 207, 102, 0.3);
|
||||
box-shadow: 0 4px 12px rgba(154, 143, 89, 0.24);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(81, 207, 102, 0.4);
|
||||
background: linear-gradient(135deg, #40c057 0%, #2f9e44 100%);
|
||||
box-shadow: 0 6px 16px rgba(154, 143, 89, 0.32);
|
||||
background: linear-gradient(135deg, var(--momo-warm-honey), var(--momo-warm-earth));
|
||||
}
|
||||
|
||||
/* 表格樣式優化 */
|
||||
@@ -484,7 +476,7 @@
|
||||
}
|
||||
|
||||
.table thead {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: linear-gradient(135deg, var(--momo-warm-mahogany), var(--momo-warm-caramel));
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@@ -1307,10 +1299,33 @@
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.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>
|
||||
<script>
|
||||
{% if not error %}
|
||||
if (typeof Chart === 'undefined') {
|
||||
console.error('Chart.js 未載入,無法繪製當日業績圖表');
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
const chartPalette = {
|
||||
caramel: 'rgba(201, 100, 66, 1)',
|
||||
caramelSoft: 'rgba(201, 100, 66, 0.14)',
|
||||
honey: 'rgba(184, 132, 22, 1)',
|
||||
honeySoft: 'rgba(184, 132, 22, 0.14)',
|
||||
rust: 'rgba(181, 52, 47, 1)',
|
||||
rustSoft: 'rgba(181, 52, 47, 0.12)',
|
||||
mahogany: 'rgba(143, 69, 48, 1)',
|
||||
mahoganySoft: 'rgba(143, 69, 48, 0.12)',
|
||||
earth: 'rgba(138, 90, 43, 1)',
|
||||
earthSoft: 'rgba(138, 90, 43, 0.12)',
|
||||
muted: 'rgba(126, 111, 92, 0.52)'
|
||||
};
|
||||
|
||||
const chartData = {{ chart_data | tojson }};
|
||||
|
||||
// 調試:檢查數據
|
||||
@@ -1345,8 +1360,8 @@
|
||||
{
|
||||
label: '業績',
|
||||
data: safeData.revenue,
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.1)',
|
||||
borderColor: chartPalette.caramel,
|
||||
backgroundColor: chartPalette.caramelSoft,
|
||||
borderWidth: 2,
|
||||
yAxisID: 'y',
|
||||
tension: 0.3,
|
||||
@@ -1355,8 +1370,8 @@
|
||||
{
|
||||
label: '毛利',
|
||||
data: safeData.profit,
|
||||
borderColor: 'rgba(46, 204, 113, 1)',
|
||||
backgroundColor: 'rgba(46, 204, 113, 0.1)',
|
||||
borderColor: chartPalette.honey,
|
||||
backgroundColor: chartPalette.honeySoft,
|
||||
borderWidth: 2,
|
||||
yAxisID: 'y',
|
||||
tension: 0.3,
|
||||
@@ -1365,8 +1380,8 @@
|
||||
{
|
||||
label: '客單價',
|
||||
data: safeData.avg_price,
|
||||
borderColor: 'rgba(153, 102, 255, 1)',
|
||||
backgroundColor: 'rgba(153, 102, 255, 0.1)',
|
||||
borderColor: chartPalette.mahogany,
|
||||
backgroundColor: chartPalette.mahoganySoft,
|
||||
borderWidth: 2,
|
||||
yAxisID: 'y1',
|
||||
tension: 0.3,
|
||||
@@ -1375,8 +1390,8 @@
|
||||
{
|
||||
label: '銷量',
|
||||
data: safeData.qty,
|
||||
borderColor: 'rgba(255, 159, 64, 1)',
|
||||
backgroundColor: 'rgba(255, 159, 64, 0.1)',
|
||||
borderColor: chartPalette.earth,
|
||||
backgroundColor: chartPalette.earthSoft,
|
||||
borderWidth: 2,
|
||||
yAxisID: 'y2',
|
||||
tension: 0.3,
|
||||
@@ -1403,7 +1418,7 @@
|
||||
display: true,
|
||||
position: 'left',
|
||||
beginAtZero: true,
|
||||
title: { display: true, text: '業績/毛利 ($)', color: '#54a0ff' }
|
||||
title: { display: true, text: '業績/毛利 ($)', color: chartPalette.caramel }
|
||||
},
|
||||
y1: {
|
||||
type: 'linear',
|
||||
@@ -1411,7 +1426,7 @@
|
||||
position: 'right',
|
||||
beginAtZero: true,
|
||||
grid: { drawOnChartArea: false },
|
||||
title: { display: true, text: '客單價 ($)', color: '#9966ff' }
|
||||
title: { display: true, text: '客單價 ($)', color: chartPalette.mahogany }
|
||||
},
|
||||
y2: {
|
||||
type: 'linear',
|
||||
@@ -1433,8 +1448,8 @@
|
||||
{
|
||||
label: '業績 DoD%',
|
||||
data: safeData.dod_revenue,
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.1)',
|
||||
borderColor: chartPalette.caramel,
|
||||
backgroundColor: chartPalette.caramelSoft,
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false
|
||||
@@ -1442,8 +1457,8 @@
|
||||
{
|
||||
label: '毛利 DoD%',
|
||||
data: safeData.dod_profit,
|
||||
borderColor: 'rgba(46, 204, 113, 1)',
|
||||
backgroundColor: 'rgba(46, 204, 113, 0.1)',
|
||||
borderColor: chartPalette.honey,
|
||||
backgroundColor: chartPalette.honeySoft,
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false
|
||||
@@ -1451,8 +1466,8 @@
|
||||
{
|
||||
label: '客單 DoD%',
|
||||
data: safeData.dod_avg_price,
|
||||
borderColor: 'rgba(153, 102, 255, 1)',
|
||||
backgroundColor: 'rgba(153, 102, 255, 0.1)',
|
||||
borderColor: chartPalette.mahogany,
|
||||
backgroundColor: chartPalette.mahoganySoft,
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false
|
||||
@@ -1460,8 +1475,8 @@
|
||||
{
|
||||
label: '銷量 DoD%',
|
||||
data: safeData.dod_qty,
|
||||
borderColor: 'rgba(255, 159, 64, 1)',
|
||||
backgroundColor: 'rgba(255, 159, 64, 0.1)',
|
||||
borderColor: chartPalette.earth,
|
||||
backgroundColor: chartPalette.earthSoft,
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false
|
||||
@@ -1506,57 +1521,57 @@
|
||||
{
|
||||
label: '業績 WoW%',
|
||||
data: safeData.wow_revenue,
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.1)',
|
||||
borderColor: chartPalette.caramel,
|
||||
backgroundColor: chartPalette.caramelSoft,
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false,
|
||||
segment: {
|
||||
borderColor: ctx => {
|
||||
// 前 7 天顯示為淺灰色
|
||||
return ctx.p0DataIndex < 7 ? 'rgba(200, 200, 200, 0.5)' : 'rgba(54, 162, 235, 1)';
|
||||
return ctx.p0DataIndex < 7 ? chartPalette.muted : chartPalette.caramel;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '毛利 WoW%',
|
||||
data: safeData.wow_profit,
|
||||
borderColor: 'rgba(46, 204, 113, 1)',
|
||||
backgroundColor: 'rgba(46, 204, 113, 0.1)',
|
||||
borderColor: chartPalette.honey,
|
||||
backgroundColor: chartPalette.honeySoft,
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false,
|
||||
segment: {
|
||||
borderColor: ctx => {
|
||||
return ctx.p0DataIndex < 7 ? 'rgba(200, 200, 200, 0.5)' : 'rgba(46, 204, 113, 1)';
|
||||
return ctx.p0DataIndex < 7 ? chartPalette.muted : chartPalette.honey;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '客單 WoW%',
|
||||
data: safeData.wow_avg_price,
|
||||
borderColor: 'rgba(153, 102, 255, 1)',
|
||||
backgroundColor: 'rgba(153, 102, 255, 0.1)',
|
||||
borderColor: chartPalette.mahogany,
|
||||
backgroundColor: chartPalette.mahoganySoft,
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false,
|
||||
segment: {
|
||||
borderColor: ctx => {
|
||||
return ctx.p0DataIndex < 7 ? 'rgba(200, 200, 200, 0.5)' : 'rgba(153, 102, 255, 1)';
|
||||
return ctx.p0DataIndex < 7 ? chartPalette.muted : chartPalette.mahogany;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '銷量 WoW%',
|
||||
data: safeData.wow_qty,
|
||||
borderColor: 'rgba(255, 159, 64, 1)',
|
||||
backgroundColor: 'rgba(255, 159, 64, 0.1)',
|
||||
borderColor: chartPalette.earth,
|
||||
backgroundColor: chartPalette.earthSoft,
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false,
|
||||
segment: {
|
||||
borderColor: ctx => {
|
||||
return ctx.p0DataIndex < 7 ? 'rgba(200, 200, 200, 0.5)' : 'rgba(255, 159, 64, 1)';
|
||||
return ctx.p0DataIndex < 7 ? chartPalette.muted : chartPalette.earth;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1610,8 +1625,8 @@
|
||||
datasets: [{
|
||||
label: '銷售金額',
|
||||
data: safeData.top10_values,
|
||||
backgroundColor: 'rgba(255, 159, 64, 0.6)',
|
||||
borderColor: 'rgba(255, 159, 64, 1)',
|
||||
backgroundColor: 'rgba(201, 100, 66, 0.62)',
|
||||
borderColor: chartPalette.caramel,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
.momo-app[data-active-page="daily_sales"],
|
||||
.momo-app[data-active-page="monthly"],
|
||||
.momo-app[data-active-page="growth"] {
|
||||
--momo-page-accent: var(--momo-warm-earth);
|
||||
--momo-page-accent-dark: #65411f;
|
||||
--momo-page-accent-soft: var(--momo-warm-earth-soft);
|
||||
--momo-page-accent: var(--momo-warm-caramel);
|
||||
--momo-page-accent-dark: var(--momo-warm-mahogany);
|
||||
--momo-page-accent-soft: var(--momo-warm-peach-soft);
|
||||
}
|
||||
.momo-app[data-active-page="vendor_stockout"] {
|
||||
--momo-page-accent: var(--momo-warm-rust);
|
||||
@@ -57,7 +57,7 @@
|
||||
.momo-app[data-active-page="auto_import"],
|
||||
.momo-app[data-active-page="market_intel"] {
|
||||
--momo-page-accent: var(--momo-warm-mahogany);
|
||||
--momo-page-accent-dark: #5e2e20;
|
||||
--momo-page-accent-dark: #7f3f32;
|
||||
--momo-page-accent-soft: var(--momo-warm-mahogany-soft);
|
||||
}
|
||||
.momo-app[data-active-page^="obs_"],
|
||||
|
||||
@@ -1,33 +1,62 @@
|
||||
<!-- cspell:ignore MOMO -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-TW">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>營運成長報表 - MOMO 監控系統</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
||||
{% extends 'ewoooc_base.html' %}
|
||||
{% block title %}營運成長報表 - EwoooC{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: #f4f6f9; }
|
||||
.navbar { box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
|
||||
.card { border: none; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.03); margin-bottom: 1.5rem; transition: all 0.3s ease; background: #fff; }
|
||||
.card:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0,0,0,0.08); }
|
||||
.card-header { background-color: transparent; border-bottom: 1px solid rgba(0,0,0,0.05); font-weight: 700; color: #2c3e50; padding: 1.25rem; }
|
||||
.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; }
|
||||
|
||||
.trend-up { color: #2ecc71; }
|
||||
.trend-down { color: #e74c3c; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-body-tertiary">
|
||||
{% include 'components/_navbar.html' %}
|
||||
.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;
|
||||
}
|
||||
|
||||
<div class="container-fluid px-4">
|
||||
.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>
|
||||
@@ -135,9 +164,11 @@
|
||||
</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>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Data Injection -->
|
||||
<script id="chart-data" type="application/json">
|
||||
{{ chart_data | tojson }}
|
||||
@@ -145,6 +176,20 @@
|
||||
|
||||
<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'), {
|
||||
@@ -155,14 +200,14 @@
|
||||
{
|
||||
label: '月營收 ($)',
|
||||
data: data.revenue,
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.6)',
|
||||
backgroundColor: chartPalette.caramelSoft,
|
||||
order: 2
|
||||
},
|
||||
{
|
||||
label: 'YoY 年增率 (%)',
|
||||
data: data.yoy,
|
||||
type: 'line',
|
||||
borderColor: '#ff6384',
|
||||
borderColor: chartPalette.rust,
|
||||
borderWidth: 2,
|
||||
yAxisID: 'y1',
|
||||
order: 1,
|
||||
@@ -194,7 +239,7 @@
|
||||
data: data.mom,
|
||||
backgroundColor: (ctx) => {
|
||||
const val = ctx.raw;
|
||||
return val >= 0 ? 'rgba(75, 192, 192, 0.6)' : 'rgba(255, 99, 132, 0.6)';
|
||||
return val >= 0 ? chartPalette.honeySoft : chartPalette.rustSoft;
|
||||
}
|
||||
}]
|
||||
},
|
||||
@@ -213,8 +258,8 @@
|
||||
datasets: [{
|
||||
label: '平均客單價 ($)',
|
||||
data: data.aov,
|
||||
borderColor: '#36a2eb',
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.1)',
|
||||
borderColor: chartPalette.caramel,
|
||||
backgroundColor: 'rgba(201, 100, 66, 0.12)',
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
@@ -234,8 +279,8 @@
|
||||
datasets: [{
|
||||
label: '毛利率 (%)',
|
||||
data: data.margin_rate,
|
||||
borderColor: '#2ecc71',
|
||||
backgroundColor: 'rgba(46, 204, 113, 0.1)',
|
||||
borderColor: chartPalette.honey,
|
||||
backgroundColor: 'rgba(184, 132, 22, 0.12)',
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
@@ -247,5 +292,4 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,78 +1,15 @@
|
||||
<!-- cspell:ignore MOMO datatables Treemap -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-TW">
|
||||
{% extends 'ewoooc_base.html' %}
|
||||
{% block title %}業績分析 - EwoooC{% endblock %}
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>業績分析 - EwoooC</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
{% block extra_css %}
|
||||
<!-- DataTables CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.11.5/css/dataTables.bootstrap5.min.css">
|
||||
<!-- V-New: Flatpickr 日期選擇器 CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css">
|
||||
<!-- V-Fix: 使用 Chart.js v3.9.1 以確保與 Treemap v2.0.2 相容 -->
|
||||
<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>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #f4f6f9;
|
||||
padding-top: 70px;
|
||||
}
|
||||
|
||||
.navbar-dark.bg-primary {
|
||||
background: linear-gradient(135deg, #4F46E5 0%, #6366F1 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-brand {
|
||||
color: #ffffff !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-nav .nav-link {
|
||||
color: rgba(255, 255, 255, 0.9) !important;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-nav .nav-link:hover {
|
||||
color: #ffffff !important;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-nav .nav-link.active {
|
||||
color: #ffffff !important;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-text {
|
||||
color: rgba(255, 255, 255, 0.8) !important;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%) !important;
|
||||
}
|
||||
|
||||
/* V-Fix: 防止導航列 dropdown 展開時背景變黑 */
|
||||
.navbar-dark .nav-link.active,
|
||||
.navbar-dark .nav-link.show,
|
||||
.navbar-dark .nav-link:focus {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.navbar-dark .dropdown-toggle.active::after,
|
||||
.navbar-dark .dropdown-toggle.show::after {
|
||||
color: #fff !important;
|
||||
background-color: var(--momo-bg-canvas);
|
||||
}
|
||||
|
||||
/* V-Opt: 現代化卡片風格 */
|
||||
@@ -217,12 +154,12 @@
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background: linear-gradient(135deg, #4F46E5 0%, #7C3AED 100%);
|
||||
background: linear-gradient(135deg, var(--momo-warm-caramel), var(--momo-warm-rust));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
letter-spacing: 4px;
|
||||
filter: drop-shadow(0 4px 15px rgba(79, 70, 229, 0.4));
|
||||
filter: drop-shadow(0 4px 15px rgba(217, 111, 82, 0.32));
|
||||
}
|
||||
|
||||
|
||||
@@ -273,8 +210,8 @@
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border: 4px solid transparent;
|
||||
border-top-color: #4F46E5;
|
||||
border-right-color: #7C3AED;
|
||||
border-top-color: var(--momo-warm-caramel);
|
||||
border-right-color: var(--momo-warm-rust);
|
||||
border-radius: 50%;
|
||||
animation: ring-spin 2s linear infinite;
|
||||
}
|
||||
@@ -345,9 +282,9 @@
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: linear-gradient(135deg, #4F46E5, #7C3AED);
|
||||
background: linear-gradient(135deg, var(--momo-warm-caramel), var(--momo-warm-rust));
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px rgba(79, 70, 229, 0.8);
|
||||
box-shadow: 0 0 10px rgba(217, 111, 82, 0.68);
|
||||
}
|
||||
|
||||
#loadingOverlay .orbit-particle:nth-child(1) {
|
||||
@@ -452,7 +389,7 @@
|
||||
|
||||
#loadingOverlay .loading-text {
|
||||
font-size: 1.2rem;
|
||||
color: #4F46E5;
|
||||
color: var(--momo-warm-mahogany);
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
letter-spacing: 0.5px;
|
||||
@@ -468,14 +405,14 @@
|
||||
#loadingOverlay .loading-progress {
|
||||
width: 200px;
|
||||
height: 4px;
|
||||
background: rgba(79, 70, 229, 0.2);
|
||||
background: rgba(217, 111, 82, 0.18);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#loadingOverlay .loading-progress-bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #4F46E5, #7C3AED, #4F46E5);
|
||||
background: linear-gradient(90deg, var(--momo-warm-caramel), var(--momo-warm-rust), var(--momo-warm-honey));
|
||||
background-size: 200% 100%;
|
||||
animation: progress-flow 1.5s linear infinite;
|
||||
width: 100%;
|
||||
@@ -562,11 +499,36 @@
|
||||
.navbar.bg-custom-dark .navbar-text {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
.sales-analysis-page .card,
|
||||
.sales-analysis-page .panel,
|
||||
.sales-analysis-page .filter-section {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
<body class="bg-body-tertiary">
|
||||
{% include 'components/_navbar.html' %}
|
||||
.sales-analysis-page .bg-primary,
|
||||
.sales-analysis-page .btn-primary {
|
||||
background: linear-gradient(135deg, var(--momo-warm-caramel), var(--momo-warm-mahogany)) !important;
|
||||
border-color: var(--momo-warm-mahogany) !important;
|
||||
}
|
||||
|
||||
.sales-analysis-page .btn-success,
|
||||
.sales-analysis-page .bg-success {
|
||||
background: linear-gradient(135deg, var(--momo-warm-earth), var(--momo-warm-honey)) !important;
|
||||
border-color: var(--momo-warm-earth) !important;
|
||||
}
|
||||
|
||||
.sales-analysis-page .text-primary,
|
||||
.sales-analysis-page .text-success {
|
||||
color: var(--momo-warm-caramel) !important;
|
||||
}
|
||||
|
||||
.sales-analysis-page .badge.bg-secondary {
|
||||
background-color: rgba(111, 102, 90, 0.86) !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block ewooo_content %}
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div id="loadingOverlay">
|
||||
@@ -607,7 +569,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid px-4">
|
||||
<div class="sales-analysis-page">
|
||||
{% include 'components/_analysis_report_tabs.html' %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 mt-4">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
@@ -651,29 +613,26 @@
|
||||
<!-- V-New: 控制面板 (篩選器) -->
|
||||
<div class="card mb-4 shadow-sm" style="z-index: 100; transform: none !important; transition: none;">
|
||||
<div class="card-header bg-gradient"
|
||||
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none;">
|
||||
style="background: linear-gradient(135deg, var(--momo-warm-caramel), var(--momo-warm-mahogany)); border: none;">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 text-white"><i class="fas fa-sliders-h me-2"></i>進階篩選與分析</h5>
|
||||
<!-- V-New: 分析維度切換(更顯眼的設計) -->
|
||||
<div class="btn-group shadow-sm" role="group">
|
||||
<input type="radio" class="btn-check" name="metric" id="metricAmount" value="amount"
|
||||
autocomplete="off" onchange="setFilter('metric', 'amount')" {% if selected_metric=='amount'
|
||||
%}checked{% endif %}>
|
||||
autocomplete="off" onchange="setFilter('metric', 'amount')" {% if selected_metric=='amount' %}checked{% endif %}>
|
||||
<label class="btn btn-sm btn-light fw-bold" for="metricAmount">
|
||||
<i class="fas fa-dollar-sign me-1"></i>依金額分析
|
||||
</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="metric" id="metricQty" value="qty"
|
||||
autocomplete="off" onchange="setFilter('metric', 'qty')" {% if selected_metric=='qty'
|
||||
%}checked{% endif %}>
|
||||
autocomplete="off" onchange="setFilter('metric', 'qty')" {% if selected_metric=='qty' %}checked{% endif %}>
|
||||
<label class="btn btn-sm btn-light fw-bold" for="metricQty">
|
||||
<i class="fas fa-box me-1"></i>依銷售量分析
|
||||
</label>
|
||||
|
||||
{% if cols.cost or cols.profit %}
|
||||
<input type="radio" class="btn-check" name="metric" id="metricProfit" value="profit"
|
||||
autocomplete="off" onchange="setFilter('metric', 'profit')" {% if selected_metric=='profit'
|
||||
%}checked{% endif %}>
|
||||
autocomplete="off" onchange="setFilter('metric', 'profit')" {% if selected_metric=='profit' %}checked{% endif %}>
|
||||
<label class="btn btn-sm btn-light fw-bold" for="metricProfit">
|
||||
<i class="fas fa-chart-line me-1"></i>依毛利分析
|
||||
</label>
|
||||
@@ -701,16 +660,11 @@
|
||||
onchange="handleDataRangeChange(this)">
|
||||
<option value="" {% if not request.args.get('data_range') %}selected{% endif %}>
|
||||
-- 請選擇 --</option>
|
||||
<option value="1" {% if request.args.get('data_range')=='1' %}selected{% endif
|
||||
%}>最近 1 個月 (推薦)</option>
|
||||
<option value="3" {% if request.args.get('data_range')=='3' %}selected{% endif
|
||||
%}>最近 3 個月</option>
|
||||
<option value="6" {% if request.args.get('data_range')=='6' %}selected{% endif
|
||||
%}>最近 6 個月</option>
|
||||
<option value="12" {% if request.args.get('data_range')=='12' %}selected{% endif
|
||||
%}>最近 12 個月</option>
|
||||
<option value="0" {% if request.args.get('data_range')=='0' %}selected{% endif
|
||||
%}>全部資料</option>
|
||||
<option value="1" {% if request.args.get('data_range')=='1' %}selected{% endif %}>最近 1 個月 (推薦)</option>
|
||||
<option value="3" {% if request.args.get('data_range')=='3' %}selected{% endif %}>最近 3 個月</option>
|
||||
<option value="6" {% if request.args.get('data_range')=='6' %}selected{% endif %}>最近 6 個月</option>
|
||||
<option value="12" {% if request.args.get('data_range')=='12' %}selected{% endif %}>最近 12 個月</option>
|
||||
<option value="0" {% if request.args.get('data_range')=='0' %}selected{% endif %}>全部資料</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -1750,14 +1704,18 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<!-- Chart.js v3 + Treemap v2 are paired intentionally. -->
|
||||
<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>
|
||||
|
||||
<!-- DataTables JS -->
|
||||
<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 %}
|
||||
|
||||
{% if not error %}
|
||||
<!-- V-New: JSON Data Block for Data Injection -->
|
||||
<script id="sales-data" type="application/json">
|
||||
@@ -1960,6 +1918,20 @@
|
||||
const treemapDataPoints = salesData.treemapData;
|
||||
const cols = salesData.cols;
|
||||
const selectedMetric = salesData.selectedMetric;
|
||||
const momoChartColors = {
|
||||
coral: 'rgba(217, 111, 82, 0.64)',
|
||||
coralLine: 'rgba(217, 111, 82, 1)',
|
||||
peach: 'rgba(242, 159, 126, 0.62)',
|
||||
amber: 'rgba(214, 161, 47, 0.64)',
|
||||
amberLine: 'rgba(214, 161, 47, 1)',
|
||||
rose: 'rgba(200, 95, 106, 0.62)',
|
||||
roseLine: 'rgba(200, 95, 106, 1)',
|
||||
clay: 'rgba(169, 88, 70, 0.62)',
|
||||
clayLine: 'rgba(169, 88, 70, 1)',
|
||||
olive: 'rgba(154, 143, 89, 0.62)',
|
||||
oliveLine: 'rgba(154, 143, 89, 1)',
|
||||
neutral: 'rgba(111, 102, 90, 0.42)'
|
||||
};
|
||||
|
||||
// 1. 橫向長條圖 (Horizontal Bar Chart) - 更易讀
|
||||
const ctxBar = document.getElementById('barChart').getContext('2d');
|
||||
@@ -1970,8 +1942,8 @@
|
||||
datasets: [{
|
||||
label: barData.metricLabel,
|
||||
data: barData.values,
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.6)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
backgroundColor: momoChartColors.coral,
|
||||
borderColor: momoChartColors.coralLine,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
@@ -2003,7 +1975,14 @@
|
||||
labels: catData.labels,
|
||||
datasets: [{
|
||||
data: catData.values,
|
||||
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#C9CBCF']
|
||||
backgroundColor: [
|
||||
momoChartColors.coral,
|
||||
momoChartColors.peach,
|
||||
momoChartColors.amber,
|
||||
momoChartColors.rose,
|
||||
momoChartColors.olive,
|
||||
momoChartColors.neutral
|
||||
]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
@@ -2044,8 +2023,8 @@
|
||||
datasets: [{
|
||||
label: '區間總業績 ($)',
|
||||
data: priceDistData.values,
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.6)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
backgroundColor: momoChartColors.amber,
|
||||
borderColor: momoChartColors.amberLine,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
@@ -2127,8 +2106,8 @@
|
||||
datasets: [{
|
||||
label: metricLabel,
|
||||
data: mkt.discount.map(i => i[metricKey] || 0),
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.7)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
backgroundColor: momoChartColors.peach,
|
||||
borderColor: momoChartColors.coralLine,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
@@ -2162,8 +2141,8 @@
|
||||
datasets: [{
|
||||
label: metricLabel,
|
||||
data: mkt.coupon.map(i => i[metricKey] || 0),
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.7)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
backgroundColor: momoChartColors.amber,
|
||||
borderColor: momoChartColors.amberLine,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
@@ -2385,8 +2364,8 @@
|
||||
datasets: [{
|
||||
label: '月總業績 ($)',
|
||||
data: monthlyData.values,
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.6)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
backgroundColor: momoChartColors.coral,
|
||||
borderColor: momoChartColors.coralLine,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
@@ -2414,8 +2393,8 @@
|
||||
datasets: [{
|
||||
label: '週總業績 ($)',
|
||||
data: weeklyData.values,
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
backgroundColor: 'rgba(214, 161, 47, 0.16)',
|
||||
borderColor: momoChartColors.amberLine,
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.3
|
||||
@@ -2453,8 +2432,8 @@
|
||||
datasets: [{
|
||||
label: '時段總業績 ($)',
|
||||
data: hourlyData.values,
|
||||
backgroundColor: 'rgba(153, 102, 255, 0.2)',
|
||||
borderColor: 'rgba(153, 102, 255, 1)',
|
||||
backgroundColor: 'rgba(200, 95, 106, 0.14)',
|
||||
borderColor: momoChartColors.roseLine,
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.3 // 平滑曲線
|
||||
@@ -3160,8 +3139,4 @@
|
||||
window.endDatePicker = endPicker;
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* MOMO Pro × Nothing × Claude 設計 Token v2.0
|
||||
* — Nothing 的點陣骨架(黑白、像素、工業)
|
||||
* — Claude 的暖米基底(#f0eee9、焦糖橘 #c96442)
|
||||
* — Claude 的暖米基底(#f0eee9、EwoooC 珊瑚橘 #d96f52)
|
||||
*/
|
||||
|
||||
:root {
|
||||
@@ -22,32 +22,34 @@
|
||||
--momo-line-soft: rgba(42,37,32,0.18);
|
||||
--momo-line-faint: rgba(42,37,32,0.10);
|
||||
|
||||
/* Claude 焦糖橘(accent) */
|
||||
--momo-accent: #c96442; /* 主 accent */
|
||||
--momo-accent-50: #fbf2ef;
|
||||
--momo-accent-100: #f5e1d9;
|
||||
--momo-accent-200: #ecc3b3;
|
||||
--momo-accent-500: #c96442;
|
||||
--momo-accent-600: #b1543a;
|
||||
--momo-accent-700: #8f4530;
|
||||
--momo-accent-soft: rgba(201,100,66,0.12);
|
||||
/* EwoooC 珊瑚橘(accent) */
|
||||
--momo-accent: #d96f52; /* 主 accent */
|
||||
--momo-accent-50: #fff4ef;
|
||||
--momo-accent-100: #fde4d8;
|
||||
--momo-accent-200: #f8c6b3;
|
||||
--momo-accent-500: #d96f52;
|
||||
--momo-accent-600: #c65f45;
|
||||
--momo-accent-700: #9f4f3e;
|
||||
--momo-accent-soft: rgba(217,111,82,0.13);
|
||||
--momo-accent-strong: var(--momo-accent-700);
|
||||
|
||||
/* ===== EwoooC 暖色家族(全站運用) =====
|
||||
* 全部留在暖色域(紅/橘/金/土),不混入冷色
|
||||
* 全部留在暖色域(珊瑚/蜜桃/琥珀/玫瑰銅/暖橄欖),避免髒棕與冷藍紫
|
||||
* 用法:活動頁 / 標籤色 / 圖表分類色 / 各區段視覺主軸
|
||||
*/
|
||||
--momo-warm-caramel: #c96442; /* 焦糖橘 — 主 accent / 限時搶購 */
|
||||
--momo-warm-honey: #b88416; /* 蜂蜜金 — 1.1 狂歡 / 警示 */
|
||||
--momo-warm-rust: #b5342f; /* 暖紅 — 母親節 / danger */
|
||||
--momo-warm-mahogany: #8f4530; /* 深焦糖 — 520 情人節 / 強調 */
|
||||
--momo-warm-earth: #8a5a2b; /* 焦土 — 勞動節 / 中性暖 */
|
||||
--momo-warm-caramel: #d96f52; /* 珊瑚橘 — 主 accent / 主要動作 */
|
||||
--momo-warm-peach: #f29f7e; /* 蜜桃 — hover / 淡底層 */
|
||||
--momo-warm-honey: #d6a12f; /* 琥珀金 — 提醒 / 次重點 */
|
||||
--momo-warm-rust: #c85f6a; /* 玫瑰銅 — 下降 / 風險 */
|
||||
--momo-warm-mahogany: #a95846; /* 暖陶紅 — 強調 / active */
|
||||
--momo-warm-earth: #9a8f59; /* 暖橄欖 — 中性成功 / 輔助 */
|
||||
/* 對應淡色(背景 / 軟標籤用) */
|
||||
--momo-warm-caramel-soft: rgba(201,100,66,0.12);
|
||||
--momo-warm-honey-soft: rgba(184,132,22,0.12);
|
||||
--momo-warm-rust-soft: rgba(181,52,47,0.12);
|
||||
--momo-warm-mahogany-soft:rgba(143,69,48,0.12);
|
||||
--momo-warm-earth-soft: rgba(138,90,43,0.12);
|
||||
--momo-warm-caramel-soft: rgba(217,111,82,0.13);
|
||||
--momo-warm-peach-soft: rgba(242,159,126,0.18);
|
||||
--momo-warm-honey-soft: rgba(214,161,47,0.15);
|
||||
--momo-warm-rust-soft: rgba(200,95,106,0.14);
|
||||
--momo-warm-mahogany-soft:rgba(169,88,70,0.13);
|
||||
--momo-warm-earth-soft: rgba(154,143,89,0.15);
|
||||
|
||||
/* 頁面調性:共用 shell 與反白 UI 以此套用各頁暖色 accent */
|
||||
--momo-page-accent: var(--momo-warm-caramel);
|
||||
@@ -140,7 +142,7 @@
|
||||
--momo-primary-600: var(--momo-accent-600);
|
||||
--momo-primary-700: var(--momo-accent-700);
|
||||
--momo-primary-800: var(--momo-accent-700);
|
||||
--momo-primary-900: #5e2e20;
|
||||
--momo-primary-900: #7f3f32;
|
||||
|
||||
/* 導航(Nothing 黑) */
|
||||
--momo-nav-start: #1a1a1a;
|
||||
@@ -154,8 +156,8 @@
|
||||
--momo-gradient-primary: #1a1a1a;
|
||||
--momo-gradient-nav: linear-gradient(180deg, #1a1a1a 0%, #000 100%);
|
||||
--momo-gradient-success: #2a7a3f;
|
||||
--momo-gradient-danger: #b5342f;
|
||||
--momo-gradient-warning: #b88416;
|
||||
--momo-gradient-danger: var(--momo-warm-rust);
|
||||
--momo-gradient-warning: var(--momo-warm-honey);
|
||||
--momo-gradient-info: #2d5d80;
|
||||
--momo-gradient-subtle: linear-gradient(180deg, #f7f5ef 0%, #ebe8e1 100%);
|
||||
|
||||
@@ -164,11 +166,11 @@
|
||||
--momo-success-bg: #e3ebd9;
|
||||
--momo-success-border: #c5d4b0;
|
||||
--momo-success-text: #1f5a2d;
|
||||
--momo-danger: #b5342f;
|
||||
--momo-danger: var(--momo-warm-rust);
|
||||
--momo-danger-bg: #f0d8d4;
|
||||
--momo-danger-border: #d9b1ac;
|
||||
--momo-danger-text: #7d2520;
|
||||
--momo-warning: #b88416;
|
||||
--momo-warning: var(--momo-warm-honey);
|
||||
--momo-warning-bg: #f3e7c4;
|
||||
--momo-warning-border: #d9c590;
|
||||
--momo-warning-text: #6e500e;
|
||||
@@ -183,8 +185,8 @@
|
||||
--momo-text-tertiary: #9b9081;
|
||||
--momo-text-disabled: #c4baa8;
|
||||
--momo-text-inverse: #faf7f0;
|
||||
--momo-text-link: #c96442;
|
||||
--momo-text-link-hover: #8f4530;
|
||||
--momo-text-link: var(--momo-warm-caramel);
|
||||
--momo-text-link-hover: var(--momo-warm-mahogany);
|
||||
--momo-text-strong: var(--momo-text-primary);
|
||||
--momo-text-muted: var(--momo-text-secondary);
|
||||
--momo-muted: var(--momo-text-secondary);
|
||||
@@ -196,7 +198,7 @@
|
||||
--momo-border-strong: rgba(42,37,32,0.42);
|
||||
--momo-border-subtle: var(--momo-border-light);
|
||||
--momo-border-dark: #2a2520;
|
||||
--momo-border-focus: #c96442;
|
||||
--momo-border-focus: var(--momo-warm-caramel);
|
||||
--momo-divider: rgba(42,37,32,0.12);
|
||||
|
||||
/* Overlay */
|
||||
|
||||
Reference in New Issue
Block a user