@lark-apaas/coding-steering 0.1.22-alpha.20260724081248 → 0.1.22
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/README.md +18 -20
- package/package.json +2 -2
- package/steering/design-html/skills/animated-video/SKILL.md +4 -2
- package/steering/design-html/skills/charts/SKILL.md +5 -3
- package/steering/design-html/skills/data-report/SKILL.md +6 -4
- package/steering/design-html/skills/frontend-design/SKILL.md +36 -34
- package/steering/design-html/skills/hi-fi-design/SKILL.md +4 -2
- package/steering/design-html/skills/interactive-prototype/SKILL.md +4 -2
- package/steering/design-html/skills/make-a-deck/SKILL.md +112 -94
- package/steering/design-html/skills/visual-exposure/SKILL.md +6 -4
- package/steering/design-html/skills/wireframe/SKILL.md +7 -5
- package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +61 -375
- package/steering/nestjs-react-fullstack/skills_common/trigger-guide/SKILL.md +180 -0
- package/steering/nestjs-react-fullstack/{skills/trigger-guide/SKILL.md → skills_common/trigger-guide/references/trigger-lifecycle.md} +11 -162
- package/steering/nestjs-react-fullstack/{skills → skills_common}/user-identity/SKILL.md +12 -5
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/SKILL.md +196 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/dynamic-permission-guide.md +643 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/management-page-spec.md +505 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/runtime-role-controller-spec.md +203 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/sdk-examples.md +92 -0
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/sdk-types.md +229 -0
- package/steering/nestjs-react-fullstack/skills_local/client-builtins-user-service/SKILL.md +240 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: visual-exposure
|
|
3
3
|
description: 用于制作可视化报告、专题视觉页、信息图、视觉长图、概念可视化、产品能力曝光、方案亮点展示等内容型 HTML 视觉作品。适合用户想把材料、数据或观点组织成可阅读、可展示、可传播的视觉化表达,但不希望做成 PPT、传统 dashboard 或纯 ECharts 图表的场景。触发词:可视化报告, 视觉报告, 可视化曝光, 视觉化曝光, 信息图, 长图, infographic, 视觉表达, 概念可视化, 亮点展示, 能力曝光
|
|
4
|
-
|
|
5
|
-
-
|
|
4
|
+
metadata:
|
|
5
|
+
display-names:
|
|
6
|
+
zh-CN: 可视化报告
|
|
7
|
+
en-US: Visual Report
|
|
6
8
|
---
|
|
7
9
|
|
|
8
10
|
# 可视化报告与专题表达
|
|
@@ -17,7 +19,7 @@ available-agents:
|
|
|
17
19
|
4. 按材料逻辑组织内容,而不是套固定目录、固定模块或固定视觉模板。参考样式只能启发表达方式,不能替代对当前材料的判断。
|
|
18
20
|
5. 把材料拆成具体阅读任务:这一段要让读者完成什么判断、理解什么关系、记住什么事实、比较什么差异、追踪什么过程、相信什么证据。不要把这些任务名直接变成目录或模块标题。
|
|
19
21
|
6. 为每个阅读任务现场生成合适的组件、视觉和布局:先说明这段内容需要什么表达方式,再落成具体 UI / 图形 / 排版 / 图表 / 截图 / 文字组合。可以创造新的结构和视觉隐喻,不受现有组件名限制;避免所有章节共享同一套组件组合。
|
|
20
|
-
7. 先写风格 brief
|
|
22
|
+
7. 先写风格 brief:主题隐喻、受众姿态、材料语言、配色逻辑和签名元素。财务报告可以像正式报告册,员工调研可以像组织研究档案,产品上市总结可以像品牌战报;这些只是启发,必须从用户材料里推导。
|
|
21
23
|
8. 建立版式系统:画幅、栅格、字号层级、颜色、图标/线条语言、强调方式和章节节奏。版式系统必须说明不同章节如何变化,而不是所有章节都用同一种上下结构。
|
|
22
24
|
9. 产出单个 HTML 文档。用户需求明确时直接做;只有主题、素材或交付形态完全无法判断时,才问少量必要问题。
|
|
23
25
|
|
|
@@ -50,7 +52,7 @@ available-agents:
|
|
|
50
52
|
- 默认平面化处理:内容区优先使用细边框、分隔线、浅底色、色块、表格斑马纹、编号和标签建立层级;不要给章节、卡片、图表容器加各种 `box-shadow`。
|
|
51
53
|
- 少用装饰性渐变、发光、玻璃拟态。视觉效果要帮助分组、强调或引导视线。
|
|
52
54
|
- 风格跟随内容、受众和品牌:可以正式、温和、技术、编辑化、品牌化或实验感,但不要从某个样例场景继承固定颜色、固定目录或固定组件。
|
|
53
|
-
-
|
|
55
|
+
- 每份报告应有一个可解释的签名元素。签名元素要从用户主题、材料质感和阅读任务中生成,而不是复用固定手法;它可以是任何能组织内容、建立记忆点并保持一致性的视觉规则。
|
|
54
56
|
- 真实素材优先:用户给的截图、logo、图片、图标、数据片段要优先使用。没有素材时,用清楚的占位结构和可替换文案。
|
|
55
57
|
- 允许少量动效,但只用于进入、强调或引导阅读,不做干扰理解的持续动画。
|
|
56
58
|
- 可以包含数字、图表和表格,但它们服务于报告叙事;不要为了“可视化”而把所有内容都做成图。
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wireframe
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
-
|
|
3
|
+
description: 用线框图和故事板探索多种想法。触发词:wireframe, storyboard, 线框图, 故事板, 分镜, 草图, 低保真, 方案探索, 设计探索
|
|
4
|
+
metadata:
|
|
5
|
+
display-names:
|
|
6
|
+
zh-CN: 线框图
|
|
7
|
+
en-US: Wireframe
|
|
6
8
|
---
|
|
7
9
|
|
|
8
|
-
#
|
|
10
|
+
# 线框图
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
帮助用户快速探索设计想法。先访谈用户,再生成多个粗略的线框图,在锁定方向之前把设计空间勾勒出来。优先追求广度而非精细打磨:每个想法给出 3-5 种明显不同的方案。用简单的形状、占位文字和极少的颜色,把焦点留在结构和流程上。整体保持手绘草图的感觉——手写风格但清晰可读的字体;以黑白为主、点缀少量颜色;低保真、简洁。提供简单的微调控件(Tweaks);选项占地小就并排展示,占地大就用 tab 控件切换。
|
|
@@ -13,27 +13,20 @@ match-template-name: nestjs-react-fullstack
|
|
|
13
13
|
项目通过 `authClient`(来自 `@lark-apaas/client-toolkit/auth`)提供用户信息与鉴权服务,用于用户登录、登出、获取用户信息等身份认证相关功能。
|
|
14
14
|
|
|
15
15
|
> **边界说明**:`authClient.session` 仅用于用户登录/登出/获取用户信息等鉴权操作。**插件调用(capability)不属于账户 SDK**,须使用独立的 `capabilityClient`(参见 plugin-guide)。
|
|
16
|
+
>
|
|
17
|
+
> **运行时边界**:本 skill 所有能力(`authClient`、`useCurrentUserProfile`、UserSelect/UserDisplay 等)仅限前端代码使用,**严禁在 `server/**` 中 import**。服务端获取用户身份用 `req.userContext` / `AuthNPaasService`(见 `user-identity` skill),完整边界规则见 coding-guide。
|
|
18
|
+
|
|
19
|
+
## 怎么选(决策指引)
|
|
20
|
+
|
|
21
|
+
- **React 组件内展示当前用户**(名称/头像/邮箱/飞书 ID)→ 用 `useCurrentUserProfile()`(见下文),不要手动调 `getUserInfo`
|
|
22
|
+
- **登录/登出/跳转用户详情页,或非 React 上下文取用户信息** → `authClient.session.*`(本节)
|
|
23
|
+
- **选人/选部门/展示任意用户** → UserSelect / DepartmentSelect / UserDisplay 组件(见下文)
|
|
16
24
|
|
|
17
25
|
## 统一响应结构
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
所有 `authClient.session.*` 接口返回统一的 `DataloomServiceResponse<T>` 结构。在非浏览器环境调用会返回失败结构(`status: 400`,`error.message: 'Incompatible runtime environment'`)。
|
|
20
28
|
|
|
21
29
|
```typescript
|
|
22
|
-
/**
|
|
23
|
-
* 统一结果返回结构
|
|
24
|
-
* 在非浏览器环境调用时返回示例:
|
|
25
|
-
* {
|
|
26
|
-
* data: null,
|
|
27
|
-
* error: {
|
|
28
|
-
* code: 400,
|
|
29
|
-
* message: 'Incompatible runtime environment',
|
|
30
|
-
* hint: 'Please check if the current environment is browser.',
|
|
31
|
-
* details: 'This method can only be invoked in browser environment.',
|
|
32
|
-
* },
|
|
33
|
-
* status: 400,
|
|
34
|
-
* statusText: 'Bad Request',
|
|
35
|
-
* }
|
|
36
|
-
*/
|
|
37
30
|
interface DataloomServiceBase {
|
|
38
31
|
status: number;
|
|
39
32
|
statusText: string;
|
|
@@ -64,202 +57,32 @@ import { authClient } from "@lark-apaas/client-toolkit/auth";
|
|
|
64
57
|
// authClient 是 SDK 内置的 singleton,零参可用,不需要异步初始化
|
|
65
58
|
```
|
|
66
59
|
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
#### 1. 登录跳转接口 (redirectToLogin)
|
|
70
|
-
|
|
71
|
-
##### 用途
|
|
60
|
+
## 接口速查表
|
|
72
61
|
|
|
73
|
-
|
|
62
|
+
| 方法 | 入参 | 成功时 data 类型 | 说明 |
|
|
63
|
+
| ----------------------------------------- | ------------------------------ | -------------------------- | ---------------------------------------------- |
|
|
64
|
+
| `session.redirectToLogin(options?)` | `SignInRedirectionOptions` | `'success'` | 跳转至 Dataloom 登录页(身份认证/单点登录) |
|
|
65
|
+
| `session.signOut()` | 无 | `null`(异步) | 退出登录,删除 cookie 中的登录态 |
|
|
66
|
+
| `session.navigateToUserProfile(options?)` | `NavigateToUserProfileOptions` | `'success'` | 跳转至当前登录用户详情页(头像/姓名点击进入) |
|
|
67
|
+
| `session.getUserInfo()` | 无 | `UserInfoResponse`(异步) | 获取当前登录用户信息,未登录返回 `status: 401` |
|
|
74
68
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
需要用户进行身份认证的场景
|
|
78
|
-
|
|
79
|
-
##### 入参说明
|
|
69
|
+
### 入参类型定义
|
|
80
70
|
|
|
81
71
|
```typescript
|
|
82
72
|
interface SignInRedirectionOptions {
|
|
83
|
-
/**
|
|
84
|
-
* 选填,登录成功后跳转回的页面。省略会默认用调用接口时页面的url。
|
|
85
|
-
*/
|
|
73
|
+
/** 选填,登录成功后跳转回的页面。省略默认用当前页面 URL */
|
|
86
74
|
returnUrl?: string;
|
|
87
|
-
/**
|
|
88
|
-
* 选填,是否在新浏览器tab上打开登录页。默认为false
|
|
89
|
-
*/
|
|
75
|
+
/** 选填,是否在新浏览器 tab 打开登录页。默认 false */
|
|
90
76
|
newTab?: boolean;
|
|
91
77
|
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|
|
95
|
-
| ----------- | --------- | ---- | ----------- | ------------------------------ |
|
|
96
|
-
| `returnUrl` | `string` | ❌ | 当前页面URL | 登录成功后跳转回的页面 |
|
|
97
|
-
| `newTab` | `boolean` | ❌ | `false` | 是否在新浏览器标签页打开登录页 |
|
|
98
|
-
|
|
99
|
-
##### 出参说明
|
|
100
|
-
|
|
101
|
-
| 字段名 | 类型 | 说明 |
|
|
102
|
-
| ------------ | ----------- | ---------------------- |
|
|
103
|
-
| `data` | `'success'` | 成功标识 |
|
|
104
|
-
| `error` | `null` | 错误信息,成功时为null |
|
|
105
|
-
| `status` | `200` | HTTP状态码 |
|
|
106
|
-
| `statusText` | `'OK'` | HTTP状态文本 |
|
|
107
|
-
|
|
108
|
-
##### 使用示例
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
/**
|
|
112
|
-
* 跳转至dataloom登录页
|
|
113
|
-
* @param {string} brand - 品牌id 例如:妙搭为 1.
|
|
114
|
-
* @param {string} appId - 运行态应用的id,由dataloom authn 服务下发。
|
|
115
|
-
* @return 成功返回示例:
|
|
116
|
-
* {
|
|
117
|
-
* data: 'success',
|
|
118
|
-
* error: null,
|
|
119
|
-
* status: 200,
|
|
120
|
-
* statusText: 'OK',
|
|
121
|
-
* }
|
|
122
|
-
*/
|
|
123
|
-
const res: DataloomServiceResponse<'success'> = authClient
|
|
124
|
-
.session
|
|
125
|
-
.redirectToLogin(options: SignInRedirectionOptions);
|
|
126
|
-
|
|
127
|
-
// 基本使用
|
|
128
|
-
const loginResult = authClient
|
|
129
|
-
.session
|
|
130
|
-
.redirectToLogin();
|
|
131
|
-
|
|
132
|
-
// 带参数使用
|
|
133
|
-
const loginResult = authClient
|
|
134
|
-
.session
|
|
135
|
-
.redirectToLogin({
|
|
136
|
-
returnUrl: 'https://example.com/dashboard',
|
|
137
|
-
newTab: true
|
|
138
|
-
});
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
#### 2. 退出登录接口 (signOut)
|
|
142
|
-
|
|
143
|
-
##### 用途
|
|
144
|
-
|
|
145
|
-
退出登录,删除cookie中的登录态,适用于:用户主动登出、会话清理、安全退出
|
|
146
|
-
|
|
147
|
-
##### 适用场景
|
|
148
|
-
|
|
149
|
-
需要清除用户登录状态的场景
|
|
150
|
-
|
|
151
|
-
##### 入参说明
|
|
152
|
-
|
|
153
|
-
无需传入参数
|
|
154
|
-
|
|
155
|
-
##### 出参说明
|
|
156
|
-
|
|
157
|
-
| 字段名 | 类型 | 说明 |
|
|
158
|
-
| ------------ | ------ | ---------------------- |
|
|
159
|
-
| `data` | `null` | 数据为空 |
|
|
160
|
-
| `error` | `null` | 错误信息,成功时为null |
|
|
161
|
-
| `status` | `200` | HTTP状态码 |
|
|
162
|
-
| `statusText` | `'OK'` | HTTP状态文本 |
|
|
163
|
-
|
|
164
|
-
##### 使用示例
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
/**
|
|
168
|
-
* 退出登录,删除cookie中的登陆态
|
|
169
|
-
* @return 成功返回示例:
|
|
170
|
-
* {
|
|
171
|
-
* data: null,
|
|
172
|
-
* error: null,
|
|
173
|
-
* status: 200,
|
|
174
|
-
* statusText: 'OK',
|
|
175
|
-
* }
|
|
176
|
-
*/
|
|
177
|
-
const res: Promise<DataloomServiceResponse<null>> = await authClient.session.signOut();
|
|
178
|
-
|
|
179
|
-
// 使用示例
|
|
180
|
-
import { logger } from "@lark-apaas/client-toolkit/logger";
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const result = await authClient.session.signOut();
|
|
184
|
-
|
|
185
|
-
if (result.error) {
|
|
186
|
-
logger.error("退出登录失败:", result.error.message);
|
|
187
|
-
} else {
|
|
188
|
-
logger.info("退出登录成功");
|
|
189
|
-
// 跳转到登录页或首页
|
|
190
|
-
authClient.session.redirectToLogin();
|
|
191
|
-
}
|
|
192
|
-
} catch (error) {
|
|
193
|
-
logger.error("退出登录异常:", error);
|
|
194
|
-
}
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
#### 3. 跳转用户详情页接口 (navigateToUserProfile)
|
|
198
|
-
|
|
199
|
-
##### 用途
|
|
200
|
-
|
|
201
|
-
跳转至当前登录用户的详情页,适用于:查看个人资料、从用户头像/姓名点击进入详情页面
|
|
202
|
-
|
|
203
|
-
##### 适用场景
|
|
204
|
-
|
|
205
|
-
需要在应用中快速打开用户详情页面的场景
|
|
206
78
|
|
|
207
|
-
##### 入参说明
|
|
208
|
-
|
|
209
|
-
```typescript
|
|
210
79
|
interface NavigateToUserProfileOptions {
|
|
211
|
-
/**
|
|
212
|
-
* 选填,是否在新浏览器tab上打开详情页。默认为false
|
|
213
|
-
*/
|
|
80
|
+
/** 选填,是否在新浏览器 tab 打开详情页。默认 false */
|
|
214
81
|
newTab?: boolean;
|
|
215
82
|
}
|
|
216
83
|
```
|
|
217
84
|
|
|
218
|
-
|
|
219
|
-
| -------- | --------- | ---- | ------- | ------------------------------ |
|
|
220
|
-
| `newTab` | `boolean` | ❌ | `false` | 是否在新浏览器标签页打开详情页 |
|
|
221
|
-
|
|
222
|
-
##### 出参说明
|
|
223
|
-
|
|
224
|
-
| 字段名 | 类型 | 说明 |
|
|
225
|
-
| ------------ | ----------- | ---------------------- |
|
|
226
|
-
| `data` | `'success'` | 成功标识 |
|
|
227
|
-
| `error` | `null` | 错误信息,成功时为null |
|
|
228
|
-
| `status` | `200` | HTTP状态码 |
|
|
229
|
-
| `statusText` | `'OK'` | HTTP状态文本 |
|
|
230
|
-
|
|
231
|
-
##### 使用示例
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
/**
|
|
235
|
-
* 跳转至用户详情页
|
|
236
|
-
* @return 成功返回示例:
|
|
237
|
-
* {
|
|
238
|
-
* data: 'success',
|
|
239
|
-
* error: null,
|
|
240
|
-
* status: 200,
|
|
241
|
-
* statusText: 'OK',
|
|
242
|
-
* }
|
|
243
|
-
*/
|
|
244
|
-
const res: DataloomServiceResponse<"success"> = authClient.session.navigateToUserProfile();
|
|
245
|
-
|
|
246
|
-
// 在新标签页打开
|
|
247
|
-
const resNewTab = authClient.session.navigateToUserProfile({ newTab: true });
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
## 用户信息服务
|
|
251
|
-
|
|
252
|
-
#### 4. 获取用户信息接口 (getUserInfo)
|
|
253
|
-
|
|
254
|
-
##### 用途
|
|
255
|
-
|
|
256
|
-
根据当前登录态获取已登录的用户信息,适用于:用户资料展示、权限判断、个性化配置
|
|
257
|
-
|
|
258
|
-
##### 适用场景
|
|
259
|
-
|
|
260
|
-
需要获取当前登录用户详细信息的场景
|
|
261
|
-
|
|
262
|
-
##### 数据类型定义
|
|
85
|
+
### getUserInfo 返回类型定义
|
|
263
86
|
|
|
264
87
|
```typescript
|
|
265
88
|
interface I18n {
|
|
@@ -291,75 +114,39 @@ interface UserInfoResponse {
|
|
|
291
114
|
}
|
|
292
115
|
```
|
|
293
116
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
无需传入参数
|
|
297
|
-
|
|
298
|
-
##### 出参说明
|
|
299
|
-
|
|
300
|
-
| 字段名 | 类型 | 说明 |
|
|
301
|
-
| ----------------------------- | -------------- | ---------------------- |
|
|
302
|
-
| `data.user_info` | `UserBaseInfo` | 用户基本信息对象 |
|
|
303
|
-
| `data.user_info.user_id` | `number` | 用户唯一标识符 |
|
|
304
|
-
| `data.user_info.name` | `I18ns` | 用户名称(支持多语言) |
|
|
305
|
-
| `data.user_info.avatar` | `Avatar` | 用户头像信息 |
|
|
306
|
-
| `data.user_info.email` | `string` | 用户邮箱地址 |
|
|
307
|
-
| `data.user_info.phone_number` | `string` | 用户手机号码 |
|
|
308
|
-
| `data.user_info.tenant_name` | `string` | 租户名称 |
|
|
309
|
-
| `error` | `null` | 错误信息,成功时为null |
|
|
310
|
-
|
|
311
|
-
##### 使用示例
|
|
117
|
+
### 综合示例
|
|
312
118
|
|
|
313
119
|
```typescript
|
|
314
|
-
|
|
315
|
-
* 根据当前登陆态获取已登录的用户信息。
|
|
316
|
-
* @param {string} brand - 品牌id 例如:妙搭为 1.
|
|
317
|
-
* @param {string} appId - 运行态应用的id,由dataloom authn 服务下发。
|
|
318
|
-
* @return
|
|
319
|
-
*/
|
|
320
|
-
const res: Promise<DataloomServiceResponse<UserInfoResponse>> = await authClient.session.getUserInfo();
|
|
321
|
-
|
|
322
|
-
// 使用示例
|
|
120
|
+
import { authClient } from "@lark-apaas/client-toolkit/auth";
|
|
323
121
|
import { logger } from "@lark-apaas/client-toolkit/logger";
|
|
324
122
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
// 跳转到登录页
|
|
333
|
-
authClient.session.redirectToLogin();
|
|
334
|
-
}
|
|
335
|
-
} else if (result.data?.user_info) {
|
|
336
|
-
const userInfo = result.data.user_info;
|
|
337
|
-
logger.info("用户信息:", userInfo);
|
|
338
|
-
|
|
339
|
-
// 显示用户名
|
|
340
|
-
const userName = userInfo.name?.[0]?.text || "未知用户";
|
|
341
|
-
document.getElementById("username").textContent = userName;
|
|
342
|
-
|
|
343
|
-
// 显示用户头像
|
|
344
|
-
const avatarUrl = userInfo.avatar?.image?.large;
|
|
345
|
-
if (avatarUrl) {
|
|
346
|
-
document.getElementById("avatar").src = avatarUrl;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// 显示用户邮箱
|
|
350
|
-
if (userInfo.email) {
|
|
351
|
-
document.getElementById("email").textContent = userInfo.email;
|
|
352
|
-
}
|
|
123
|
+
// 获取用户信息(React 组件内展示当前用户请优先用 useCurrentUserProfile,见下文)
|
|
124
|
+
const result = await authClient.session.getUserInfo();
|
|
125
|
+
if (result.error) {
|
|
126
|
+
logger.error("获取用户信息失败:", result.error.message);
|
|
127
|
+
if (result.status === 401) {
|
|
128
|
+
// 未登录:跳转登录页(可传 returnUrl / newTab,默认回到当前页)
|
|
129
|
+
authClient.session.redirectToLogin();
|
|
353
130
|
}
|
|
354
|
-
}
|
|
355
|
-
|
|
131
|
+
} else if (result.data?.user_info) {
|
|
132
|
+
const info = result.data.user_info;
|
|
133
|
+
const userName = info.name?.[0]?.text || "未知用户"; // 名称是多语言数组
|
|
134
|
+
const avatarUrl = info.avatar?.image?.large; // 头像 URL 可能不返回
|
|
135
|
+
// React 中通过 state 渲染上述字段,禁止 document.getElementById 等直接 DOM 操作
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 退出登录后回到登录页
|
|
139
|
+
const signOutResult = await authClient.session.signOut();
|
|
140
|
+
if (!signOutResult.error) {
|
|
141
|
+
authClient.session.redirectToLogin();
|
|
356
142
|
}
|
|
143
|
+
|
|
144
|
+
// 跳转当前用户详情页(新标签页打开)
|
|
145
|
+
authClient.session.navigateToUserProfile({ newTab: true });
|
|
357
146
|
```
|
|
358
147
|
|
|
359
148
|
## 错误处理
|
|
360
149
|
|
|
361
|
-
### 常见错误类型
|
|
362
|
-
|
|
363
150
|
| 错误码 | 说明 | 处理建议 |
|
|
364
151
|
| ------ | -------------- | ---------------------- |
|
|
365
152
|
| `400` | 请求参数错误 | 检查传入参数是否正确 |
|
|
@@ -368,60 +155,19 @@ try {
|
|
|
368
155
|
| `404` | 资源不存在 | 检查请求的资源是否存在 |
|
|
369
156
|
| `500` | 服务器内部错误 | 稍后重试或联系技术支持 |
|
|
370
157
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
import { toast } from "sonner";
|
|
375
|
-
|
|
376
|
-
// 统一错误处理函数
|
|
377
|
-
function handleDataloomError(response: DataloomServiceResponse<any>) {
|
|
378
|
-
if (response.error) {
|
|
379
|
-
switch (response.status) {
|
|
380
|
-
case 401:
|
|
381
|
-
// 未授权,跳转登录
|
|
382
|
-
authClient.session.redirectToLogin();
|
|
383
|
-
break;
|
|
384
|
-
case 403:
|
|
385
|
-
// 权限不足
|
|
386
|
-
toast.error("权限不足,请联系管理员");
|
|
387
|
-
break;
|
|
388
|
-
case 500:
|
|
389
|
-
// 服务器错误
|
|
390
|
-
toast.error("服务器错误,请稍后重试");
|
|
391
|
-
break;
|
|
392
|
-
default:
|
|
393
|
-
toast.error(`操作失败: ${response.error.message}`);
|
|
394
|
-
}
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
return true;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// 使用示例
|
|
401
|
-
import { logger } from "@lark-apaas/client-toolkit/logger";
|
|
402
|
-
|
|
403
|
-
const userInfoResult = await authClient.session.getUserInfo();
|
|
404
|
-
if (handleDataloomError(userInfoResult)) {
|
|
405
|
-
// 处理成功逻辑
|
|
406
|
-
logger.info("用户信息:", userInfoResult.data);
|
|
407
|
-
}
|
|
408
|
-
```
|
|
158
|
+
统一处理建议:`401` 调用 `authClient.session.redirectToLogin()`(见综合示例);`403`/`500` 用 toast(如 `sonner`)提示用户;其余情况展示 `error.message`。
|
|
409
159
|
|
|
410
160
|
## 注意事项
|
|
411
161
|
|
|
412
|
-
1.
|
|
413
|
-
2.
|
|
414
|
-
3.
|
|
415
|
-
4. **安全性**:不要在客户端代码中暴露敏感的配置信息
|
|
416
|
-
5. **错误处理**:建议对所有接口调用进行统一的错误处理
|
|
162
|
+
1. **环境限制**:本 skill 涉及的 `@lark-apaas/client-toolkit/**` 能力只能在前端/浏览器环境中使用,服务端调用会返回环境不兼容错误;更重要的是,服务端代码中禁止 import 这些前端 SDK
|
|
163
|
+
2. **跨域配置**:确保应用域名已在 Dataloom 后台配置白名单
|
|
164
|
+
3. **安全性**:不要在客户端代码中暴露敏感的配置信息
|
|
417
165
|
|
|
418
166
|
# 用户系统前端相关规范
|
|
419
167
|
|
|
420
168
|
## 概述
|
|
421
169
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
同时提供 DepartmentSelect(部门选择组件),用于部门字段的单选/多选选择,交互与受控规范与 UserSelect 保持一致。
|
|
170
|
+
内置用户前端组件:UserSelect(单选/多选用户选择器,onChange 返回用户对象)、DepartmentSelect(部门选择组件,交互与受控规范与 UserSelect 一致)、UserDisplay(用户信息展示组件)。均基于统一的 userid 数据,用于用户相关的表单输入和数据展示场景。
|
|
425
171
|
|
|
426
172
|
## 类型定义
|
|
427
173
|
|
|
@@ -448,17 +194,8 @@ export type User = {
|
|
|
448
194
|
|
|
449
195
|
## 当前用户信息的获取方案
|
|
450
196
|
|
|
451
|
-
### 常用场景
|
|
452
|
-
|
|
453
|
-
- **用户信息展示**:在界面中显示当前用户名称和头像
|
|
454
|
-
- **权限验证**:基于用户ID进行权限检查和控制
|
|
455
|
-
- **数据关联**:在数据操作时关联当前用户信息
|
|
456
|
-
- **日志记录**:记录用户操作日志时获取用户标识
|
|
457
|
-
|
|
458
197
|
### Hooks 方法: `useCurrentUserProfile` - 在 React 中获取当前用户信息
|
|
459
198
|
|
|
460
|
-
### 基本信息
|
|
461
|
-
|
|
462
199
|
- **文件路径**:`@lark-apaas/client-toolkit/hooks/useCurrentUserProfile`
|
|
463
200
|
- **功能**:获取当前登录用户的个人信息(含飞书 user_id)
|
|
464
201
|
- **返回值**:`Partial<IUserProfile>`(初始为空对象 `{}`,异步获取后填充完整字段)
|
|
@@ -473,29 +210,18 @@ export type User = {
|
|
|
473
210
|
| `avatar` | `string` | 用户头像 URL |
|
|
474
211
|
| `lark_user_id` | `string` | 飞书 user_id,通过额外异步请求获取,可能晚于其他字段就绪 |
|
|
475
212
|
|
|
476
|
-
> ⚠️ **空值处理(CRITICAL)**:Hook 初始返回空对象 `{}`(truthy
|
|
477
|
-
|
|
478
|
-
### 使用方法
|
|
213
|
+
> ⚠️ **空值处理(CRITICAL)**:Hook 初始返回空对象 `{}`(truthy),**MUST** 用 `if (!userInfo?.user_id)` 判加载态(不是 `!userInfo`);`lark_user_id` 可能为 `undefined`,使用前条件渲染。完整禁止行为清单与飞书 ID 转换指南(后端 `AuthNPaasService` 等)参见 `user-identity` skill。
|
|
479
214
|
|
|
480
215
|
```typescript
|
|
481
216
|
import { useCurrentUserProfile } from "@lark-apaas/client-toolkit/hooks/useCurrentUserProfile";
|
|
482
217
|
|
|
483
218
|
const MyComponent = () => {
|
|
484
219
|
const userInfo = useCurrentUserProfile();
|
|
485
|
-
|
|
486
|
-
// 正确:安全访问 + 加载态处理
|
|
487
220
|
if (!userInfo?.user_id) return <div>加载中...</div>;
|
|
488
|
-
return
|
|
489
|
-
<div>
|
|
490
|
-
<p>{userInfo.name}</p>
|
|
491
|
-
{userInfo.lark_user_id && <p>飞书 ID: {userInfo.lark_user_id}</p>}
|
|
492
|
-
</div>
|
|
493
|
-
);
|
|
221
|
+
return <p>{userInfo.name}</p>;
|
|
494
222
|
};
|
|
495
223
|
```
|
|
496
224
|
|
|
497
|
-
> **飞书 ID 转换详细指南**(后端 `AuthNPaasService` 用法、自定义转换接口等)参见 `user-identity` skill。
|
|
498
|
-
|
|
499
225
|
## 用户展示与选择方案
|
|
500
226
|
|
|
501
227
|
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
@@ -509,7 +235,7 @@ const MyComponent = () => {
|
|
|
509
235
|
|
|
510
236
|
{% else %}
|
|
511
237
|
|
|
512
|
-
##
|
|
238
|
+
## UserSelect - 用户选择组件
|
|
513
239
|
|
|
514
240
|
### 基本信息
|
|
515
241
|
|
|
@@ -529,14 +255,9 @@ interface UserSelectProps {
|
|
|
529
255
|
}
|
|
530
256
|
```
|
|
531
257
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
- **单选模式** (`mode="single"`):值为 userid,返回为`IUserProfile` 对象
|
|
535
|
-
- **多选模式** (`mode="multiple"`):值为 userid数组,返回为`IUserProfile` 数组
|
|
258
|
+
值类型:单选模式(`mode="single"`)值为 userid、onChange 返回 `IUserProfile` 对象;多选模式(`mode="multiple"`)值为 userid 数组、onChange 返回 `IUserProfile` 数组。
|
|
536
259
|
|
|
537
|
-
###
|
|
538
|
-
|
|
539
|
-
#### 表单集成
|
|
260
|
+
### 使用示例(表单集成)
|
|
540
261
|
|
|
541
262
|
```typescript
|
|
542
263
|
import { useForm } from "react-hook-form";
|
|
@@ -545,10 +266,8 @@ import * as z from "zod";
|
|
|
545
266
|
import { Form, FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form";
|
|
546
267
|
import { UserSelect } from "@lark-apaas/client-toolkit/components/User";
|
|
547
268
|
|
|
548
|
-
// 定义表单验证schema
|
|
549
269
|
const formSchema = z.object({
|
|
550
270
|
assignee: z.string(),
|
|
551
|
-
participants: z.array(z.string()).optional(),
|
|
552
271
|
});
|
|
553
272
|
|
|
554
273
|
const form = useForm<z.infer<typeof formSchema>>({
|
|
@@ -574,24 +293,8 @@ const form = useForm<z.infer<typeof formSchema>>({
|
|
|
574
293
|
</FormItem>
|
|
575
294
|
)}
|
|
576
295
|
/>
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
control={form.control}
|
|
580
|
-
name="participants"
|
|
581
|
-
render={({ field }) => (
|
|
582
|
-
<FormItem>
|
|
583
|
-
<FormLabel>参与人(多选)</FormLabel>
|
|
584
|
-
<FormControl>
|
|
585
|
-
<UserSelect
|
|
586
|
-
mode="multiple"
|
|
587
|
-
placeholder="选择参与人员"
|
|
588
|
-
value={field.value}
|
|
589
|
-
onChange={(users) => field.onChange(users.map(user => user.user_id))}
|
|
590
|
-
/>
|
|
591
|
-
</FormControl>
|
|
592
|
-
</FormItem>
|
|
593
|
-
)}
|
|
594
|
-
/>
|
|
296
|
+
{/* 多选字段差异仅:schema 用 z.array(z.string()),mode="multiple",
|
|
297
|
+
onChange={(users) => field.onChange(users.map((u) => u.user_id))} */}
|
|
595
298
|
</form>
|
|
596
299
|
</Form>
|
|
597
300
|
```
|
|
@@ -604,41 +307,24 @@ const form = useForm<z.infer<typeof formSchema>>({
|
|
|
604
307
|
- **功能**:用于所有用户信息的展示场景
|
|
605
308
|
- **特性**:显示用户**头像**和**姓名**,支持多用户展示
|
|
606
309
|
|
|
607
|
-
IMPORTANT:当不传递showLabel时,组件会同时展示用户头像和姓名,如果只需要头像,则需要传递showLabel的值为false
|
|
608
|
-
|
|
609
310
|
### 属性定义
|
|
610
311
|
|
|
611
312
|
```typescript
|
|
612
313
|
interface UserDisplayProps {
|
|
613
|
-
users: string[]; // 用户id
|
|
314
|
+
users: string[]; // 用户id数组(必需),单个用户传 [userId]
|
|
614
315
|
size?: "small" | "medium" | "large"; // 头像尺寸
|
|
615
316
|
className?: string; // 自定义样式类名
|
|
616
|
-
showLabel?: boolean; // 默认为true
|
|
317
|
+
showLabel?: boolean; // 默认为true,同时展示头像和姓名;只需要头像时必须显式传 false
|
|
617
318
|
}
|
|
618
319
|
```
|
|
619
320
|
|
|
620
321
|
### 使用示例
|
|
621
322
|
|
|
622
|
-
#### 基础用法
|
|
623
|
-
|
|
624
323
|
```jsx
|
|
625
324
|
import { UserDisplay } from "@lark-apaas/client-toolkit/components/User";
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
// 单个用户展示
|
|
631
|
-
<UserDisplay
|
|
632
|
-
users={[employeesId]}
|
|
633
|
-
size="small"
|
|
634
|
-
/>
|
|
635
|
-
|
|
636
|
-
// 多用户展示
|
|
637
|
-
<UserDisplay
|
|
638
|
-
users={project.participants}
|
|
639
|
-
size="medium"
|
|
640
|
-
className="project-members"
|
|
641
|
-
/>
|
|
325
|
+
|
|
326
|
+
// 单个或多个用户展示:users 传用户 id 数组
|
|
327
|
+
<UserDisplay users={project.participants} size="medium" className="project-members" />
|
|
642
328
|
```
|
|
643
329
|
|
|
644
330
|
## 使用注意事项
|