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.
This commit is contained in:
xiaomai
2025-10-14 14:09:36 +08:00
parent 1d2ce32ab9
commit 8cc389630e
2 changed files with 75 additions and 31 deletions

View File

@@ -1,10 +1,5 @@
<template>
<div>
<!-- <UPageHero
title="智能物品管理系统"
description="专业的物品数据库管理工具支持图片记录、CSV导出和历史价格追踪"
headline="New release"
/> -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- 统计 -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
@@ -50,9 +45,11 @@
</div>
</div>
<!-- UTable 一个 keytableKey用于在需要时强制重挂载 -->
<UTable
:key="tableKey"
v-model:global-filter="globalFilter"
:data="items"
:data="itemsList"
:columns="itemColumns"
>
<template #select-cell="{ row }">
@@ -78,11 +75,16 @@
</div>
</template>
<!-- tags 空值兜底,并加 key 避免重复 key 警告 -->
<template #tags-cell="{ row }">
<div class="flex gap-2">
<UBadge v-for="tag in row.original.tags" variant="subtle">{{
tag
}}</UBadge>
<UBadge
v-for="(tag, idx) in row.original.tags ?? []"
:key="`${row.original.id}-tag-${idx}`"
variant="subtle"
>
{{ tag }}
</UBadge>
</div>
</template>
@@ -115,6 +117,7 @@
</template>
<script lang="ts" setup>
import { computed, ref, reactive, watch } from "vue";
import type { TableColumn, DropdownMenuItem } from "@nuxt/ui";
import type { Row } from "@tanstack/vue-table";
@@ -127,16 +130,34 @@ const { items, stats } = useItemsStore();
const itemEditModal = ref<any>(null);
const deleteModal = reactive<{
open: boolean;
selectedItem: Item | null;
}>({
const deleteModal = reactive<{ open: boolean; selectedItem: Item | null }>({
open: false,
selectedItem: null,
});
const globalFilter = ref<string>("");
// --- computed 包装,保证传给 UTable 的始终是响应式且值随 items 变化
const itemsList = computed(() => items.value ?? []);
// --- 强制表格重挂载的 key当 items 改变时递增)
const tableKey = ref(0);
// 深度监听 items数组变化每次 items 内容或引用变化都让 tableKey++,从而触发 UTable 重新挂载
watch(
items,
() => {
tableKey.value += 1;
// Console debug运行时可在浏览器控制台查看
console.debug(
"[items watch] tableKey ->",
tableKey.value,
"items.length ->",
(items.value ?? []).length
);
},
{ deep: true }
);
// 统计
const statistics = [
{
@@ -221,7 +242,7 @@ function getRowItems(row: Row<Item>): DropdownMenuItem[] {
icon: "lucide:trash-2",
onSelect() {
deleteModal.selectedItem = row.original;
deleteModal.open = true; // 👈 打开 Modal
deleteModal.open = true;
toast.add({
title: `Deleting ${row.original.name}...`,
color: "error",