diff --git a/src/app/api/public/products/route.ts b/src/app/api/public/products/route.ts index 418e826..35b4d4d 100644 --- a/src/app/api/public/products/route.ts +++ b/src/app/api/public/products/route.ts @@ -66,11 +66,8 @@ export async function GET(req: NextRequest) { totalDocs: preorders.totalDocs + products.totalDocs, limit, page, - totalPages: Math.ceil( - (preorders.totalDocs + products.totalDocs) / limit, - ), - hasNextPage: - page < Math.ceil((preorders.totalDocs + products.totalDocs) / limit), + totalPages: Math.ceil((preorders.totalDocs + products.totalDocs) / limit), + hasNextPage: page < Math.ceil((preorders.totalDocs + products.totalDocs) / limit), hasPrevPage: page > 1, } } else if (type === 'preorder') { diff --git a/src/components/fields/RelatedProductsField.tsx b/src/components/fields/RelatedProductsField.tsx index 1954600..a9c4dec 100644 --- a/src/components/fields/RelatedProductsField.tsx +++ b/src/components/fields/RelatedProductsField.tsx @@ -4,11 +4,14 @@ import { useState, useEffect, useCallback } from 'react' import type { RelationshipFieldClientComponent } from 'payload' /** - * 相关商品字段组件 - 保留原始格子搜索布局,使用 Payload 默认样式 + * 相关商品字段组件 - 支持多选和单选模式,搜索 products 和 preorder-products * 横向滚动显示搜索结果,支持实时搜索联想 */ export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, field }) => { - const { value, setValue } = useField({ path }) + const hasMany = field.hasMany !== false // 默认多选 + const relationTo = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo] + + const { value, setValue } = useField({ path }) const { config } = useConfig() const [inputValue, setInputValue] = useState('') @@ -19,7 +22,7 @@ export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, f // Fetch details for selected items useEffect(() => { const fetchSelectedDetails = async () => { - if (!value || value.length === 0) { + if (!value || (Array.isArray(value) && value.length === 0)) { setSelectedDetails([]) return } @@ -27,22 +30,30 @@ export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, f const ids = Array.isArray(value) ? value : [value as unknown as string] try { - const searchParams = new URLSearchParams() - ids.forEach((id, index) => { - const idStr = typeof id === 'object' ? (id as any).id : id - searchParams.append(`where[id][in][${index}]`, idStr) - }) + // Fetch from both collections + const allDocs: any[] = [] - const res = await fetch( - `${config.routes.api}/products?${searchParams.toString()}&limit=${ids.length}`, - ) - const data = await res.json() + for (const collection of relationTo) { + const searchParams = new URLSearchParams() + ids.forEach((id, index) => { + const idStr = typeof id === 'object' ? (id as any).value || (id as any).id : id + searchParams.append(`where[id][in][${index}]`, idStr) + }) - if (data.docs) { - const docsMap = new Map(data.docs.map((d: any) => [d.id, d])) + const res = await fetch( + `${config.routes.api}/${collection}?${searchParams.toString()}&limit=${ids.length}`, + ) + const data = await res.json() + if (data.docs) { + allDocs.push(...data.docs.map((d: any) => ({ ...d, _collection: collection }))) + } + } + + if (allDocs.length > 0) { + const docsMap = new Map(allDocs.map((d: any) => [d.id, d])) const ordered = ids .map((id) => { - const idStr = typeof id === 'object' ? (id as any).id : id + const idStr = typeof id === 'object' ? (id as any).value || (id as any).id : id return docsMap.get(idStr) }) .filter(Boolean) @@ -56,7 +67,7 @@ export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, f fetchSelectedDetails() }, [value, config.routes.api]) - // Search function with debounce + // Search function with debounce - search across all related collections const searchProducts = useCallback( async (term: string) => { if (!term) { @@ -66,11 +77,20 @@ export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, f setIsLoading(true) try { - const res = await fetch( - `${config.routes.api}/products?where[title][like]=${encodeURIComponent(term)}&limit=10`, - ) - const data = await res.json() - setSearchResults(data.docs || []) + const allResults: any[] = [] + + // Search in all relationTo collections + for (const collection of relationTo) { + const res = await fetch( + `${config.routes.api}/${collection}?where[title][like]=${encodeURIComponent(term)}&limit=5`, + ) + const data = await res.json() + if (data.docs) { + allResults.push(...data.docs.map((d: any) => ({ ...d, _collection: collection }))) + } + } + + setSearchResults(allResults) } catch (e) { console.error('Search error:', e) setSearchResults([]) @@ -78,7 +98,7 @@ export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, f setIsLoading(false) } }, - [config.routes.api], + [config.routes.api, relationTo], ) // Debounced search @@ -91,26 +111,52 @@ export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, f }, [inputValue, searchProducts]) const handleAdd = (product: any) => { + if (!hasMany) { + // Single select mode + const relationValue = { + relationTo: product._collection, + value: product.id, + } + setValue(relationValue as any) + setSelectedDetails([product]) + setInputValue('') + setSearchResults([]) + return + } + + // Multiple select mode const currentIds = Array.isArray(value) ? value : [] const exists = currentIds.some((id: any) => { - const idStr = typeof id === 'object' ? id.id : id + const idStr = typeof id === 'object' ? (id as any).value || (id as any).id : id return idStr === product.id }) if (!exists) { - setValue([...currentIds, product.id]) + const relationValue = { + relationTo: product._collection, + value: product.id, + } + setValue([...currentIds, relationValue] as any) setSelectedDetails((prev) => [...prev, product]) } setInputValue('') } const handleRemove = (idToRemove: string) => { + if (!hasMany) { + // Single select mode + setValue(null as any) + setSelectedDetails([]) + return + } + + // Multiple select mode const currentIds = Array.isArray(value) ? value : [] const newValue = currentIds.filter((id: any) => { - const idStr = typeof id === 'object' ? id.id : id + const idStr = typeof id === 'object' ? (id as any).value || (id as any).id : id return idStr !== idToRemove }) - setValue(newValue) + setValue(newValue as any) setSelectedDetails((prev) => prev.filter((p) => p.id !== idToRemove)) } @@ -188,7 +234,7 @@ export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, f {product.title}
- {product.status} + {product.status} • {product._collection === 'preorder-products' ? '预售' : '常规'}