From cccbe20aa0377b5ae7db4edfd39b5f941c6ebe1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9F=E7=94=B7=E6=97=A5=E8=AE=B0=5Cwww?= Date: Mon, 9 Feb 2026 03:52:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(payload)/admin/importMap.js | 4 ++ src/collections/Products.ts | 6 +- src/components/products/HiddenField.tsx | 7 +++ .../products/ThumbnailAndStatusField.tsx | 60 +++++++++++++++++++ src/components/products/ThumbnailCell.tsx | 30 ++++++++++ .../products/product-grid-styler.scss | 31 +++++----- 6 files changed, 120 insertions(+), 18 deletions(-) create mode 100644 src/components/products/HiddenField.tsx create mode 100644 src/components/products/ThumbnailAndStatusField.tsx create mode 100644 src/components/products/ThumbnailCell.tsx diff --git a/src/app/(payload)/admin/importMap.js b/src/app/(payload)/admin/importMap.js index 6be7a0d..25424a7 100644 --- a/src/app/(payload)/admin/importMap.js +++ b/src/app/(payload)/admin/importMap.js @@ -1,3 +1,5 @@ +import { ThumbnailCell as ThumbnailCell_a0b2acb813359aec894b6644d7c3bfd2 } from '../../../components/products/ThumbnailCell' +import { ThumbnailAndStatusField as ThumbnailAndStatusField_8fa95ec6265982d11b99fbeb81e24c1c } from '../../../components/products/ThumbnailAndStatusField' import { SyncMedusaButton as SyncMedusaButton_8c90663551920f0510ea531726668adc } from '../../../components/products/SyncMedusaButton' import { default as default_3fd1353246fc8a459244c8dc11f58470 } from '../../../components/products/ProductGridStyler' import { ForceSyncButton as ForceSyncButton_86f9d5df4f20495427521354d06db618 } from '../../../components/products/ForceSyncButton' @@ -5,6 +7,8 @@ import { S3ClientUploadHandler as S3ClientUploadHandler_f97aa6c64367fa259c5bc056 import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc' export const importMap = { + "/components/products/ThumbnailCell#ThumbnailCell": ThumbnailCell_a0b2acb813359aec894b6644d7c3bfd2, + "/components/products/ThumbnailAndStatusField#ThumbnailAndStatusField": ThumbnailAndStatusField_8fa95ec6265982d11b99fbeb81e24c1c, "/components/products/SyncMedusaButton#SyncMedusaButton": SyncMedusaButton_8c90663551920f0510ea531726668adc, "/components/products/ProductGridStyler#default": default_3fd1353246fc8a459244c8dc11f58470, "/components/products/ForceSyncButton#ForceSyncButton": ForceSyncButton_86f9d5df4f20495427521354d06db618, diff --git a/src/collections/Products.ts b/src/collections/Products.ts index d10eedd..7e5b423 100644 --- a/src/collections/Products.ts +++ b/src/collections/Products.ts @@ -8,7 +8,7 @@ export const Products: CollectionConfig = { description: '管理 Medusa 商品的详细内容和描述', listSearchableFields: ['title', 'medusaId', 'handle'], pagination: { - defaultLimit: 48, + defaultLimit: 25, }, components: { edit: { @@ -57,6 +57,10 @@ export const Products: CollectionConfig = { admin: { description: '商品缩略图 URL(从 Medusa 同步)', readOnly: true, + components: { + Cell: '/components/products/ThumbnailCell#ThumbnailCell', + Field: '/components/products/ThumbnailAndStatusField#ThumbnailAndStatusField', + }, }, }, { diff --git a/src/components/products/HiddenField.tsx b/src/components/products/HiddenField.tsx new file mode 100644 index 0000000..acb12bc --- /dev/null +++ b/src/components/products/HiddenField.tsx @@ -0,0 +1,7 @@ +'use client' +import type { SelectFieldClientComponent } from 'payload' + +// 隐藏字段(因为在ThumbnailAndStatusField中已经显示和编辑) +export const HiddenField: SelectFieldClientComponent = () => { + return null +} diff --git a/src/components/products/ThumbnailAndStatusField.tsx b/src/components/products/ThumbnailAndStatusField.tsx new file mode 100644 index 0000000..4e030a1 --- /dev/null +++ b/src/components/products/ThumbnailAndStatusField.tsx @@ -0,0 +1,60 @@ +'use client' +import { useFormFields, useField } from '@payloadcms/ui' +import type { TextFieldClientComponent } from 'payload' + +// 并排显示缩略图和状态(带状态选择器) +export const ThumbnailAndStatusField: TextFieldClientComponent = ({ path }) => { + // 获取thumbnail值 + const fields = useFormFields(([fields]) => fields) + const thumbnail = fields.thumbnail?.value + + // 获取status字段的值和setter + const { value: status, setValue: setStatus } = useField({ path: 'status' }) + + const isImage = typeof thumbnail === 'string' && thumbnail.match(/^https?:\/\/.+/) + + return ( +
+ +
+
+ {isImage ? ( + 商品缩略图 + ) : ( + 无图片 + )} +
+
+ + +
+
+
+ ) +} diff --git a/src/components/products/ThumbnailCell.tsx b/src/components/products/ThumbnailCell.tsx new file mode 100644 index 0000000..694e902 --- /dev/null +++ b/src/components/products/ThumbnailCell.tsx @@ -0,0 +1,30 @@ +'use client' +import Link from 'next/link' + +export const ThumbnailCell = (props: any) => { + console.log('=== ThumbnailCell All Props ===', props) + console.log('Props keys:', Object.keys(props)) + + // 尝试从不同的 props 路径获取值 + const value = props.value || props.cellData || props.data + const rowData = props.rowData || props.row + + console.log('Extracted value:', value) + console.log('Extracted rowData:', rowData) + + const isImage = typeof value === 'string' && value.match(/^https?:\/\/.+/) + const editUrl = `/admin/collections/products/${rowData?.id || ''}` + + return ( + + {isImage ? ( + 商品缩略图 + ) : ( +
{value || '无图片'}
+ )} + + ) +} diff --git a/src/components/products/product-grid-styler.scss b/src/components/products/product-grid-styler.scss index 1195f7c..10e1e81 100644 --- a/src/components/products/product-grid-styler.scss +++ b/src/components/products/product-grid-styler.scss @@ -27,11 +27,14 @@ transition: all 0.2s ease-in-out !important; height: 100% !important; min-height: 320px; - + cursor: pointer; + text-decoration: none !important; + &:hover { transform: translateY(-4px); box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.1); border-color: var(--theme-elevation-300) !important; + background: var(--theme-elevation-100) !important; } td { @@ -59,8 +62,6 @@ } // 2. Thumbnail (First content column) - // We depend on 'thumbnail' being the first custom column in Products.ts - // Note: td:nth-child(2) because checkbox is #1 &:nth-child(2) { padding: 0 !important; height: 200px !important; @@ -69,27 +70,23 @@ background: var(--theme-elevation-100) !important; order: -1 !important; // Force to top flex-grow: 0 !important; - - // Support for Payload's file cell or specific image structures - .file-cell, .thumbnail { - width: 100%; - height: 100%; - - img { - width: 100%; - height: 100%; - object-fit: cover; - } + position: relative; + + img { + width: 100% !important; + height: 100% !important; + object-fit: cover !important; + display: block !important; + background: var(--theme-elevation-100); } - - // Fallback purely text content in that cell - span { + .no-image { display: flex; justify-content: center; align-items: center; height: 100%; color: var(--theme-elevation-400); font-size: 0.8rem; + background: transparent; } }