@lark-apaas/coding-steering 0.1.58 → 0.1.59

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.
Files changed (26) hide show
  1. package/package.json +1 -1
  2. package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +15 -13
  3. package/steering/nestjs-react-fullstack/skills/code-fix/SKILL.md +2 -2
  4. package/steering/nestjs-react-fullstack/skills/coding-guide/SKILL.md +20 -5
  5. package/steering/nestjs-react-fullstack/skills/contacts-service/SKILL.md +46 -32
  6. package/steering/nestjs-react-fullstack/skills/design-guide/SKILL.md +0 -3
  7. package/steering/nestjs-react-fullstack/skills/design-guide/references/token-mapping.md +3 -3
  8. package/steering/nestjs-react-fullstack/skills/openapi-guide/SKILL.md +28 -59
  9. package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +16 -4
  10. package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +75 -14
  11. package/steering/nestjs-react-fullstack/skills/server-builtins-file-storage-service/SKILL.md +178 -168
  12. package/steering/nestjs-react-fullstack/skills_common/mcp-guide/SKILL.md +78 -0
  13. package/steering/nestjs-react-fullstack/skills_common/mcp-guide/assets/eslint.mcp-ui.config.cjs +19 -0
  14. package/steering/nestjs-react-fullstack/skills_common/mcp-guide/assets/ui-tsconfig.json +19 -0
  15. package/steering/nestjs-react-fullstack/skills_common/mcp-guide/references/client-onboarding.md +82 -0
  16. package/steering/nestjs-react-fullstack/skills_common/mcp-guide/references/debugging.md +81 -0
  17. package/steering/nestjs-react-fullstack/skills_common/mcp-guide/references/mcp-apps.md +197 -0
  18. package/steering/nestjs-react-fullstack/skills_common/mcp-guide/references/tool-authoring.md +249 -0
  19. package/steering/nestjs-react-fullstack/skills_common/server-contacts-contract/SKILL.md +19 -0
  20. package/steering/nestjs-react-fullstack/skills_common/user-identity/SKILL.md +5 -1
  21. package/steering/nestjs-react-fullstack/skills_local/code-fix/SKILL.md +2 -2
  22. package/steering/nestjs-react-fullstack/skills_local/coding-guide/SKILL.md +18 -3
  23. package/steering/nestjs-react-fullstack/skills_local/plugin-guide/SKILL.md +3 -3
  24. package/steering/nestjs-react-fullstack/skills_local/plugin-guide/references/plugin-coding-guide.md +93 -2
  25. package/steering/vite-react/skills/plugin-guide/SKILL.md +2 -0
  26. package/steering/nestjs-react-fullstack/skills/design-guide/references/corporate-blueprint.md +0 -252
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: user-identity
3
- description: "Use when getting current user info/profile, displaying user name/avatar/email, converting miaoda userId ↔ lark_user_id (both directions via AuthNPaasService), or reading req.userContext fields (userId/roles/tenantId): useCurrentUserProfile, AuthNPaasService, FeishuID conversion. 触发词:用户身份, 用户信息, 用户资料, 当前用户, userProfile, useCurrentUserProfile, 飞书ID, FeishuID, 飞书用户ID, lark_user_id, 用户ID转换, AuthNPaasService, getBatchMiaodaUserIds, 飞书ID转妙搭, employee_id 转 userId, 用户上下文, userContext, userContext.roles, 用户角色, 当前用户角色, 获取请求者角色, 展示用户, 显示用户, 用户面板, 我是谁, 获取用户"
3
+ description: "当前登录用户「我是谁」+ 妙搭 ↔ 飞书 ID 转换。Use when getting current user info/profile, displaying user name/avatar/email, converting miaoda userId ↔ lark_user_id (both directions via AuthNPaasService ID-conversion methods), or reading req.userContext fields (userId/roles/tenantId): useCurrentUserProfile, FeishuID conversion. ⚠️ 按 userId 查他人姓名/头像请用 contacts-service 第六节。触发词:用户身份, 用户信息, 用户资料, 当前用户, userProfile, useCurrentUserProfile, 飞书ID, FeishuID, 飞书用户ID, lark_user_id, 用户ID转换, AuthNPaasService ID 转换, getCurrentUserLarkUserId, getBatchLarkUserIds, getBatchMiaodaUserIds, 飞书ID转妙搭, employee_id 转 userId, 用户上下文, userContext, userContext.roles, 用户角色, 当前用户角色, 获取请求者角色, 展示当前用户, 我是谁"
4
4
  steering: true
5
5
  steering-topic: user_identity
6
6
  match-template-name: nestjs-react-fullstack
@@ -10,6 +10,10 @@ match-template-name: nestjs-react-fullstack
10
10
 
11
11
  本 skill 专注于**用户身份**:ID 体系、`req.userContext` 字段、`useCurrentUserProfile`、妙搭 ↔ 飞书 ID 转换。
12
12
 
13
+ > `AuthNPaasService` 分流:`getCurrentUserLarkUserId`、`getBatchLarkUserIds`、`getBatchMiaodaUserIds` 属于本 skill 的 ID 转换;`listUsersByIds` 属于按妙搭 userId 查他人姓名 / 头像,见 [`contacts-service`](../contacts-service/SKILL.md) 第六节。
14
+ >
15
+ > 要拿姓名头像只看 `contacts-service` 第六节及其字段合同,禁止按常见 SDK 命名推断。
16
+
13
17
  **接口认证**(`@NeedLogin()` 装饰器、`AuthNPaasGuard` opt-in 模式、公开接口处理、401)请使用 [`authn-guide`](../authn-guide/SKILL.md) skill。
