Files
bid-setup.tootaio.com/app/components/item/EditModal.vue
xiaomai 1d2ce32ab9 feat(item): implement item editing functionality
This commit introduces the capability to edit existing items. The `ItemEditModal` has been refactored to support both creation and editing modes.

- The modal UI (title, buttons) and submission logic now adapt based on whether an item is being added or edited.
- A new `editItem` function is added to the `useItemsStore` to handle the update logic.
- Form validation for the item name is enhanced to correctly check for uniqueness during edits.
- The table's row action menu now opens the modal in edit mode, pre-populating the form.
- The `latestId` generation logic is improved for robustness.
- Additionally, this commit adds row selection checkboxes to the items table.
2025-10-14 13:40:49 +08:00

193 lines
5.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<UModal
v-model:open="open"
:title="isEditMode ? '编辑物品' : '新增物品'"
:description="isEditMode ? '修改现有物品信息' : '新增一件新的物品'"
>
<UButton
label="新增"
icon="lucide:plus"
@click="openModal()"
/>
<template #body>
<UForm
ref="itemForm"
:schema="formSchema"
:state="itemState"
@submit="onSubmit"
class="space-y-4"
>
<UFormField label="标品名称" required name="name">
<UInput
v-model="itemState.name"
placeholder="请输入物品名称"
class="w-full"
/>
</UFormField>
<UFormField label="图片 URL">
<UInput
v-model="itemState.imageUrl"
placeholder="https://img.tootaio.com/i/2025/09/26/gxuf17.jpeg"
class="w-full"
/>
<template #hint>
<div class="text-xs text-gray-500">请粘贴您的图床图片链接</div>
</template>
</UFormField>
<UFormField label="描述">
<UTextarea
v-model="itemState.description"
placeholder="请输入物品描述(可选)"
class="w-full"
/>
</UFormField>
<UFormField label="标签">
<UInput
v-model="itemState.tags"
placeholder="Johnnie Walker,Whiskey"
class="w-full"
/>
<template #hint>
<div class="text-xs text-gray-500">用英文逗号隔开</div>
</template>
</UFormField>
</UForm>
</template>
<template #footer>
<div class="flex justify-end w-full gap-2">
<UButton
label="取消"
color="neutral"
variant="subtle"
@click="open = false"
/>
<UButton
:label="isEditMode ? '保存修改' : '创建'"
color="primary"
variant="solid"
@click="itemForm.submit()"
/>
</div>
</template>
</UModal>
</template>
<script lang="ts" setup>
import type { FormSubmitEvent } from "@nuxt/ui";
import * as z from "zod";
const itemForm = ref();
const open = ref<boolean>(false);
const isEditMode = ref<boolean>(false);
const currentId = ref<number | null>(null);
const toast = useToast();
const { stats, isNameAvailable, addItem, editItem, items } = useItemsStore();
// 🧩 基础校验(动态 schema
const baseSchema = z.object({
name: z.string().min(1, "名称不能为空"),
imageUrl: z.string().nullable().optional(),
description: z.string().nullable().optional(),
tags: z.string().nullable().optional(),
});
// ⚙️ 根据模式动态生成 Schema编辑模式允许当前名称
const formSchema = computed(() =>
baseSchema.refine(
(data) =>
isEditMode.value
? !items.value.some(
(i) => i.name === data.name && i.id !== currentId.value
)
: isNameAvailable(data.name),
"该名称已被占用,请更换一个"
)
);
type ItemSchema = z.output<typeof baseSchema>;
const itemState = reactive<ItemSchema>({
name: "",
imageUrl: null,
description: null,
tags: null,
});
/** 🔧 打开 Modal新增 / 编辑) */
const openModal = (item?: Item) => {
if (item) {
// 编辑模式
isEditMode.value = true;
currentId.value = item.id;
itemState.name = item.name;
itemState.imageUrl = item.imageUrl ?? null;
itemState.description = item.description ?? null;
itemState.tags = item.tags?.join(", ") ?? null;
} else {
// 新增模式
isEditMode.value = false;
currentId.value = null;
itemState.name = "";
itemState.imageUrl = null;
itemState.description = null;
itemState.tags = null;
}
open.value = true;
};
const onSubmit = async (event: FormSubmitEvent<ItemSchema>) => {
if (isEditMode.value && currentId.value !== null) {
// ✅ 编辑模式
const updatedItem: Item = {
id: currentId.value,
name: event.data.name,
imageUrl: event.data.imageUrl,
description: event.data.description,
tags: event.data.tags?.split(",").map((tag) => tag.trim()),
createdAt:
items.value.find((i) => i.id === currentId.value)?.createdAt ??
new Date(),
updatedAt: new Date(),
};
editItem(updatedItem);
toast.add({
title: "已保存修改",
description: `${event.data.name} 的信息已更新`,
color: "success",
});
} else {
// ✅ 新增模式
const newItem: Item = {
id: stats.latestId.value,
name: event.data.name,
imageUrl: event.data.imageUrl,
description: event.data.description,
tags: event.data.tags?.split(",").map((tag) => tag.trim()),
createdAt: new Date(),
updatedAt: new Date(),
};
addItem(newItem);
toast.add({
title: "成功",
description: `${event.data.name} 已添加到数据库中`,
color: "success",
});
}
open.value = false;
};
// expose method so parent can call modal.openModal(item)
defineExpose({ openModal });
</script>
<style></style>