Files
bid-setup.tootaio.com/app/pages/index.vue
xiaomai e05c41eb07 feat(items): implement initial item management system
This commit introduces the foundational structure and core features for the item management application.

Key features implemented:
- **Item Listing:** A main page displaying all items in a searchable UTable.
- **Item Creation:** An "Add Item" modal with a UForm for input and validation using Zod.
- **State Management:** A `useItemsStore` composable built with `@vueuse/core`'s `useLocalStorage` to persist item data
in the browser.
- **UI Framework:** Integrated Nuxt UI for components like tables, modals, forms, and the overall layout.
- **Application Shell:** Established a default layout with a header, navigation menu, and placeholders for Export and
History pages.
- **Project Setup:** Configured pnpm workspace, Tailwind CSS, and VSCode editor settings for an improved development
experience.
2025-10-14 09:36:59 +08:00

196 lines
4.8 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>
<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">
<div
v-for="stat in statistics"
:key="stat.label"
class="bg-white rounded-xl p-6 shadow-sm border border-gray-100"
>
<div class="flex items-center">
<div
:class="`p-3 rounded-lg flex bg-${stat.color}-100 items-center`"
>
<Icon
class="w-6 h-6"
:name="stat.icon"
:style="`color: ${stat.color}`"
/>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">{{ stat.label }}</p>
<p class="text-2xl font-bold text-gray-900">{{ stat.value }}</p>
</div>
</div>
</div>
</div>
<!-- 列表 -->
<div class="flex flex-col flex-1 w-full">
<div
class="flex justify-between items-center gap-2 overflow-x-auto px-4 py-3.5 border-b border-accented"
>
<div>
<UInput
v-model="globalFilter"
class="max-w-sm"
placeholder="Search..."
icon="lucide:search"
/>
</div>
<div>
<ItemAddModal />
</div>
</div>
<UTable
v-model:global-filter="globalFilter"
:data="items"
:columns="itemColumns"
>
<template #name-cell="{ row }">
<div class="flex items-center gap-3">
<UAvatar
:src="row.original.imageUrl"
size="lg"
:alt="row.original.name"
/>
<div>
<p class="font-medium text-highlighted">
{{ row.original.name }}
</p>
</div>
</div>
</template>
<template #tags-cell="{ row }">
<div class="flex gap-2">
<UBadge v-for="tag in row.original.tags" variant="subtle">{{
tag
}}</UBadge>
</div>
</template>
<template #actions-cell="{ row }">
<div class="text-right">
<UDropdownMenu
:items="getRowItems(row)"
:content="{ align: 'end' }"
aria-label="Actions dropdown"
>
<UButton
icon="i-lucide-ellipsis-vertical"
color="neutral"
variant="ghost"
class="ml-auto"
aria-label="Actions dropdown"
/>
</UDropdownMenu>
</div>
</template>
</UTable>
</div>
</main>
</div>
</template>
<script lang="ts" setup>
import type { TableColumn, DropdownMenuItem } from "@nuxt/ui";
import type { Row } from "@tanstack/vue-table";
import { useClipboard } from "@vueuse/core";
const UBadge = resolveComponent("UBadge");
const UAvatar = resolveComponent("UAvatar");
const toast = useToast();
const { copy } = useClipboard();
const { items, stats } = useItemsStore();
// 统计
const statistics = [
{
icon: "lucide:box",
label: "总物品数",
value: stats.totalItems,
color: "blue",
},
{
icon: "lucide:package-plus",
label: "本月新增",
value: stats.addedThisMonth,
color: "green",
},
{
icon: "lucide:history",
label: "历史记录",
value: computed(() => 0),
color: "orange",
},
{
icon: "lucide:image",
label: "图片总数",
value: computed(() => 0),
color: "purple",
},
];
const globalFilter = ref<string>("");
const itemColumns: TableColumn<Item>[] = [
{
accessorKey: "id",
header: "#",
cell: ({ row }) => `#${row.getValue("id")}`,
},
{
accessorKey: "name",
header: "标品名称",
},
{
accessorKey: "tags",
header: "标签",
},
{
id: "actions",
header: "", // 不需要标题
},
];
function getRowItems(row: Row<Item>): DropdownMenuItem[] {
return [
{ type: "label", label: "操作" },
{
label: "编辑",
color: "primary",
icon: "lucide:pencil-line",
onSelect() {
copy(row.original.id.toString());
toast.add({
title: "Payment ID copied to clipboard!",
color: "success",
icon: "i-lucide-circle-check",
});
},
},
{ type: "separator" },
{ label: "删除", color: "error", icon: "lucide:trash-2" },
] as const;
}
// Define Shortcut
defineShortcuts({
meta_i: () => {
console.log("Shortcut works!");
},
});
</script>
<style></style>