import { useLocalStorage, createSharedComposable } from "@vueuse/core"; export type Item = { id: number; brand?: string; name: string; imageUrl?: string | null; description?: string | null; tags?: string[] | null; createdAt?: Date; updatedAt?: Date; }; const _useItems = () => { const stored = useLocalStorage("item-database", [], { serializer: { read: (v) => { if (!v) return []; try { const parsed = JSON.parse(v); return (parsed ?? []).map((item: any) => ({ ...item, createdAt: item.createdAt ? new Date(item.createdAt) : new Date(), updatedAt: item.updatedAt ? new Date(item.updatedAt) : new Date(), // ensure tags is an array or null tags: item.tags ? Array.isArray(item.tags) ? item.tags : String(item.tags) .split(",") .map((t: string) => t.trim()) : null, })); } catch (err) { console.error("Failed to parse item-database from localStorage", err); return []; } }, write: (v) => { try { return JSON.stringify(v ?? []); } catch (err) { console.error("Failed to stringify item-database", err); return "[]"; } }, }, }); const items = stored; // Ref const stats = { totalItems: computed(() => (items.value ?? []).length), addedThisMonth: computed(() => { const now = new Date(); const thisMonth = now.getMonth(); const thisYear = now.getFullYear(); return (items.value ?? []).filter( (item) => item.createdAt?.getMonth() === thisMonth && item.createdAt?.getFullYear() === thisYear ).length; }), }; const utils = { // Get all brandings as array of strings allBrands: ref( (() => { const brands = new Set(); (items.value ?? []).forEach((item) => { if (item.brand) { brands.add(item.brand); } }); return Array.from(brands).sort((a, b) => a.localeCompare(b)); })() ), // Get all tags as array of strings getAllTags: () => { const tags = new Set(); (items.value ?? []).forEach((item) => { if (item.tags) { item.tags.forEach((tag) => tags.add(tag)); } }); return Array.from(tags).sort((a, b) => a.localeCompare(b)); }, }; const latestId = computed(() => { const arr = items.value ?? []; if (arr.length === 0) return 1; return Math.max(...arr.map((i) => i.id)) + 1; }); const isNameAvailable = (id: number | null, name: string, brand?: string) => { const list = items.value ?? []; return !list.some((item) => { // 跳过自己(编辑时) if (id && item.id === id) return false; // 有品牌时需同时匹配品牌 if (brand) { return item.name === name && item.brand === brand; } // 没品牌时只比较名字 return item.name === name; }); }; const addItem = (item: Item) => { item.id = latestId.value; item.createdAt = new Date(); item.updatedAt = new Date(); items.value = [...(items.value ?? []), item]; console.debug("[addItem] new length:", (items.value ?? []).length); }; const editItem = (item: Item) => { const index = (items.value ?? []).findIndex((i) => i.id === item.id); if (index === -1) return; const updatedItems = [...(items.value ?? [])]; updatedItems[index] = { ...updatedItems[index], ...item, updatedAt: new Date(), }; items.value = updatedItems; console.debug("[editItem] updated id:", item.id); }; const removeItem = (id: number) => { items.value = (items.value ?? []).filter((i) => i.id !== id); console.debug( "[removeItem] removed id:", id, "new length:", (items.value ?? []).length ); }; return { items, stats, utils, isNameAvailable, addItem, editItem, removeItem, }; }; export const useItemsStore = createSharedComposable(_useItems);