14
18
 
15
19
  ## 零、ID 体系警告(CRITICAL)
@@ -260,7 +260,7 @@ useEffect 无限循环、依赖数组管理、useMemo/useCallback 记忆化等
260
260
  | 场景 | 做法 | 注意事项 |
261
261
  |------|------|----------|
262
262
  | 用户反馈 | 使用 `toast` (sonner) 显示友好消息 | 消息简洁、可操作,避免暴露技术细节 |
263
- | 前端日志 | 使用 `logger` (`@lark-apaas/client-toolkit/logger`) | 禁止 console;参数为 string,对象需 `JSON.stringify` |
264
- | 后端日志 | 使用 `@nestjs/common` 的 Logger | 禁止 console;参数为 string,对象需 `JSON.stringify` |
263
+ | 前端日志 | 使用 `logger` (`@lark-apaas/client-toolkit/logger`) | 禁止 console;签名见 `coding-guide`「日志约定」,可多参,`logger.log` 只接 `{ level, args }` |
264
+ | 后端日志 | 使用 `@nestjs/common` 的 Logger | 禁止 console;签名见 `coding-guide`「日志约定」,可多参,无 `info` 方法 |
265
265
  | 业务错误 | 区分预期错误与意外错误 | 预期错误用 `logger.warn`,意外错误用 `logger.error` |
266
266
  | 异常处理 | 禁止静默处理异常 | 必须显示明确的错误信息,参考 `coding-guide` 相关规范 |
