预览
预览功能允许你生成指向前端应用的直接链接。启用后,Admin Panel 的编辑视图中会出现一个"预览"按钮,其 href 指向你提供的 URL。这为编辑人员提供了一种快速导航到前端应用的方式,在那里可以查看该文档数据的呈现效果。否则,他们将不得不自行确定该 URL,这在复杂应用中往往并不直观。
预览功能还可用于实现所谓的"草稿预览"。通过草稿预览,你可以导航到前端应用并进入"草稿模式",此时你的查询会被修改为获取草稿内容而非已发布内容。这对于在发布前查看内容效果非常有用。更多详情。
注意: 预览功能与 Live Preview 不同。 Live Preview 会在 Admin Panel 的 iframe 中加载并渲染你的应用,让你可以实时查看更改。而预览功能则是生成指向前端应用的直接链接。
要添加预览功能,可以在任何 Collection Config 或 Global Config 中向 admin.preview
属性传递一个函数:
import type { CollectionConfig } from 'payload'
export const Pages: CollectionConfig = {
slug: 'pages',
admin: {
preview: ({ slug }) => `http://localhost:3000/${slug}`,
},
fields: [
{
name: 'slug',
type: 'text',
},
],
}
选项
preview
函数解析为一个指向你的前端应用的字符串,并带有额外的 URL 参数。这可以是一个绝对 URL 或相对路径,如果需要也可以异步执行。
preview
函数接收以下参数:
Path | 描述 |
---|---|
doc | 正在编辑的文档数据。这包括尚未保存的更改。 |
options | 包含额外属性的对象。 |
options
对象包含以下属性:
Path | 描述 |
---|---|
locale | 正在编辑的文档的当前语言环境。 |
req | Payload 的 Request 对象。 |
token | 当前认证用户的 JWT 令牌。 |
如果你的应用需要一个完全限定的 URL(例如部署到 Vercel 预览部署时),可以使用 req
属性来构建这个 URL:
preview: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlight-line
草稿预览
预览功能可用于实现"草稿预览"。从管理面板点击预览按钮后,你可以在前端应用中进入"草稿模式"。这将允许你调整页面查询以包含 draft: true
参数。当请求中存在此参数时,Payload 会根据文档的 _status
字段返回草稿文档而非已发布的文档。
要进入草稿模式,提供给 preview
函数的 URL 可以指向前端应用中的自定义端点,该端点会设置 cookie 或会话变量来指示草稿模式已启用。这是框架特定的,因此具体实现方式因框架而异,但底层概念是相同的。
Next.js
如果你使用 Next.js,可以按照以下代码进入草稿模式。
步骤 1: 格式化预览 URL
首先,格式化你的 admin.preview
函数,使其指向前端应用中你将打开的自定义端点。此 URL 应包含几个关键的查询参数:
import type { CollectionConfig } from 'payload'
export const Pages: CollectionConfig = {
slug: 'pages',
admin: {
preview: ({ slug, collection }) => {
const encodedParams = new URLSearchParams({
slug,
collection,
path: `/${slug}`,
previewSecret: process.env.PREVIEW_SECRET || '',
})
return `/preview?${encodedParams.toString()}` // highlight-line
},
},
fields: [
{
name: 'slug',
type: 'text',
},
],
}
第二步:创建预览路由
接下来,创建一个 API 路由用于验证预览密钥、认证用户并进入草稿模式:
/app/preview/route.ts
import type { CollectionSlug, PayloadRequest } from 'payload'
import { getPayload } from 'payload'
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
import configPromise from '@payload-config'
export async function GET(
req: {
cookies: {
get: (name: string) => {
value: string
}
}
} & Request,
): Promise<Response> {
const payload = await getPayload({ config: configPromise })
const { searchParams } = new URL(req.url)
const path = searchParams.get('path')
const collection = searchParams.get('collection') as CollectionSlug
const slug = searchParams.get('slug')
const previewSecret = searchParams.get('previewSecret')
if (previewSecret !== process.env.PREVIEW_SECRET) {
return new Response('您无权预览此页面', {
status: 403,
})
}
if (!path || !collection || !slug) {
return new Response('搜索参数不足', { status: 404 })
}
if (!path.startsWith('/')) {
return new Response(
'此端点仅可用于相对路径预览',
{ status: 500 },
)
}
let user
try {
user = await payload.auth({
req: req as unknown as PayloadRequest,
headers: req.headers,
})
} catch (error) {
payload.logger.error(
{ err: error },
'实时预览时验证令牌出错',
)
return new Response('您无权预览此页面', {
status: 403,
})
}
const draft = await draftMode()
if (!user) {
draft.disable()
return new Response('您无权预览此页面', {
status: 403,
})
}
// 可以在此添加额外检查,确认用户是否有权预览此页面
draft.enable()
redirect(path)
}
第三步:查询草稿内容
最后,在你的前端应用中,你可以检测草稿模式并调整查询以包含草稿:
/app/[slug]/page.tsx
export default async function Page({ params: paramsPromise }) {
const { slug = 'home' } = await paramsPromise
const { isEnabled: isDraftMode } = await draftMode()
const payload = await getPayload({ config })
const page = await payload.find({
collection: 'pages',
depth: 0,
draft: isDraftMode, // highlight-line
limit: 1,
overrideAccess: isDraftMode,
where: {
slug: {
equals: slug,
},
},
})?.then(({ docs }) => docs?.[0])
if (page === null) {
return notFound()
}
return (
<main>
<h1>{page?.title}</h1>
</main>
)
}
注意: 要查看完整可运行的示例,请参考官方 Draft Preview Example 在 Examples Directory 中。