add file
This commit is contained in:
parent
c11a70fdf2
commit
2e2b166e08
@ -91,6 +91,7 @@
|
|||||||
<button id="add-stock-btn" class="btn btn-primary" style="margin-right: 10px;">新增库存</button>
|
<button id="add-stock-btn" class="btn btn-primary" style="margin-right: 10px;">新增库存</button>
|
||||||
<button id="import-stock-btn" class="btn btn-secondary" style="margin-right: 10px;">导入 Excel</button>
|
<button id="import-stock-btn" class="btn btn-secondary" style="margin-right: 10px;">导入 Excel</button>
|
||||||
<button id="download-template-btn" class="btn btn-secondary" style="margin-right: 10px;">下载模板</button>
|
<button id="download-template-btn" class="btn btn-secondary" style="margin-right: 10px;">下载模板</button>
|
||||||
|
<button id="restore-backup-btn" class="btn btn-secondary" style="margin-right: 10px;">📦 数据恢复</button>
|
||||||
<button id="batch-delete-btn" class="btn btn-danger">批量删除</button>
|
<button id="batch-delete-btn" class="btn btn-danger">批量删除</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -282,12 +283,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 数据恢复弹窗 -->
|
||||||
|
<div id="restore-modal" class="modal" style="display:none;">
|
||||||
|
<div class="modal-content" style="max-width: 800px;">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>📦 数据恢复</h2>
|
||||||
|
<button class="modal-close" onclick="InitialStock.closeRestoreModal()">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div style="margin-bottom: 16px; padding: 12px; background: var(--warning-bg, #fff3cd); border-left: 4px solid var(--warning, #ffc107); border-radius: 4px;">
|
||||||
|
<strong>⚠️ 重要提示:</strong>
|
||||||
|
<ul style="margin: 8px 0 0 20px; padding: 0;">
|
||||||
|
<li>恢复数据将<strong>完全替换</strong>当前的期初库存数据</li>
|
||||||
|
<li>恢复前会自动备份当前数据,可以再次恢复</li>
|
||||||
|
<li>请仔细确认要恢复的备份版本</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 12px;">
|
||||||
|
<strong>可用备份列表:</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="backup-list" style="max-height: 400px; overflow-y: auto;">
|
||||||
|
<div style="text-align: center; padding: 40px; color: var(--text-3);">加载中...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-secondary" onclick="InitialStock.closeRestoreModal()">关闭</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.getElementById('add-stock-btn')?.addEventListener('click', () => openModal());
|
document.getElementById('add-stock-btn')?.addEventListener('click', () => openModal());
|
||||||
document.getElementById('import-stock-btn')?.addEventListener('click', () => showImportDialog());
|
document.getElementById('import-stock-btn')?.addEventListener('click', () => showImportDialog());
|
||||||
document.getElementById('download-template-btn')?.addEventListener('click', () => downloadTemplate());
|
document.getElementById('download-template-btn')?.addEventListener('click', () => downloadTemplate());
|
||||||
|
document.getElementById('restore-backup-btn')?.addEventListener('click', () => showRestoreDialog());
|
||||||
document.getElementById('batch-delete-btn')?.addEventListener('click', () => batchDelete());
|
document.getElementById('batch-delete-btn')?.addEventListener('click', () => batchDelete());
|
||||||
document.getElementById('search-keyword')?.addEventListener('keypress', (e) => {
|
document.getElementById('search-keyword')?.addEventListener('keypress', (e) => {
|
||||||
if (e.key === 'Enter') InitialStock.search();
|
if (e.key === 'Enter') InitialStock.search();
|
||||||
@ -623,6 +656,112 @@
|
|||||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function showRestoreDialog() {
|
||||||
|
document.getElementById('restore-modal').style.display = 'flex';
|
||||||
|
await loadBackupList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeRestoreModal() {
|
||||||
|
document.getElementById('restore-modal').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadBackupList() {
|
||||||
|
const container = document.getElementById('backup-list');
|
||||||
|
try {
|
||||||
|
const res = await API.get('/api/backups?table_name=initial_stock');
|
||||||
|
const backups = res.list || [];
|
||||||
|
|
||||||
|
if (backups.length === 0) {
|
||||||
|
container.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--text-3);">暂无备份记录</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = backups.map(backup => `
|
||||||
|
<div style="border: 1px solid var(--border); border-radius: 8px; padding: 16px; margin-bottom: 12px; background: white;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 12px;">
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<div style="font-weight: 600; font-size: 15px; margin-bottom: 8px;">
|
||||||
|
${getOperationText(backup.operation)} - ${backup.factory || '全部工厂'}
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 13px; color: var(--text-2); margin-bottom: 4px;">
|
||||||
|
📦 记录数量: <strong>${backup.record_count}</strong> 条
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 13px; color: var(--text-2); margin-bottom: 4px;">
|
||||||
|
👤 操作人: ${backup.created_by || '系统'}
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 13px; color: var(--text-2); margin-bottom: 4px;">
|
||||||
|
🕐 备份时间: ${formatTime(backup.created_at)}
|
||||||
|
</div>
|
||||||
|
${backup.description ? `<div style="font-size: 13px; color: var(--text-3); margin-top: 8px; padding: 8px; background: var(--surface); border-radius: 4px;">
|
||||||
|
💬 ${escapeHtml(backup.description)}
|
||||||
|
</div>` : ''}
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 8px; margin-left: 16px;">
|
||||||
|
<button class="btn btn-sm btn-primary" onclick="InitialStock.restoreBackup(${backup.id})">
|
||||||
|
恢复此版本
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-danger" onclick="InitialStock.deleteBackup(${backup.id})">
|
||||||
|
删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
} catch (e) {
|
||||||
|
container.innerHTML = `<div style="text-align: center; padding: 40px; color: var(--danger);">加载失败: ${e.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOperationText(operation) {
|
||||||
|
const map = {
|
||||||
|
'import': '📥 导入前备份',
|
||||||
|
'batch_delete': '🗑️ 批量删除前备份',
|
||||||
|
'restore': '🔄 恢复前备份',
|
||||||
|
'manual': '💾 手动备份'
|
||||||
|
};
|
||||||
|
return map[operation] || operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restoreBackup(backupId) {
|
||||||
|
if (!confirm('确定要恢复此备份吗?\n\n当前数据将被完全替换,但会自动备份当前数据。')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const overlay = document.getElementById('overlay');
|
||||||
|
overlay.classList.remove('hidden');
|
||||||
|
|
||||||
|
const res = await API.post(`/api/backups/${backupId}/restore`, {});
|
||||||
|
|
||||||
|
overlay.classList.add('hidden');
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
alert(res.message || '数据恢复成功');
|
||||||
|
closeRestoreModal();
|
||||||
|
loadList();
|
||||||
|
} else {
|
||||||
|
alert(res.error || '恢复失败');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
document.getElementById('overlay').classList.add('hidden');
|
||||||
|
alert('恢复失败: ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteBackup(backupId) {
|
||||||
|
if (!confirm('确定要删除此备份吗?此操作不可恢复。')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await API.delete(`/api/backups/${backupId}`);
|
||||||
|
alert('备份已删除');
|
||||||
|
await loadBackupList();
|
||||||
|
} catch (e) {
|
||||||
|
alert('删除失败: ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.InitialStock = {
|
window.InitialStock = {
|
||||||
search,
|
search,
|
||||||
resetSearch,
|
resetSearch,
|
||||||
@ -635,6 +774,9 @@
|
|||||||
updateSelectAll,
|
updateSelectAll,
|
||||||
goPage,
|
goPage,
|
||||||
closeImportModal,
|
closeImportModal,
|
||||||
importExcel
|
importExcel,
|
||||||
|
closeRestoreModal,
|
||||||
|
restoreBackup,
|
||||||
|
deleteBackup
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
BIN
server/data.db.backup_20260331_093801
Normal file
BIN
server/data.db.backup_20260331_093801
Normal file
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 602 KiB |
Loading…
Reference in New Issue
Block a user