@lark-apaas/coding-steering 0.1.47-alpha.20260903185732 → 0.1.47
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/app-init-feasibility-guide/SKILL.md +2 -0
- package/steering/nestjs-react-fullstack/skills/authz-guide/references/management-page-spec.md +9 -4
- package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +2 -11
- package/steering/nestjs-react-fullstack/skills/coding-guide/SKILL.md +24 -46
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/table.md +3 -3
- package/steering/nestjs-react-fullstack/skills_common/user-identity/SKILL.md +3 -1
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/management-page-spec.md +9 -4
- package/steering/nestjs-react-fullstack/skills_local/client-builtins-user-service/SKILL.md +5 -6
- package/steering/nestjs-react-fullstack/skills_local/coding-guide/SKILL.md +29 -36
package/package.json
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/coding-steering",
|
|
3
|
-
"version": "0.1.47
|
|
3
|
+
"version": "0.1.47",
|
|
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
|
-
},
|
|
12
9
|
"devDependencies": {
|
|
13
10
|
"markdownlint-cli": "^0.47.0"
|
|
14
11
|
},
|
|
@@ -20,5 +17,8 @@
|
|
|
20
17
|
"miaoda",
|
|
21
18
|
"coding-steering"
|
|
22
19
|
],
|
|
23
|
-
"license": "MIT"
|
|
24
|
-
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"lint:md": "markdownlint 'steering/**/*.md' --ignore 'steering/**/skills/**' --ignore 'steering/**/skills_common/**' --ignore 'steering/**/skills_local/**'"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -83,6 +83,7 @@ available-agents:
|
|
|
83
83
|
| 无持久化状态、纯展示或前端工具 | 纯前端 |
|
|
84
84
|
| 需要数据存储、用户隔离、后端逻辑 | 全栈 |
|
|
85
85
|
| 涉及文件上传与管理 | 全栈 + 文件服务 |
|
|
86
|
+
| 服务端图像、音视频处理(易引入 native addon) | 先确认能否改用 client 处理、平台服务或纯 JS/WASM |
|
|
86
87
|
| 以飞书多维表格为数据源 | 插件优先;复杂查询场景评估是否同步至本地库 |
|
|
87
88
|
| 需要飞书通知、群组、AI 能力 | 插件集成,标记插件实例需求 |
|
|
88
89
|
| 需要定时或事件驱动的后台处理 | 全栈 + 自动化任务 |
|
|
@@ -113,6 +114,7 @@ available-agents:
|
|
|
113
114
|
- 集成点:飞书、多维表格、AI、内网接口、OpenAPI、自动化触发。
|
|
114
115
|
- 边界与限制:不支持的能力、外部依赖、数据权限边界。
|
|
115
116
|
- 交给 Code agent 时标明需要加载的专项 skill(表格、表单、图表、插件、权限、文件、自动化)。
|
|
117
|
+
- 需要 native addon 时:确认它提供覆盖 linux-x64 / glibc 的预编译二进制并记录包名与版本;没有预编译二进制、装的时候要现场编译的不能用。
|
|
116
118
|
|
|
117
119
|
## Common Mistakes
|
|
118
120
|
|
package/steering/nestjs-react-fullstack/skills/authz-guide/references/management-page-spec.md
CHANGED
|
@@ -16,7 +16,7 @@ Step 0: 制定实施计划(强制,禁止跳过)
|
|
|
16
16
|
|
|
17
17
|
Step 1: 编码前确认(闸门)
|
|
18
18
|
├─ 确认 SDK 方法签名:读取 sdk-types.md,摘录 roles / members / search 的出入参
|
|
19
|
-
├─
|
|
19
|
+
├─ 确认搜索组件目录存在:client/src/components/business-ui/ 下包含 user-select、department-select、chat-select 目录
|
|
20
20
|
└─ 确认 Tag 胶囊组件存在:上述目录中分别包含 user-select-tag、department-select-tag、chat-select-tag 文件
|
|
21
21
|
|
|
22
22
|
⛔ 以上任一不存在,停止实现并报告
|
|
@@ -63,7 +63,7 @@ Step 5: 验证
|
|
|
63
63
|
|
|
64
64
|
按以下优先级顺序拼接展示,无任何内容时显示 `--`:
|
|
65
65
|
|
|
66
|
-
1.
|
|
66
|
+
1. **特殊范围胶囊**:使用 `ItemPill` 组件(`business-ui/entity-combobox/item-pill`)展示,每项带**品牌色**(`bg-primary`,跟随 CSS 变量 `--primary`,默认蓝色)圆底 20px 图标(`SPECIAL_MEMBER_ICONS` 常量,见代码骨架),图标前景色使用 `text-primary-foreground`,在表格成员摘要列和成员编辑面板中**同时复用**:
|
|
67
67
|
- `企业全员`(`allEmployees`):`Building` 图标(lucide-react)
|
|
68
68
|
- `互联网公开`(`public`):`Globe` 图标(lucide-react)
|
|
69
69
|
- `应用开发者`(`presetGroup.isContainsAdmin`):`Users` 图标(lucide-react)
|
|
@@ -229,14 +229,19 @@ const SPECIAL_MEMBER_ICONS = {
|
|
|
229
229
|
// RoleManagementPage.tsx 骨架
|
|
230
230
|
import { useState, useEffect } from 'react';
|
|
231
231
|
import { Table, type TableProps } from '@lark-apaas/client-toolkit/antd-table';
|
|
232
|
-
// 妙搭业务组件(UserSelect / DepartmentSelect / ChatSelect 及其 Tag、胶囊)的 import 路径
|
|
233
|
-
// 以 get_ui_component_info 返回的 docs 为准,安装后按 docs 补齐,勿凭记忆写死路径
|
|
234
232
|
import { Button } from '@client/src/components/ui/button';
|
|
235
233
|
import { Badge } from '@client/src/components/ui/badge';
|
|
236
234
|
import { Switch } from '@client/src/components/ui/switch';
|
|
237
235
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@client/src/components/ui/dialog';
|
|
238
236
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@client/src/components/ui/dropdown-menu';
|
|
239
237
|
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@client/src/components/ui/hover-card';
|
|
238
|
+
import { UserSelectTag } from '@client/src/components/business-ui/user-select/user-select-tag';
|
|
239
|
+
import { DepartmentSelectTag } from '@client/src/components/business-ui/department-select/department-select-tag';
|
|
240
|
+
import { ChatSelectTag } from '@client/src/components/business-ui/chat-select/chat-select-tag';
|
|
241
|
+
import { ItemPill } from '@client/src/components/business-ui/entity-combobox/item-pill';
|
|
242
|
+
import { UserSelect } from '@client/src/components/business-ui/user-select';
|
|
243
|
+
import { DepartmentSelect } from '@client/src/components/business-ui/department-select';
|
|
244
|
+
import { ChatSelect } from '@client/src/components/business-ui/chat-select';
|
|
240
245
|
import type { ForceRoleDTO } from '@shared/api.interface';
|
|
241
246
|
|
|
242
247
|
export default function RoleManagementPage() {
|
|
@@ -224,16 +224,7 @@ const MyComponent = () => {
|
|
|
224
224
|
|
|
225
225
|
## 用户展示与选择方案
|
|
226
226
|
|
|
227
|
-
{% if projectMeta['flags']['
|
|
228
|
-
用户展示 / 选择使用组件市场的妙搭业务组件,项目内默认未安装。使用前必须先调用 get_ui_component_info(["@miaoda/<name>"]) 安装并阅读返回的 docs,Props / import 路径以 docs 为准:
|
|
229
|
-
|
|
230
|
-
- @miaoda/user-select 用户选择组件
|
|
231
|
-
- @miaoda/department-select 部门选择组件
|
|
232
|
-
- @miaoda/user-display 用户展示组件:展示任意用户时传 `value`(用户 ID 字符串或字符串数组);**不要**传 `users`。
|
|
233
|
-
|
|
234
|
-
列表、详情、卡片等任意位置展示一个或多个用户 ID,都使用 @miaoda/user-display,不要退回到直接渲染、拼接或 `join` 用户 ID 文本。
|
|
235
|
-
|
|
236
|
-
{% elif projectMeta['flags']['supportBusinessUser'] %}
|
|
227
|
+
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
237
228
|
这些组件在使用之前必须读取 client/src/components/business-ui/README.md 文件来理解用法,并以该 README 与本地源码为准。
|
|
238
229
|
|
|
239
230
|
目前可用的组件有:
|
|
@@ -325,7 +316,7 @@ const form = useForm<z.infer<typeof formSchema>>({
|
|
|
325
316
|
- **组件路径**:`@lark-apaas/client-toolkit/components/User`
|
|
326
317
|
- **功能**:用于所有用户信息的展示场景
|
|
327
318
|
- **特性**:显示用户**头像**和**姓名**,支持多用户展示
|
|
328
|
-
- **边界**:本节仅适用于 `@lark-apaas/client-toolkit/components/User
|
|
319
|
+
- **边界**:本节仅适用于 `@lark-apaas/client-toolkit/components/User`。如果当前项目启用了本地 `@/components/business-ui/user-display`,必须按上方 business-ui 分支读取 README 并使用 `value` / `userId` / `user_id`,不要把这里的 `users` 参数迁移过去。
|
|
329
320
|
|
|
330
321
|
### 属性定义
|
|
331
322
|
|
|
@@ -18,8 +18,7 @@ unavailable-agents:
|
|
|
18
18
|
- `client/src/index.tsx`
|
|
19
19
|
- `client/index.html`
|
|
20
20
|
- `client/src/app.tsx`(加路由 / Provider 可改,但**禁止删除其中的平台根组件 / Provider 包裹**,禁止用 `eslint-disable` 关掉平台校验规则)
|
|
21
|
-
- `client/src/components/business-ui
|
|
22
|
-
- `client/src/components/miaoda/*`(组件市场装机产物,装完不要改写)
|
|
21
|
+
- `client/src/components/business-ui/*`
|
|
23
22
|
- `server/modules/view/view.controller.ts`
|
|
24
23
|
- `vite.config.ts`
|
|
25
24
|
|
|
@@ -90,9 +89,7 @@ client/
|
|
|
90
89
|
│ ├── components/ # 可复用的 UI 组件
|
|
91
90
|
│ │ ├── ui/ # shadcn/ui 组件[Use the components for functionality, but heavily style them.]
|
|
92
91
|
│ │ │ ├── README.md # shadcn/ui 组件库使用说明 [请务必参考该文档进行组件使用]
|
|
93
|
-
{% if projectMeta['flags']['
|
|
94
|
-
│ │ ├── miaoda/ # 组件市场装机的妙搭业务组件(初始不存在,按需安装,见「妙搭业务组件」一节)
|
|
95
|
-
{% elif projectMeta['flags']['supportBusinessUser'] %}
|
|
92
|
+
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
96
93
|
│ │ ├── business-ui/ # 封装好的用户,部门选择组件以及展示组件
|
|
97
94
|
│ │ │ ├── README.md # 用户展示,用户头像展示,部门选择展示逻辑必须使用 [请务必参考该文档进行组件使用],禁止直接使用avatar组件等方式自行实现
|
|
98
95
|
{% endif %}
|
|
@@ -158,8 +155,9 @@ shared/ # 前后端共享的目录
|
|
|
158
155
|
|
|
159
156
|
## 依赖使用规范
|
|
160
157
|
|
|
161
|
-
1. **子包完整性检查**:部分库有多个子包(如 `@
|
|
162
|
-
2.
|
|
158
|
+
1. **子包完整性检查**:部分库有多个子包(如 `@dnd-kit/core` + `@dnd-kit/sortable` + `@dnd-kit/utilities`),添加 import 后必须确认 package.json 中包含所有需要的子包
|
|
159
|
+
2. **禁止安装时需现场编译的 native addon**(`node-gyp rebuild`、`binding.gyp`、需要系统头文件或编译器)。依赖按平台内置能力 / 浏览器能力 → 纯 JS 或 WASM → 有预编译二进制的 native addon 的顺序选型
|
|
160
|
+
3. 用法不清时查看 readme,可进一步搜索或网页访问获取信息
|
|
163
161
|
|
|
164
162
|
## 文件命名约定
|
|
165
163
|
|
|
@@ -236,6 +234,7 @@ shared/ # 前后端共享的目录
|
|
|
236
234
|
- **能力边界**:服务端不支持文件上传(FaaS 限制),前端用 dataloom SDK 上传,服务端仅保存元信息
|
|
237
235
|
- **环境判断**:`process.env.NODE_ENV === "production"` 表示生产环境
|
|
238
236
|
- **文件系统**:临时文件、下载内容和处理中间产物统一写入 `/tmp` 下的独立目录,用完后清理
|
|
237
|
+
- **启动关键路径**:`bootstrap`、`onModuleInit` 和 `OnApplicationBootstrap` 只做必须的同步装配,不得执行 DDL、全量数据同步、长轮询或无界外部请求。应用应尽早 `listen()`;非关键预热放到后台任务,外部调用必须有超时、错误日志和降级路径
|
|
239
238
|
- **服务端运行时资源文件**(字体 / 证书 / 模板 / wasm 等需在运行时读取的非代码文件,CRITICAL — 发布后静默失效根因):① 必须在 `nest-cli.json` 的 `assets` 中声明(如 `{"include": "assets/<dir>/**/*", "outDir": "dist/server"}`),否则不会进 `dist/` 构建产物;② 路径用 `__dirname` 相对**编译产物**定位(如 `path.join(__dirname, "../assets/...")`),**禁止 `process.cwd()` 相对源码路径**——dev 跑源码能命中、发布跑 `dist/` 会落空;③ 资源缺失或加载失败必须 **fail-loud**(抛错或明确错误日志),禁止 `catch` 后静默返回残缺产物
|
|
240
239
|
- **启动生命周期**:`constructor`、`onModuleInit` 和 `onApplicationBootstrap` 应快速完成,避免在其中下载大文件、全量同步或批量迁移;仅将钩子声明为 `async` 仍会被等待,无法缩短启动时间
|
|
241
240
|
|
|
@@ -478,10 +477,13 @@ await db.select().from(users).where(eq(users.adminUser, userId));
|
|
|
478
477
|
|
|
479
478
|
- **必须从 `req.userContext` 获取**,禁止从前端传递,禁止硬编码(`const { userId } = req.userContext`)
|
|
480
479
|
- 依赖用户信息的接口不要用 API 测试工具测试,用 `think` 工具思考
|
|
480
|
+
- 后端按 `user-identity` 和 `@lark-apaas/fullstack-nestjs-core` 的 Express `Request` 增强读取 `req.userContext`;`tenantId` 为可选 number,不能套用前端 `window.tenantId` / `viewContext` 字符串语义。
|
|
481
|
+
- Controller 直接用 `@Req() req: Request`;测试、mock 或业务窄类型用独立接口,只声明实际读取字段。不要继承 Express `Request` 或重声明 `userContext`;未读 `tenantId` 就省略,必须保留时用 `unknown` 或 SDK 兼容类型。
|
|
481
482
|
|
|
482
483
|
```typescript
|
|
483
|
-
// req.userContext
|
|
484
|
-
// - userId/
|
|
484
|
+
// req.userContext 字段(由身份认证中间件挂载,完整合同见 user-identity):
|
|
485
|
+
// - userId/appId: string
|
|
486
|
+
// - tenantId: SDK Request.userContext 的可选 number
|
|
485
487
|
// - env: 'preview' | 'runtime' (preview=预览态, runtime=发布运行态)
|
|
486
488
|
// - userName/userNameEn: string
|
|
487
489
|
// - userNameI18n: string (多语名字, 如 {zh_cn: '用户', en_us: 'user'})
|
|
@@ -573,15 +575,12 @@ async createArticle(@Req() req: Request, @Body() dto: CreateArticleDto) {
|
|
|
573
575
|
- **图表**: ReactECharts,**开发前必须调用 `/charts-skill`**
|
|
574
576
|
- **图标**: Lucide React(唯一图标库,禁止 Emoji 和其他图标库)
|
|
575
577
|
- **表格/表单/图表**: 见下方"组件 Skill 召回规则",开发前必须先调用对应 Skill
|
|
576
|
-
{% if projectMeta['flags']['
|
|
577
|
-
- **用户**: 用户信息展示/选择必须使用组件市场的妙搭业务组件(见「妙搭业务组件」一节,先安装后使用),禁止手写选人/头像组件、禁止直接展示 userId
|
|
578
|
-
{% elif projectMeta['flags']['supportBusinessUser'] %}
|
|
578
|
+
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
579
579
|
- **用户**: 用户信息展示/选择必须用 `business-ui` 组件(阅读 README.md),禁止直接展示 userId
|
|
580
580
|
{% endif %}
|
|
581
581
|
{% if projectMeta['flags']['supportTiptapAndStreamdown'] %}
|
|
582
|
+
- **富文本**: `business-ui/tiptap-editor`(阅读 README.md)
|
|
582
583
|
- **Markdown 渲染**: `components/ui/streamdown`(内置 prose 排版)
|
|
583
|
-
{% else %}
|
|
584
|
-
- **Markdown 渲染**: `components/ui/markdown`(react-markdown + remark-gfm,内置 prose 排版)
|
|
585
584
|
{% endif %}
|
|
586
585
|
|
|
587
586
|
## API 请求
|
|
@@ -613,34 +612,12 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
613
612
|
| 组件 | 来源 | 用途 |
|
|
614
613
|
|------|------|------|
|
|
615
614
|
| Table | `@lark-apaas/client-toolkit/antd-table` | 数据表格,**先调 `/table-skill`** |
|
|
616
|
-
{% if projectMeta['flags']['supportBusinessUser']
|
|
615
|
+
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
617
616
|
| UserSelect/UserDisplay/UserProfile/DepartmentSelect | `business-ui/*` | 用户/部门选择展示 |
|
|
618
617
|
{% endif %}
|
|
619
618
|
{% if projectMeta['flags']['supportTiptapAndStreamdown'] %}
|
|
619
|
+
| TiptapEditorComplete | `business-ui/tiptap-editor` | 富文本编辑器 |
|
|
620
620
|
| Streamdown | `components/ui/streamdown` | Markdown/流式渲染 |
|
|
621
|
-
{% else %}
|
|
622
|
-
| Markdown | `components/ui/markdown` | Markdown 渲染(react-markdown + remark-gfm) |
|
|
623
|
-
{% endif %}
|
|
624
|
-
|
|
625
|
-
{% if projectMeta['flags']['businessUiFromMarket'] %}
|
|
626
|
-
### 妙搭业务组件(组件市场,强制使用)
|
|
627
|
-
|
|
628
|
-
企业用户 / 组织类组件已上架组件市场,项目内**默认未安装**。命中以下场景时禁止手写等价组件,必须先安装后使用:
|
|
629
|
-
|
|
630
|
-
| 组件 ID | 用途 |
|
|
631
|
-
|---------|------|
|
|
632
|
-
| @miaoda/user-select | 用户选择器(搜索 / 单多选 / 外部联系人)——选人、成员、指派、审批人等场景 |
|
|
633
|
-
| @miaoda/department-select | 部门选择器——组织架构场景 |
|
|
634
|
-
| @miaoda/chat-select | 群组(群聊)选择器 |
|
|
635
|
-
| @miaoda/user-display | 用户头像 + 姓名展示(传用户 ID 自动拉取资料)——任何展示用户的场景 |
|
|
636
|
-
| @miaoda/user-profile | 用户详情卡(hover 弹出完整资料) |
|
|
637
|
-
|
|
638
|
-
**强制流程**:
|
|
639
|
-
|
|
640
|
-
1. 编码前调用 get_ui_component_info(["@miaoda/user-select", ...]) 一次性安装本轮所需的全部组件(依赖会自动递归安装)
|
|
641
|
-
2. Props / import 路径 / Provider 前置 100% 以工具返回的 docs 为准,禁止凭记忆猜测
|
|
642
|
-
3. 组件源码安装在 client/src/components/miaoda/,不要移动或改写
|
|
643
|
-
4. 工具返回 Component not found 时,告知用户该组件未上架,不要手写等价实现
|
|
644
621
|
{% endif %}
|
|
645
622
|
|
|
646
623
|
### 组件 Skill 召回规则(强制执行)
|
|
@@ -770,9 +747,7 @@ return <h1>{data?.title || '未知标题'}</h1>;
|
|
|
770
747
|
## 图片规范
|
|
771
748
|
|
|
772
749
|
- **展示**:必须用 `@client/src/components/ui/image` 组件(非原生 `<img>`),响应式设 `sizes`,固定宽设 `width`
|
|
773
|
-
{% if projectMeta['flags']['
|
|
774
|
-
- 用户头像用市场组件 @miaoda/user-display(见「妙搭业务组件」一节)
|
|
775
|
-
{% elif projectMeta['flags']['supportBusinessUser'] %}
|
|
750
|
+
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
776
751
|
- 用户头像用 business-ui 组件
|
|
777
752
|
{% endif %}
|
|
778
753
|
|
|
@@ -806,12 +781,15 @@ return <h1>{data?.title || '未知标题'}</h1>;
|
|
|
806
781
|
| 验证 | zod |
|
|
807
782
|
| 工具函数 | lodash |
|
|
808
783
|
| 样式 | clsx |
|
|
809
|
-
|
|
|
784
|
+
| Excel | xlsx(**仅前端实现,禁止服务端实现**。解析后将结构化数据传到服务端保存) |
|
|
785
|
+
| PDF 导出 | jspdf + html2canvas(**仅前端实现,禁止服务端实现**) |
|
|
786
|
+
| 文件上传 | react-dropzone |
|
|
787
|
+
| 二维码 | qrcode.react |
|
|
810
788
|
| 用户反馈 | sonner |
|
|
811
|
-
|
|
|
812
|
-
|
|
|
813
|
-
|
|
814
|
-
|
|
789
|
+
| 拖拽 | @dnd-kit/core |
|
|
790
|
+
| 数字动画 | react-countup |
|
|
791
|
+
| Base64 | js-base64 — `import { encode, decode } from 'js-base64'` |
|
|
792
|
+
| 3D 场景 | cobe |
|
|
815
793
|
|
|
816
794
|
## 滚动分页最佳实践
|
|
817
795
|
|
|
@@ -326,12 +326,12 @@ const chartData =
|
|
|
326
326
|
| `Progress` | `${value}%` 或进度条 | `<Input type="number" min={0} max={100} />` |
|
|
327
327
|
| `Rating` | `'★'.repeat(value) + '☆'.repeat(5-value)` | `<Input type="number" min={0} max={5} />` |
|
|
328
328
|
|
|
329
|
-
BizType 为 `User`
|
|
329
|
+
BizType 为 `User` 的字段展示必须使用 business-ui `UserDisplay`;不要直接渲染、拼接或 `join` 用户 ID 文本。
|
|
330
330
|
|
|
331
331
|
### 组件代码示例
|
|
332
332
|
|
|
333
333
|
```tsx
|
|
334
|
-
//
|
|
334
|
+
// 人员展示(business-ui UserDisplay 使用 value;不要传 users)
|
|
335
335
|
<UserDisplay
|
|
336
336
|
value={record['人员字段']?.map(userId => userId.toString()) || []}
|
|
337
337
|
size="small"
|
|
@@ -381,7 +381,7 @@ BizType 为 `User` 的字段展示必须使用组件市场的 `@miaoda/user-disp
|
|
|
381
381
|
const columns = [
|
|
382
382
|
// 文本字段 - 注意读取格式是 { text: string }
|
|
383
383
|
{ title: '文本', dataIndex: ['record', '文本字段', 'text'], key: 'text' },
|
|
384
|
-
// 人员字段 -
|
|
384
|
+
// 人员字段 - 使用 business-ui UserDisplay 组件,传 value(用户 ID 字符串数组),不要传 users
|
|
385
385
|
{
|
|
386
386
|
title: '负责人',
|
|
387
387
|
key: 'user',
|
|
@@ -93,12 +93,14 @@ match-template-name: nestjs-react-fullstack
|
|
|
93
93
|
|
|
94
94
|
> 完整的解析→守卫流程见 `authn-guide` skill 第二节"认证流程"。
|
|
95
95
|
|
|
96
|
+
类型使用规则:Controller 直接用 `@Req() req: Request` 读取 SDK 增强的 `req.userContext`。测试、mock 或局部业务需要窄类型时,定义独立接口,只声明实际读取字段;不要继承 Express `Request`,也不要重声明平台增强的 `userContext`。未读取的 `tenantId` 省略;必须保留时用 `unknown` 或 SDK 兼容类型。
|
|
97
|
+
|
|
96
98
|
### 字段表
|
|
97
99
|
|
|
98
100
|
| 字段 | 类型 | 说明 |
|
|
99
101
|
|------|------|------|
|
|
100
102
|
| `userId` | `string` | 妙搭用户 ID |
|
|
101
|
-
| `tenantId` | `number` | 租户 ID |
|
|
103
|
+
| `tenantId` | `number \| undefined` | 租户 ID;按 SDK Express `Request.userContext` 增强:可选 number |
|
|
102
104
|
| `appId` | `string` | 应用 ID |
|
|
103
105
|
| `loginUrl` | `string` | 登录跳转 URL |
|
|
104
106
|
| `userType` | `string` | 用户类型(如 `_employee`) |
|
package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/management-page-spec.md
CHANGED
|
@@ -16,7 +16,7 @@ Step 0: 制定实施计划(强制,禁止跳过)
|
|
|
16
16
|
|
|
17
17
|
Step 1: 编码前确认(闸门)
|
|
18
18
|
├─ 确认 SDK 方法签名:读取 sdk-types.md,摘录 roles / members / search 的出入参
|
|
19
|
-
├─
|
|
19
|
+
├─ 确认搜索组件目录存在:client/src/components/business-ui/ 下包含 user-select、department-select、chat-select 目录
|
|
20
20
|
└─ 确认 Tag 胶囊组件存在:上述目录中分别包含 user-select-tag、department-select-tag、chat-select-tag 文件
|
|
21
21
|
|
|
22
22
|
⛔ 以上任一不存在,停止实现并报告
|
|
@@ -63,7 +63,7 @@ Step 5: 验证
|
|
|
63
63
|
|
|
64
64
|
按以下优先级顺序拼接展示,无任何内容时显示 `--`:
|
|
65
65
|
|
|
66
|
-
1.
|
|
66
|
+
1. **特殊范围胶囊**:使用 `ItemPill` 组件(`business-ui/entity-combobox/item-pill`)展示,每项带**品牌色**(`bg-primary`,跟随 CSS 变量 `--primary`,默认蓝色)圆底 20px 图标(`SPECIAL_MEMBER_ICONS` 常量,见代码骨架),图标前景色使用 `text-primary-foreground`,在表格成员摘要列和成员编辑面板中**同时复用**:
|
|
67
67
|
- `企业全员`(`allEmployees`):`Building` 图标(lucide-react)
|
|
68
68
|
- `互联网公开`(`public`):`Globe` 图标(lucide-react)
|
|
69
69
|
- `应用开发者`(`presetGroup.isContainsAdmin`):`Users` 图标(lucide-react)
|
|
@@ -229,14 +229,19 @@ const SPECIAL_MEMBER_ICONS = {
|
|
|
229
229
|
// RoleManagementPage.tsx 骨架
|
|
230
230
|
import { useState, useEffect } from 'react';
|
|
231
231
|
import { Table, type TableProps } from '@lark-apaas/client-toolkit/antd-table';
|
|
232
|
-
// 妙搭业务组件(UserSelect / DepartmentSelect / ChatSelect 及其 Tag、胶囊)的 import 路径
|
|
233
|
-
// 以 get_ui_component_info 返回的 docs 为准,安装后按 docs 补齐,勿凭记忆写死路径
|
|
234
232
|
import { Button } from '@client/src/components/ui/button';
|
|
235
233
|
import { Badge } from '@client/src/components/ui/badge';
|
|
236
234
|
import { Switch } from '@client/src/components/ui/switch';
|
|
237
235
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@client/src/components/ui/dialog';
|
|
238
236
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@client/src/components/ui/dropdown-menu';
|
|
239
237
|
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@client/src/components/ui/hover-card';
|
|
238
|
+
import { UserSelectTag } from '@client/src/components/business-ui/user-select/user-select-tag';
|
|
239
|
+
import { DepartmentSelectTag } from '@client/src/components/business-ui/department-select/department-select-tag';
|
|
240
|
+
import { ChatSelectTag } from '@client/src/components/business-ui/chat-select/chat-select-tag';
|
|
241
|
+
import { ItemPill } from '@client/src/components/business-ui/entity-combobox/item-pill';
|
|
242
|
+
import { UserSelect } from '@client/src/components/business-ui/user-select';
|
|
243
|
+
import { DepartmentSelect } from '@client/src/components/business-ui/department-select';
|
|
244
|
+
import { ChatSelect } from '@client/src/components/business-ui/chat-select';
|
|
240
245
|
import type { ForceRoleDTO } from '@shared/api.interface';
|
|
241
246
|
|
|
242
247
|
export default function RoleManagementPage() {
|
|
@@ -224,14 +224,13 @@ const MyComponent = () => {
|
|
|
224
224
|
|
|
225
225
|
## 用户展示与选择方案
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
这些组件在使用之前必须读取 client/src/components/business-ui/README.md文件来理解用法
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
- @miaoda/department-select 部门选择组件
|
|
231
|
-
- @miaoda/user-display 用户展示组件:展示任意用户时传 `value`(用户 ID 字符串或字符串数组);**不要**传 `users`。
|
|
232
|
-
|
|
233
|
-
列表、详情、卡片等任意位置展示一个或多个用户 ID,都使用 @miaoda/user-display,不要退回到直接渲染、拼接或 `join` 用户 ID 文本。
|
|
229
|
+
目前可用的组件有:
|
|
234
230
|
|
|
231
|
+
- UserSelect 用户选择组件(@/components/business-ui/user-select)
|
|
232
|
+
- DepartmentSelect 部门选择组件(@/components/business-ui/department-select)
|
|
233
|
+
- UserDisplay - 用户展示组件(@/components/business-ui/user-display)
|
|
235
234
|
|
|
236
235
|
## 使用注意事项
|
|
237
236
|
|
|
@@ -64,7 +64,8 @@ client/
|
|
|
64
64
|
│ ├── components/ # 可复用的 UI 组件
|
|
65
65
|
│ │ ├── ui/ # shadcn/ui 组件[Use the components for functionality, but heavily style them.]
|
|
66
66
|
│ │ │ ├── README.md # shadcn/ui 组件库使用说明 [请务必参考该文档进行组件使用]
|
|
67
|
-
│ │ ├──
|
|
67
|
+
│ │ ├── business-ui/ # 封装好的用户,部门选择组件以及展示组件
|
|
68
|
+
│ │ │ ├── README.md # 用户展示,用户头像展示,部门选择展示逻辑必须使用 [请务必参考该文档进行组件使用],禁止直接使用avatar组件等方式自行实现
|
|
68
69
|
│ │ ├── Layout.tsx # 布局包装器
|
|
69
70
|
│ ├── hooks/ # 自定义 React hooks
|
|
70
71
|
│ └── utils/ # 工具函数
|
|
@@ -128,7 +129,7 @@ shared/ # 前后端共享的目录
|
|
|
128
129
|
|
|
129
130
|
1. 优先使用项目已有依赖,仅在无法实现时安装新依赖
|
|
130
131
|
2. 使用前先查看 `package.json` 确保依赖已存在
|
|
131
|
-
3. **子包完整性检查**:部分库有多个子包(如 `@
|
|
132
|
+
3. **子包完整性检查**:部分库有多个子包(如 `@dnd-kit/core` + `@dnd-kit/sortable` + `@dnd-kit/utilities`),添加 import 后必须确认 package.json 中包含所有需要的子包
|
|
132
133
|
4. 用法不清时查看 readme,可进一步搜索或网页访问获取信息
|
|
133
134
|
|
|
134
135
|
## 文件命名约定
|
|
@@ -334,10 +335,13 @@ await db.select().from(users).where(eq(users.adminUser, userId));
|
|
|
334
335
|
|
|
335
336
|
- **必须从 `req.userContext` 获取**,禁止从前端传递,禁止硬编码
|
|
336
337
|
- 依赖用户信息的接口不要用 API 测试工具测试,用 `think` 工具思考
|
|
338
|
+
- 后端按 `user-identity` 和 `@lark-apaas/fullstack-nestjs-core` 的 Express `Request` 增强读取 `req.userContext`;`tenantId` 为可选 number,不能套用前端 `window.tenantId` / `viewContext` 字符串语义。
|
|
339
|
+
- Controller 直接用 `@Req() req: Request`;测试、mock 或业务窄类型用独立接口,只声明实际读取字段。不要继承 Express `Request` 或重声明 `userContext`;未读 `tenantId` 就省略,必须保留时用 `unknown` 或 SDK 兼容类型。
|
|
337
340
|
|
|
338
341
|
```typescript
|
|
339
|
-
// req.userContext
|
|
340
|
-
// - userId/
|
|
342
|
+
// req.userContext 字段(由身份认证中间件挂载,完整合同见 user-identity):
|
|
343
|
+
// - userId/appId: string
|
|
344
|
+
// - tenantId: SDK Request.userContext 的可选 number
|
|
341
345
|
// - env: 'preview' | 'runtime' (preview=预览态, runtime=发布运行态)
|
|
342
346
|
// - userName/userNameEn: string
|
|
343
347
|
// - userNameI18n: string (多语名字, 如 {zh_cn: '用户', en_us: 'user'})
|
|
@@ -448,13 +452,14 @@ NestJS 自己不读 env。直连 NestJS 端口 → header 缺失 → `req.userCo
|
|
|
448
452
|
|
|
449
453
|
- **框架**: React 19 + TypeScript
|
|
450
454
|
- **路由**: React Router DOM v6
|
|
451
|
-
- **样式**: tailwindcss(语义化 token
|
|
455
|
+
- **样式**: styled-jsx + tailwindcss(语义化 token)。styled-jsx 使用前提见下方"样式开发"
|
|
452
456
|
- **UI 组件库**: shadcn/ui — Use components for functionality, heavily style them
|
|
453
457
|
- **图表**: ReactECharts,**开发前必须调用 `/charts-skill`**
|
|
454
458
|
- **图标**: Lucide React(唯一图标库,禁止 Emoji 和其他图标库)
|
|
455
459
|
- **表格/表单/图表**: 见下方"组件 Skill 召回规则",开发前必须先调用对应 Skill
|
|
456
|
-
- **用户**:
|
|
457
|
-
-
|
|
460
|
+
- **用户**: 用户信息展示/选择必须用 `business-ui` 组件(阅读 README.md),禁止直接展示 userId
|
|
461
|
+
- **富文本**: `business-ui/tiptap-editor`(阅读 README.md)
|
|
462
|
+
- **Markdown 渲染**: `components/ui/streamdown`(内置 prose 排版)
|
|
458
463
|
|
|
459
464
|
## API 请求
|
|
460
465
|
|
|
@@ -486,28 +491,9 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
486
491
|
| 组件 | 来源 | 用途 |
|
|
487
492
|
| --------------------------------------------------- | --------------------------------------- | --------------------------------- |
|
|
488
493
|
| Table | `@lark-apaas/client-toolkit/antd-table` | 数据表格,**先调 `/table-skill`** |
|
|
489
|
-
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
> 本节适用于项目内**没有** `client/src/components/business-ui/` 预置目录的新版模板;有预置目录时直接使用预置组件,不走市场安装。
|
|
494
|
-
|
|
495
|
-
企业用户 / 组织类组件已上架组件市场,项目内**默认未安装**。命中以下场景时禁止手写等价组件,必须先安装后使用:
|
|
496
|
-
|
|
497
|
-
| 组件 ID | 用途 |
|
|
498
|
-
|---------|------|
|
|
499
|
-
| @miaoda/user-select | 用户选择器(搜索 / 单多选 / 外部联系人)——选人、成员、指派、审批人等场景 |
|
|
500
|
-
| @miaoda/department-select | 部门选择器——组织架构场景 |
|
|
501
|
-
| @miaoda/chat-select | 群组(群聊)选择器 |
|
|
502
|
-
| @miaoda/user-display | 用户头像 + 姓名展示(传用户 ID 自动拉取资料)——任何展示用户的场景 |
|
|
503
|
-
| @miaoda/user-profile | 用户详情卡(hover 弹出完整资料) |
|
|
504
|
-
|
|
505
|
-
**强制流程**:
|
|
506
|
-
|
|
507
|
-
1. 编码前一次性安装本轮所需的全部组件:有 get_ui_component_info 工具时调用它(componentIds 传上表 ID);无该工具时执行 `fullstack-cli component add @miaoda/<name>`
|
|
508
|
-
2. Props / import 路径 / Provider 前置 100% 以安装返回的 docs 为准,禁止凭记忆猜测
|
|
509
|
-
3. 组件源码安装在 client/src/components/miaoda/,不要移动或改写
|
|
510
|
-
4. 返回 Component not found 时,告知用户该组件未上架,不要手写等价实现
|
|
494
|
+
| UserSelect/UserDisplay/UserProfile/DepartmentSelect | `business-ui/*` | 用户/部门选择展示 |
|
|
495
|
+
| TiptapEditorComplete | `business-ui/tiptap-editor` | 富文本编辑器 |
|
|
496
|
+
| Streamdown | `components/ui/streamdown` | Markdown/流式渲染 |
|
|
511
497
|
|
|
512
498
|
### 组件 Skill 召回规则(强制执行)
|
|
513
499
|
|
|
@@ -558,7 +544,7 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
558
544
|
```
|
|
559
545
|
需要写样式?
|
|
560
546
|
├─ 基础布局/间距/颜色 → Tailwind ✅
|
|
561
|
-
├─ 复杂动画/伪元素/高级CSS →
|
|
547
|
+
├─ 复杂动画/伪元素/高级CSS → styled-jsx ✅
|
|
562
548
|
└─ JS动态计算值 → 行内 style ✅
|
|
563
549
|
```
|
|
564
550
|
|
|
@@ -571,6 +557,10 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
571
557
|
- **arbitrary values 中空格用下划线**:`from-[hsl(215_60%_18%)]` 非 `from-[hsl(215 60% 18%)]`
|
|
572
558
|
- `tailwind-theme.css` 自定义属性用 `hsl(H, S%, L%)` 格式(非 `23 10% 23%`)
|
|
573
559
|
|
|
560
|
+
### styled-jsx 规范
|
|
561
|
+
|
|
562
|
+
- **技术栈一致性**:仅在已配置 styled-jsx 插件的项目中使用。`package.json` 无 `styled-jsx` 依赖则**禁用**,否则运行时 SyntaxError
|
|
563
|
+
- **禁止动态插值**:`<style jsx>` 内禁止 `${...}` 等表达式(会卡死)。动态值放 CSS 变量,用 `var(--xxx)` 引用
|
|
574
564
|
|
|
575
565
|
### 布局/排版
|
|
576
566
|
|
|
@@ -633,7 +623,7 @@ return <h1>{data?.title || '未知标题'}</h1>;
|
|
|
633
623
|
## 图片规范
|
|
634
624
|
|
|
635
625
|
- **展示**:必须用 `@client/src/components/ui/image` 组件(非原生 `<img>`),响应式设 `sizes`,固定宽设 `width`
|
|
636
|
-
-
|
|
626
|
+
- 用户头像用 business-ui 组件
|
|
637
627
|
|
|
638
628
|
### 图片资源优先级
|
|
639
629
|
|
|
@@ -654,12 +644,15 @@ return <h1>{data?.title || '未知标题'}</h1>;
|
|
|
654
644
|
| 验证 | zod |
|
|
655
645
|
| 工具函数 | lodash |
|
|
656
646
|
| 样式 | clsx |
|
|
657
|
-
|
|
|
647
|
+
| Excel | xlsx(**仅前端实现,禁止服务端实现**。解析后将结构化数据传到服务端保存) |
|
|
648
|
+
| PDF 导出 | jspdf + html2canvas(**仅前端实现,禁止服务端实现**) |
|
|
649
|
+
| 文件上传 | react-dropzone |
|
|
650
|
+
| 二维码 | qrcode.react |
|
|
658
651
|
| 用户反馈 | sonner |
|
|
659
|
-
|
|
|
660
|
-
|
|
|
661
|
-
|
|
662
|
-
|
|
652
|
+
| 拖拽 | @dnd-kit/core |
|
|
653
|
+
| 数字动画 | react-countup |
|
|
654
|
+
| Base64 | js-base64 — `import { encode, decode } from 'js-base64'` |
|
|
655
|
+
| 3D 场景 | cobe |
|
|
663
656
|
|
|
664
657
|
## 滚动分页最佳实践
|
|
665
658
|
|