@lark-apaas/coding-steering 0.1.21 → 0.1.22-alpha.20260724081248
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 +21 -19
- package/package.json +2 -2
- package/steering/design-html/skills/animated-video/SKILL.md +2 -4
- package/steering/design-html/skills/charts/SKILL.md +3 -5
- package/steering/design-html/skills/data-report/SKILL.md +4 -6
- package/steering/design-html/skills/frontend-design/SKILL.md +34 -36
- package/steering/design-html/skills/hi-fi-design/SKILL.md +2 -4
- package/steering/design-html/skills/interactive-prototype/SKILL.md +2 -4
- package/steering/design-html/skills/make-a-deck/SKILL.md +94 -112
- package/steering/design-html/skills/visual-exposure/SKILL.md +4 -6
- package/steering/design-html/skills/wireframe/SKILL.md +5 -7
- package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +375 -61
- package/steering/nestjs-react-fullstack/{skills_common/trigger-guide/references/trigger-lifecycle.md → skills/trigger-guide/SKILL.md} +162 -11
- package/steering/nestjs-react-fullstack/{skills_common → skills}/user-identity/SKILL.md +5 -12
- package/steering/nestjs-react-fullstack/skills_common/trigger-guide/SKILL.md +0 -180
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/SKILL.md +0 -196
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/dynamic-permission-guide.md +0 -643
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/management-page-spec.md +0 -505
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/runtime-role-controller-spec.md +0 -203
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/sdk-examples.md +0 -92
- package/steering/nestjs-react-fullstack/skills_local/authz-guide/references/sdk-types.md +0 -229
- package/steering/nestjs-react-fullstack/skills_local/client-builtins-user-service/SKILL.md +0 -240
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: visual-exposure
|
|
3
3
|
description: 用于制作可视化报告、专题视觉页、信息图、视觉长图、概念可视化、产品能力曝光、方案亮点展示等内容型 HTML 视觉作品。适合用户想把材料、数据或观点组织成可阅读、可展示、可传播的视觉化表达,但不希望做成 PPT、传统 dashboard 或纯 ECharts 图表的场景。触发词:可视化报告, 视觉报告, 可视化曝光, 视觉化曝光, 信息图, 长图, infographic, 视觉表达, 概念可视化, 亮点展示, 能力曝光
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
zh-CN: 可视化报告
|
|
7
|
-
en-US: Visual Report
|
|
4
|
+
available-agents:
|
|
5
|
+
- CreativeDesign
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
# 可视化报告与专题表达
|
|
@@ -19,7 +17,7 @@ metadata:
|
|
|
19
17
|
4. 按材料逻辑组织内容,而不是套固定目录、固定模块或固定视觉模板。参考样式只能启发表达方式,不能替代对当前材料的判断。
|
|
20
18
|
5. 把材料拆成具体阅读任务:这一段要让读者完成什么判断、理解什么关系、记住什么事实、比较什么差异、追踪什么过程、相信什么证据。不要把这些任务名直接变成目录或模块标题。
|
|
21
19
|
6. 为每个阅读任务现场生成合适的组件、视觉和布局:先说明这段内容需要什么表达方式,再落成具体 UI / 图形 / 排版 / 图表 / 截图 / 文字组合。可以创造新的结构和视觉隐喻,不受现有组件名限制;避免所有章节共享同一套组件组合。
|
|
22
|
-
7. 先写风格 brief
|
|
20
|
+
7. 先写风格 brief:主题隐喻、受众姿态、材料语言、配色逻辑和视觉签名。财务报告可以像正式报告册,员工调研可以像组织研究档案,产品上市总结可以像品牌战报;这些只是启发,必须从用户材料里推导。
|
|
23
21
|
8. 建立版式系统:画幅、栅格、字号层级、颜色、图标/线条语言、强调方式和章节节奏。版式系统必须说明不同章节如何变化,而不是所有章节都用同一种上下结构。
|
|
24
22
|
9. 产出单个 HTML 文档。用户需求明确时直接做;只有主题、素材或交付形态完全无法判断时,才问少量必要问题。
|
|
25
23
|
|
|
@@ -52,7 +50,7 @@ metadata:
|
|
|
52
50
|
- 默认平面化处理:内容区优先使用细边框、分隔线、浅底色、色块、表格斑马纹、编号和标签建立层级;不要给章节、卡片、图表容器加各种 `box-shadow`。
|
|
53
51
|
- 少用装饰性渐变、发光、玻璃拟态。视觉效果要帮助分组、强调或引导视线。
|
|
54
52
|
- 风格跟随内容、受众和品牌:可以正式、温和、技术、编辑化、品牌化或实验感,但不要从某个样例场景继承固定颜色、固定目录或固定组件。
|
|
55
|
-
-
|
|
53
|
+
- 每份报告应有一个可解释的视觉签名。视觉签名要从用户主题、材料质感和阅读任务中生成,而不是复用固定手法;它可以是任何能组织内容、建立记忆点并保持一致性的视觉规则。
|
|
56
54
|
- 真实素材优先:用户给的截图、logo、图片、图标、数据片段要优先使用。没有素材时,用清楚的占位结构和可替换文案。
|
|
57
55
|
- 允许少量动效,但只用于进入、强调或引导阅读,不做干扰理解的持续动画。
|
|
58
56
|
- 可以包含数字、图表和表格,但它们服务于报告叙事;不要为了“可视化”而把所有内容都做成图。
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wireframe
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
zh-CN: 线框图
|
|
7
|
-
en-US: Wireframe
|
|
3
|
+
description: Explore many ideas with wireframes and storyboards
|
|
4
|
+
available-agents:
|
|
5
|
+
- CreativeDesign
|
|
8
6
|
---
|
|
9
7
|
|
|
10
|
-
#
|
|
8
|
+
# Wireframe
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
Help the user explore design ideas quickly. Interview them, then generate multiple rough wireframes to map out the design space before committing to a direction. Prioritize breadth over polish: show 3-5 distinctly different approaches for each idea. Use simple shapes, placeholder text, and minimal color to keep the focus on structure and flow. Use a sketchy vibe -- handwritten but readable fonts; b&w with some color; low-fi and simple. Provide simple tweaks; show options side-by-side if small or using a tab control if large.
|
|
@@ -13,20 +13,27 @@ 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 组件(见下文)
|
|
24
16
|
|
|
25
17
|
## 统一响应结构
|
|
26
18
|
|
|
27
|
-
|
|
19
|
+
### 响应类型定义
|
|
28
20
|
|
|
29
21
|
```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
|
+
*/
|
|
30
37
|
interface DataloomServiceBase {
|
|
31
38
|
status: number;
|
|
32
39
|
statusText: string;
|
|
@@ -57,32 +64,202 @@ import { authClient } from "@lark-apaas/client-toolkit/auth";
|
|
|
57
64
|
// authClient 是 SDK 内置的 singleton,零参可用,不需要异步初始化
|
|
58
65
|
```
|
|
59
66
|
|
|
60
|
-
##
|
|
67
|
+
## 鉴权服务接口
|
|
68
|
+
|
|
69
|
+
#### 1. 登录跳转接口 (redirectToLogin)
|
|
70
|
+
|
|
71
|
+
##### 用途
|
|
61
72
|
|
|
62
|
-
|
|
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` |
|
|
73
|
+
跳转至 Dataloom 登录页面,适用于:用户身份认证、单点登录、权限验证
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
##### 适用场景
|
|
76
|
+
|
|
77
|
+
需要用户进行身份认证的场景
|
|
78
|
+
|
|
79
|
+
##### 入参说明
|
|
70
80
|
|
|
71
81
|
```typescript
|
|
72
82
|
interface SignInRedirectionOptions {
|
|
73
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* 选填,登录成功后跳转回的页面。省略会默认用调用接口时页面的url。
|
|
85
|
+
*/
|
|
74
86
|
returnUrl?: string;
|
|
75
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* 选填,是否在新浏览器tab上打开登录页。默认为false
|
|
89
|
+
*/
|
|
76
90
|
newTab?: boolean;
|
|
77
91
|
}
|
|
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
|
+
需要在应用中快速打开用户详情页面的场景
|
|
78
206
|
|
|
207
|
+
##### 入参说明
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
79
210
|
interface NavigateToUserProfileOptions {
|
|
80
|
-
/**
|
|
211
|
+
/**
|
|
212
|
+
* 选填,是否在新浏览器tab上打开详情页。默认为false
|
|
213
|
+
*/
|
|
81
214
|
newTab?: boolean;
|
|
82
215
|
}
|
|
83
216
|
```
|
|
84
217
|
|
|
85
|
-
|
|
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
|
+
##### 数据类型定义
|
|
86
263
|
|
|
87
264
|
```typescript
|
|
88
265
|
interface I18n {
|
|
@@ -114,39 +291,75 @@ interface UserInfoResponse {
|
|
|
114
291
|
}
|
|
115
292
|
```
|
|
116
293
|
|
|
117
|
-
|
|
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
|
+
##### 使用示例
|
|
118
312
|
|
|
119
313
|
```typescript
|
|
120
|
-
|
|
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
|
+
// 使用示例
|
|
121
323
|
import { logger } from "@lark-apaas/client-toolkit/logger";
|
|
122
324
|
|
|
123
|
-
|
|
124
|
-
const result = await authClient.session.getUserInfo();
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
//
|
|
129
|
-
|
|
325
|
+
try {
|
|
326
|
+
const result = await authClient.session.getUserInfo();
|
|
327
|
+
|
|
328
|
+
if (result.error) {
|
|
329
|
+
logger.error("获取用户信息失败:", result.error.message);
|
|
330
|
+
// 可能需要重新登录
|
|
331
|
+
if (result.status === 401) {
|
|
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
|
+
}
|
|
130
353
|
}
|
|
131
|
-
}
|
|
132
|
-
|
|
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();
|
|
354
|
+
} catch (error) {
|
|
355
|
+
logger.error("获取用户信息异常:", error);
|
|
142
356
|
}
|
|
143
|
-
|
|
144
|
-
// 跳转当前用户详情页(新标签页打开)
|
|
145
|
-
authClient.session.navigateToUserProfile({ newTab: true });
|
|
146
357
|
```
|
|
147
358
|
|
|
148
359
|
## 错误处理
|
|
149
360
|
|
|
361
|
+
### 常见错误类型
|
|
362
|
+
|
|
150
363
|
| 错误码 | 说明 | 处理建议 |
|
|
151
364
|
| ------ | -------------- | ---------------------- |
|
|
152
365
|
| `400` | 请求参数错误 | 检查传入参数是否正确 |
|
|
@@ -155,19 +368,60 @@ authClient.session.navigateToUserProfile({ newTab: true });
|
|
|
155
368
|
| `404` | 资源不存在 | 检查请求的资源是否存在 |
|
|
156
369
|
| `500` | 服务器内部错误 | 稍后重试或联系技术支持 |
|
|
157
370
|
|
|
158
|
-
|
|
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
|
+
```
|
|
159
409
|
|
|
160
410
|
## 注意事项
|
|
161
411
|
|
|
162
|
-
1.
|
|
163
|
-
2.
|
|
164
|
-
3.
|
|
412
|
+
1. **环境限制**:部分接口只能在浏览器环境中使用,服务端调用会返回环境不兼容错误
|
|
413
|
+
2. **登录状态**:获取用户信息前需要确保用户已登录,否则会返回401错误
|
|
414
|
+
3. **跨域配置**:确保应用域名已在 Dataloom 后台配置白名单
|
|
415
|
+
4. **安全性**:不要在客户端代码中暴露敏感的配置信息
|
|
416
|
+
5. **错误处理**:建议对所有接口调用进行统一的错误处理
|
|
165
417
|
|
|
166
418
|
# 用户系统前端相关规范
|
|
167
419
|
|
|
168
420
|
## 概述
|
|
169
421
|
|
|
170
|
-
|
|
422
|
+
内置的用户前端组件规范,提供了 UserSelect(支持单选/多选的用户选择器)和 UserDisplay(用户信息展示组件)两个核心 React 组件,基于统一的userid数据,并且用户选择组件中会通过onchange返回User类型数据,专门用于处理用户相关的表单输入和数据展示场景。
|
|
423
|
+
|
|
424
|
+
同时提供 DepartmentSelect(部门选择组件),用于部门字段的单选/多选选择,交互与受控规范与 UserSelect 保持一致。
|
|
171
425
|
|
|
172
426
|
## 类型定义
|
|
173
427
|
|
|
@@ -194,8 +448,17 @@ export type User = {
|
|
|
194
448
|
|
|
195
449
|
## 当前用户信息的获取方案
|
|
196
450
|
|
|
451
|
+
### 常用场景
|
|
452
|
+
|
|
453
|
+
- **用户信息展示**:在界面中显示当前用户名称和头像
|
|
454
|
+
- **权限验证**:基于用户ID进行权限检查和控制
|
|
455
|
+
- **数据关联**:在数据操作时关联当前用户信息
|
|
456
|
+
- **日志记录**:记录用户操作日志时获取用户标识
|
|
457
|
+
|
|
197
458
|
### Hooks 方法: `useCurrentUserProfile` - 在 React 中获取当前用户信息
|
|
198
459
|
|
|
460
|
+
### 基本信息
|
|
461
|
+
|
|
199
462
|
- **文件路径**:`@lark-apaas/client-toolkit/hooks/useCurrentUserProfile`
|
|
200
463
|
- **功能**:获取当前登录用户的个人信息(含飞书 user_id)
|
|
201
464
|
- **返回值**:`Partial<IUserProfile>`(初始为空对象 `{}`,异步获取后填充完整字段)
|
|
@@ -210,18 +473,29 @@ export type User = {
|
|
|
210
473
|
| `avatar` | `string` | 用户头像 URL |
|
|
211
474
|
| `lark_user_id` | `string` | 飞书 user_id,通过额外异步请求获取,可能晚于其他字段就绪 |
|
|
212
475
|
|
|
213
|
-
> ⚠️ **空值处理(CRITICAL)**:Hook 初始返回空对象 `{}`(truthy
|
|
476
|
+
> ⚠️ **空值处理(CRITICAL)**:Hook 初始返回空对象 `{}`(truthy)、字段均 `undefined`。**MUST** 用可选链,并以 `if (!userInfo?.user_id)` 判加载态(不是 `!userInfo`);`lark_user_id` 异步获取、可能为 `undefined`,使用前条件渲染。
|
|
477
|
+
|
|
478
|
+
### 使用方法
|
|
214
479
|
|
|
215
480
|
```typescript
|
|
216
481
|
import { useCurrentUserProfile } from "@lark-apaas/client-toolkit/hooks/useCurrentUserProfile";
|
|
217
482
|
|
|
218
483
|
const MyComponent = () => {
|
|
219
484
|
const userInfo = useCurrentUserProfile();
|
|
485
|
+
|
|
486
|
+
// 正确:安全访问 + 加载态处理
|
|
220
487
|
if (!userInfo?.user_id) return <div>加载中...</div>;
|
|
221
|
-
return
|
|
488
|
+
return (
|
|
489
|
+
<div>
|
|
490
|
+
<p>{userInfo.name}</p>
|
|
491
|
+
{userInfo.lark_user_id && <p>飞书 ID: {userInfo.lark_user_id}</p>}
|
|
492
|
+
</div>
|
|
493
|
+
);
|
|
222
494
|
};
|
|
223
495
|
```
|
|
224
496
|
|
|
497
|
+
> **飞书 ID 转换详细指南**(后端 `AuthNPaasService` 用法、自定义转换接口等)参见 `user-identity` skill。
|
|
498
|
+
|
|
225
499
|
## 用户展示与选择方案
|
|
226
500
|
|
|
227
501
|
{% if projectMeta['flags']['supportBusinessUser'] %}
|
|
@@ -235,7 +509,7 @@ const MyComponent = () => {
|
|
|
235
509
|
|
|
236
510
|
{% else %}
|
|
237
511
|
|
|
238
|
-
##
|
|
512
|
+
##
|
|
239
513
|
|
|
240
514
|
### 基本信息
|
|
241
515
|
|
|
@@ -255,9 +529,14 @@ interface UserSelectProps {
|
|
|
255
529
|
}
|
|
256
530
|
```
|
|
257
531
|
|
|
258
|
-
|
|
532
|
+
### 值类型说明
|
|
533
|
+
|
|
534
|
+
- **单选模式** (`mode="single"`):值为 userid,返回为`IUserProfile` 对象
|
|
535
|
+
- **多选模式** (`mode="multiple"`):值为 userid数组,返回为`IUserProfile` 数组
|
|
259
536
|
|
|
260
|
-
###
|
|
537
|
+
### 使用示例
|
|
538
|
+
|
|
539
|
+
#### 表单集成
|
|
261
540
|
|
|
262
541
|
```typescript
|
|
263
542
|
import { useForm } from "react-hook-form";
|
|
@@ -266,8 +545,10 @@ import * as z from "zod";
|
|
|
266
545
|
import { Form, FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form";
|
|
267
546
|
import { UserSelect } from "@lark-apaas/client-toolkit/components/User";
|
|
268
547
|
|
|
548
|
+
// 定义表单验证schema
|
|
269
549
|
const formSchema = z.object({
|
|
270
550
|
assignee: z.string(),
|
|
551
|
+
participants: z.array(z.string()).optional(),
|
|
271
552
|
});
|
|
272
553
|
|
|
273
554
|
const form = useForm<z.infer<typeof formSchema>>({
|
|
@@ -293,8 +574,24 @@ const form = useForm<z.infer<typeof formSchema>>({
|
|
|
293
574
|
</FormItem>
|
|
294
575
|
)}
|
|
295
576
|
/>
|
|
296
|
-
|
|
297
|
-
|
|
577
|
+
|
|
578
|
+
<FormField
|
|
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
|
+
/>
|
|
298
595
|
</form>
|
|
299
596
|
</Form>
|
|
300
597
|
```
|
|
@@ -307,24 +604,41 @@ const form = useForm<z.infer<typeof formSchema>>({
|
|
|
307
604
|
- **功能**:用于所有用户信息的展示场景
|
|
308
605
|
- **特性**:显示用户**头像**和**姓名**,支持多用户展示
|
|
309
606
|
|
|
607
|
+
IMPORTANT:当不传递showLabel时,组件会同时展示用户头像和姓名,如果只需要头像,则需要传递showLabel的值为false
|
|
608
|
+
|
|
310
609
|
### 属性定义
|
|
311
610
|
|
|
312
611
|
```typescript
|
|
313
612
|
interface UserDisplayProps {
|
|
314
|
-
users: string[]; // 用户id
|
|
613
|
+
users: string[]; // 用户id数组(必需)
|
|
315
614
|
size?: "small" | "medium" | "large"; // 头像尺寸
|
|
316
615
|
className?: string; // 自定义样式类名
|
|
317
|
-
showLabel?: boolean; // 默认为true
|
|
616
|
+
showLabel?: boolean; // 默认为true,会展示用户姓名,如果只需要展示头像则需要设置为false
|
|
318
617
|
}
|
|
319
618
|
```
|
|
320
619
|
|
|
321
620
|
### 使用示例
|
|
322
621
|
|
|
622
|
+
#### 基础用法
|
|
623
|
+
|
|
323
624
|
```jsx
|
|
324
625
|
import { UserDisplay } from "@lark-apaas/client-toolkit/components/User";
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
626
|
+
import { useEmployeeStore } from "@/models/employee";
|
|
627
|
+
|
|
628
|
+
const { employeesId } = useEmployeeStore();
|
|
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
|
+
/>
|
|
328
642
|
```
|
|
329
643
|
|
|
330
644
|
## 使用注意事项
|