重新布局
This commit is contained in:
parent
b4991fcefd
commit
84f94b53d9
|
|
@ -1,5 +1,5 @@
|
||||||
import { ThumbnailCell as ThumbnailCell_a0b2acb813359aec894b6644d7c3bfd2 } from '../../../components/products/ThumbnailCell'
|
import { ThumbnailCell as ThumbnailCell_c4ec43b3e74df5c75a3fb90c93e06b1d } from '../../../components/cells/ThumbnailCell'
|
||||||
import { ThumbnailField as ThumbnailField_ba44ab32cac4d742a03a48fb6960602e } from '../../../components/products/ThumbnailField'
|
import { ThumbnailField as ThumbnailField_0d2fbe11370060d58b3925e5dbbb79d6 } from '../../../components/fields/ThumbnailField'
|
||||||
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
|
|
@ -24,15 +24,16 @@ import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93
|
||||||
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { SyncMedusaButton as SyncMedusaButton_8c90663551920f0510ea531726668adc } from '../../../components/products/SyncMedusaButton'
|
import { RelatedProductsField as RelatedProductsField_f3e26ca26ab1ef52a2ee0f6932180426 } from '../../../components/fields/RelatedProductsField'
|
||||||
import { default as default_3fd1353246fc8a459244c8dc11f58470 } from '../../../components/products/ProductGridStyler'
|
import { SyncMedusaButton as SyncMedusaButton_31e6578e170fdd0bad7013c8202d6e08 } from '../../../components/sync/SyncMedusaButton'
|
||||||
import { ForceSyncButton as ForceSyncButton_86f9d5df4f20495427521354d06db618 } from '../../../components/products/ForceSyncButton'
|
import { default as default_c2e3814fe427263135b1f5931c37f6f2 } from '../../../components/list/ProductGridStyler'
|
||||||
|
import { ForceSyncButton as ForceSyncButton_28396efe36d6238add95cf44109e281c } from '../../../components/sync/ForceSyncButton'
|
||||||
import { S3ClientUploadHandler as S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24 } from '@payloadcms/storage-s3/client'
|
import { S3ClientUploadHandler as S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24 } from '@payloadcms/storage-s3/client'
|
||||||
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
||||||
|
|
||||||
export const importMap = {
|
export const importMap = {
|
||||||
"/components/products/ThumbnailCell#ThumbnailCell": ThumbnailCell_a0b2acb813359aec894b6644d7c3bfd2,
|
"/components/cells/ThumbnailCell#ThumbnailCell": ThumbnailCell_c4ec43b3e74df5c75a3fb90c93e06b1d,
|
||||||
"/components/products/ThumbnailField#ThumbnailField": ThumbnailField_ba44ab32cac4d742a03a48fb6960602e,
|
"/components/fields/ThumbnailField#ThumbnailField": ThumbnailField_0d2fbe11370060d58b3925e5dbbb79d6,
|
||||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
|
|
@ -57,9 +58,10 @@ export const importMap = {
|
||||||
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"/components/products/SyncMedusaButton#SyncMedusaButton": SyncMedusaButton_8c90663551920f0510ea531726668adc,
|
"/components/fields/RelatedProductsField#RelatedProductsField": RelatedProductsField_f3e26ca26ab1ef52a2ee0f6932180426,
|
||||||
"/components/products/ProductGridStyler#default": default_3fd1353246fc8a459244c8dc11f58470,
|
"/components/sync/SyncMedusaButton#SyncMedusaButton": SyncMedusaButton_31e6578e170fdd0bad7013c8202d6e08,
|
||||||
"/components/products/ForceSyncButton#ForceSyncButton": ForceSyncButton_86f9d5df4f20495427521354d06db618,
|
"/components/list/ProductGridStyler#default": default_c2e3814fe427263135b1f5931c37f6f2,
|
||||||
|
"/components/sync/ForceSyncButton#ForceSyncButton": ForceSyncButton_28396efe36d6238add95cf44109e281c,
|
||||||
"@payloadcms/storage-s3/client#S3ClientUploadHandler": S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24,
|
"@payloadcms/storage-s3/client#S3ClientUploadHandler": S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24,
|
||||||
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,11 @@ export const Products: CollectionConfig = {
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
edit: {
|
edit: {
|
||||||
PreviewButton: '/components/products/ForceSyncButton#ForceSyncButton',
|
PreviewButton: '/components/sync/ForceSyncButton#ForceSyncButton',
|
||||||
},
|
},
|
||||||
beforeListTable: [
|
beforeListTable: [
|
||||||
'/components/products/SyncMedusaButton#SyncMedusaButton',
|
'/components/sync/SyncMedusaButton#SyncMedusaButton',
|
||||||
'/components/products/ProductGridStyler',
|
'/components/list/ProductGridStyler',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -103,8 +103,8 @@ export const Products: CollectionConfig = {
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
width: '20%',
|
width: '20%',
|
||||||
components: {
|
components: {
|
||||||
Cell: '/components/products/ThumbnailCell#ThumbnailCell',
|
Cell: '/components/cells/ThumbnailCell#ThumbnailCell',
|
||||||
Field: '/components/products/ThumbnailField#ThumbnailField',
|
Field: '/components/fields/ThumbnailField#ThumbnailField',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -131,7 +131,8 @@ export const Products: CollectionConfig = {
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
options: ['noopener', 'noreferrer', 'nofollow'],
|
options: ['noopener', 'noreferrer', 'nofollow'],
|
||||||
admin: {
|
admin: {
|
||||||
description: 'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
|
description:
|
||||||
|
'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -170,7 +171,10 @@ export const Products: CollectionConfig = {
|
||||||
relationTo: 'products',
|
relationTo: 'products',
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: {
|
admin: {
|
||||||
description: '相关商品',
|
description: '相关商品,支持搜索联想',
|
||||||
|
components: {
|
||||||
|
Field: '/components/fields/RelatedProductsField#RelatedProductsField',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,377 @@
|
||||||
|
'use client'
|
||||||
|
import { useField, useConfig, FieldLabel } from '@payloadcms/ui'
|
||||||
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
|
import type { RelationshipFieldClientComponent } from 'payload'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 相关商品字段组件 - 保留原始格子搜索布局,使用 Payload 默认样式
|
||||||
|
* 横向滚动显示搜索结果,支持实时搜索联想
|
||||||
|
*/
|
||||||
|
export const RelatedProductsField: RelationshipFieldClientComponent = ({ path, field }) => {
|
||||||
|
const { value, setValue } = useField<string[]>({ path })
|
||||||
|
const { config } = useConfig()
|
||||||
|
|
||||||
|
const [inputValue, setInputValue] = useState('')
|
||||||
|
const [searchResults, setSearchResults] = useState<any[]>([])
|
||||||
|
const [selectedDetails, setSelectedDetails] = useState<any[]>([])
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
|
||||||
|
// Fetch details for selected items
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSelectedDetails = async () => {
|
||||||
|
if (!value || value.length === 0) {
|
||||||
|
setSelectedDetails([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await fetch(
|
||||||
|
`${config.routes.api}/products?${searchParams.toString()}&limit=${ids.length}`,
|
||||||
|
)
|
||||||
|
const data = await res.json()
|
||||||
|
|
||||||
|
if (data.docs) {
|
||||||
|
const docsMap = new Map(data.docs.map((d: any) => [d.id, d]))
|
||||||
|
const ordered = ids
|
||||||
|
.map((id) => {
|
||||||
|
const idStr = typeof id === 'object' ? (id as any).id : id
|
||||||
|
return docsMap.get(idStr)
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
setSelectedDetails(ordered)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error fetching selected products:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchSelectedDetails()
|
||||||
|
}, [value, config.routes.api])
|
||||||
|
|
||||||
|
// Search function with debounce
|
||||||
|
const searchProducts = useCallback(
|
||||||
|
async (term: string) => {
|
||||||
|
if (!term) {
|
||||||
|
setSearchResults([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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 || [])
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Search error:', e)
|
||||||
|
setSearchResults([])
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[config.routes.api],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Debounced search
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
searchProducts(inputValue)
|
||||||
|
}, 300)
|
||||||
|
|
||||||
|
return () => clearTimeout(timer)
|
||||||
|
}, [inputValue, searchProducts])
|
||||||
|
|
||||||
|
const handleAdd = (product: any) => {
|
||||||
|
const currentIds = Array.isArray(value) ? value : []
|
||||||
|
const exists = currentIds.some((id: any) => {
|
||||||
|
const idStr = typeof id === 'object' ? id.id : id
|
||||||
|
return idStr === product.id
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
setValue([...currentIds, product.id])
|
||||||
|
setSelectedDetails((prev) => [...prev, product])
|
||||||
|
}
|
||||||
|
setInputValue('')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemove = (idToRemove: string) => {
|
||||||
|
const currentIds = Array.isArray(value) ? value : []
|
||||||
|
const newValue = currentIds.filter((id: any) => {
|
||||||
|
const idStr = typeof id === 'object' ? id.id : id
|
||||||
|
return idStr !== idToRemove
|
||||||
|
})
|
||||||
|
setValue(newValue)
|
||||||
|
setSelectedDetails((prev) => prev.filter((p) => p.id !== idToRemove))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: 'var(--base)' }}>
|
||||||
|
<FieldLabel field={field} />
|
||||||
|
|
||||||
|
{/* Selected Items Grid - 网格显示已选商品 */}
|
||||||
|
{selectedDetails.length > 0 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))',
|
||||||
|
gap: 'var(--base)',
|
||||||
|
marginBottom: 'var(--base)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{selectedDetails.map((product) => (
|
||||||
|
<div
|
||||||
|
key={product.id}
|
||||||
|
style={{
|
||||||
|
border: '1px solid var(--theme-elevation-150)',
|
||||||
|
borderRadius: 'var(--border-radius-m)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
background: 'var(--theme-elevation-50)',
|
||||||
|
position: 'relative',
|
||||||
|
transition: 'all 0.2s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'var(--theme-elevation-400)'
|
||||||
|
e.currentTarget.style.background = 'var(--theme-elevation-100)'
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'var(--theme-elevation-150)'
|
||||||
|
e.currentTarget.style.background = 'var(--theme-elevation-50)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '120px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
background: 'var(--theme-elevation-100)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{product.thumbnail ? (
|
||||||
|
<img
|
||||||
|
src={product.thumbnail}
|
||||||
|
alt={product.title}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
objectFit: 'cover',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: '12px', color: 'var(--theme-elevation-500)' }}>
|
||||||
|
无图片
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div style={{ padding: 'calc(var(--base) / 2)' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 500,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
color: 'var(--theme-text)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{product.title}
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: '11px', color: 'var(--theme-elevation-500)' }}>
|
||||||
|
{product.status}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleRemove(product.id)}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '4px',
|
||||||
|
right: '4px',
|
||||||
|
background: 'var(--theme-elevation-0)',
|
||||||
|
border: '1px solid var(--theme-elevation-400)',
|
||||||
|
borderRadius: '50%',
|
||||||
|
width: '24px',
|
||||||
|
height: '24px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: 'var(--theme-text)',
|
||||||
|
opacity: 0.8,
|
||||||
|
fontSize: '16px',
|
||||||
|
lineHeight: '1',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.opacity = '1'
|
||||||
|
e.currentTarget.style.background = 'var(--theme-error-100)'
|
||||||
|
e.currentTarget.style.color = 'var(--theme-error-600)'
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.opacity = '0.8'
|
||||||
|
e.currentTarget.style.background = 'var(--theme-elevation-0)'
|
||||||
|
e.currentTarget.style.color = 'var(--theme-text)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Search Input - 搜索输入框 */}
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={inputValue}
|
||||||
|
onChange={(e) => setInputValue(e.target.value)}
|
||||||
|
placeholder="搜索商品..."
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: 'calc(var(--base) / 2) var(--base)',
|
||||||
|
borderRadius: 'var(--border-radius-m)',
|
||||||
|
border: '1px solid var(--theme-elevation-400)',
|
||||||
|
background: 'var(--theme-input-bg)',
|
||||||
|
color: 'var(--theme-text)',
|
||||||
|
fontFamily: 'inherit',
|
||||||
|
fontSize: '1rem',
|
||||||
|
}}
|
||||||
|
onFocus={() => inputValue && searchProducts(inputValue)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Horizontal Scroll Results - 横向滚动搜索结果(保留原始格子布局) */}
|
||||||
|
{inputValue && searchResults.length > 0 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 'calc(var(--base) / 2)',
|
||||||
|
display: 'flex',
|
||||||
|
overflowX: 'auto',
|
||||||
|
gap: 'var(--base)',
|
||||||
|
padding: 'calc(var(--base) / 2) 0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{searchResults.map((product) => (
|
||||||
|
<div
|
||||||
|
key={product.id}
|
||||||
|
onClick={() => handleAdd(product)}
|
||||||
|
style={{
|
||||||
|
flex: '0 0 160px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
border: '1px solid var(--theme-elevation-150)',
|
||||||
|
borderRadius: 'var(--border-radius-m)',
|
||||||
|
background: 'var(--theme-elevation-50)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
transition: 'all 0.2s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'var(--theme-primary-500)'
|
||||||
|
e.currentTarget.style.background = 'var(--theme-elevation-100)'
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'var(--theme-elevation-150)'
|
||||||
|
e.currentTarget.style.background = 'var(--theme-elevation-50)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: '120px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
background: 'var(--theme-elevation-100)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{product.thumbnail ? (
|
||||||
|
<img
|
||||||
|
src={product.thumbnail}
|
||||||
|
alt=""
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
objectFit: 'cover',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: '10px', color: 'var(--theme-elevation-500)' }}>
|
||||||
|
无图
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div style={{ padding: 'calc(var(--base) / 2)' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 500,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
color: 'var(--theme-text)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{product.title}
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: '11px', color: 'var(--theme-elevation-500)' }}>
|
||||||
|
ID: {product.medusaId || product.id}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* No results or loading - 加载状态和空结果提示 */}
|
||||||
|
{inputValue && !isLoading && searchResults.length === 0 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 'calc(var(--base) / 2)',
|
||||||
|
padding: 'var(--base)',
|
||||||
|
textAlign: 'center',
|
||||||
|
color: 'var(--theme-elevation-500)',
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
未找到匹配的商品
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isLoading && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 'calc(var(--base) / 2)',
|
||||||
|
padding: 'var(--base)',
|
||||||
|
textAlign: 'center',
|
||||||
|
color: 'var(--theme-elevation-500)',
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
搜索中...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Helper text */}
|
||||||
|
{field.admin?.description && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 'calc(var(--base) / 4)',
|
||||||
|
fontSize: '0.875rem',
|
||||||
|
color: 'var(--theme-elevation-500)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{field.admin.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import './product-grid-styler.scss'
|
import './product-grid-styler.scss'
|
||||||
|
|
||||||
// 这个组件本身不渲染任何内容,只负责在 Products 列表页注入 CSS
|
// 这个组件本身不渲染任何内容,只负责在 Products 列表页注入 CSS
|
||||||
|
|
@ -207,7 +207,7 @@ export interface Product {
|
||||||
*/
|
*/
|
||||||
handle?: string | null;
|
handle?: string | null;
|
||||||
/**
|
/**
|
||||||
* 相关商品
|
* 相关商品,支持搜索联想
|
||||||
*/
|
*/
|
||||||
relatedProducts?: (number | Product)[] | null;
|
relatedProducts?: (number | Product)[] | null;
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue