This commit is contained in:
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.123"
|
||||
SYSTEM_VERSION = "V10.124"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -8,17 +8,17 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block ewooo_content %}
|
||||
{% set status_labels = {'pending': '待發送', 'sent': '已發送', 'failed': '失敗'} %}
|
||||
{% set status_labels = {'pending': '待發送', 'sent': '已發送', 'failed': '失敗', 'duplicate': '重複', 'unknown': '未標記'} %}
|
||||
<div class="stockout-list-stack">
|
||||
|
||||
<section class="stockout-header">
|
||||
<div>
|
||||
<div class="stockout-eyebrow momo-mono">
|
||||
<i class="fas fa-table-list"></i>STOCKOUT LIST
|
||||
<i class="fas fa-table-list"></i>缺貨清單 · 即時狀態
|
||||
</div>
|
||||
<h1 class="stockout-title">缺貨清單</h1>
|
||||
<p class="stockout-subtitle">
|
||||
依現有缺貨匯入資料呈現,可用批次、廠商、商品與發送狀態篩選;所有數字皆來自 vendor_stockout。
|
||||
依正式缺貨資料呈現,可用批次、廠商、商品與發送狀態篩選;桌機保留密度,手機改為逐筆資料卡。
|
||||
</p>
|
||||
</div>
|
||||
<div class="stockout-actions">
|
||||
@@ -31,6 +31,8 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="momo-dot-divider is-tight is-whisper is-accent" aria-hidden="true"></div>
|
||||
|
||||
<section class="stockout-kpi-grid" aria-label="缺貨清單統計">
|
||||
<div class="stockout-kpi">
|
||||
<div class="stockout-kpi-label momo-mono">符合筆數</div>
|
||||
@@ -62,6 +64,9 @@
|
||||
<section class="stockout-filter-card" aria-label="缺貨清單篩選">
|
||||
<form class="stockout-filter-form" method="GET" action="{{ url_for('vendor.list_page') }}">
|
||||
<input type="hidden" name="ui" value="v2">
|
||||
{% if current_status and current_status != 'all' %}
|
||||
<input type="hidden" name="status" value="{{ current_status }}">
|
||||
{% endif %}
|
||||
<input class="stockout-input" type="search" name="q" value="{{ search_query }}" placeholder="搜尋廠商、商品名稱或編號">
|
||||
<select class="stockout-select momo-mono" name="batch">
|
||||
<option value="">全部批次</option>
|
||||
@@ -77,6 +82,11 @@
|
||||
<option value="vendor_asc" {% if current_sort == 'vendor_asc' %}selected{% endif %}>廠商代碼</option>
|
||||
<option value="stockout_days_desc" {% if current_sort == 'stockout_days_desc' %}selected{% endif %}>缺貨天數</option>
|
||||
</select>
|
||||
<select class="stockout-select momo-mono" name="page_size" aria-label="每頁筆數">
|
||||
{% for size in [30, 50, 100] %}
|
||||
<option value="{{ size }}" {% if page_size == size %}selected{% endif %}>每頁 {{ size }} 筆</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button class="stockout-action is-primary" type="submit">
|
||||
<i class="fas fa-filter"></i>套用
|
||||
</button>
|
||||
@@ -85,15 +95,15 @@
|
||||
|
||||
<nav class="stockout-status-tabs" aria-label="缺貨狀態篩選">
|
||||
<a class="stockout-tab {% if current_status == 'all' %}is-active{% endif %}"
|
||||
href="{{ url_for('vendor.list_page', q=search_query, batch=current_batch, sort=current_sort) }}">全部</a>
|
||||
href="{{ url_for('vendor.list_page', q=search_query, batch=current_batch, sort=current_sort, page_size=page_size) }}">全部</a>
|
||||
<a class="stockout-tab {% if current_status == 'pending' %}is-active{% endif %}"
|
||||
href="{{ url_for('vendor.list_page', status='pending', q=search_query, batch=current_batch, sort=current_sort) }}">待發送</a>
|
||||
href="{{ url_for('vendor.list_page', status='pending', q=search_query, batch=current_batch, sort=current_sort, page_size=page_size) }}">待發送</a>
|
||||
<a class="stockout-tab {% if current_status == 'sent' %}is-active{% endif %}"
|
||||
href="{{ url_for('vendor.list_page', status='sent', q=search_query, batch=current_batch, sort=current_sort) }}">已發送</a>
|
||||
href="{{ url_for('vendor.list_page', status='sent', q=search_query, batch=current_batch, sort=current_sort, page_size=page_size) }}">已發送</a>
|
||||
<a class="stockout-tab {% if current_status == 'failed' %}is-active{% endif %}"
|
||||
href="{{ url_for('vendor.list_page', status='failed', q=search_query, batch=current_batch, sort=current_sort) }}">失敗</a>
|
||||
href="{{ url_for('vendor.list_page', status='failed', q=search_query, batch=current_batch, sort=current_sort, page_size=page_size) }}">失敗</a>
|
||||
<a class="stockout-tab {% if current_status == 'duplicate' %}is-active{% endif %}"
|
||||
href="{{ url_for('vendor.list_page', status='duplicate', q=search_query, batch=current_batch, sort=current_sort) }}">重複</a>
|
||||
href="{{ url_for('vendor.list_page', status='duplicate', q=search_query, batch=current_batch, sort=current_sort, page_size=page_size) }}">重複</a>
|
||||
</nav>
|
||||
|
||||
<section class="stockout-table-card" aria-label="缺貨資料表">
|
||||
@@ -124,33 +134,34 @@
|
||||
<tbody>
|
||||
{% for record in records %}
|
||||
{% set record_status = record.status or 'pending' %}
|
||||
{% set status_class = record_status if record_status in status_labels else 'unknown' %}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="stockout-chip is-{{ record_status }} momo-mono">
|
||||
<td data-label="狀態">
|
||||
<span class="stockout-chip is-{{ status_class }} momo-mono">
|
||||
{{ status_labels.get(record_status, record_status) }}
|
||||
</span>
|
||||
{% if record.is_duplicate %}
|
||||
<div class="stockout-product-code momo-mono">重複 {{ record.duplicate_count or 0 }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<td data-label="商品">
|
||||
<div class="stockout-product-name" title="{{ record.product_name }}">{{ record.product_name }}</div>
|
||||
<div class="stockout-product-code momo-mono">{{ record.product_code }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<td data-label="廠商">
|
||||
<div class="stockout-vendor-name" title="{{ record.vendor_name }}">{{ record.vendor_name }}</div>
|
||||
<div class="stockout-vendor-code momo-mono">{{ record.vendor_code }}</div>
|
||||
</td>
|
||||
<td class="momo-mono">{{ record.batch_id }}</td>
|
||||
<td class="momo-mono">{{ record.current_stock if record.current_stock is not none else '—' }}</td>
|
||||
<td class="momo-mono">{{ record.stockout_date.strftime('%Y-%m-%d') if record.stockout_date else '—' }}</td>
|
||||
<td class="momo-mono">{{ record.stockout_days if record.stockout_days is not none else '—' }}</td>
|
||||
<td class="momo-mono">
|
||||
<td class="momo-mono" data-label="批次">{{ record.batch_id }}</td>
|
||||
<td class="momo-mono" data-label="庫存">{{ record.current_stock if record.current_stock is not none else '—' }}</td>
|
||||
<td class="momo-mono" data-label="缺貨日期">{{ record.stockout_date.strftime('%Y-%m-%d') if record.stockout_date else '—' }}</td>
|
||||
<td class="momo-mono" data-label="缺貨天數">{{ record.stockout_days if record.stockout_days is not none else '—' }}</td>
|
||||
<td class="momo-mono" data-label="30 日業績">
|
||||
{% if record.monthly_sales_amount is not none %}
|
||||
${{ record.monthly_sales_amount | int | number_format }}
|
||||
{% else %}—{% endif %}
|
||||
</td>
|
||||
<td class="momo-mono">{{ record.created_at.strftime('%Y-%m-%d %H:%M') if record.created_at else '—' }}</td>
|
||||
<td class="momo-mono" data-label="建立時間">{{ record.created_at.strftime('%Y-%m-%d %H:%M') if record.created_at else '—' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
.stockout-list-stack {
|
||||
display: grid;
|
||||
gap: 22px;
|
||||
min-width: 0;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.stockout-header {
|
||||
@@ -46,6 +47,7 @@
|
||||
}
|
||||
|
||||
.stockout-subtitle {
|
||||
max-width: 720px;
|
||||
margin: 8px 0 0;
|
||||
color: var(--momo-text-secondary);
|
||||
font-size: 13px;
|
||||
@@ -98,6 +100,7 @@
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.stockout-kpi,
|
||||
@@ -108,7 +111,11 @@
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.stockout-kpi { padding: 16px; }
|
||||
.stockout-kpi {
|
||||
min-width: 0;
|
||||
padding: 16px;
|
||||
box-shadow: inset 3px 0 0 var(--momo-border-strong);
|
||||
}
|
||||
|
||||
.stockout-kpi-label {
|
||||
color: var(--momo-text-secondary);
|
||||
@@ -137,7 +144,7 @@
|
||||
|
||||
.stockout-filter-form {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 1.4fr) minmax(180px, 0.9fr) minmax(160px, 0.8fr) auto;
|
||||
grid-template-columns: minmax(220px, 1.6fr) minmax(180px, 0.9fr) minmax(160px, 0.8fr) minmax(130px, 0.62fr) auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -183,7 +190,10 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.stockout-table-wrap { overflow-x: auto; }
|
||||
.stockout-table-wrap {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.stockout-table {
|
||||
width: 100%;
|
||||
@@ -256,6 +266,18 @@
|
||||
border-color: rgba(214, 83, 68, 0.2);
|
||||
}
|
||||
|
||||
.stockout-chip.is-duplicate {
|
||||
color: var(--momo-warning-text);
|
||||
background: var(--momo-tag-honey-bg);
|
||||
border-color: var(--momo-tag-honey-border);
|
||||
}
|
||||
|
||||
.stockout-chip.is-unknown {
|
||||
color: var(--momo-text-tertiary);
|
||||
background: var(--momo-bg-paper);
|
||||
border-color: var(--momo-border-light);
|
||||
}
|
||||
|
||||
.stockout-empty {
|
||||
padding: 34px;
|
||||
color: var(--momo-text-secondary);
|
||||
@@ -358,6 +380,82 @@
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.stockout-table-wrap {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.stockout-table {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.stockout-table,
|
||||
.stockout-table thead,
|
||||
.stockout-table tbody,
|
||||
.stockout-table tr,
|
||||
.stockout-table td {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stockout-table thead {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.stockout-table tbody {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
background: var(--momo-bg-paper);
|
||||
}
|
||||
|
||||
.stockout-table tr {
|
||||
background: var(--momo-bg-surface);
|
||||
border: 1px solid var(--momo-border-light);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stockout-table td {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(86px, 0.36fr) minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
align-items: start;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--momo-border-light);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.stockout-table td:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.stockout-table td::before {
|
||||
content: attr(data-label);
|
||||
color: var(--momo-text-tertiary);
|
||||
font-family: var(--momo-font-mono);
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.stockout-product-name,
|
||||
.stockout-vendor-name {
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.stockout-pagination {
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.stockout-page-link {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.stockout-title { font-size: 26px; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user