From 8cc389630e1d53ac1accd99e26e41e462a29d20a Mon Sep 17 00:00:00 2001 From: xiaomai Date: Tue, 14 Oct 2025 14:09:36 +0800 Subject: [PATCH] fix(ui): ensure table updates correctly on data changes The UTable component was not reliably updating when items were added, edited, or removed due to a reactivity issue. This commit resolves the problem by forcing the table to re-render when its data source changes. - A `watch` with `{ deep: true }` is now used to monitor the `items` array for any changes. - A `tableKey` is incremented on each change and passed as a `:key` to `UTable`, triggering a re-mount. Additionally, data handling has been made more robust: - The localStorage serializer now gracefully handles parsing errors and ensures data integrity for dates and tags. - Added null-safe access for tags in the table template to prevent rendering errors. --- app/composables/items.ts | 57 ++++++++++++++++++++++++++++------------ app/pages/index.vue | 49 ++++++++++++++++++++++++---------- 2 files changed, 75 insertions(+), 31 deletions(-) diff --git a/app/composables/items.ts b/app/composables/items.ts index a1a69dc..9abd26a 100644 --- a/app/composables/items.ts +++ b/app/composables/items.ts @@ -15,21 +15,39 @@ const _useItems = () => { serializer: { read: (v) => { if (!v) return []; - const parsed = JSON.parse(v); - return parsed.map((item: any) => ({ - ...item, - createdAt: new Date(item.createdAt), - updatedAt: new Date(item.updatedAt), - })); + try { + const parsed = JSON.parse(v); + return (parsed ?? []).map((item: any) => ({ + ...item, + createdAt: item.createdAt ? new Date(item.createdAt) : new Date(), + updatedAt: item.updatedAt ? new Date(item.updatedAt) : new Date(), + // ensure tags is an array or null + tags: item.tags + ? Array.isArray(item.tags) + ? item.tags + : String(item.tags) + .split(",") + .map((t: string) => t.trim()) + : null, + })); + } catch (err) { + console.error("Failed to parse item-database from localStorage", err); + return []; + } + }, + write: (v) => { + try { + return JSON.stringify(v ?? []); + } catch (err) { + console.error("Failed to stringify item-database", err); + return "[]"; + } }, - write: (v) => JSON.stringify(v), }, }); - // 直接使用 stored(Ref)。模板会自动解包,其他逻辑也简单。 const items = stored; // Ref - // 统计项使用 items.value const stats = { totalItems: computed(() => (items.value ?? []).length), addedThisMonth: computed(() => { @@ -53,26 +71,31 @@ const _useItems = () => { !(items.value ?? []).some((item) => item.name === name); const addItem = (item: Item) => { - items.value = [...(items.value ?? []), item]; // <-- 替换数组引用 + items.value = [...(items.value ?? []), item]; + console.debug("[addItem] new length:", (items.value ?? []).length); }; const editItem = (item: Item) => { const index = (items.value ?? []).findIndex((i) => i.id === item.id); - if (index === -1) return; // 未找到则直接返回 - - // 生成一个新的数组(保持响应式) + if (index === -1) return; const updatedItems = [...(items.value ?? [])]; updatedItems[index] = { ...updatedItems[index], ...item, - updatedAt: new Date(), // 自动更新时间戳 + updatedAt: new Date(), }; - - items.value = updatedItems; // 替换引用,触发 UI 更新 + items.value = updatedItems; + console.debug("[editItem] updated id:", item.id); }; const removeItem = (id: number) => { - items.value = (items.value ?? []).filter((i) => i.id !== id); // <-- 替换数组引用 + items.value = (items.value ?? []).filter((i) => i.id !== id); + console.debug( + "[removeItem] removed id:", + id, + "new length:", + (items.value ?? []).length + ); }; return { items, stats, isNameAvailable, addItem, editItem, removeItem }; diff --git a/app/pages/index.vue b/app/pages/index.vue index ca70536..3543d3c 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -1,10 +1,5 @@