@lark-apaas/coding-template-nestjs-react-fullstack 0.1.60-alpha.20261105142153 → 0.1.60-beta.0
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 +1 -1
- package/template/client/src/components/business-ui/README.md +0 -2
- package/template/package-lock.json +30 -546
- package/template/package.json +3 -14
- package/template/scripts/build.sh +39 -22
- package/template/scripts/prune-smart.js +20 -1
- package/template/client/src/components/business-ui/comments/README.md +0 -101
- package/template/client/src/components/business-ui/comments/comments.tsx +0 -670
- package/template/client/src/components/business-ui/comments/contracts.ts +0 -256
- package/template/client/src/components/business-ui/comments/index.ts +0 -20
- package/template/client/src/components/business-ui/comments/use-comments.ts +0 -492
- package/template/client/src/components/business-ui/record-comments/README.md +0 -141
- package/template/client/src/components/business-ui/record-comments/app-source.ts +0 -93
- package/template/client/src/components/business-ui/record-comments/assets/reaction-sprite.png +0 -0
- package/template/client/src/components/business-ui/record-comments/comment-panel.contract.ts +0 -240
- package/template/client/src/components/business-ui/record-comments/comment-panel.tsx +0 -231
- package/template/client/src/components/business-ui/record-comments/extensions.ts +0 -130
- package/template/client/src/components/business-ui/record-comments/index.ts +0 -9
- package/template/client/src/components/business-ui/record-comments/internal/composer.tsx +0 -531
- package/template/client/src/components/business-ui/record-comments/internal/content.tsx +0 -213
- package/template/client/src/components/business-ui/record-comments/internal/discussion.tsx +0 -523
- package/template/client/src/components/business-ui/record-comments/internal/editor.tsx +0 -681
- package/template/client/src/components/business-ui/record-comments/internal/icons.tsx +0 -148
- package/template/client/src/components/business-ui/record-comments/internal/images.tsx +0 -155
- package/template/client/src/components/business-ui/record-comments/internal/mentions.tsx +0 -163
- package/template/client/src/components/business-ui/record-comments/internal/model.ts +0 -581
- package/template/client/src/components/business-ui/record-comments/internal/mutations.ts +0 -198
- package/template/client/src/components/business-ui/record-comments/internal/reaction-catalog.ts +0 -196
- package/template/client/src/components/business-ui/record-comments/internal/reactions.tsx +0 -683
- package/template/client/src/components/business-ui/record-comments/internal/styles.css +0 -1109
- package/template/client/src/components/business-ui/record-comments/internal/suggestions.tsx +0 -456
- package/template/client/src/components/business-ui/record-comments/internal/view-types.ts +0 -91
- package/template/client/src/components/business-ui/record-comments/types.ts +0 -121
package/template/package.json
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"crypto-js": "^4.2.0",
|
|
46
46
|
"dayjs": "^1.11.15",
|
|
47
47
|
"dotenv": "^17.2.2",
|
|
48
|
-
"drizzle-orm": "0.
|
|
48
|
+
"drizzle-orm": "0.45.2",
|
|
49
49
|
"hbs": "^4.2.0",
|
|
50
50
|
"reflect-metadata": "^0.1.14",
|
|
51
51
|
"tslib": "^2.8.1"
|
|
@@ -133,16 +133,7 @@
|
|
|
133
133
|
"uuid": "11.0.5",
|
|
134
134
|
"vaul": "^1.1.2",
|
|
135
135
|
"vite": "^8",
|
|
136
|
-
"zod": "^4.3.6"
|
|
137
|
-
"@floating-ui/dom": "1.7.4",
|
|
138
|
-
"@tiptap/core": "3.20.0",
|
|
139
|
-
"@tiptap/extension-document": "3.20.0",
|
|
140
|
-
"@tiptap/extension-hard-break": "3.20.0",
|
|
141
|
-
"@tiptap/extension-paragraph": "3.20.0",
|
|
142
|
-
"@tiptap/extension-text": "3.20.0",
|
|
143
|
-
"@tiptap/pm": "3.20.0",
|
|
144
|
-
"@tiptap/react": "3.20.0",
|
|
145
|
-
"@tiptap/suggestion": "3.20.0"
|
|
136
|
+
"zod": "^4.3.6"
|
|
146
137
|
},
|
|
147
138
|
"overrides": {
|
|
148
139
|
"dayjs": "^1.11.15",
|
|
@@ -150,9 +141,7 @@
|
|
|
150
141
|
"tailwind-merge": "^3.5.0",
|
|
151
142
|
"zod": "^4.3.6",
|
|
152
143
|
"echarts": "^6.1.0",
|
|
153
|
-
"stylelint": "^17.3.0"
|
|
154
|
-
"@tiptap/extension-bubble-menu": "3.20.0",
|
|
155
|
-
"@tiptap/extension-floating-menu": "3.20.0"
|
|
144
|
+
"stylelint": "^17.3.0"
|
|
156
145
|
},
|
|
157
146
|
"engines": {
|
|
158
147
|
"node": ">=22.0.0",
|
|
@@ -45,32 +45,49 @@ STEP_START=$(node -e "console.log(Date.now())")
|
|
|
45
45
|
|
|
46
46
|
# 在 client/server 构建之前生成到 dist/,供 DefinePlugin 注入前端 bundle
|
|
47
47
|
# 注意:nest-cli.json 中 deleteOutDir 必须为 false(模板默认值),否则 nest build 会清掉 dist/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
wait $API_ROUTES_PID || API_ROUTES_EXIT=$?
|
|
60
|
-
wait $PAGE_ROUTES_PID || PAGE_ROUTES_EXIT=$?
|
|
61
|
-
|
|
62
|
-
if [ $API_ROUTES_EXIT -ne 0 ]; then
|
|
63
|
-
echo " ⚠️ API 路由生成失败(不影响构建)"
|
|
64
|
-
cat /tmp/gen-api-routes.log
|
|
48
|
+
# 生成器来自 @lark-apaas/devtool-kits(由 preset 间接安装)。只用本地命令:
|
|
49
|
+
# 未安装时直接跳过,不走 npx,避免联网查同名包 404 的等待;preset 读不到文件会注入空路由。
|
|
50
|
+
API_ROUTES_BIN="$ROOT_DIR/node_modules/.bin/generate-api-routes"
|
|
51
|
+
PAGE_ROUTES_BIN="$ROOT_DIR/node_modules/.bin/generate-page-routes"
|
|
52
|
+
API_ROUTES_PID=""
|
|
53
|
+
PAGE_ROUTES_PID=""
|
|
54
|
+
|
|
55
|
+
if [ -x "$API_ROUTES_BIN" ]; then
|
|
56
|
+
echo " ├─ 生成 API 路由定义..."
|
|
57
|
+
"$API_ROUTES_BIN" --server-dir ./server --out-dir ./dist > /tmp/gen-api-routes.log 2>&1 &
|
|
58
|
+
API_ROUTES_PID=$!
|
|
65
59
|
else
|
|
66
|
-
echo "
|
|
60
|
+
echo " ⏭️ 未安装 generate-api-routes,跳过 API 路由定义生成"
|
|
67
61
|
fi
|
|
68
62
|
|
|
69
|
-
if [
|
|
70
|
-
echo "
|
|
71
|
-
|
|
63
|
+
if [ -x "$PAGE_ROUTES_BIN" ]; then
|
|
64
|
+
echo " ├─ 生成页面路由定义..."
|
|
65
|
+
"$PAGE_ROUTES_BIN" --app-path ./client/src/app.tsx --out-dir ./dist > /tmp/gen-page-routes.log 2>&1 &
|
|
66
|
+
PAGE_ROUTES_PID=$!
|
|
72
67
|
else
|
|
73
|
-
echo "
|
|
68
|
+
echo " ⏭️ 未安装 generate-page-routes,跳过页面路由定义生成"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ -n "$API_ROUTES_PID" ]; then
|
|
72
|
+
API_ROUTES_EXIT=0
|
|
73
|
+
wait $API_ROUTES_PID || API_ROUTES_EXIT=$?
|
|
74
|
+
if [ $API_ROUTES_EXIT -ne 0 ]; then
|
|
75
|
+
echo " ⚠️ API 路由生成失败(不影响构建)"
|
|
76
|
+
cat /tmp/gen-api-routes.log
|
|
77
|
+
else
|
|
78
|
+
echo " ✅ API 路由生成完成"
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if [ -n "$PAGE_ROUTES_PID" ]; then
|
|
83
|
+
PAGE_ROUTES_EXIT=0
|
|
84
|
+
wait $PAGE_ROUTES_PID || PAGE_ROUTES_EXIT=$?
|
|
85
|
+
if [ $PAGE_ROUTES_EXIT -ne 0 ]; then
|
|
86
|
+
echo " ⚠️ 页面路由生成失败(不影响构建)"
|
|
87
|
+
cat /tmp/gen-page-routes.log
|
|
88
|
+
else
|
|
89
|
+
echo " ✅ 页面路由生成完成"
|
|
90
|
+
fi
|
|
74
91
|
fi
|
|
75
92
|
print_time $STEP_START
|
|
76
93
|
echo ""
|
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// This file is auto-generated by @lark-apaas/fullstack-cli
|
|
3
3
|
|
|
4
|
-
const { nodeFileTrace } = require('@vercel/nft');
|
|
5
4
|
const fs = require('fs');
|
|
6
5
|
const path = require('path');
|
|
6
|
+
const { createRequire } = require('module');
|
|
7
|
+
|
|
8
|
+
// @vercel/nft 是 fullstack-cli 自身的依赖:应用装了 CLI 时从应用 node_modules 加载;
|
|
9
|
+
// 构建镜像全局预装 CLI、应用未安装时,从 PATH 上 fullstack-cli 的安装目录加载
|
|
10
|
+
function loadNft() {
|
|
11
|
+
try {
|
|
12
|
+
return require('@vercel/nft');
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
15
|
+
}
|
|
16
|
+
for (const dir of (process.env.PATH || '').split(path.delimiter)) {
|
|
17
|
+
const cliBin = dir && path.join(dir, 'fullstack-cli');
|
|
18
|
+
if (cliBin && fs.existsSync(cliBin)) {
|
|
19
|
+
return createRequire(fs.realpathSync(cliBin))('@vercel/nft');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
throw new Error('未找到 @vercel/nft:应用未安装 fullstack-cli,PATH 上也没有全局 fullstack-cli');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { nodeFileTrace } = loadNft();
|
|
7
26
|
|
|
8
27
|
const ROOT_DIR = path.resolve(__dirname, '..');
|
|
9
28
|
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# 评论组件
|
|
2
|
-
|
|
3
|
-
默认通过 `useComments` 接入。页面只提供业务对象、当前用户和 adapter;请求方法、认证及服务协议由应用自己实现。UI、回复/编辑/删除、乐观更新仍由同一套组件管理。
|
|
4
|
-
|
|
5
|
-
```tsx
|
|
6
|
-
import {
|
|
7
|
-
useComments,
|
|
8
|
-
CommentsProvider,
|
|
9
|
-
CommentList,
|
|
10
|
-
CommentComposer,
|
|
11
|
-
} from '@/components/business-ui/comments';
|
|
12
|
-
|
|
13
|
-
function Comments({ recordId, currentUser, adapter }) {
|
|
14
|
-
const comments = useComments({ resourceKey: recordId, currentUser, adapter });
|
|
15
|
-
return (
|
|
16
|
-
<CommentsProvider value={comments}>
|
|
17
|
-
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
18
|
-
<CommentList />
|
|
19
|
-
</div>
|
|
20
|
-
<CommentComposer />
|
|
21
|
-
</CommentsProvider>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Provider 不输出布局 DOM。标题、边框、尺寸、滚动容器归页面。也可 `<CommentPanel value={comments} />` 使用默认完整布局。主输入框在同一个 Provider 内挂一次,行内编辑由列表承载。
|
|
27
|
-
|
|
28
|
-
## 最小 adapter
|
|
29
|
-
|
|
30
|
-
```ts
|
|
31
|
-
import type { CommentAdapter } from '@/components/business-ui/comments';
|
|
32
|
-
|
|
33
|
-
const adapter: CommentAdapter = {
|
|
34
|
-
scopeKey: 'my-api:tenant:authenticated-account',
|
|
35
|
-
async list({ resourceKey, cursor, signal }) {
|
|
36
|
-
// 调用应用自己的 API,并映射为 CommentPage;不要把后端原始结构直接返回。
|
|
37
|
-
return api.listComments({ recordId: resourceKey, cursor, signal });
|
|
38
|
-
},
|
|
39
|
-
async create(
|
|
40
|
-
{ resourceKey, threadId, replyToMessageId, content, attachments },
|
|
41
|
-
context,
|
|
42
|
-
) {
|
|
43
|
-
return api.createComment(
|
|
44
|
-
{
|
|
45
|
-
recordId: resourceKey,
|
|
46
|
-
threadId,
|
|
47
|
-
replyToMessageId,
|
|
48
|
-
content,
|
|
49
|
-
attachments,
|
|
50
|
-
},
|
|
51
|
-
context,
|
|
52
|
-
);
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
`api` 是应用自身代码,不是组件内置客户端。只有 list 就是只读;提供 create 才能新增/回复,update/remove 配合每条消息权限启用编辑/删除。不要用组件参数替代后端鉴权。
|
|
58
|
-
|
|
59
|
-
- scopeKey 隔离提供方、租户和认证上下文,不含凭证;切换连接或权限身份时变化。hook 额外根据 resourceKey 和 currentUser 的完整引用隔离会话。推荐用 useMemo 或模块变量保持 adapter 稳定;同一 scope 的新对象本身不会反复加载。
|
|
60
|
-
- list 返回 `{threads,nextCursor?}`;讨论包含 id/messages,第一条为正文。messages 使用字符串 ID 和毫秒时间。nextCursor 省略表示没有下一页,不自行生成游标。
|
|
61
|
-
- 单个讨论未读全时返回 nextReplyCursor,并提供 listReplies,返回 `{messages,nextCursor?}`。hook 接管两层分页、去重、游标保护,以及刷新后保留已加载页数。
|
|
62
|
-
- create/update 返回 `{threadId,messageId,message?}`,message 是可选权威回读。create 不传 threadId 表示新讨论;回复同时带 threadId 和精确 replyToMessageId,后端只支持线性讨论时可以忽略后者。
|
|
63
|
-
- remove 接收选中消息 ID,包括首条。删除后第二条自然提升;返回 `{threadId,threadDeleted?}`,不能确定讨论是否为空就省略该字段,不凭局部列表推断。
|
|
64
|
-
- hook 自动首屏读取;写入前丢弃旧读取,等并发写入完成再静默刷新。正常写后回读不触发整体 loading,刷新失败不反转已成功写入。`comments.refresh()` 是主动刷新,`comments.refreshing` 可供宿主显示后台读取状态。
|
|
65
|
-
- 每次写操作收到 `{operationId,signal}`;同一次重试保持 operationId。确定未写入可抛 Error 并设置 outcome:'not-applied';超时/回包丢失为 outcome:'unknown',普通未标注异常也按未知处理,不能盲重发。
|
|
66
|
-
- 服务支持客户端幂等标识时,adapter 将 context.operationId 写入并在读取消息上返回 clientOperationId,组件据此核对未知新增结果。没有服务端关联证据时不按正文去重,也不宣称能自动恢复所有未知结果。
|
|
67
|
-
|
|
68
|
-
## 可选能力都放 adapter
|
|
69
|
-
|
|
70
|
-
| 字段 | 作用 |
|
|
71
|
-
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
72
|
-
| people.source | search/recommend/resolve;search 或 recommend 可用时启用 @;只有 resolve 时只补展示资料 |
|
|
73
|
-
| people.canMention / onClick | 可选人员插入策略、人员卡片回调 |
|
|
74
|
-
| documents | `{source,canInsert?}`,注册后默认可插入;canInsert:false 只提供元数据解析 |
|
|
75
|
-
| reactions | `{catalog?,allowedKeys?,onChange?}`,无 onChange 为只读,onChange 接收 resourceKey、threadId、messageId、key、add/remove |
|
|
76
|
-
| images | 设 true 才启用文件选择;create/update 必须能上传并持久化完整消息,注册不会自动上传 |
|
|
77
|
-
| links | 是否允许插入结构化普通链接,默认 false |
|
|
78
|
-
| validateDraft | 服务特有正文限制;返回错误文案或 undefined |
|
|
79
|
-
| getMessageLink | 提供消息链接时显示复制链接菜单 |
|
|
80
|
-
|
|
81
|
-
catalog 项为 `{key,label,text?,url?,group?}`;不传时沿用现有表情资产。allowedKeys 同时约束快捷点击和选择列表。回应数据放 message.reactions,undefined 表示未读取、[] 表示权威为空、mine 省略表示本人状态未知,不根据截断人员名单推断 false。
|
|
82
|
-
|
|
83
|
-
正文是 CommentContent,不是 Tiptap JSON 或服务 wire schema。读取遇到不支持的节点要保留为 unsupported 并禁用编辑。不能丢图片或未知节点后保存。禁用插入新引用时,编辑可原样保留已有引用,最终仍由 validateDraft/后端确认支持。
|
|
84
|
-
|
|
85
|
-
用户引用当前为 `{kind:'user',namespace,id}`。不能把 open_id 当妙搭 userId;需要对接平台人员体系时在服务端 adapter 转换,统一目录、作者和 currentUser 的身份域。组件没有内置身份转换或 owner 权限兜底。
|
|
86
|
-
|
|
87
|
-
## 高级受控方式继续可用
|
|
88
|
-
|
|
89
|
-
已有 React Query、自有缓存或实时订阅时,可以继续传 `scopeKey/subjectKey/threads/actions/loading/error/pagination/actors/input/extensions` 等原始 props,详见 contracts.ts。该模式由宿主维护读取、分页和写入屏障,组件仍管理乐观覆盖。不要同时传 value 和受控 props,不再另建一套临时消息队列。
|
|
90
|
-
|
|
91
|
-
原 `business-ui/record-comments` 的多维表格合同与数字 ID/秒级时间入口保留兼容;不能与新入口的类型混用。新通用入口仍共享原 UI 内部实现,未移动 CSS/assets。
|
|
92
|
-
|
|
93
|
-
## 样式与验收
|
|
94
|
-
|
|
95
|
-
项目入口需要模板自身的全局 `index.css`(Tailwind、主题与基础重置),组件会引入原内部 CSS。使用 hook 不改变任何视觉 token。
|
|
96
|
-
|
|
97
|
-
真实 OpenAPI 验收示例位于仓库 `scripts/__tests__/fixtures/record-comments/openapi/`,使用同一个 useComments 入口。它是开发工具,不随模板运行时携带;运行中的应用不能 shell 执行 lark-cli 或把 token 放到浏览器。
|
|
98
|
-
|
|
99
|
-
本地完整能力示例在同级测试目录 `memory/`:用独立 adapter 验收人员选择、混合图片回复、文档、表情、确定失败重试和未知结果核对;图片仅在本地持久化,不代表 OpenAPI 图片接入。
|
|
100
|
-
|
|
101
|
-
多维表格插件的真实应用 adapter 示例位于 `scripts/__tests__/fixtures/record-comments/bitable/`,使用与 OpenAPI 相同的 useComments 接口,保留服务自身身份域和权限。
|