@@ -248,9 +248,24 @@ dev server 自己只代理 `/api`、`/openapi`、`/__innerapi__`。`/app/<appId>
248
248
 
249
249
  ## 日志约定
250
250
 
251
- - 后端禁止 console,**必须总使用** `@nestjs/common` 的 Logger(无 info 方法,用 `logger.log` 代替)
252
- - Logger 参数必须为 string,对象需 `JSON.stringify`
253
- - 输出完整错误堆栈
251
+ 前后端是两个独立的 logger,签名不同,不要互相套用。
252
+
253
+ **前端** `@lark-apaas/client-toolkit/logger`:同一个 `logger` 对象,六个方法分两类签名。
254
+
255
+ - `debug` / `info` / `warn` / `error` / `success`: `(message: any, ...args: any[])`
256
+ 级别已在方法名里;可多参,`message` 不限 string。
257
+ 例:`logger.error('创建失败', error)`
258
+ - `log`: `({ level, args, meta? }: LogWithMeta)`
259
+ 级别不在方法名里必须显式传;只接对象,传裸字符串会 TS2345。
260
+ 例:`logger.log({ level: 'info', args: ['创建成功', id] })`
261
+
262
+ **后端** `@nestjs/common` 的 `Logger`:
263
+
264
+ - 没有 `info` 方法,用 `logger.log` 代替;有 `debug` / `verbose` / `fatal`
265
+ - `(message: any, ...optionalParams)`,可多参
266
+ - 最后一个 string 参数会被当作 `context`(`error` 是 `stack` + `context`),传对象前自己 `JSON.stringify`
267
+
268
+ 两端共同:禁止 `console`;错误日志输出完整堆栈。
254
269
 
255
270
  ## Database
256
271
 
@@ -42,8 +42,8 @@ lark-cli apps --help 2>&1 | grep -q '+plugin-install' && echo "READY" || echo "M
42
42
  | 插件实例配置位置 | `server/capabilities/<instance_id>.json`(默认全栈应用)。见「配置目录」段 |
43
43
 
44
44
  > **capabilityClient 导入警告**:`capabilityClient` 是从 `@lark-apaas/client-toolkit` 直接导入的独立对象,**不是**从 `getDataloom()` 上获取的。正确且唯一的方式:`import { capabilityClient } from '@lark-apaas/client-toolkit'`。
45
- > **文件类参数**:Client 侧原始 `File` / `Blob` 直接传 `capabilityClient`。已持久化文件/图片由 Server 侧 `CapabilityService` 调 AI 插件时,取 `file_path`(或 `bucket_id + file_path`)用 `FileService.createSignedUrl(filePath, 3600)` / `fileService.from(bucketId).createSignedUrl(filePath, 3600)` 签成临时 http(s) URL 后传入。标准 `/app` 或 `/spark/app/.../runtime/api/v1/storage/object/<bucket>/<key>` 是可被平台 resolver 消费的内部路径,但不是 `createSignedUrl` `filePath` 入参;禁止给它拼接 `localhost`、`window.location.origin` 或 `/app/<app_id>`。非标准相对路径、前端展示 URL、非法 data URL 不作稳定入参。签名失败要 `warn`,业务状态保持 `failed` / `pending` / `null`,不写成功态。
46
- > **改造已有调用**:检查所有文档和图片插件调用点,统一使用上面的签名 URL。删除下载后转 base64 的旧逻辑。缺少 `file_path` 或签名失败时记录警告,写入 `failed` / `pending` / `null`,并停止调用插件;不得改用原始 URL。
45
+ > **文件类参数**:Client 侧原始 `File` / `Blob` 直接传 `capabilityClient`,禁止先经 dataloom 上传再传 URL。已持久化文件/图片由 Server 侧 `CapabilityService` 调 AI 插件时,优先取 `file_path`(或 `bucket_id + file_path`)用 `FileService.createSignedUrl(filePath, 3600)` / `fileService.from(bucketId).createSignedUrl(filePath, 3600)` 签成临时 http(s) URL 后传入。旧记录只有标准 `/app` 或 `/spark/app/.../runtime/api/v1/storage/object/<bucket>/<key>` download URL 时,必须先解析其中的 bucket 与 file path,再调用 `fileService.from(bucketId).createSignedUrl(filePath, 3600)`;默认 `createSignedUrl()` 只使用默认 bucket,不能从 URL 切换 bucket。禁止给标准路径拼接 `localhost`、`window.location.origin` 或 `/app/<app_id>`;非标准相对路径、前端展示 URL、非法 data URL 不作稳定入参。签名失败或返回空 URL 时要 `warn`,业务状态保持 `failed` / `pending` / `null`,不写成功态。
46
+ > **稳定契约**:已持久化文件不得先下载再转 base64;应统一使用签名 URL。缺少可识别的文件路径、签名失败或返回空 URL 时记录警告,写入 `failed` / `pending` / `null`,并停止调用插件;不得改用原始 URL。
47
47
 
48
48
  ## Plugin 代码编写指南
49
49
 
@@ -569,7 +569,7 @@ npx @lark-apaas/miaoda-cli plugin list --id <instance_id>
569
569
  | Mock `capabilityClient` 返回值 | 必须真实调用 |
570
570
  | 通过 `getDataloom().capability` 调用插件 | `capabilityClient` 是独立导入 |
571
571
  | Client 侧先通过 dataloom 上传文件再传给插件 | 直接传 File/Blob 对象 |
572
- | Server 侧把非标准相对路径、误拼 origin 的 storage object path、前端展示 URL 或非法 base64 data URL 传给文件/图片参数 | 已持久化文件用 `file_path` 或 `bucket_id + file_path` 生成 1h 签名 http(s) URL 后再传;标准 storage object path 原样经过平台 resolver 时可消费,但不能直接作为 `createSignedUrl` `filePath` |
572
+ | Server 侧把非标准相对路径、误拼 origin 的 storage object path、前端展示 URL 或非法 base64 data URL 传给文件/图片参数 | 已持久化文件优先用 `file_path` 或 `bucket_id + file_path` 生成 1h 签名 http(s) URL 后再传;标准 storage download URL 要先解析 bucket file path,再从对应 bucket 签名 |
573
573
  | 用正则/字符串解析处理 AI 输出 | 用 `ai-text-to-json` / `ai-image-to-json` |
574
574
  | 认为 `ai-doc-parser` 能直接输出结构化 JSON | 只输出纯文本,需链式调用 `ai-text-to-json` |
575
575
  | 图片提取结构化用两步链 | 优先 `ai-image-to-json` 单步直达 |
@@ -294,7 +294,97 @@ Please make sure that the argument Function at index [0] is available in the Xxx
294
294
 
295
295
  同一规则适用于从 `@lark-apaas/fullstack-nestjs-core` 注入的其他平台服务(`AuthNPaasService`、`FileService` 等)。
296
296
 
297
- #### 3. 调用示例
297
+ #### 3. Server 侧已持久化文件进入 AI capability
298
+
299
+ Client 侧拿到用户刚选择的原始 `File` / `Blob` 时,直接传 `capabilityClient`,不要为了调用插件先上传到应用存储。文件/图片已作为业务数据持久化,且必须由 Server 侧 `CapabilityService` 调 `ai-doc-parser`、`ai-image-understanding` 或其它 `file` / `picture` / `plugin-file-url` / `plugin-image-url` 参数时,才在服务端把存储路径换成临时 http(s) URL 后传入插件。
300
+
301
+ 优先从业务记录读取存储 `file_path`;字段类型是 `file_attachment` 时,同时读取 `bucket_id` 与 `file_path`。旧记录只有标准 `/app` 或 `/spark/app/.../runtime/api/v1/storage/object/<bucket>/<key>` download URL 时,可以解析出 bucket 与 file path 后再签名;不能把它直接交给默认 `createSignedUrl()`,因为该入口只使用默认 bucket,不会使用 URL 里的 bucket。签名 URL 才能交给 AI plugin。禁止给标准路径拼接 `localhost`、`window.location.origin` 或 `/app/<app_id>`,否则会绕过内部路径解析并被当成不安全的外部 URL。非标准相对路径、前端展示 URL、非法 base64 data URL 不应作为 Server 侧插件稳定入参。
302
+
303
+ ```typescript
304
+ import { FileService } from '@lark-apaas/fullstack-nestjs-core';
305
+
306
+ type StoredFileRef = {
307
+ file_path?: string | null;
308
+ bucket_id?: string | null;
309
+ download_url?: string | null;
310
+ };
311
+
312
+ const STORAGE_DOWNLOAD_URL_RE =
313
+ /^\/(?:spark\/)?app\/app_[\w]+\/runtime\/api\/v1\/storage\/object\/([^/]+)\/(.+)$/;
314
+
315
+ function parseStorageDownloadUrl(
316
+ downloadUrl: string,
317
+ ): { bucketId: string; filePath: string } | null {
318
+ const match = STORAGE_DOWNLOAD_URL_RE.exec(downloadUrl);
319
+ if (!match) return null;
320
+
321
+ try {
322
+ const bucketId = decodeURIComponent(match[1]);
323
+ const filePath = decodeURIComponent(match[2]);
324
+ return bucketId && !bucketId.includes('/') && filePath
325
+ ? { bucketId, filePath }
326
+ : null;
327
+ } catch {
328
+ return null;
329
+ }
330
+ }
331
+
332
+ async function createTemporaryFileUrl(
333
+ fileService: FileService,
334
+ file: StoredFileRef,
335
+ ): Promise<string | null> {
336
+ try {
337
+ let signedUrl: string;
338
+ if (file.file_path) {
339
+ signedUrl = file.bucket_id
340
+ ? await fileService
341
+ .from(file.bucket_id)
342
+ .createSignedUrl(file.file_path, 3600)
343
+ : await fileService.createSignedUrl(file.file_path, 3600);
344
+ } else {
345
+ const parsed = file.download_url
346
+ ? parseStorageDownloadUrl(file.download_url)
347
+ : null;
348
+ if (!parsed) return null;
349
+ signedUrl = await fileService
350
+ .from(parsed.bucketId)
351
+ .createSignedUrl(parsed.filePath, 3600);
352
+ }
353
+ return signedUrl || null;
354
+ } catch (error) {
355
+ // 这里不要返回伪成功 URL;由调用方把业务状态置为 failed/pending/null 等可见状态
356
+ return null;
357
+ }
358
+ }
359
+ ```
360
+
361
+ 调用 AI 插件前先签名并校验 URL:
362
+
363
+ ```typescript
364
+ const docParserPluginInstanceId = '<doc-parser-plugin-instance-id>';
365
+ const signedUrl = await createTemporaryFileUrl(this.fileService, storedFile);
366
+ if (!signedUrl) {
367
+ this.logger.warn('missing file path or create signed url failed before AI capability call', {
368
+ filePath: storedFile.file_path,
369
+ bucketId: storedFile.bucket_id,
370
+ hasDownloadUrl: Boolean(storedFile.download_url),
371
+ pluginInstanceId: docParserPluginInstanceId,
372
+ actionKey: 'parseDocToMarkdown',
373
+ });
374
+ await this.updateRecord(recordId, { parseStatus: 'failed', parsedText: null });
375
+ return;
376
+ }
377
+
378
+ const output = await this.capabilityService
379
+ .load(docParserPluginInstanceId)
380
+ .call('parseDocToMarkdown', {
381
+ fileUrl: [signedUrl], // 是否数组必须以 inputSchema 为准
382
+ });
383
+ ```
384
+
385
+ 稳定契约:已持久化文件统一使用签名函数返回的 URL,不要先下载后转 base64,也不要把原始 URL 作为备用值传给插件。缺少可识别的文件路径、签名失败或返回空 URL 时记录警告,写入 `failed` / `pending` / `null`,并停止调用插件。
386
+
387
+ #### 4. 调用示例
298
388
 
299
389
  服务端 `CapabilityExecutor` 的**三个方法全都没有泛型参数**(`@lark-apaas/nestjs-capability`):
300
390
 
@@ -328,11 +418,12 @@ try {
328
418
  }
329
419
  ```
