富文本模式

This commit is contained in:
龟男日记\www 2026-02-09 04:27:32 +08:00
parent 4f14ac59f3
commit b4991fcefd
4 changed files with 250 additions and 56 deletions

View File

@ -1,5 +1,29 @@
import { ThumbnailCell as ThumbnailCell_a0b2acb813359aec894b6644d7c3bfd2 } from '../../../components/products/ThumbnailCell' import { ThumbnailCell as ThumbnailCell_a0b2acb813359aec894b6644d7c3bfd2 } from '../../../components/products/ThumbnailCell'
import { ThumbnailAndStatusField as ThumbnailAndStatusField_8fa95ec6265982d11b99fbeb81e24c1c } from '../../../components/products/ThumbnailAndStatusField' import { ThumbnailField as ThumbnailField_ba44ab32cac4d742a03a48fb6960602e } from '../../../components/products/ThumbnailField'
import { RscEntryLexicalCell as RscEntryLexicalCell_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 { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { FixedToolbarFeatureClient as FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { StrikethroughFeatureClient as StrikethroughFeatureClient_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 { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SyncMedusaButton as SyncMedusaButton_8c90663551920f0510ea531726668adc } from '../../../components/products/SyncMedusaButton' import { SyncMedusaButton as SyncMedusaButton_8c90663551920f0510ea531726668adc } from '../../../components/products/SyncMedusaButton'
import { default as default_3fd1353246fc8a459244c8dc11f58470 } from '../../../components/products/ProductGridStyler' import { default as default_3fd1353246fc8a459244c8dc11f58470 } from '../../../components/products/ProductGridStyler'
import { ForceSyncButton as ForceSyncButton_86f9d5df4f20495427521354d06db618 } from '../../../components/products/ForceSyncButton' import { ForceSyncButton as ForceSyncButton_86f9d5df4f20495427521354d06db618 } from '../../../components/products/ForceSyncButton'
@ -8,7 +32,31 @@ import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } f
export const importMap = { export const importMap = {
"/components/products/ThumbnailCell#ThumbnailCell": ThumbnailCell_a0b2acb813359aec894b6644d7c3bfd2, "/components/products/ThumbnailCell#ThumbnailCell": ThumbnailCell_a0b2acb813359aec894b6644d7c3bfd2,
"/components/products/ThumbnailAndStatusField#ThumbnailAndStatusField": ThumbnailAndStatusField_8fa95ec6265982d11b99fbeb81e24c1c, "/components/products/ThumbnailField#ThumbnailField": ThumbnailField_ba44ab32cac4d742a03a48fb6960602e,
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#FixedToolbarFeatureClient": FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"/components/products/SyncMedusaButton#SyncMedusaButton": SyncMedusaButton_8c90663551920f0510ea531726668adc, "/components/products/SyncMedusaButton#SyncMedusaButton": SyncMedusaButton_8c90663551920f0510ea531726668adc,
"/components/products/ProductGridStyler#default": default_3fd1353246fc8a459244c8dc11f58470, "/components/products/ProductGridStyler#default": default_3fd1353246fc8a459244c8dc11f58470,
"/components/products/ForceSyncButton#ForceSyncButton": ForceSyncButton_86f9d5df4f20495427521354d06db618, "/components/products/ForceSyncButton#ForceSyncButton": ForceSyncButton_86f9d5df4f20495427521354d06db618,

View File

@ -1,4 +1,25 @@
import type { CollectionConfig } from 'payload' import type { CollectionConfig } from 'payload'
import {
AlignFeature,
BlocksFeature,
BoldFeature,
ChecklistFeature,
HeadingFeature,
IndentFeature,
InlineCodeFeature,
ItalicFeature,
lexicalEditor,
LinkFeature,
OrderedListFeature,
ParagraphFeature,
RelationshipFeature,
UnorderedListFeature,
UploadFeature,
FixedToolbarFeature,
InlineToolbarFeature,
HorizontalRuleFeature,
BlockquoteFeature,
} from '@payloadcms/richtext-lexical'
export const Products: CollectionConfig = { export const Products: CollectionConfig = {
slug: 'products', slug: 'products',
@ -25,23 +46,115 @@ export const Products: CollectionConfig = {
}, },
fields: [ fields: [
{ {
name: 'medusaId', type: 'row',
type: 'text', fields: [
required: true, {
unique: true, name: 'medusaId',
index: true, type: 'text',
admin: { required: true,
description: 'Medusa 商品 ID', unique: true,
readOnly: true, index: true,
}, admin: {
description: 'Medusa 商品 ID',
readOnly: true,
width: '80%',
},
},
{
name: 'status',
type: 'select',
required: true,
defaultValue: 'draft',
options: [
{
label: '草稿',
value: 'draft',
},
{
label: '已发布',
value: 'published',
},
],
admin: {
description: '商品详情状态',
width: '20%',
},
},
],
}, },
{ {
name: 'title', type: 'row',
type: 'text', fields: [
required: true, {
name: 'title',
type: 'text',
required: true,
admin: {
description: '商品标题(从 Medusa 同步)',
readOnly: true,
width: '80%',
},
},
{
name: 'thumbnail',
type: 'text',
admin: {
description: '商品封面 URL从 Medusa 同步)',
readOnly: true,
width: '20%',
components: {
Cell: '/components/products/ThumbnailCell#ThumbnailCell',
Field: '/components/products/ThumbnailField#ThumbnailField',
},
},
},
],
},
{
name: 'content',
type: 'richText',
admin: { admin: {
description: '商品标题(从 Medusa 同步)', description: '商品详细内容',
}, },
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3', 'h4'] }),
LinkFeature({
enabledCollections: ['products'],
fields: ({ defaultFields }) => [
...defaultFields,
{
name: 'rel',
label: 'Rel Attribute',
type: 'select',
hasMany: true,
options: ['noopener', 'noreferrer', 'nofollow'],
admin: {
description: 'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
},
},
],
}),
UploadFeature({
collections: {
media: {
fields: [
{
name: 'caption',
type: 'richText',
label: '图片说明',
editor: lexicalEditor(),
},
],
},
},
}),
FixedToolbarFeature(),
InlineToolbarFeature(),
HorizontalRuleFeature(),
],
}),
}, },
{ {
name: 'handle', name: 'handle',
@ -51,37 +164,6 @@ export const Products: CollectionConfig = {
readOnly: true, readOnly: true,
}, },
}, },
{
name: 'thumbnail',
type: 'text',
admin: {
description: '商品缩略图 URL从 Medusa 同步)',
readOnly: true,
components: {
Cell: '/components/products/ThumbnailCell#ThumbnailCell',
Field: '/components/products/ThumbnailAndStatusField#ThumbnailAndStatusField',
},
},
},
{
name: 'status',
type: 'select',
required: true,
defaultValue: 'draft',
options: [
{
label: '草稿',
value: 'draft',
},
{
label: '已发布',
value: 'published',
},
],
admin: {
description: '商品详情状态',
},
},
{ {
name: 'relatedProducts', name: 'relatedProducts',
type: 'relationship', type: 'relationship',

View File

@ -0,0 +1,45 @@
'use client'
import { useFormFields } from '@payloadcms/ui'
import type { TextFieldClientComponent } from 'payload'
// 只显示缩略图封面
export const ThumbnailField: TextFieldClientComponent = ({ path }) => {
const fields = useFormFields(([fields]) => fields)
const thumbnail = fields.thumbnail?.value
const isImage = typeof thumbnail === 'string' && thumbnail.match(/^https?:\/\/.+/)
return (
<div>
<label style={{ display: 'block', marginBottom: 8, fontWeight: 500 }}></label>
<div
style={{
width: '100%',
maxWidth: 300,
height: 200,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid #eee',
borderRadius: 8,
background: '#fafbfc',
overflow: 'hidden',
}}
>
{isImage ? (
<img
src={thumbnail}
alt="商品封面"
style={{
width: '100%',
height: '100%',
objectFit: 'contain',
}}
/>
) : (
<span style={{ color: '#bbb' }}></span>
)}
</div>
</div>
)
}

View File

@ -172,22 +172,40 @@ export interface Product {
* Medusa ID * Medusa ID
*/ */
medusaId: string; medusaId: string;
/**
*
*/
status: 'draft' | 'published';
/** /**
* Medusa * Medusa
*/ */
title: string; title: string;
/** /**
* URL slug Medusa * URL Medusa
*/
handle?: string | null;
/**
* URL Medusa
*/ */
thumbnail?: string | null; thumbnail?: string | null;
/** /**
* *
*/ */
status: 'draft' | 'published'; content?: {
root: {
type: string;
children: {
type: any;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
/**
* URL slug Medusa
*/
handle?: string | null;
/** /**
* *
*/ */
@ -323,10 +341,11 @@ export interface MediaSelect<T extends boolean = true> {
*/ */
export interface ProductsSelect<T extends boolean = true> { export interface ProductsSelect<T extends boolean = true> {
medusaId?: T; medusaId?: T;
title?: T;
handle?: T;
thumbnail?: T;
status?: T; status?: T;
title?: T;
thumbnail?: T;
content?: T;
handle?: T;
relatedProducts?: T; relatedProducts?: T;
lastSyncedAt?: T; lastSyncedAt?: T;
updatedAt?: T; updatedAt?: T;