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