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="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="restore-backup-btn" class="btn btn-secondary" style="margin-right: 10px;">📦 数据恢复</button>
|
||||
<button id="batch-delete-btn" class="btn btn-danger">批量删除</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -282,12 +283,44 @@
|
||||
</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(() => {
|
||||
document.getElementById('add-stock-btn')?.addEventListener('click', () => openModal());
|
||||
document.getElementById('import-stock-btn')?.addEventListener('click', () => showImportDialog());
|
||||
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('search-keyword')?.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') InitialStock.search();
|
||||
@ -623,6 +656,112 @@
|
||||
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 = {
|
||||
search,
|
||||
resetSearch,
|
||||
@ -635,6 +774,9 @@
|
||||
updateSelectAll,
|
||||
goPage,
|
||||
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