355 lines
12 KiB
HTML
355 lines
12 KiB
HTML
{% extends "ewoooc_base.html" %}
|
||
|
||
{% block title %}系統設定與資料匯入 - EwoooC{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.system-import-page {
|
||
display: grid;
|
||
gap: var(--momo-space-4, 16px);
|
||
}
|
||
|
||
.system-import-page .system-import-head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: var(--momo-space-4, 16px);
|
||
padding: var(--momo-space-4, 16px) var(--momo-space-5, 24px);
|
||
background: var(--momo-bg-surface);
|
||
border: 1px solid var(--momo-border-light);
|
||
border-radius: var(--momo-radius-md);
|
||
}
|
||
|
||
.system-import-page .system-import-head h1 {
|
||
margin: 0;
|
||
color: var(--momo-text-primary);
|
||
font-family: var(--momo-font-display);
|
||
font-size: var(--momo-text-headline);
|
||
font-weight: 700;
|
||
line-height: var(--momo-line-height-tight);
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.system-import-page .system-import-head p {
|
||
margin: var(--momo-space-1, 4px) 0 0;
|
||
color: var(--momo-text-secondary);
|
||
font-size: var(--momo-text-body-sm);
|
||
}
|
||
|
||
.system-import-page .table-container {
|
||
padding: var(--momo-space-5, 24px);
|
||
background: var(--momo-bg-surface);
|
||
border: 1px solid var(--momo-border-light);
|
||
border-radius: var(--momo-radius-md);
|
||
}
|
||
|
||
.system-import-page .table-container h5 {
|
||
color: var(--momo-text-primary);
|
||
font-family: var(--momo-font-display);
|
||
font-size: var(--momo-text-title);
|
||
font-weight: 700;
|
||
}
|
||
|
||
.system-import-page .import-panel {
|
||
padding: var(--momo-space-4, 16px);
|
||
background: var(--momo-bg-paper);
|
||
border: 1px solid var(--momo-border-light);
|
||
border-radius: var(--momo-radius-md);
|
||
}
|
||
|
||
.system-import-page .system-version-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
align-self: flex-start;
|
||
min-height: 24px;
|
||
padding: 4px 10px;
|
||
background: var(--momo-tag-terra-bg);
|
||
border: 1px solid var(--momo-tag-terra-border);
|
||
border-radius: var(--momo-radius-sm);
|
||
color: var(--momo-tag-terra-text);
|
||
font-family: var(--momo-font-mono, monospace);
|
||
font-size: var(--momo-text-label);
|
||
font-weight: 800;
|
||
}
|
||
|
||
@media (max-width: 760px) {
|
||
.system-import-page .system-import-head,
|
||
.system-import-page .table-container .d-flex {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.system-import-page .table-container {
|
||
padding: var(--momo-space-4, 16px);
|
||
}
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="system-import-page">
|
||
<header class="system-import-head">
|
||
<div>
|
||
<h1><i class="fas fa-cogs me-2"></i>系統設定與資料匯入</h1>
|
||
<p>先補齊業績與備份,讓分析、建議與解法有可靠資料。</p>
|
||
</div>
|
||
<span class="system-version-pill">版本 {{ system_version }}</span>
|
||
</header>
|
||
|
||
<div class="table-container">
|
||
<h5 class="mb-3">系統維護</h5>
|
||
<div class="d-flex align-items-center justify-content-between gap-3">
|
||
<div>
|
||
<h6 class="fw-bold mb-1">營運資料備份</h6>
|
||
<p class="text-muted mb-0 small">上線或匯入前建立備份,保留可回復的業績與設定狀態。</p>
|
||
</div>
|
||
<button class="btn btn-outline-secondary" onclick="triggerBackup()">
|
||
<i class="fas fa-file-archive me-2"></i>建立備份
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<h5 class="mb-3 text-primary"><i class="fas fa-file-import me-2"></i>月份總表數據分析匯入</h5>
|
||
<div class="import-panel">
|
||
<div class="row align-items-center g-3">
|
||
<div class="col-md-9">
|
||
<h6 class="fw-bold mb-1">月份業績匯總分析(標準版)</h6>
|
||
<p class="text-muted small mb-3">匯入月結業績,更新成長、毛利與品類結構判斷。</p>
|
||
<input class="form-control" type="file" id="monthlySummaryFile" accept=".xlsx, .xls">
|
||
</div>
|
||
<div class="col-md-3 text-end">
|
||
<button class="btn btn-primary w-100 py-2 mt-3 mt-md-0" onclick="uploadMonthlySummary()">
|
||
<i class="fas fa-upload me-2"></i>執行大批量匯入
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="monthlyImportProgress" class="mt-3 d-none">
|
||
<div class="progress" style="height: 10px;">
|
||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 100%"></div>
|
||
</div>
|
||
<p class="text-center small text-primary mt-2 mb-0">資料處理中,請稍候...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<h5 class="mb-3"><i class="fas fa-file-invoice-dollar me-2"></i>即時業績(全月)匯入</h5>
|
||
<div class="import-panel">
|
||
<div class="row align-items-center g-3">
|
||
<div class="col-md-9">
|
||
<h6 class="fw-bold mb-1">匯入即時業績 (全月版)</h6>
|
||
<p class="text-muted small mb-3">匯入全月即時業績,補齊當日與月度分析來源。</p>
|
||
<input class="form-control" type="file" id="salesReportFile" accept=".xlsx, .xls">
|
||
</div>
|
||
<div class="col-md-3 text-end">
|
||
<button class="btn btn-outline-primary w-100 py-2 mt-3 mt-md-0" onclick="uploadSalesReport()">
|
||
<i class="fas fa-upload me-2"></i>執行業績匯入
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<h5 class="mb-3">一般 Excel 匯入</h5>
|
||
<div class="row align-items-end g-3">
|
||
<div class="col-md-8">
|
||
<label for="excelFile" class="form-label text-muted small">匯入指定 Excel,補齊分析或營運需要的資料集。</label>
|
||
<input class="form-control" type="file" id="excelFile" accept=".xlsx, .xls">
|
||
</div>
|
||
<div class="col-md-4">
|
||
<button class="btn btn-primary w-100" onclick="uploadExcel()">
|
||
<i class="fas fa-file-import me-2"></i>匯入營運資料
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
<script>
|
||
function getCSRFToken() {
|
||
return document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
}
|
||
|
||
function triggerBackup() {
|
||
if (confirm('確定要建立營運資料備份嗎?\n會保留目前程式與資料狀態,方便必要時回復。')) {
|
||
fetch('/api/backup', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCSRFToken()
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.status === 'success') {
|
||
alert(data.message);
|
||
if (data.download_url) {
|
||
window.location.href = data.download_url;
|
||
}
|
||
} else {
|
||
alert('錯誤: ' + data.message);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('備份請求失敗,請檢查日誌。');
|
||
});
|
||
}
|
||
}
|
||
|
||
function uploadSalesReport() {
|
||
const fileInput = document.getElementById('salesReportFile');
|
||
const file = fileInput.files[0];
|
||
|
||
if (!file) {
|
||
alert('請先選擇一個業績報表檔案');
|
||
return;
|
||
}
|
||
|
||
if (!file.name.includes('即時業績') || !file.name.includes('全月')) {
|
||
if (!confirm('檔案名稱似乎不符合「即時業績(全月)」的格式,確定要繼續匯入嗎?\n系統會嘗試辨識檔案內容並更新業績資料。')) {
|
||
return;
|
||
}
|
||
} else if (!confirm('確定要匯入此份業績報表嗎?\n匯入後會更新月度業績判斷,供成長分析與報表使用。')) {
|
||
return;
|
||
}
|
||
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
|
||
const btn = document.querySelector('button[onclick="uploadSalesReport()"]');
|
||
const originalText = btn.innerHTML;
|
||
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>匯入中...';
|
||
btn.disabled = true;
|
||
|
||
fetch('/api/import_excel', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCSRFToken()
|
||
},
|
||
body: formData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.status === 'success') {
|
||
if (data.table === 'realtime_sales_monthly') {
|
||
alert('業績報表匯入成功!\n共 ' + data.rows + ' 筆資料已更新,可回到分析頁確認結果。');
|
||
} else {
|
||
alert('匯入操作完成。\n系統偵測到資料落點與預期不同,請確認月度分析是否已更新。\n共寫入 ' + data.rows + ' 筆資料。');
|
||
}
|
||
fileInput.value = '';
|
||
} else {
|
||
alert('匯入失敗: ' + data.message);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('發生系統錯誤,請檢查日誌');
|
||
})
|
||
.finally(() => {
|
||
btn.innerHTML = originalText;
|
||
btn.disabled = false;
|
||
});
|
||
}
|
||
|
||
function uploadExcel() {
|
||
const fileInput = document.getElementById('excelFile');
|
||
const file = fileInput.files[0];
|
||
|
||
if (!file) {
|
||
alert('請先選擇一個 Excel 檔案');
|
||
return;
|
||
}
|
||
|
||
if (!confirm('確定要匯入嗎?\n系統會整理成可分析的營運資料集。')) {
|
||
return;
|
||
}
|
||
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
|
||
const btn = document.querySelector('button[onclick="uploadExcel()"]');
|
||
const originalText = btn.innerHTML;
|
||
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>處理中...';
|
||
btn.disabled = true;
|
||
|
||
fetch('/api/import_excel', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCSRFToken()
|
||
},
|
||
body: formData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.status === 'success') {
|
||
alert('匯入成功!\n共寫入 ' + data.rows + ' 筆資料,可回到分析頁確認結果。');
|
||
fileInput.value = '';
|
||
} else {
|
||
alert('匯入失敗: ' + data.message);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('發生系統錯誤,請檢查日誌');
|
||
})
|
||
.finally(() => {
|
||
btn.innerHTML = originalText;
|
||
btn.disabled = false;
|
||
});
|
||
}
|
||
|
||
function uploadMonthlySummary() {
|
||
const fileInput = document.getElementById('monthlySummaryFile');
|
||
const file = fileInput.files[0];
|
||
const progress = document.getElementById('monthlyImportProgress');
|
||
|
||
if (!file) {
|
||
alert('請先選擇一個月份總表 Excel 檔案');
|
||
return;
|
||
}
|
||
|
||
if (!confirm(`確定要匯入「${file.name}」嗎?\n系統會更新對應年月份的業績資料。\n匯入期間請勿關閉視窗。`)) {
|
||
return;
|
||
}
|
||
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
|
||
const btn = document.querySelector('button[onclick="uploadMonthlySummary()"]');
|
||
const originalText = btn.innerHTML;
|
||
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>匯入中...';
|
||
btn.disabled = true;
|
||
progress.classList.remove('d-none');
|
||
|
||
fetch('/api/import/monthly_summary', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': getCSRFToken()
|
||
},
|
||
body: formData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.status === 'success') {
|
||
alert('匯入完成:' + data.message);
|
||
fileInput.value = '';
|
||
} else {
|
||
alert('匯入失敗: ' + data.message);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('發生連線錯誤或系統超時,請檢查背景處理狀態。');
|
||
})
|
||
.finally(() => {
|
||
btn.innerHTML = originalText;
|
||
btn.disabled = false;
|
||
progress.classList.add('d-none');
|
||
});
|
||
}
|
||
</script>
|
||
{% endblock %}
|