feat: 优化数据操作体验,使用乐观更新

- 保存/删除操作立即更新UI,无需等待API
- 避免页面闪烁和数据重新加载
- API失败时自动恢复数据
This commit is contained in:
小龙 2026-04-06 18:05:05 +00:00
parent 4a16ce7707
commit 0dccb698fe

View File

@ -125,12 +125,16 @@ function App() {
}
if (editingCategory) {
await categoryApi.update(editingCategory.id, payload)
//
setCategories(categories.map(c => c.id === editingCategory.id ? { ...c, ...payload } : c))
} else {
await categoryApi.create(payload)
const res = await categoryApi.create(payload)
//
const newCat = res.data
setCategories([...categories, newCat])
}
setCategoryForm({ name: '', icon: 'folder', color: '#667eea', sort_order: 0 })
setEditingCategory(null)
loadData()
} catch (e) { console.error(e) } finally {
setSaving(false)
}
@ -138,11 +142,18 @@ function App() {
const deleteCategory = async (id) => {
if (!confirm('确定删除该分类?该分类下的网站将变为未分类状态。')) return
// UI
const catName = categories.find(c => c.id === id)?.name
setCategories(categories.filter(c => c.id !== id))
//
setWebsites(websites.map(s => s.category_id === id ? { ...s, category_id: null, category: null } : s))
if (selectedCategory === id) setSelectedCategory(null)
try {
await categoryApi.delete(id)
if (selectedCategory === id) setSelectedCategory(null)
} catch (e) {
//
loadData()
} catch (e) {}
}
}
//
@ -151,13 +162,17 @@ function App() {
setSaving(true)
try {
const payload = { ...websiteForm, category_id: websiteForm.category_id || null }
const catName = categories.find(c => c.id === websiteForm.category_id)?.name
if (modal.data) {
await websiteApi.update(modal.data.id, payload)
//
setWebsites(websites.map(s => s.id === modal.data.id ? { ...s, ...payload, category: catName ? { name: catName } : null } : s))
} else {
await websiteApi.create(payload)
const res = await websiteApi.create(payload)
const newSite = { ...res.data, category: catName ? { name: catName } : null }
setWebsites([...websites, newSite])
}
closeModal()
loadData()
} catch (e) {} finally {
setSaving(false)
}
@ -165,10 +180,13 @@ function App() {
const handleDeleteWebsite = async (id) => {
if (!confirm('确定删除?')) return
//
setWebsites(websites.filter(s => s.id !== id))
try {
await websiteApi.delete(id)
} catch (e) {
loadData()
} catch (e) {}
}
}
const toggleFavorite = async (site) => {