@ 焦点反应/ payload- plugin- comments
协作评论插件 有效载荷CMS v3. 在有效载荷管理员面板上添加一个全功能的注释系统——支持文档级别和字段级别对收藏和全局的评论,@mentions同时带有电子邮件通知,注释解析,多版本,以及本地化意识过滤.
目录
AI 整合提示
复制并粘贴此提示到您的 AI 助手( Cursor, Claude等) 中, 将插件整合到已有的 Biveload v3 工程中 .
I want to add a collaborative commenting system to my Payload CMS v3 project using @focus-reactive/payload-plugin-comments.
## How it works
The plugin injects into every collection and global:
- A field-level comment badge on every field label — shows comment count, opens a popup to post
- A document-level comments drawer (sidebar) scoped to the current document
- A global comments panel (header button) listing all comments across every document and global
Comments are stored in an auto-generated `comments` collection (hidden from the sidebar). Each comment records:
- documentId / collectionSlug / globalSlug — what it belongs to
- fieldPath — dot-notation field path (null = document-level)
- locale — for field-level comments (null = shown in all locales)
- text — may contain @(userId) mention tokens
- author, mentions, isResolved, resolvedBy, resolvedAt
- tenant (optional, when multi-tenancy is enabled)
@mentions use autocomplete from the users collection. Mentioned users receive email notifications via Resend.
Comments are automatically deleted when their parent document is deleted.
## Installation
pnpm add @focus-reactive/payload-plugin-comments
## Step 1 — Register the plugin in payload.config.ts
import { buildConfig } from 'payload'
import { commentsPlugin } from '@focus-reactive/payload-plugin-comments'
export default buildConfig({
plugins: [
commentsPlugin({
// Optional: specify which field to use as document title in the UI
collections: [
{ slug: 'pages', titleField: 'title' },
{ slug: 'products', titleField: 'name' },
],
// Optional: customize the display name field on users
usernameFieldPath: 'name', // default
}),
],
})
## Step 2 — Import the stylesheet
In your global CSS or admin layout:
@import "@focus-reactive/payload-plugin-comments/styles.css";
Or in a TypeScript/JS file:
import "@focus-reactive/payload-plugin-comments/styles.css";
## Step 3 — Regenerate the import map
Run this after adding the plugin so Payload registers the plugin's admin components:
npx payload generate:importmap
(Or the equivalent for your package manager: pnpm payload generate:importmap / bunx payload generate:importmap)
If you skip this step the comment badges, drawer, and header button will not appear in the admin UI.
## Step 4 — Create and run a migration (SQL adapters only)
The plugin adds a `comments` collection to your database. If you use PostgreSQL or SQLite, create and apply a migration:
npx payload migrate:create create_comments
npx payload migrate
Skip this step if you use the MongoDB adapter.
## Optional: Multi-tenancy
commentsPlugin({
tenant: {
enabled: true,
collectionSlug: 'tenants', // default
documentTenantField: 'tenant', // default
},
})
## Optional: Collection overrides
commentsPlugin({
overrides: {
access: { /* custom access control */ },
fields: (defaultFields) => [...defaultFields],
hooks: {
afterChange: [async ({ doc }) => { /* ... */ }],
},
},
})
## Environment variables (for email notifications)
RESEND_API_KEY=re_xxxxxxxxxxxxxxxx
[email protected]
## Important notes
- All collections and globals automatically get field-level comments — `collections` config only sets UI metadata (titleField).
- Comments on globals use globalSlug instead of collectionSlug/documentId.
- Field-level comments are locale-scoped; document-level comments show in all locales.
- The `comments` collection is hidden from the admin sidebar by default.
- Auto-cleanup: when a document is deleted, all its comments are deleted too.
- usernameFieldPath supports dot-notation (e.g. "profile.displayName").
- If RESEND_FROM_EMAIL is not set, mention emails are silently skipped.
UI 屏幕截图
全球评论小组
在“有效载荷”管理员中,一个标题按钮打开一个抽屉,列出每个文件和收藏中的所有注释.
文件评论小组
在查看一个文件时,一个侧面面板会用过滤标签显示该文件的所有评论(打开/解决/提及我).
字段注释弹出
点击字段标签上的注释徽章打开弹出符,您可以写出并发布该特定字段的新注释.
外地标签按钮——两个国家
嵌入于字段标签中的评论按钮有两个可视状态:没有评论(不活动,出现在悬浮上)和一个或多个可公开的评论(活性,显示计数徽章).
| 不活动( 无评论) | 活动( 有评论) |
|---|---|
特征
- 文件级评论 - 将任何文件的评论留在任何汇编或全局
- 外地一级的评论 ——直接对单个字段进行注释;字段标签显示带有注释计数的徽章
- 全球支助 ——外地一级和文件一级对有效载荷全球的评论工作,而不仅仅是收集
- @mention 用户 - 在注释中提及其他用户时使用
@name自动补全 - 电子邮件通知 - 被提及用户通过电子邮件收到通知 重新发送
- 解析注释 - 将评论意见标为已解决/未解决;通过打开、解决或提及过滤
- 全球评论小组 – 一个页眉按钮打开一个抽屉,显示所有文档和全局的所有注释
- 乐观 UI ——在服务器确认前立即出现评论
- 多重租赁 - 通过下列方式对租户提出范围意见:
@payloadcms/plugin-multi-tenant - 地方意识 – 外地一级的注释被绑入一个地块;在所有地块中都显示文档级别的评论
- 自动清理 ——当其母文档被删除时,注释会被自动删除
- i18n/ 翻译 ——所有UI字符串都是可转换的;在内置的英语默认值的同时,将您自己的语言域覆盖
先决条件
- 20或以上节点
- 一个工作 有效载荷 CMS v3 使用 Next.js 的工程
- pnpm(建议)
- A 类 重新发送 账户(仅需提及电子邮件通知)
安装
pnpm add @focus-reactive/payload-plugin-comments
npm install @focus-reactive/payload-plugin-comments
# or
yarn add @focus-reactive/payload-plugin-comments
同伴依赖性 : payload ^3.0.0, (中文(简体) ), @payloadcms/ui ^3.0.0。 。 。 . next ^14 || ^15, (中文(简体) ), react ^18 || ^19, (中文(简体) ), react-dom,以及 @payloadcms/plugin-multi-tenant 是可选的.
设置
1. 将插件添加到您的有效载荷配置中
// payload.config.ts
import { buildConfig } from "payload";
import { commentsPlugin } from "@focus-reactive/payload-plugin-comments";
export default buildConfig({
plugins: [
commentsPlugin({
// Optional: customize which field is used as the document title in the UI
collections: [
{ slug: "pages", titleField: "title" },
{ slug: "products", titleField: "name" },
],
}),
],
// ... rest of your config
});
2. 导入样式
将插件的样式表添加到您的全局 CSS 或管理版式 :
/* global CSS */
@import "@focus-reactive/payload-plugin-comments/styles.css";
或导入到布局/ 页面文件中 :
import "@focus-reactive/payload-plugin-comments/styles.css";
3. 重新生成导入地图
当您添加或删除注册管理员组件的插件时, 有效载荷需要重新生成其导入地图 。 运行 :
npx payload generate:importmap
此命令不管您的软件包管理器如何有效( Namenpm, (中文(简体) ), pnpm, (中文(简体) ), yarn, (中文(简体) ), bun) (中文(简体) ). 或者对您的软件包管理器使用等效:
pnpm payload generate:importmap
# yarn payload generate:importmap
# bunx payload generate:importmap
如果您跳过此步骤, 插件的管理员组件( 注释徽章、 抽屉、 标题按钮) 将不会出现在 有效载荷管理员 UI 中 .
4. 创建和管理移徙
插件添加一个 comments 收藏到数据库。 如果您使用 SQL 适配器( PostgreSQL, SQLite), 请创建并应用迁移 :
npx payload migrate:create create_comments
npx payload migrate
如果您使用 MongoDB 适配器, 请跳过此步骤 .
配置
这个 commentsPlugin 工厂接受可选 CommentsPluginConfig 对象 :
commentsPlugin(config?: CommentsPluginConfig)
CommentsPluginConfig
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
collections | CollectionEntry[] | —— 说 | 用于收藏的UI元数据(标题字段). 所有集合都会收到评论. |
enabled | boolean | true | 设定为 false 完全禁用插件 |
tenant | TenantPluginConfig | —— 说 | 多租赁设置(见下文) |
overrides | CollectionOverrides | —— 说 | 自定义生成 comments 收藏 |
translations | Translations | —— 说 | 覆盖每个地方的 UI 字符串(见下文) |
usernameFieldPath | string | "name" | 用户收藏中显示名称字段的点注释路径 |
CollectionEntry
每个条目 collections 是一个为特定集合提供UI元数据的对象 :
interface CollectionEntry {
slug: string;
titleField?: string; // Field used as document title in the UI. Default: "id"
}
说明: 您不需要列出每个收藏 。 所有集合(和所有全局)自动支持注释. 这个
collections数组只设置显示像标题字段这样的元数据.
实例:
commentsPlugin({
collections: [
{ slug: "pages", titleField: "title" },
{ slug: "products", titleField: "name" },
],
});
usernameFieldPath
指定在 users 库被用作注释 UI 和 @mention 自动完成中的显示名 。 支持嵌入地的点注释.
commentsPlugin({
usernameFieldPath: "name", // default
// usernameFieldPath: "firstName",
// usernameFieldPath: "profile.displayName",
});
TenantPluginConfig
使用时配置多租赁 @payloadcms/plugin-multi-tenant数字 :
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
enabled | boolean | false | 启用租户范围界定 |
collectionSlug | string | "tenants" | 租户的积蓄 |
documentTenantField | string | "tenant" | 存有租户参考文献的栏目 |
示例
commentsPlugin({
tenant: {
enabled: true,
collectionSlug: "tenants",
documentTenantField: "tenant",
},
});
收藏覆盖
使用 overrides 以自定义生成 comments 收藏——例如,用于扩展访问控制或添加自定义字段:
commentsPlugin({
overrides: {
access: {
// Override the default "authenticated only" access
},
fields: (defaultFields) => [...defaultFields],
hooks: {
afterChange: [
async ({ doc }) => {
console.log("Comment changed:", doc.id);
},
],
},
},
});
翻译
使用 translations 以覆盖一个或多个地方的任意 UI 字符串。 每个密钥都是一个地块代码;该值是一个部分对象 CommentsTranslations 形状 —— 您省略的密钥会回到内置的英语默认值.
commentsPlugin({
translations: {
fr: {
label: "Commentaires",
add: "Ajouter un commentaire",
writeComment: "Écrire un commentaire",
comment: "Commenter",
cancel: "Annuler",
resolve: "Résoudre",
reopen: "Rouvrir",
delete: "Supprimer",
filterOpen: "Ouverts",
filterResolved: "Résolus",
filterMentioned: "Me mentionnent",
},
},
});
所有可变键( 带有英语默认值):
| 密钥 | 默认(英文) |
|---|---|
label | "Comments" |
openComments_one | "{{count}} open comment" |
openComments_other | "{{count}} open comments" |
add | "Add comment" |
writeComment | "Write a comment" |
comment | "Comment" |
cancel | "Cancel" |
posting | "Posting…" |
resolve | "Resolve" |
reopen | "Reopen" |
delete | "Delete" |
general | "General" |
close | "Close" |
syncingComments | "Syncing comments" |
openCommentsAria | "Open comments" |
failedToPost | "Failed to post comment" |
failedToUpdate | "Failed to update comment" |
failedToDelete | "Failed to delete comment" |
failedToAdd | "Failed to add comment" |
unknownAuthor | "Unknown" |
deletedUser | "Deleted user" |
noOpenComments | "No open comments" |
noResolvedComments | "No resolved comments" |
noMentionedComments | "No comments mentioning you" |
filterOpen | "Open" |
filterResolved | "Resolved" |
filterMentioned | "Mentioned me" |
noMentionMatches | "No matches" |
这个 CommentsTranslations 类型是从软件包中导出,以便输入翻译对象:
import type { CommentsTranslations } from "@focus-reactive/payload-plugin-comments";
const myTranslations: Record<string, Partial<CommentsTranslations>> = {
fr: { label: "Commentaires" },
de: { label: "Kommentare" },
};
commentsPlugin({ translations: myTranslations });
环境变量
电子邮件通知要求
| 变量 | 说明 | 示例 |
|---|---|---|
RESEND_API_KEY | 您重新发送 API 密钥 | re_xxxxxxxxxxxxxxxx |
RESEND_FROM_EMAIL | 发送者电子邮件地址以获取引用通知 | [email protected] |
若为 RESEND_FROM_EMAIL 未设置, 请提及电子邮件通知被悄悄跳过, 错误会被登录到控制台上 .
.env. 本地实例:
RESEND_API_KEY=re_xxxxxxxxxxxxxxxx
[email protected]
结构概览
如何运作
插件初始化 (单位:千美元)plugin.ts:
- 插件收到一个
CommentsPluginConfig返回标准有效载荷Plugin函数. - 它创造了一个
comments收藏(默认从管理员边栏中隐藏). - 补丁 每个收藏 输入
FieldCommentLabel输入每个字段的管理标签并登记一个afterDelete键入删除文档时的级联删除注释. - 补丁 每个全球 输入
FieldCommentLabel输入每个字段的管理员标签. - 它登记行政提供者(
CommentsProviderWrapper)和一个管理行动(CommentsHeaderButton) (中文(简体) ). - 它将REST的终点登记在以下
/api/comments-plugin/*(列表,创建,删除,解析,标记读取,不读取-计数,可引用用户,文档标题,字段标签). 每个都需要一个经过认证的用户,并使用请求中的“有效载荷”实例,因此它工作在一个刚开始的无服务器实例上.
数据加载 (单位:千美元)src/api:
- 管理员UI从插件的端点获取注释、文档标题、可引用用户、字段标签和未读引用数.
- 突变(添加,删除,解析,标记为已读)调用相同的端点并乐观地更新查询缓存.
国家管理 (单位:千美元)CommentsProvider:
- 持有
allComments在反应状态下,有乐观的更新通过useOptimistic。 。 。 . visibleComments来源:根据下个.js过滤到当前文档/收集/全球/本地pathname。 。 。 .- 展览
addComment, (中文(简体) ),removeComment, (中文(简体) ),resolveComment,以及syncComments变相.
外地一级的评论 (单位:千美元)FieldCommentLabel:
- 插件覆盖
Label用于每个已配置的集合和全局中每个指定字段的组件. - 标签从上下文读取注释并用字段路径过滤出,显示带有计数的徽章.
- 点击徽章打开预滚动到该字段评论组的评论抽屉.
注释集计划 :
| 外地 | 类型 | 说明 |
|---|---|---|
documentId | 编号 | 正在被评论的文件的编号( 以全局为单位) |
collectionSlug | 文本 | 收藏的块块( 不包括全局评论) |
globalSlug | 文本 | 有效载荷全球屏障( 用于收集文件注释的空白) |
fieldPath | 文本 | 字段的点注释路径( null = 文档级别) |
locale | 文本 | 注释的语系( null = 在所有语系中显示) |
text | 文本区域 | 注释正文( 可包含 @(userId) 提及符号) |
mentions | 数组 − 关系 | 本评论中提到的用户 |
author | − 用户 | 注释作者( 自动设置) |
isResolved | 复选框 | 评论是否解决 |
resolvedBy | − 用户 | 谁解决的 |
resolvedAt | 日期 | 当它解决 |
tenant | 关系(可选) | 租户范围(在允许多租房时) |
参考文献
| 导入路径 | 出口 |
|---|---|
@focus-reactive/payload-plugin-comments | commentsPlugin, (中文(简体) ), CommentsPluginConfig (类型), CommentsTranslations (类型) |
@focus-reactive/payload-plugin-comments/styles.css | 插件样式表( 已编译的 CSS) |
可用的脚本
从工程根中运行这些 pnpm数字 :
| 命令 | 说明 |
|---|---|
pnpm build | 构建插件到 dist/ (Tsup + Tailwind CSS 微调) |
pnpm dev | 在监视模式下构建——在文件更改中重建 |
pnpm lint | Lint + 检查格式 (Ultracite: oxlint + oxfmt) |
pnpm lint:fix | Lint + 固定,包括格式化(Ultracite) |
pnpm format | 格式文件( Ultracite: oxfmt) |
pnpm format:check | 检查格式而不写入 |
捐款
- 叉出寄存器并创建一个特性分支.
- 安装依赖性 :
pnpm install - 启动建筑监视器 :
pnpm dev - 更改您在
src/。 。 。 . - 运行
pnpm lint财务报告和审定财务报表pnpm format:check在提交之前. - 打开拉动请求
main。 。 。 .
许可证
麻省理工学院 焦点反应