From 3ad86524d4020b3fb848a3304172d9e4042176be 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:14:16 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B2=BE=E7=AE=80=E4=B8=BA=E7=BD=91=E6=A0=BC?= =?UTF-8?q?=E5=B1=95=E7=A4=BA?= 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/ProductGridStyler.tsx | 10 ++ .../products/product-grid-styler.scss | 126 ++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/components/products/ProductGridStyler.tsx create mode 100644 src/components/products/product-grid-styler.scss diff --git a/src/app/(payload)/admin/importMap.js b/src/app/(payload)/admin/importMap.js index ad824b6..6be7a0d 100644 --- a/src/app/(payload)/admin/importMap.js +++ b/src/app/(payload)/admin/importMap.js @@ -1,8 +1,12 @@ +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' import { S3ClientUploadHandler as S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24 } from '@payloadcms/storage-s3/client' import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc' export const importMap = { + "/components/products/SyncMedusaButton#SyncMedusaButton": SyncMedusaButton_8c90663551920f0510ea531726668adc, + "/components/products/ProductGridStyler#default": default_3fd1353246fc8a459244c8dc11f58470, "/components/products/ForceSyncButton#ForceSyncButton": ForceSyncButton_86f9d5df4f20495427521354d06db618, "@payloadcms/storage-s3/client#S3ClientUploadHandler": S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24, "@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 diff --git a/src/collections/Products.ts b/src/collections/Products.ts index 8823f05..d10eedd 100644 --- a/src/collections/Products.ts +++ b/src/collections/Products.ts @@ -8,12 +8,16 @@ export const Products: CollectionConfig = { description: '管理 Medusa 商品的详细内容和描述', listSearchableFields: ['title', 'medusaId', 'handle'], pagination: { - defaultLimit: 20, + defaultLimit: 48, }, components: { edit: { PreviewButton: '/components/products/ForceSyncButton#ForceSyncButton', }, + beforeListTable: [ + '/components/products/SyncMedusaButton#SyncMedusaButton', + '/components/products/ProductGridStyler', + ], }, }, access: { diff --git a/src/components/products/ProductGridStyler.tsx b/src/components/products/ProductGridStyler.tsx new file mode 100644 index 0000000..56eb3cb --- /dev/null +++ b/src/components/products/ProductGridStyler.tsx @@ -0,0 +1,10 @@ +'use client' + +import React from 'react' +import './product-grid-styler.scss' + +// 这个组件本身不渲染任何内容,只负责在 Products 列表页注入 CSS +// 从而将 Payload 默认的表格变换为 Grid 布局 +export default function ProductGridStyler() { + return
+} diff --git a/src/components/products/product-grid-styler.scss b/src/components/products/product-grid-styler.scss new file mode 100644 index 0000000..1195f7c --- /dev/null +++ b/src/components/products/product-grid-styler.scss @@ -0,0 +1,126 @@ +// 这是为了覆盖 Payload 默认表格样式的 SCSS +// 我们使用 CSS Grid 强制改变表格布局,从而实现 Grid 视图,同时保留 Payload 所有原生功能 + +.collection-list.collection-list--products { + table { + display: block !important; + + thead { + display: none !important; + } + + tbody { + display: grid !important; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)) !important; + gap: 1.5rem !important; + width: 100% !important; + } + + tr { + display: flex !important; + flex-direction: column !important; + background: var(--theme-elevation-50) !important; + border: 1px solid var(--theme-elevation-150) !important; + border-radius: 8px !important; + overflow: hidden !important; + position: relative !important; + transition: all 0.2s ease-in-out !important; + height: 100% !important; + min-height: 320px; + + &:hover { + transform: translateY(-4px); + box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.1); + border-color: var(--theme-elevation-300) !important; + } + + td { + display: block !important; + border: none !important; + padding: 0.5rem 1rem !important; + width: 100% !important; + white-space: normal !important; + height: auto !important; + + // 1. Selector/Checkbox (Always the first column usually) + &:first-child { + position: absolute !important; + top: 10px !important; + right: 10px !important; + width: auto !important; + padding: 0 !important; + z-index: 10 !important; + background: transparent !important; + + .checkbox { + background: rgba(255, 255, 255, 0.8); + border-radius: 4px; + } + } + + // 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; + width: 100% !important; + overflow: hidden !important; + 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; + } + } + + // Fallback purely text content in that cell + span { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + color: var(--theme-elevation-400); + font-size: 0.8rem; + } + } + + // 3. Title (Second content column) + &:nth-child(3) { + font-size: 1.1rem !important; + font-weight: 600 !important; + padding-top: 1rem !important; + margin-bottom: 0.5rem !important; + line-height: 1.4 !important; + flex-grow: 1 !important; // Push footer down + + a { + text-decoration: none !important; + color: var(--theme-text) !important; + + &:hover { + color: var(--theme-primary-500) !important; + } + } + } + + // 4. Medusa ID or Status or Date + &:nth-child(n+4) { + font-size: 0.8rem !important; + color: var(--theme-elevation-500) !important; + padding-bottom: 0.25rem !important; + padding-top: 0 !important; + border-top: none !important; + } + } + } + } +}