330
420
 
331
- #### 4. Server 侧编排与容错原则
421
+ #### 5. Server 侧编排与容错原则
332
422
 
333
423
  - PluginInstance 调用在 Server 侧通常属于 **外部依赖 / side-effect**
334
424
  - 除非业务明确要求强一致性,**默认不应阻塞主业务流程**
335
425
  - 已选择 Server 侧承接的高耗时 AI capability 必须有可观测状态:创建任务时记录处理进度、完成终态、失败终态、输入摘要、错误信息和结果引用;触发接口只返回任务标识与当前状态,前端通过短轮询读取进度和最终结果
426
+ - 文件/图片签名 URL 生成失败或返回空 URL 时必须 `warn` 并把业务记录保持在 `failed` / `pending` / `null` 等用户可见状态,禁止构造假 URL 或写入“解析成功”
336
427
 
337
428
  推荐写法:异步触发 + catch 兜底:
338
429
 
@@ -169,6 +169,8 @@ const structured = await (jsonExtractor as any).call('textToJson', { text: rawRe
169
169
 
170
170
  创建 `ai-text-to-json` 类型的 PluginInstance 时:一次性定义所有提取字段(参考 schema/表单/UI),最多 20 个。字段类型以当次 `formSchema.paramType.enum` 为准;静态指南/`readme` 只解释 enum 内类型,冲突时按 enum 建模并说明冲突,不得降级为 String;enum 缺失、不可读或缺业务所需类型时停止并报告合同冲突。基础字段用 String/Number/Boolean 等对应类型,同构列表用 Array,固定字段集合用 Object,嵌套成员写进 `paramDescription`。对象/列表结果不得序列化进 String;只有业务要求原样透传、不解析、不改写 JSON 文本时才用 String。创建后调用 `get_plugin_ai_json`,按 `outputSchema` 核验字段并生成调用代码,不把它作为创建前类型依据;按契约直接消费结果,禁止无依据 `JSON.parse`。
171
171
 
172
+ `jsonStructure` 的顶级字段都会参与必填校验:模型漏任一 key 时整次调用抛 `INVALID_OUTPUT`。插件只用文本 prompt,不用 JSON Schema 强制生成;顶级字段越多,整体失败概率越高。N 套同构结果用一个 Array 字段承载,元素结构写进 `paramDescription`,不要摊成 N x M 个顶级字段。Array/Object 内部只做 `z.any()` 校验,`paramDescription` 仅影响 prompt;消费方仍须校验元素数量、成员字段和空值。
173
+
172
174
  创建 `ai-image-to-json` 实例时,必须一次性定义所有需要提取的字段,字段类型仅支持 String / Number / Boolean,最多 20 个;先调用 `get_plugin_ai_json` 确认上游插件的 `outputSchema`,确保输入格式正确。图片→结构化数据场景优先使用 `ai-image-to-json`(单步),避免不必要的链式调用。
173
175
 
174
176
  ## 核心概念
@@ -1,252 +0,0 @@
1
- ---
2
- version: alpha
3
- name: corporate-blueprint
4
- name_zh: 蓝图
5
- description: 企业蓝图视觉风——浅灰底上白色零圆角卡片,统一顶部3px深蓝强调色边线为核心识别特征;深蓝渐变强调区可叠加斜切几何装饰块;超小号全大写加粗字距标签与巨大主标题构成极端字号对比建立层级;极细0.5px分隔线传达精密感;图表配色收敛为单一蓝色相深浅五级渐变,编号 Section 标记可按内容需要使用。专业、权威、精密、可信赖。
6
- colors:
7
- bg: "#F4F7F9"
8
- surface: "#FFFFFF"
9
- muted: "#F8FAFC"
10
- text: "#1A1A1A"
11
- text-muted: "#94A3B8"
12
- accent: "#0033A0"
13
- border: "#E2E8F0"
14
- success: "#10B981"
15
- success-bg: "#ECFDF5"
16
- danger: "#EF4444"
17
- danger-bg: "#FEF2F2"
18
- warning: "#F59E0B"
19
- warning-bg: "#FFFBEB"
20
- header-start: "#001D4A"
21
- header-end: "#004B93"
22
- chart-2: "#0066FF"
23
- chart-3: "#4D94FF"
24
- chart-4: "#99C2FF"
25
- chart-5: "#CCE0FF"
26
- typography:
27
- sans:
28
- fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
29
- usage: 全局唯一字体族,覆盖所有文字层级(非手写/展示体,无需 CJK 兜底)
30
- mono:
31
- fontFamily: 系统等宽栈(Tailwind font-mono 默认)
32
- usage: 表格数值列/辅助列等宽对齐
33
- rounded:
34
- none: 0
35
- sm: 2px
36
- chip: 4px
37
- bar-top: "2px 2px 0 0"
38
- scrollbar: 3px
39
- full: 9999px
40
- spacing:
41
- section-gap: 32px
42
- container-max: 1280px
43
- container-px: 32px
44
- card-padding: 24px
45
- grid-gap: 32px
46
- grid-gap-lg: 40px
47
- header-overlap: -40px
48
- section-title-gap: 24px
49
- page-bottom: 80px
50
- components:
51
- header:
52
- background: "linear-gradient(to bottom right,{colors.header-start},{colors.accent},{colors.header-end})"
53
- decoration: "absolute top-0 right-0 w-1/2 h-full bg-white/5 skew-x-[-20deg] translate-x-1/4(斜切几何装饰块)"
54
- padding: "pt-16 pb-20 px-8"
55
- border: "底边 border-b border-blue-900"
56
- meta-panel: "bg-black/10 backdrop-blur-sm p-6 border border-white/10(日期/地点/状态)"
57
- status-badge: "px-2 py-0.5 bg-emerald-500 text-white {rounded.sm}"
58
- report-card:
59
- background: "{colors.surface}"
60
- border: "HARD REQUIREMENT——必须使用 Tailwind 类 border-t-[3px] border-t-[#0033A0](仅顶边,其余三边 border-0/无边框),禁止省略、换色或用其他粗细替代;这是本风格唯一识别签名,任何新增卡片型组件(KPI卡/结论卡/明细卡/风险卡等)复用本组件时必须原样带上"
61
- borderRadius: "HARD REQUIREMENT——必须使用 Tailwind 类 rounded-none,禁止 rounded/rounded-sm/rounded-md/rounded-lg/rounded-xl/rounded-2xl 等任何默认圆角类"
62
- boxShadow: shadow-md(轻阴影)
63
- padding: "{spacing.card-padding}"
64
- section-header:
65
- label: "text-[11px] font-black {colors.accent} uppercase tracking-[0.15em] mb-1,格式\"01. SECTION NAME\""
66
- subtitle: "text-[10px] {colors.text-muted} font-medium,English description,整体 mb-6({spacing.section-title-gap})"
67
- metric-block:
68
- layout: "按指标数量自适应排列,单元 p-4,以 thin-border 分隔"
69
- label: "text-[9px] font-black {colors.text-muted} uppercase tracking-tight mb-2"
70
- value: "text-xl font-bold {colors.text}(slate-800)"
71
- badge: "目标达成标签 text-[10px] {colors.success} font-bold {colors.success-bg} px-1 {rounded.chip}"
72
- aux: "text-[10px] {colors.text-muted} mt-1"
73
- data-table:
74
- header: "border-b thin-border,text-[9px] font-black {colors.text-muted} uppercase,py-2"
75
- row: "divide-y thin-border,hover:bg-slate-50 transition-colors"
76
- primary-col: "text-xs font-bold {colors.accent}"
77
- desc-col: "text-[11px] {colors.text-muted} font-medium(slate-600)"
78
- value-col: "text-xs font-bold text-right {colors.text}(slate-800)"
79
- aux-col: "text-xs font-mono text-right {colors.text-muted}"
80
- risk-item:
81
- background: "{colors.surface}"
82
- border: "1px solid {colors.border}(thin-border),flex justify-between items-center,p-3"
83
- badge: "高风险={colors.danger-bg}底{colors.danger}字 | 中风险={colors.warning-bg}底{colors.warning}字,text-[9px] font-bold px-1 {rounded.chip}"
84
- title: "text-xs font-bold {colors.text}(slate-800)"
85
- description: "text-[10px] {colors.text-muted} font-medium"
86
- verdict-card:
87
- base: "report-card,{colors.accent} 底白字反转,p-6 shadow-md"
88
- title: "text-[11px] font-black text-white/50 uppercase tracking-[0.15em]"
89
- value: "text-3xl font-black,主结论文字"
90
- subtitle: "text-[11px] text-white/60 font-medium uppercase tracking-widest"
91
- todo-item: "flex items-center gap-3,icon CheckCircle2 size=12 text-emerald-400,文字 text-[11px] font-bold text-white"
92
- timeline:
93
- connector: "轴线 absolute top-[1.1rem] left-0 right-0 h-[1px] bg-slate-100"
94
- node: "圆环 w-10 h-10 {rounded.full} bg-white border thin-border,内圆 w-2 h-2 {rounded.full} {colors.accent},hover 边框变 {colors.accent}"
95
- label: "日期 text-[9px] font-black {colors.text-muted} uppercase mb-1;阶段 text-[11px] font-bold {colors.text}(slate-800);宽度 max-w-[100px] 居中"
96
- charts:
97
- palette: "{colors.accent} {colors.chart-2} {colors.chart-3} {colors.chart-4} {colors.chart-5}(深→浅单色蓝五级);HARD REQUIREMENT——即便是风险预警/逾期账龄/客户流失等语义上容易联想红绿告警色的图表,数据可视化本体(柱/线/面/环/漏斗填充色)也一律只用本蓝色阶,禁止用 {colors.success}/{colors.danger}/{colors.warning} 或 emerald/red/orange 等任何非蓝色相填充图表;语义强调只能放在图表旁边的文字/徽章/图例上"
98
- container: "report-card,零圆角,shadow-md,高度 h-[180px](紧凑)/h-[240px](标准)"
99
- grid: "containLabel true,left/right 0,top 20,bottom 16(顶部留图例/标签空间)"
100
- axis: "x 轴标签 9px font-weight 700 {colors.text-muted};y 轴刻度隐藏,仅留 {colors.border} 网格线"
101
- tooltip: "{colors.surface}底,1px {colors.border}边框,{rounded.none},padding 12,文字 11px font-weight 700 {colors.text}"
102
- legend: "9px font-weight 700 {colors.text-muted},名称 uppercase 格式化,icon=rect 10×6,itemGap 12"
103
- bar: "barWidth 30,顶部微圆角 {rounded.bar-top}"
104
- line: "smooth=false,lineWidth 2,symbol circle size 6,areaStyle opacity 0(默认无填充,需要时 0.15)"
105
- pie: "实心 radius 60% / 环形 radius 40%~60% padAngle 2,均 borderWidth 0"
106
- anchors:
107
- - id: blueprint-top-border
108
- type: component
109
- desc: 白色零圆角报告卡片统一顶部粗强调色边线(见 components.report-card),风格核心识别特征
110
- - id: header-gradient-skew
111
- type: component
112
- desc: 深蓝渐变 Header 叠加斜切几何装饰块(见 components.header),企业感开场视觉
113
- - id: micro-label-scale
114
- type: pattern
115
- desc: 全大写加粗字距标签与超大主标题构成极端字号对比,建立强烈层级感
116
- - id: thin-border-hairline
117
- type: token
118
- desc: 极细分隔线统一使用 colors.border,全局分隔精密而不显重
119
- - id: mono-blue-chart-scale
120
- type: token
121
- desc: 图表序列色仅用单一色相深浅五级渐变(见 components.charts.palette),避免混入其他色相
122
- - id: numbered-section-header
123
- type: pattern
124
- desc: Section 标题统一"编号+全大写标签+英文副标题"格式(见 components.section-header)
125
- - id: zero-radius-cards
126
- type: pattern
127
- desc: 卡片/Tooltip/图表容器统一零圆角,仅徽章/标签/时间线节点例外(见 Exceptions)
128
- - id: verdict-card-invert
129
- type: component
130
- desc: 结论区块使用强调色反转底色+白字的高对比收尾卡片(见 components.verdict-card)
131
- gaps:
132
- - 无暗色模式定义
133
- - 无手写/展示体字体,仅 Inter 单一字体族,不适用 CJK 兜底规则
134
- - 无 deck 画布/幻灯片专属排版规格(源为单页纵向滚动企业报告布局,幻灯片场景沿用同一体系)
135
- - 无表单控件(input/select 等)规范
136
- exceptions:
137
- - 状态标签/风险等级标签/滚动条滑块/时间线节点/柱状图柱体允许非零圆角,是零圆角规则的例外
138
- - Header 渐变中间色复用 accent,不单独建色板 token
139
- - 讨论列表警告类图标使用 orange-500,是唯一脱离主色板的图标色
140
- - 风险/达标类徽章(badge)允许使用 success/danger/warning 语义色,该例外仅限徽章/标签文字与底色,不适用于图表填充色(见 Hard Rules)
141
- ---
142
-
143
- ## Overview
144
-
145
- 企业蓝图式视觉风格。冷浅灰画布、白色报告表面、深蓝渐变强调区与编号标记共同形成专业、权威、精密、可信赖的气质。页面结构由实际内容决定,编号章节可作为签名元素按需使用。
146
-
147
- ## Colors
148
-
149
- | 角色 | 值 | 用法 |
150
- |-----|-----|-----|
151
- | bg | {colors.bg} | 页面背景 |
152
- | surface | {colors.surface} | 卡片背景 |
153
- | muted | {colors.muted} | 次级背景区域(侧栏、辅助面板) |
154
- | text | {colors.text} | 主要文字 |
155
- | text-muted | {colors.text-muted} | 次要文字、标签、坐标轴文字 |
156
- | accent | {colors.accent} | 强调色、标题色、卡片顶边线、图表主色 |
157
- | border | {colors.border} | 卡片边框、分隔线(0.5px 极细线) |
158
- | success | {colors.success} | 状态标签、目标达成标识(仅限徽章/文字,不进图表填充色) |
159
- | success-bg | {colors.success-bg} | 正向标签底色 |
160
- | danger | {colors.danger} | 高风险标识(仅限徽章/文字,不进图表填充色) |
161
- | danger-bg | {colors.danger-bg} | 高风险标签底色 |
162
- | warning | {colors.warning} | 中风险、警告图标(仅限徽章/文字,不进图表填充色) |
163
- | warning-bg | {colors.warning-bg} | 中风险标签底色 |
164
- | header-start | {colors.header-start} | Header 渐变起点(最深) |
165
- | header-end | {colors.header-end} | Header 渐变终点(最浅);渐变中间色复用 accent |
166
- | chart-2 ~ chart-5 | {colors.chart-2} … {colors.chart-5} | 图表序列第 2~5 级,与 accent(第 1 级)共构深→浅单色蓝五级渐变;风险/逾期/流失类图表也只用这 5 级,不得换用 success/danger/warning |
167
-
168
- ## Typography
169
-
170
- 全局样式首行原样写入(HARD REQUIREMENT,禁止替换):
171
-
172
- ```css
173
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
174
- ```
175
-
176
- Inter 覆盖全部文字层级,非手写/展示体,系统 sans-serif 兜底已含在字体栈内,无需额外 CJK 兜底。
177
-
178
- | 层级 | 样式(Tailwind) | 颜色 |
179
- |-----|-----|-----|
180
- | 页面主标题 | text-4xl md:text-5xl font-extrabold tracking-tight leading-tight | white |
181
- | 页面副标题 | text-sm font-medium tracking-wide | blue-200/70 |
182
- | Section 编号标签 | text-[11px] font-black uppercase tracking-[0.15em] | accent |
183
- | Section 副标题 | text-[10px] font-medium | text-muted(slate-400) |
184
- | KPI 数值 | text-xl font-bold | slate-800 |
185
- | KPI 标签 | text-[9px] font-black uppercase tracking-tight | slate-400 |
186
- | 表头文字 | text-[9px] font-black uppercase | slate-400 |
187
- | 表格数据 | text-xs font-bold / text-[11px] font-medium | slate-800 / slate-600 |
188
- | 正文描述 | text-[11px] font-medium leading-relaxed | slate-600 |
189
- | 辅助小字 | text-[10px] font-medium | slate-400 |
190
- | 元信息标签(Header 内) | text-[10px] font-bold uppercase tracking-[0.2em] | blue-100 |
191
- | Footer | text-[10px] font-bold uppercase tracking-widest | slate-300 |
192
- | 等宽数值 | font-mono(见 typography.mono) | 表格数值列对齐 |
193
-
194
- ## Layout
195
-
196
- - 内容容器可参考 max-w-7xl({spacing.container-max}) 与 px-8({spacing.container-px}),但应按任务形态调整。
197
- - 网格 gap-8({spacing.grid-gap}),大组件 gap-10({spacing.grid-gap-lg}),卡片内边距 p-6({spacing.card-padding})。
198
- - 使用深蓝渐变强调区、编号章节、斜切装饰与顶部蓝线建立蓝图气质;这些元素可分布在任意合适区域,不规定 Header、KPI、表格、风险卡或时间线的顺序和比例。
199
- - 负 margin 重叠仅作可选强调手法,不要求出现。
200
- - 响应式布局按内容关系重排;列数和分栏比例不作为风格约束。
201
- - 滚动条:宽 6px,轨道浅灰(#f1f1f1),滑块 accent 色,圆角 {rounded.scrollbar}。
202
-
203
- ## Elevation & Depth
204
-
205
- 阴影统一 shadow-md 轻阴影(报告卡片、图表容器、结论卡),无重阴影/多档阴影体系,依赖顶边线与极细分隔线建立层次而非阴影强度。
206
-
207
- Hover 交互:数据表格行 `hover:bg-slate-50 transition-colors`;时间线节点圆环 `group-hover:border-[accent] transition-colors`。整体交互克制,无入场动画定义。
208
-
209
- ## Shapes
210
-
211
- - 圆角语言:报告卡片/Tooltip/图表容器统一 {rounded.none}(零圆角,企业精密感,Tailwind 写作 rounded-none,**禁止使用 rounded/rounded-sm/rounded-md/rounded-lg/rounded-xl/rounded-2xl 等任何默认圆角类**);柱状图柱体顶部 {rounded.bar-top};状态标签 {rounded.sm};风险等级标签 {rounded.chip};时间线节点/圆点标记 {rounded.full};滚动条滑块 {rounded.scrollbar}。
212
- - 边框语言:卡片顶边线 3px solid accent(通过 report-card 实现,Tailwind `border-t-[3px] border-t-[#0033A0]`,核心签名,任何新增卡片型组件必须原样带上);极细分隔线 0.5px solid border(thin-border 类);Header 底边 border-b border-blue-900;列表左侧指示 border-l-2 border-slate-100。
213
- - 装饰元素:Header 斜切几何块(absolute bg-white/5 skew-x-[-20deg] translate-x-1/4);时间线横轴(absolute h-[1px] bg-slate-100,贯穿节点);色条标记(w-1 h-3 色块条,红/黄/深蓝/灰);圆点标记(w-2 h-2 rounded-full accent 色,图例/时间线节点内圆点);竖线分隔(h-4 w-[1px] bg-white/30,Header 品牌区域分隔)。
214
-
215
- ## Components
216
-
217
- 核心组件规格见 frontmatter components(header / report-card / section-header / metric-block / data-table / risk-item / verdict-card / timeline / charts)。补充细节:
218
-
219
- - **Header**:品牌标识 w-8 h-8 bg-white 方块内深蓝文字+竖线分隔+全大写部门标签;状态标签 emerald-500 底白字。
220
- - **ReportCard**:所有卡片基座,顶部边线是唯一强调,内容区自由排列(指标块/表格/图表/列表)。任何新增的卡片型组件(如新的看板模块、榜单、明细面板)只要视觉上是"白底卡片",一律直接复用 report-card 的顶边线+零圆角规格,不单独发明新样式。
221
- - **MetricBlock**:指标数量自适应排列,以细边线分隔,标签在上数值在下,可选目标达成小标签。
222
- - **DataTable**:主列(通常首列)用 accent 强调,其余数据列分深浅两级灰;数值列右对齐,辅助列等宽字体右对齐。
223
- - **RiskItem**:高/中风险两档标签色(danger/warning 对),归属区分标签(灰)与姓名(深灰粗体)。
224
- - **讨论反馈列表**:按团队分组,左侧 2px 浅灰竖线;列表项图标区分警告类(AlertTriangle,orange-500)与普通类(灰圆点)。
225
- - **VerdictCard**:全区块唯一反转配色(accent 底白字),用于收尾结论,含待办清单(CheckCircle2 绿色图标)。
226
- - **Timeline**:圆环节点 hover 变 accent 边框,宽度限制 max-w-[100px] 居中,日期/阶段/归属三行堆叠。
227
- - **图表(ECharts)**:序列色按 charts.palette 顺序(单色蓝五级);柱状/折线/饼图/雷达/散点样式见 components.charts,统一零圆角容器+浅色网格线。**逾期账龄/风险预警/客户流失等语义上容易联想红绿告警色的图表也不例外**,填充色仍只用蓝色阶,语义强调放在图表旁边的数值文字/徽章/箭头上(参考 risk-item/metric-block 的 danger/warning/success 用法)。
228
- - **Footer**:border-t thin-border,居中左右两端布局,竖线"|"分隔。
229
-
230
- ## Do's and Don'ts
231
-
232
- - 单页编号 Section(01. 02. 03...)串联内容块,适合会议纪要/技术评审/季度报告等需要权威感和结构清晰度的场景。
233
- - 文字层级通过极端字号对比建立(9px 标签 vs 3xl-5xl 主标题),避免引入中间字号削弱对比。
234
- - Hover 交互保持克制,仅行高亮/边框变色两种模式,不引入弹性动效。
235
- - 负 margin 制造 Header 与首行重叠是可选强调手法,非必须。
236
- - 不要因为某个图表的数据语义是"风险/逾期/流失"就下意识换成红绿配色——语义色只出现在文字和徽章上,图表本体永远是蓝色阶。
237
-
238
- ## Hard Rules
239
-
240
- - **报告卡片顶部必须带 3px accent 色边线**(Tailwind `border-t-[3px] border-t-[#0033A0]`),是核心视觉签名,任何卡片型组件(KPI卡/结论卡/明细卡/风险卡/新增卡片)复用 report-card 时都必须原样带上,禁止省略、换色或换粗细。
241
- - **报告卡片/Tooltip/图表容器圆角必须为零**(Tailwind `rounded-none`),禁止 `rounded`/`rounded-sm`/`rounded-md`/`rounded-lg`/`rounded-xl`/`rounded-2xl` 等任何默认圆角类(Exceptions 列明的徽章/标签/时间线节点除外)。
242
- - **图表序列色仅使用 accent→chart-5 的单一蓝色相五级渐变**,禁止引入其他色相——即便是风险预警、逾期账龄、客户流失等语义上容易联想红/绿告警色的图表,数据填充色也只用蓝色阶,语义强调只能放在图表旁边的文字/徽章/图例上,不得让 success/danger/warning 进入图表填充色。
243
- - 分隔线统一 0.5px(border 色),禁止用粗边框(卡片顶边线除外)或重阴影,阴影统一 shadow-md 轻阴影。
244
- - 全大写标签必须使用 font-black uppercase tracking-[0.15em] 字距,保持企业报告气质统一。
245
- - 字体 @import 必须原样写入全局样式首行,禁止替换或省略(HARD REQUIREMENT)。
246
-
247
- ## Exceptions
248
-
249
- - 状态标签/风险等级标签/滚动条滑块/时间线节点/柱状图柱体允许非零圆角(rounded-sm/rounded/rounded-full/2px 微圆角),是零圆角规则的例外。
250
- - 风险标签色(danger/warning)与目标达成标签色(success)仅限对应语义场景的徽章/文字使用,不进入图表填充色,也不进入主色板日常强调。
251
- - Header 渐变中间色复用 accent,不单独建色板 token。
252
- - 讨论反馈列表警告类图标使用 orange-500,是唯一脱离主色板的图标色,仅限该场景。