This commit is contained in:
@@ -412,6 +412,36 @@
|
||||
background: var(--momo-ink);
|
||||
}
|
||||
|
||||
.campaign-pagination {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-left: auto;
|
||||
color: var(--momo-text-secondary);
|
||||
font-family: var(--momo-font-family-mono);
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.campaign-page-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
min-height: 30px;
|
||||
padding: 5px 9px;
|
||||
color: var(--momo-text-primary);
|
||||
background: var(--momo-bg-surface);
|
||||
border: 1px solid var(--momo-border-light);
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.campaign-page-link[aria-disabled="true"] {
|
||||
pointer-events: none;
|
||||
color: var(--momo-text-disabled);
|
||||
background: var(--momo-bg-paper);
|
||||
}
|
||||
|
||||
.campaign-table-wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
@@ -708,6 +738,12 @@
|
||||
.campaign-kpi-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.campaign-pagination {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/page-edm.css') }}">
|
||||
@@ -825,7 +861,7 @@
|
||||
<div class="campaign-slot-tabs" role="tablist">
|
||||
{% for slot, stats in slot_stats.items() %}
|
||||
{% set slot_id = slugify(slot) %}
|
||||
<a class="campaign-slot-tab {% if slot == active_tab %}active{% endif %}" id="slot-{{ slot_id }}-tab" href="{{ url_for(current_endpoint, slot=slot, sort_by=current_sort, order=current_order) }}" role="tab" aria-selected="{{ 'true' if slot == active_tab else 'false' }}">
|
||||
<a class="campaign-slot-tab {% if slot == active_tab %}active{% endif %}" id="slot-{{ slot_id }}-tab" href="{{ url_for(current_endpoint, slot=slot, sort_by=current_sort, order=current_order, page=1, per_page=page_window.per_page) }}" role="tab" aria-selected="{{ 'true' if slot == active_tab else 'false' }}">
|
||||
<span class="momo-mono">{{ slot }}</span>
|
||||
<span class="campaign-slot-count momo-mono">{{ stats.get('on_shelf', 0) }} 件</span>
|
||||
</a>
|
||||
@@ -849,14 +885,27 @@
|
||||
<div class="campaign-table-head">
|
||||
<span class="momo-mono" style="font-size:11px;font-weight:800;color:var(--momo-text-tertiary);letter-spacing:.08em;">03</span>
|
||||
<strong>商品列表</strong>
|
||||
<span class="momo-mono" data-campaign-visible-count style="color:var(--momo-text-tertiary);font-size:12px;">{{ items|length }} 筆</span>
|
||||
<span class="momo-mono" data-campaign-visible-count style="color:var(--momo-text-tertiary);font-size:12px;">
|
||||
{{ page_window.start_item }}-{{ page_window.end_item }} / {{ page_window.total_items }} 筆
|
||||
</span>
|
||||
<div class="campaign-filterbar" aria-label="{{ slot }} 商品狀態篩選">
|
||||
<button class="campaign-filter-chip is-active" type="button" data-campaign-filter="all">全部 {{ items|length }}</button>
|
||||
<button class="campaign-filter-chip is-active" type="button" data-campaign-filter="all">本頁 {{ items|length }}</button>
|
||||
<button class="campaign-filter-chip" type="button" data-campaign-filter="new">新品 {{ stats.get('new', 0) }}</button>
|
||||
<button class="campaign-filter-chip" type="button" data-campaign-filter="up">漲價 {{ stats.get('up', 0) }}</button>
|
||||
<button class="campaign-filter-chip" type="button" data-campaign-filter="down">降價 {{ stats.get('down', 0) }}</button>
|
||||
<button class="campaign-filter-chip" type="button" data-campaign-filter="delisted">下架 {{ stats.get('delisted_last_run', 0) }}</button>
|
||||
</div>
|
||||
<nav class="campaign-pagination" aria-label="{{ slot }} 商品分頁">
|
||||
<a class="campaign-page-link" href="{{ url_for(current_endpoint, slot=active_tab, sort_by=current_sort, order=current_order, page=page_window.page - 1, per_page=page_window.per_page) }}" aria-disabled="{{ 'false' if page_window.has_prev else 'true' }}">
|
||||
<i class="fas fa-chevron-left" aria-hidden="true"></i>
|
||||
<span>上一頁</span>
|
||||
</a>
|
||||
<span>第 {{ page_window.page }} / {{ page_window.total_pages }} 頁</span>
|
||||
<a class="campaign-page-link" href="{{ url_for(current_endpoint, slot=active_tab, sort_by=current_sort, order=current_order, page=page_window.page + 1, per_page=page_window.per_page) }}" aria-disabled="{{ 'false' if page_window.has_next else 'true' }}">
|
||||
<span>下一頁</span>
|
||||
<i class="fas fa-chevron-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="campaign-table-wrap">
|
||||
<table class="campaign-table">
|
||||
@@ -865,16 +914,16 @@
|
||||
<th>分類 / 狀態</th>
|
||||
<th>
|
||||
{% set next_order_name = 'asc' if current_sort == 'name' and current_order == 'desc' else 'desc' %}
|
||||
<a href="{{ url_for(current_endpoint, slot=active_tab, sort_by='name', order=next_order_name) }}">商品資訊</a>
|
||||
<a href="{{ url_for(current_endpoint, slot=active_tab, sort_by='name', order=next_order_name, page=1, per_page=page_window.per_page) }}">商品資訊</a>
|
||||
</th>
|
||||
<th class="text-end">
|
||||
{% set next_order_price = 'asc' if current_sort == 'price' and current_order == 'desc' else 'desc' %}
|
||||
<a href="{{ url_for(current_endpoint, slot=active_tab, sort_by='price', order=next_order_price) }}">價格</a>
|
||||
<a href="{{ url_for(current_endpoint, slot=active_tab, sort_by='price', order=next_order_price, page=1, per_page=page_window.per_page) }}">價格</a>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
{% if current_promo_page == 'edm' %}
|
||||
{% set next_order_qty = 'asc' if current_sort == 'remain_qty' and current_order == 'desc' else 'desc' %}
|
||||
<a href="{{ url_for(current_endpoint, slot=active_tab, sort_by='remain_qty', order=next_order_qty) }}">銷售 / 庫存</a>
|
||||
<a href="{{ url_for(current_endpoint, slot=active_tab, sort_by='remain_qty', order=next_order_qty, page=1, per_page=page_window.per_page) }}">銷售 / 庫存</a>
|
||||
{% else %}
|
||||
狀態
|
||||
{% endif %}
|
||||
@@ -1061,12 +1110,12 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
let campaignPriceChartInstance = null;
|
||||
let activeCampaignHistoryRange = 'month';
|
||||
let currentCampaignICode = null;
|
||||
let currentCampaignProductName = '';
|
||||
let campaignChartLoader = null;
|
||||
|
||||
function getCSRFToken() {
|
||||
return document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
@@ -1122,6 +1171,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
function ensureCampaignChart() {
|
||||
if (typeof Chart !== 'undefined') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (campaignChartLoader) {
|
||||
return campaignChartLoader;
|
||||
}
|
||||
campaignChartLoader = new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://cdn.jsdelivr.net/npm/chart.js';
|
||||
script.async = true;
|
||||
script.onload = resolve;
|
||||
script.onerror = () => reject(new Error('Chart.js 載入失敗'));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
return campaignChartLoader;
|
||||
}
|
||||
|
||||
function updateCampaignHistoryRangeButtons() {
|
||||
document.querySelectorAll('[data-campaign-history-range]').forEach(button => {
|
||||
button.classList.toggle('is-active', button.dataset.campaignHistoryRange === activeCampaignHistoryRange);
|
||||
@@ -1149,12 +1216,8 @@
|
||||
const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
|
||||
modal.show();
|
||||
|
||||
if (typeof Chart === 'undefined') {
|
||||
setCampaignHistoryChartState('圖表元件尚未載入完成,請重新整理後再試。');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/api/history/i-code/${encodeURIComponent(iCode)}?range=${activeCampaignHistoryRange}`)
|
||||
ensureCampaignChart()
|
||||
.then(() => fetch(`/api/history/i-code/${encodeURIComponent(iCode)}?range=${activeCampaignHistoryRange}`))
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
|
||||
Reference in New Issue
Block a user