Files
bid-setup.tootaio.com/app/components/item/EditModal.vue
xiaomai 070aa8ff5f feat(item): add brand field to items
This commit introduces an optional 'brand' field to the item model, allowing for better organization and
differentiation.

- The Add/Edit modal now includes a form field to select or create a brand.
- The item list on the main page now displays the brand next to the item name.
- Name uniqueness validation is now brand-aware. An item name must be unique within the context of its brand.
- The `addItem` logic is centralized in the `useItemsStore` to handle ID and timestamp generation.
2025-10-14 21:11:59 +08:00

202 lines
5.5 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="品牌名称" name="brand">
<USelectMenu
v-model="itemState.brand"
:items="utils.allBrands.value"
placeholder="请输入物品品牌名(可选)"
class="w-full"
create-item
@create="(brand: string) => utils.allBrands.value.push(brand)"
/>
</UFormField>
<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 { utils, isNameAvailable, addItem, editItem, items } = useItemsStore();
// 🧩 基础校验(动态 schema
const baseSchema = z.object({
brand: z.string().optional(),
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
? isNameAvailable(currentId.value, data.name, data.brand)
: isNameAvailable(null, data.name, data.brand),
"该名称已被占用,请更换一个"
)
);
type ItemSchema = z.output<typeof baseSchema>;
const itemState = reactive<ItemSchema>({
brand: undefined,
name: "",
imageUrl: null,
description: null,
tags: null,
});
/** 🔧 打开 Modal新增 / 编辑) */
const openModal = (item?: Item) => {
if (item) {
// 编辑模式
isEditMode.value = true;
currentId.value = item.id;
itemState.brand = item.brand;
itemState.name = item.name;
itemState.imageUrl = item.imageUrl ?? null;
itemState.description = item.description ?? null;
itemState.tags = item.tags?.join(", ") ?? null;
} else {
// 新增模式
isEditMode.value = false;
itemState.brand = undefined;
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,
brand: event.data.brand,
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: 0, // 由添加函数处理
brand: event.data.brand,
name: event.data.name,
imageUrl: event.data.imageUrl,
description: event.data.description,
tags: event.data.tags?.split(",").map((tag) => tag.trim()),
};
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>