Files
bid-setup.tootaio.com/app/components/itemManage/EditModal.vue
xiaomai 3909f614d2 feat(ui): implement dashboard layout and refactor pages
This commit replaces the previous simple layout with a comprehensive dashboard interface using Nuxt UI Pro components.

- Introduced a new default layout with `UDashboardSidebar` and `UDashboardPanel`.
- Refactored the main page (`index.vue`) by splitting it into `ItemManageStatistics` and `ItemManageTable` components.
- Added new computed stats for monthly growth and total images, and displayed them in new statistic cards.
- Reorganized components from `item/` to a new `itemManage/` directory.
- Added custom primary and secondary theme colors.
2025-10-21 14:16:27 +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().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: "",
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;
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 = "";
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>