@lark-apaas/coding-steering 0.1.78 → 0.1.79-alpha.20261101040601
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +5 -1
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/record-comments.md +81 -0
- package/steering/nestjs-react-fullstack/skills_local/plugin-guide/SKILL.md +5 -1
- package/steering/nestjs-react-fullstack/skills_local/plugin-guide/references/record-comments.md +81 -0
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/coding-steering",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.79-alpha.20261101040601",
|
|
4
4
|
"description": "Stack-specific steering content for miaoda-coding templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"steering"
|
|
8
8
|
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"lint:md": "markdownlint 'steering/**/*.md' --ignore 'steering/**/skills/**' --ignore 'steering/**/skills_common/**' --ignore 'steering/**/skills_local/**'"
|
|
11
|
+
},
|
|
9
12
|
"devDependencies": {
|
|
10
13
|
"markdownlint-cli": "^0.47.0"
|
|
11
14
|
},
|
|
@@ -17,8 +20,5 @@
|
|
|
17
20
|
"miaoda",
|
|
18
21
|
"coding-steering"
|
|
19
22
|
],
|
|
20
|
-
"license": "MIT"
|
|
21
|
-
|
|
22
|
-
"lint:md": "markdownlint 'steering/**/*.md' --ignore 'steering/**/skills/**' --ignore 'steering/**/skills_common/**' --ignore 'steering/**/skills_local/**'"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
23
|
+
"license": "MIT"
|
|
24
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plugin-guide
|
|
3
|
-
description: 飞书/AI 新版插件集成规范与 Plugin 链路指南——**给生成出来的应用写运行时代码用**,让应用跑起来后能访问飞书多维表格/Base、发送飞书消息、创建飞书群组,以及 AI 智能生文/生图/图片理解等。Use when 需要:(1) 创建或管理 PluginInstance 插件实例,(2) 调用 capabilityClient/CapabilityService 生成插件调用代码,(3) 理解 Plugin、PluginInstance、PluginInstanceAIJson 三层关系,(4) 使用 get_plugin_ai_json 或 plugin_instance 工具,(5) 排查插件相关报错或异常行为(调用报错、通知/消息发送失败、配置项不生效、参数类型与预期不符等)。NOT for Agent 自己在生成期读取飞书资源内容——用户给了多维表格/文档/表格/知识库链接、要求先读懂再建应用时,那是 lark-cli skill 的场景,用 bash 调 lark-cli 读;插件是写进应用代码、部署后由应用运行时调用的,不能拿来当 Agent 的读取工具。触发词:插件, plugin, 运行时访问飞书, 应用内发飞书消息, 应用内写多维表格, 飞书群组, AI生文, AI生图, 图片理解, capabilityClient, CapabilityService, pluginInstance, 插件报错, 配置不生效,
|
|
3
|
+
description: 飞书/AI 新版插件集成规范与 Plugin 链路指南——**给生成出来的应用写运行时代码用**,让应用跑起来后能访问飞书多维表格/Base、发送飞书消息、创建飞书群组,以及 AI 智能生文/生图/图片理解等。Use when 需要:(1) 创建或管理 PluginInstance 插件实例,(2) 调用 capabilityClient/CapabilityService 生成插件调用代码,(3) 理解 Plugin、PluginInstance、PluginInstanceAIJson 三层关系,(4) 使用 get_plugin_ai_json 或 plugin_instance 工具,(5) 排查插件相关报错或异常行为(调用报错、通知/消息发送失败、配置项不生效、参数类型与预期不符等)。NOT for Agent 自己在生成期读取飞书资源内容——用户给了多维表格/文档/表格/知识库链接、要求先读懂再建应用时,那是 lark-cli skill 的场景,用 bash 调 lark-cli 读;插件是写进应用代码、部署后由应用运行时调用的,不能拿来当 Agent 的读取工具。触发词:插件, plugin, 运行时访问飞书, 应用内发飞书消息, 应用内写多维表格, 飞书群组, AI生文, AI生图, 图片理解, capabilityClient, CapabilityService, pluginInstance, 插件报错, 配置不生效, 参数类型不匹配, 记录评论组件, 评论回复, 表情回应, 云文档引用
|
|
4
4
|
steering: true
|
|
5
5
|
steering-topic: plugin_guide
|
|
6
6
|
match-template-name: nestjs-react-fullstack
|
|
@@ -166,6 +166,10 @@ const structured = await capabilityClient
|
|
|
166
166
|
|
|
167
167
|
**创建 `ai-image-to-json` 实例时**:必须一次性定义**所有**需提取字段(参考数据库 schema / 表单定义 / UI 设计),宁多勿漏;字段类型仅支持 String/Number/Boolean,最多 20 个;先调 `get_plugin_ai_json` 确认上游插件的 `outputSchema` 确保输入格式正确。
|
|
168
168
|
|
|
169
|
+
## 多维表格评论组件
|
|
170
|
+
|
|
171
|
+
需要记录评论/回复、编辑删除、@人员、表情回应或云文档引用时,复用项目内 `@/components/business-ui/record-comments` 的 `CommentPanel`;**先读 [评论组件接入指引](references/record-comments.md)** 和组件 `README.md`。组件保持现有样式并管理乐观更新、失败重试与分页触发;真实接口、权限、缓存与分页游标由宿主提供。图片未接入插件,不默认启用。
|
|
172
|
+
|
|
169
173
|
## 多维表格数据架构
|
|
170
174
|
|
|
171
175
|
应用需读取飞书多维表格数据时,按查询模式选架构(完整对照表见 `references/table.md`「数据架构选择」)。**快捷判断**:「仪表盘/dashboard/驾驶舱/看板/统计」→ 必须 `aggregateQuery`(禁止 searchRecords 全量拉取后内存计算);「列表/明细/详情」→ `searchRecords` 分页;「排行榜/TOP N」→ `searchRecords` + sort + pageSize=N;需要把 Base 当运行数据源单向读、且数据量大到要分片/多次分页或查询复杂/需持续同步 → 先用 `lark-cli apps +db-sync-create` 把 Base 表同步成应用数据库表(streaming 持续增量 / batch 只导一次),页面查应用库;这是全栈应用的方案,配置格式和 `--preview`→`--yes` 流程见 lark-apps-db skill;跨表关联或复杂计算 → 应用数据库 + 同步。
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# 多维表格评论组件(Record comments)
|
|
2
|
+
|
|
3
|
+
需要记录评论、新增/回复、编辑/删除、@人员、表情回应或云文档引用时,先读项目内 `client/src/components/business-ui/record-comments/README.md` 与 `comment-panel.contract.ts`,复用 `CommentPanel`,不要重新实现编辑器、弹层或乐观更新。此组件随 **nestjs-react-fullstack** 模板分发;旧应用若没有该目录,需要先升级/同步组件及依赖,不能只复制入口文件。本指引不适用于缺少该资产的其他技术栈。
|
|
4
|
+
|
|
5
|
+
## 接入边界
|
|
6
|
+
|
|
7
|
+
- 从 `@/components/business-ui/record-comments` 导入 `CommentPanel` 和公共类型;所有内部文件、CSS、`assets/reaction-sprite.png` 必须保留。Tiptap 依赖及 `@floating-ui/dom` 已由模板配置,不混装 Tiptap 大版本。
|
|
8
|
+
- 组件管理显示、回复归属、编辑器、人员弹层、发送等待/失败/重试以及乐观更新;**评论 CRUD、插件实例、用户身份、读缓存、分页游标、表情及文档请求全部由页面/API hook 提供**。不要把插件凭据、实例 ID、表格配置或应用 ID 写进组件内部。
|
|
9
|
+
- `createAppCommentSource(scopeKey)` 是可选的模板人员目录适配器,只识别妙搭人员 ID;其他目录实现 `CommentDataSource`,不要把飞书 ID 冒充妙搭 ID。默认人员目录不提供空查询推荐;宿主可提供 `recommend`,例如从当前讨论参与者解析推荐列表。
|
|
10
|
+
- `scopeKey` 必须覆盖应用/租户/登录身份/插件实例/数据源与权限上下文,以上任一变化都应更换;`recordID` 切换会重建编辑会话。`mentionSource` 用 `useMemo` 稳定,避免每次渲染重建搜索和缓存。
|
|
11
|
+
|
|
12
|
+
## 真实接口先核对
|
|
13
|
+
|
|
14
|
+
按本 skill 的插件元数据流程拿到当前实例真实 Action schema。以下是已联调版本的映射,**实际 actionKey、字段和响应层级始终以当前元数据为准**,缺能力则隐藏该功能,不伪造成功、不回退 mock。
|
|
15
|
+
|
|
16
|
+
| 宿主能力 | 已联调 Action / 输入约定 |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| 评论读取 | `listRecordComments`,传 `recordID`,需要回应时传 `needReaction:true`;按 `hasMore/pageToken` 加载下一页 |
|
|
19
|
+
| 新增/回复 | `addRecordComment`;新评论不传 `commentID`,回复保留原 `commentID`,返回真实 `commentID/replyID` |
|
|
20
|
+
| 编辑 | `updateRecordComment`;传 `recordID/commentID/replyID/content`,回读并传入回复级 `updateTime` |
|
|
21
|
+
| 删除 | `deleteRecordComment`;消息菜单必须传选中消息的 `replyID`(含首条),只删该消息;第二条顺位成为正文,其余回复保留。省略 replyID 仅兼容显式整串删除,按 `commentDeleted` 更新缓存 |
|
|
22
|
+
| 回应读取/写入 | `getRecordCommentReactions` / `updateRecordCommentReaction`;写入用显式 `add/delete`,组件回调的 `remove` 需映射成插件的 `delete` |
|
|
23
|
+
| 云文档推荐/搜索/解析 | `getCommentDocuments`;外部实现 `CommentDocumentSource`,用 `withDocumentSource(people, documents)` 合并,显式传 `enableDocuments` |
|
|
24
|
+
|
|
25
|
+
每条 `comments[]` 是讨论,`replies[0]` 是正文,其余 replies 是同一讨论下的消息;不要把回复拆成独立 comment。`commentID/replyID` 始终是字符串。`createTime/updateTime` 是 Unix **秒**,排序取创建时间,展示取 `updateTime ?? createTime`;缺更新时间不能伪造“编辑过”。`mention.userID` 缺失时保留缺失引用,不猜人。
|
|
26
|
+
|
|
27
|
+
## 受控组件的宿主实现
|
|
28
|
+
|
|
29
|
+
1. 宿主发起首屏读取,维护 `comments/loading/error`,传 `onRetry`。分页可全量拉取后不给 `pagination`,也可传 `{hasMore, loading, error, onLoadMore}`;组件接近底部会自动调用,宿主用自己的游标追加数据,追加后再 resolve,失败必须 reject。游标不前进时终止并报错。
|
|
30
|
+
2. 宿主提供 `onAddComment/onUpdateComment/onDeleteComment`。写成功后立即返回真实 ID/删除结果,再单独启动回读;**不能把后续回读失败当成写失败**,否则组件重试可能重复发表。不需要在宿主再插一份临时评论,也不必等待回读才关闭编辑器。
|
|
31
|
+
3. 编辑/删除必须显式传 `getReplyPermissions`,未提供就隐藏。前端作者判断只控制菜单,服务端仍检查当前用户权限;不能用 owner 身份补足。点击消息菜单“删除”直接发起请求,不追加二次确认;删除首条也只删它本身,不能扩大到其他人的回复;只有最后一条消失时移除空讨论。
|
|
32
|
+
4. 普通 `Error` 仅用于确认未应用的失败;超时/连接中断等可能已提交时,抛 `Object.assign(new Error(message), {outcome:'unknown'})`,组件保留内容并阻止盲目重试,宿主先刷新核对。只有请求尚未发送等已确认未写入的断网失败才标记 `not-applied`。
|
|
33
|
+
5. 宿主缓存要保护读取顺序,取消/忽略旧请求,切换身份时隔离缓存;组件在匹配快照接管后无法判断一个没有版本号的旧数组。不要通过延迟 toast、二次 loading 或本地时间 Map 掩盖回读错误。
|
|
34
|
+
|
|
35
|
+
最小受控接入(`read` 和 `commands` 是应用自己的真实 API hook,不是组件内部接口):
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useMemo } from 'react';
|
|
39
|
+
import {
|
|
40
|
+
CommentPanel, createAppCommentSource,
|
|
41
|
+
type CommentPanelProps,
|
|
42
|
+
} from '@/components/business-ui/record-comments';
|
|
43
|
+
|
|
44
|
+
type Props = Pick<CommentPanelProps,
|
|
45
|
+
'scopeKey' | 'recordID' | 'comments' | 'currentUserID' |
|
|
46
|
+
'loading' | 'error' | 'onRetry' | 'pagination' |
|
|
47
|
+
'onAddComment' | 'onUpdateComment' | 'onDeleteComment' |
|
|
48
|
+
'getReplyPermissions'>;
|
|
49
|
+
|
|
50
|
+
export function RecordComments(props: Props) {
|
|
51
|
+
const mentionSource = useMemo(
|
|
52
|
+
() => createAppCommentSource(props.scopeKey), [props.scopeKey],
|
|
53
|
+
);
|
|
54
|
+
return <CommentPanel {...props} mentionSource={mentionSource} />;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
真实调用继续使用 `capabilityClient.load(instanceID).call(actionKey, input)`,不要在组件里调用 CLI 或直接手写 OAuth;`call()` 的结果已由 SDK 解包,按实际 schema 使用,不要重复套 `data.output`。写入回调须返回合同中的真实结果,不能返回 `void`。
|
|
59
|
+
|
|
60
|
+
## 可选能力与权限
|
|
61
|
+
|
|
62
|
+
- **表情回应**:传 `reactions.getReactions/onChange`,可选 `canChange/loadUsers`。列表省略 reactions 表示未知,不能当作无人回应,`mine` 也不能根据截断的 `userIDs` 推断。宿主按回复串行写入,成功先更新当前回应缓存,再刷新;失败只回退该目标,刷新失败不能回退已提交动作。读取不到权威 membership 时禁用切换,保留历史表情键(如 `MediumThumbsup`)。
|
|
63
|
+
- **云文档引用**:搜索候选的 `description` 使用接口的 `editTime`(Unix 秒)格式化为“最近修改:10月26日 20:41”等元数据副标题;缺失时间就省略,不伪造。只查询当前用户可读的元数据,搜索与解析都应检查权限;不得以 owner/其他用户补查私密标题,插入引用不更改分享权限。写入是 `{type:'link',url}`;持久化 URL,不把标题/图标当作请求字段。请求被取消后检查 signal,丢弃过期结果。解析不可读时使用组件的灰色引用状态,不能假称可读。
|
|
64
|
+
- **图片**:当前多维表格插件未接入图片评论,不传 `images`,不展示上传入口。`images` 仅是宿主自备上传/持久化能力时的独立扩展;不得把 File/blob URL 塞入插件 content。
|
|
65
|
+
- **人员卡片/链接**:通过 `onMentionClick/getCommentLink` 由宿主承接;未提供对应回调时不展示无效操作。
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## 交互保持约束
|
|
69
|
+
|
|
70
|
+
复用完整组件及其状态流,不在宿主外包一层“增强交互”。
|
|
71
|
+
|
|
72
|
+
- 消息菜单“删除”直接请求选中 replyID,不加确认弹窗;新增、回复、编辑和删除成功后不追加成功 toast。复制链接成功后关闭菜单,不在正文下插入成功提示。
|
|
73
|
+
- 保留发送等待时的蓝色 loading、失败消息上的错误图标与“点击重试”;不要添加“发送中”“保存中”“已同步”“已显示全部评论”等额外可见文案。读屏用 aria-label/status 不属于额外可见提示,不要移除。
|
|
74
|
+
- 点击回复自动插入目标作者提及,回复自己也插入;不要按当前用户身份跳过。人员悬浮底色、提及搜索面板与 placeholder 由组件样式管理,宿主只控制外层尺寸,避免全局 button/a/p 样式覆盖。
|
|
75
|
+
- 只有确认未写入的失败才能重试;结果未知时保留内容并由宿主刷新核对,不能为追求视觉一致开放可能重复发表的重试。初次读取、分页、剪贴板拒绝等失败仍要保留必要的恢复入口,不得吞错。
|
|
76
|
+
|
|
77
|
+
新增交互或提示前先取得目标官方界面的截图/操作证据;没有证据的状态说明为接入扩展,不能声称已与官方完全对齐。验收要分别记录官方实测、真实 Action 回读、本地故障用例和未覆盖项。图片上传不在当前真实插件验收范围。
|
|
78
|
+
|
|
79
|
+
## 完成前验收
|
|
80
|
+
|
|
81
|
+
必须在真实目标环境完成新增→回复→编辑→刷新回读→删除;有回应能力时验证添加/撤回并刷新,有文档能力时验证搜索/插入/重新打开。检查键盘提及选择、单条/空结果、慢网络等待与失败重试、身份/记录切换、分页去重。只在本地 mock 页通过不能声称真实链路完成;图片未接入应明确说明。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plugin-guide
|
|
3
|
-
description: 本地开发场景下的 AI 插件集成规范。使用 lark-cli 命令管理插件包和实例,通过 capabilityClient/CapabilityService 生成调用代码。触发词:插件, plugin, AI生文, AI生图, 图片理解, 文档解析, 语音转文字, 结构化提取, capabilityClient, CapabilityService, pluginInstance, lark-cli plugin
|
|
3
|
+
description: 本地开发场景下的 AI 插件集成规范。使用 lark-cli 命令管理插件包和实例,通过 capabilityClient/CapabilityService 生成调用代码。触发词:插件, plugin, AI生文, AI生图, 图片理解, 文档解析, 语音转文字, 结构化提取, capabilityClient, CapabilityService, pluginInstance, lark-cli plugin, 记录评论组件, 评论回复, 表情回应, 云文档引用
|
|
4
4
|
compatibility: "requires: lark-cli with plugin commands (apps +plugin-install, +plugin-uninstall, +plugin-list). If not met, tell user: lark-cli 当前版本不支持插件能力,请升级到最新版本"
|
|
5
5
|
when_to_use: "lark-cli apps +plugin-* commands are available. If not, tell user: lark-cli 当前版本不支持插件能力,请升级到最新版本"
|
|
6
6
|
steering: true
|
|
@@ -25,6 +25,10 @@ lark-cli apps --help 2>&1 | grep -q '+plugin-install' && echo "READY" || echo "M
|
|
|
25
25
|
- **READY** → 继续
|
|
26
26
|
- **MISSING** → 当前 lark-cli 版本不支持插件命令,请更新到最新版后重试:
|
|
27
27
|
|
|
28
|
+
## 多维表格评论组件
|
|
29
|
+
|
|
30
|
+
需要记录评论/回复、编辑删除、@人员、表情回应或云文档引用时,复用项目内 `@/components/business-ui/record-comments` 的 `CommentPanel`;**先读 [评论组件接入指引](references/record-comments.md)** 和组件 `README.md`。真实接口、权限、缓存和分页游标留在宿主;组件负责现有样式、乐观更新、失败重试和分页触发。图片未接入插件,不默认启用。
|
|
31
|
+
|
|
28
32
|
## Quick Reference
|
|
29
33
|
|
|
30
34
|
| 操作 | 方式 |
|
package/steering/nestjs-react-fullstack/skills_local/plugin-guide/references/record-comments.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# 多维表格评论组件(Record comments)
|
|
2
|
+
|
|
3
|
+
需要记录评论、新增/回复、编辑/删除、@人员、表情回应或云文档引用时,先读项目内 `client/src/components/business-ui/record-comments/README.md` 与 `comment-panel.contract.ts`,复用 `CommentPanel`,不要重新实现编辑器、弹层或乐观更新。此组件随 **nestjs-react-fullstack** 模板分发;旧应用若没有该目录,需要先升级/同步组件及依赖,不能只复制入口文件。本指引不适用于缺少该资产的其他技术栈。
|
|
4
|
+
|
|
5
|
+
## 接入边界
|
|
6
|
+
|
|
7
|
+
- 从 `@/components/business-ui/record-comments` 导入 `CommentPanel` 和公共类型;所有内部文件、CSS、`assets/reaction-sprite.png` 必须保留。Tiptap 依赖及 `@floating-ui/dom` 已由模板配置,不混装 Tiptap 大版本。
|
|
8
|
+
- 组件管理显示、回复归属、编辑器、人员弹层、发送等待/失败/重试以及乐观更新;**评论 CRUD、插件实例、用户身份、读缓存、分页游标、表情及文档请求全部由页面/API hook 提供**。不要把插件凭据、实例 ID、表格配置或应用 ID 写进组件内部。
|
|
9
|
+
- `createAppCommentSource(scopeKey)` 是可选的模板人员目录适配器,只识别妙搭人员 ID;其他目录实现 `CommentDataSource`,不要把飞书 ID 冒充妙搭 ID。默认人员目录不提供空查询推荐;宿主可提供 `recommend`,例如从当前讨论参与者解析推荐列表。
|
|
10
|
+
- `scopeKey` 必须覆盖应用/租户/登录身份/插件实例/数据源与权限上下文,以上任一变化都应更换;`recordID` 切换会重建编辑会话。`mentionSource` 用 `useMemo` 稳定,避免每次渲染重建搜索和缓存。
|
|
11
|
+
|
|
12
|
+
## 真实接口先核对
|
|
13
|
+
|
|
14
|
+
按本 skill 的插件元数据流程拿到当前实例真实 Action schema。以下是已联调版本的映射,**实际 actionKey、字段和响应层级始终以当前元数据为准**,缺能力则隐藏该功能,不伪造成功、不回退 mock。
|
|
15
|
+
|
|
16
|
+
| 宿主能力 | 已联调 Action / 输入约定 |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| 评论读取 | `listRecordComments`,传 `recordID`,需要回应时传 `needReaction:true`;按 `hasMore/pageToken` 加载下一页 |
|
|
19
|
+
| 新增/回复 | `addRecordComment`;新评论不传 `commentID`,回复保留原 `commentID`,返回真实 `commentID/replyID` |
|
|
20
|
+
| 编辑 | `updateRecordComment`;传 `recordID/commentID/replyID/content`,回读并传入回复级 `updateTime` |
|
|
21
|
+
| 删除 | `deleteRecordComment`;消息菜单必须传选中消息的 `replyID`(含首条),只删该消息;第二条顺位成为正文,其余回复保留。省略 replyID 仅兼容显式整串删除,按 `commentDeleted` 更新缓存 |
|
|
22
|
+
| 回应读取/写入 | `getRecordCommentReactions` / `updateRecordCommentReaction`;写入用显式 `add/delete`,组件回调的 `remove` 需映射成插件的 `delete` |
|
|
23
|
+
| 云文档推荐/搜索/解析 | `getCommentDocuments`;外部实现 `CommentDocumentSource`,用 `withDocumentSource(people, documents)` 合并,显式传 `enableDocuments` |
|
|
24
|
+
|
|
25
|
+
每条 `comments[]` 是讨论,`replies[0]` 是正文,其余 replies 是同一讨论下的消息;不要把回复拆成独立 comment。`commentID/replyID` 始终是字符串。`createTime/updateTime` 是 Unix **秒**,排序取创建时间,展示取 `updateTime ?? createTime`;缺更新时间不能伪造“编辑过”。`mention.userID` 缺失时保留缺失引用,不猜人。
|
|
26
|
+
|
|
27
|
+
## 受控组件的宿主实现
|
|
28
|
+
|
|
29
|
+
1. 宿主发起首屏读取,维护 `comments/loading/error`,传 `onRetry`。分页可全量拉取后不给 `pagination`,也可传 `{hasMore, loading, error, onLoadMore}`;组件接近底部会自动调用,宿主用自己的游标追加数据,追加后再 resolve,失败必须 reject。游标不前进时终止并报错。
|
|
30
|
+
2. 宿主提供 `onAddComment/onUpdateComment/onDeleteComment`。写成功后立即返回真实 ID/删除结果,再单独启动回读;**不能把后续回读失败当成写失败**,否则组件重试可能重复发表。不需要在宿主再插一份临时评论,也不必等待回读才关闭编辑器。
|
|
31
|
+
3. 编辑/删除必须显式传 `getReplyPermissions`,未提供就隐藏。前端作者判断只控制菜单,服务端仍检查当前用户权限;不能用 owner 身份补足。点击消息菜单“删除”直接发起请求,不追加二次确认;删除首条也只删它本身,不能扩大到其他人的回复;只有最后一条消失时移除空讨论。
|
|
32
|
+
4. 普通 `Error` 仅用于确认未应用的失败;超时/连接中断等可能已提交时,抛 `Object.assign(new Error(message), {outcome:'unknown'})`,组件保留内容并阻止盲目重试,宿主先刷新核对。只有请求尚未发送等已确认未写入的断网失败才标记 `not-applied`。
|
|
33
|
+
5. 宿主缓存要保护读取顺序,取消/忽略旧请求,切换身份时隔离缓存;组件在匹配快照接管后无法判断一个没有版本号的旧数组。不要通过延迟 toast、二次 loading 或本地时间 Map 掩盖回读错误。
|
|
34
|
+
|
|
35
|
+
最小受控接入(`read` 和 `commands` 是应用自己的真实 API hook,不是组件内部接口):
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useMemo } from 'react';
|
|
39
|
+
import {
|
|
40
|
+
CommentPanel, createAppCommentSource,
|
|
41
|
+
type CommentPanelProps,
|
|
42
|
+
} from '@/components/business-ui/record-comments';
|
|
43
|
+
|
|
44
|
+
type Props = Pick<CommentPanelProps,
|
|
45
|
+
'scopeKey' | 'recordID' | 'comments' | 'currentUserID' |
|
|
46
|
+
'loading' | 'error' | 'onRetry' | 'pagination' |
|
|
47
|
+
'onAddComment' | 'onUpdateComment' | 'onDeleteComment' |
|
|
48
|
+
'getReplyPermissions'>;
|
|
49
|
+
|
|
50
|
+
export function RecordComments(props: Props) {
|
|
51
|
+
const mentionSource = useMemo(
|
|
52
|
+
() => createAppCommentSource(props.scopeKey), [props.scopeKey],
|
|
53
|
+
);
|
|
54
|
+
return <CommentPanel {...props} mentionSource={mentionSource} />;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
真实调用继续使用 `capabilityClient.load(instanceID).call(actionKey, input)`,不要在组件里调用 CLI 或直接手写 OAuth;`call()` 的结果已由 SDK 解包,按实际 schema 使用,不要重复套 `data.output`。写入回调须返回合同中的真实结果,不能返回 `void`。
|
|
59
|
+
|
|
60
|
+
## 可选能力与权限
|
|
61
|
+
|
|
62
|
+
- **表情回应**:传 `reactions.getReactions/onChange`,可选 `canChange/loadUsers`。列表省略 reactions 表示未知,不能当作无人回应,`mine` 也不能根据截断的 `userIDs` 推断。宿主按回复串行写入,成功先更新当前回应缓存,再刷新;失败只回退该目标,刷新失败不能回退已提交动作。读取不到权威 membership 时禁用切换,保留历史表情键(如 `MediumThumbsup`)。
|
|
63
|
+
- **云文档引用**:搜索候选的 `description` 使用接口的 `editTime`(Unix 秒)格式化为“最近修改:10月26日 20:41”等元数据副标题;缺失时间就省略,不伪造。只查询当前用户可读的元数据,搜索与解析都应检查权限;不得以 owner/其他用户补查私密标题,插入引用不更改分享权限。写入是 `{type:'link',url}`;持久化 URL,不把标题/图标当作请求字段。请求被取消后检查 signal,丢弃过期结果。解析不可读时使用组件的灰色引用状态,不能假称可读。
|
|
64
|
+
- **图片**:当前多维表格插件未接入图片评论,不传 `images`,不展示上传入口。`images` 仅是宿主自备上传/持久化能力时的独立扩展;不得把 File/blob URL 塞入插件 content。
|
|
65
|
+
- **人员卡片/链接**:通过 `onMentionClick/getCommentLink` 由宿主承接;未提供对应回调时不展示无效操作。
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## 交互保持约束
|
|
69
|
+
|
|
70
|
+
复用完整组件及其状态流,不在宿主外包一层“增强交互”。
|
|
71
|
+
|
|
72
|
+
- 消息菜单“删除”直接请求选中 replyID,不加确认弹窗;新增、回复、编辑和删除成功后不追加成功 toast。复制链接成功后关闭菜单,不在正文下插入成功提示。
|
|
73
|
+
- 保留发送等待时的蓝色 loading、失败消息上的错误图标与“点击重试”;不要添加“发送中”“保存中”“已同步”“已显示全部评论”等额外可见文案。读屏用 aria-label/status 不属于额外可见提示,不要移除。
|
|
74
|
+
- 点击回复自动插入目标作者提及,回复自己也插入;不要按当前用户身份跳过。人员悬浮底色、提及搜索面板与 placeholder 由组件样式管理,宿主只控制外层尺寸,避免全局 button/a/p 样式覆盖。
|
|
75
|
+
- 只有确认未写入的失败才能重试;结果未知时保留内容并由宿主刷新核对,不能为追求视觉一致开放可能重复发表的重试。初次读取、分页、剪贴板拒绝等失败仍要保留必要的恢复入口,不得吞错。
|
|
76
|
+
|
|
77
|
+
新增交互或提示前先取得目标官方界面的截图/操作证据;没有证据的状态说明为接入扩展,不能声称已与官方完全对齐。验收要分别记录官方实测、真实 Action 回读、本地故障用例和未覆盖项。图片上传不在当前真实插件验收范围。
|
|
78
|
+
|
|
79
|
+
## 完成前验收
|
|
80
|
+
|
|
81
|
+
必须在真实目标环境完成新增→回复→编辑→刷新回读→删除;有回应能力时验证添加/撤回并刷新,有文档能力时验证搜索/插入/重新打开。检查键盘提及选择、单条/空结果、慢网络等待与失败重试、身份/记录切换、分页去重。只在本地 mock 页通过不能声称真实链路完成;图片未接入应明确说明。
|