@lark-apaas/coding-template-nestjs-react-fullstack 0.1.0-alpha.1

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 (250) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +41 -0
  3. package/hooks/after-install.mjs +3 -0
  4. package/hooks/after-render.mjs +3 -0
  5. package/hooks/before-render.mjs +3 -0
  6. package/package.json +36 -0
  7. package/template/.codebase/pipelines/ci.yaml +34 -0
  8. package/template/.codebase/pipelines/coco-code-review.yaml +21 -0
  9. package/template/.prettierrc +8 -0
  10. package/template/.spark_project +16 -0
  11. package/template/.stylelintrc.js +12 -0
  12. package/template/_env.local +38 -0
  13. package/template/_gitignore +64 -0
  14. package/template/_npmrc +1 -0
  15. package/template/client/index.html +23 -0
  16. package/template/client/public/favicon.svg +40 -0
  17. package/template/client/src/api/index.ts +19 -0
  18. package/template/client/src/app.tsx +22 -0
  19. package/template/client/src/components/Layout.tsx +11 -0
  20. package/template/client/src/components/business-ui/README.md +519 -0
  21. package/template/client/src/components/business-ui/api/chats/queries.ts +39 -0
  22. package/template/client/src/components/business-ui/api/chats/service.ts +29 -0
  23. package/template/client/src/components/business-ui/api/departments/queries.ts +42 -0
  24. package/template/client/src/components/business-ui/api/departments/service.ts +18 -0
  25. package/template/client/src/components/business-ui/api/files/service.ts +28 -0
  26. package/template/client/src/components/business-ui/api/user-profiles/queries.ts +42 -0
  27. package/template/client/src/components/business-ui/api/user-profiles/service.ts +23 -0
  28. package/template/client/src/components/business-ui/api/users/queries.ts +34 -0
  29. package/template/client/src/components/business-ui/api/users/service.ts +40 -0
  30. package/template/client/src/components/business-ui/chat-select/README.md +128 -0
  31. package/template/client/src/components/business-ui/chat-select/chat-item.tsx +85 -0
  32. package/template/client/src/components/business-ui/chat-select/chat-select-tag.tsx +83 -0
  33. package/template/client/src/components/business-ui/chat-select/chat-select.tsx +197 -0
  34. package/template/client/src/components/business-ui/chat-select/index.tsx +12 -0
  35. package/template/client/src/components/business-ui/chat-select/types.ts +102 -0
  36. package/template/client/src/components/business-ui/chat-select/use-chat-value.ts +180 -0
  37. package/template/client/src/components/business-ui/chat-select/utils.ts +81 -0
  38. package/template/client/src/components/business-ui/department-select/department-item.tsx +55 -0
  39. package/template/client/src/components/business-ui/department-select/department-select-field.tsx +86 -0
  40. package/template/client/src/components/business-ui/department-select/department-select-tag.tsx +56 -0
  41. package/template/client/src/components/business-ui/department-select/department-select.tsx +168 -0
  42. package/template/client/src/components/business-ui/department-select/icon-department.tsx +22 -0
  43. package/template/client/src/components/business-ui/department-select/index.tsx +7 -0
  44. package/template/client/src/components/business-ui/department-select/types.ts +15 -0
  45. package/template/client/src/components/business-ui/department-select/utils.ts +17 -0
  46. package/template/client/src/components/business-ui/entity-combobox/base-combobox-content.tsx +35 -0
  47. package/template/client/src/components/business-ui/entity-combobox/base-combobox-empty.tsx +28 -0
  48. package/template/client/src/components/business-ui/entity-combobox/base-combobox-error.tsx +35 -0
  49. package/template/client/src/components/business-ui/entity-combobox/base-combobox-item.tsx +55 -0
  50. package/template/client/src/components/business-ui/entity-combobox/base-combobox-list.tsx +174 -0
  51. package/template/client/src/components/business-ui/entity-combobox/base-combobox-loading.tsx +19 -0
  52. package/template/client/src/components/business-ui/entity-combobox/base-combobox-search.tsx +31 -0
  53. package/template/client/src/components/business-ui/entity-combobox/base-combobox-trigger.tsx +281 -0
  54. package/template/client/src/components/business-ui/entity-combobox/base-combobox.tsx +132 -0
  55. package/template/client/src/components/business-ui/entity-combobox/context.tsx +19 -0
  56. package/template/client/src/components/business-ui/entity-combobox/entity-combobox.tsx +145 -0
  57. package/template/client/src/components/business-ui/entity-combobox/highlight-text.tsx +52 -0
  58. package/template/client/src/components/business-ui/entity-combobox/hooks.tsx +21 -0
  59. package/template/client/src/components/business-ui/entity-combobox/index.tsx +31 -0
  60. package/template/client/src/components/business-ui/entity-combobox/item-pill.tsx +179 -0
  61. package/template/client/src/components/business-ui/entity-combobox/popover-wrapper.tsx +149 -0
  62. package/template/client/src/components/business-ui/entity-combobox/search-trigger.tsx +262 -0
  63. package/template/client/src/components/business-ui/entity-combobox/shared-types.ts +296 -0
  64. package/template/client/src/components/business-ui/entity-combobox/size-variants.tsx +322 -0
  65. package/template/client/src/components/business-ui/entity-combobox/types.ts +246 -0
  66. package/template/client/src/components/business-ui/entity-combobox/use-fetch-data.tsx +81 -0
  67. package/template/client/src/components/business-ui/entity-combobox/use-infinite-scroll.tsx +99 -0
  68. package/template/client/src/components/business-ui/entity-combobox/use-popover-outside-click.tsx +32 -0
  69. package/template/client/src/components/business-ui/form/checkbox-field.tsx +83 -0
  70. package/template/client/src/components/business-ui/form/context.tsx +38 -0
  71. package/template/client/src/components/business-ui/form/field-layout.tsx +91 -0
  72. package/template/client/src/components/business-ui/form/form.tsx +54 -0
  73. package/template/client/src/components/business-ui/form/hooks/form-context.tsx +4 -0
  74. package/template/client/src/components/business-ui/form/hooks/form-utils.ts +23 -0
  75. package/template/client/src/components/business-ui/form/hooks/form.tsx +40 -0
  76. package/template/client/src/components/business-ui/form/index.tsx +4 -0
  77. package/template/client/src/components/business-ui/form/input-field.tsx +74 -0
  78. package/template/client/src/components/business-ui/form/radio-group-field.tsx +138 -0
  79. package/template/client/src/components/business-ui/form/select-field.tsx +155 -0
  80. package/template/client/src/components/business-ui/form/switch-field.tsx +66 -0
  81. package/template/client/src/components/business-ui/form/textarea-field.tsx +72 -0
  82. package/template/client/src/components/business-ui/form/types.ts +32 -0
  83. package/template/client/src/components/business-ui/tiptap-editor/README.md +324 -0
  84. package/template/client/src/components/business-ui/tiptap-editor/components/attachment-toolbar-button.tsx +74 -0
  85. package/template/client/src/components/business-ui/tiptap-editor/components/blockquote-toolbar-button.tsx +41 -0
  86. package/template/client/src/components/business-ui/tiptap-editor/components/code-block-toolbar-button.tsx +41 -0
  87. package/template/client/src/components/business-ui/tiptap-editor/components/color-highlight-toolbar-button.tsx +141 -0
  88. package/template/client/src/components/business-ui/tiptap-editor/components/heading-toolbar-button.tsx +99 -0
  89. package/template/client/src/components/business-ui/tiptap-editor/components/horizontal-rule-toolbar-button.tsx +39 -0
  90. package/template/client/src/components/business-ui/tiptap-editor/components/image-upload-toolbar-button.tsx +65 -0
  91. package/template/client/src/components/business-ui/tiptap-editor/components/link-edit-form.tsx +127 -0
  92. package/template/client/src/components/business-ui/tiptap-editor/components/link-hover-toolbar.tsx +380 -0
  93. package/template/client/src/components/business-ui/tiptap-editor/components/link-toolbar-button.tsx +96 -0
  94. package/template/client/src/components/business-ui/tiptap-editor/components/list-toolbar-button.tsx +75 -0
  95. package/template/client/src/components/business-ui/tiptap-editor/components/mark-toolbar-button.tsx +118 -0
  96. package/template/client/src/components/business-ui/tiptap-editor/components/text-align-toolbar-button.tsx +84 -0
  97. package/template/client/src/components/business-ui/tiptap-editor/components/undo-redo-toolbar-button.tsx +54 -0
  98. package/template/client/src/components/business-ui/tiptap-editor/extensions/attachment.tsx +539 -0
  99. package/template/client/src/components/business-ui/tiptap-editor/extensions/code-block-shiki.tsx +236 -0
  100. package/template/client/src/components/business-ui/tiptap-editor/extensions/complete-kit.ts +260 -0
  101. package/template/client/src/components/business-ui/tiptap-editor/extensions/image.tsx +456 -0
  102. package/template/client/src/components/business-ui/tiptap-editor/hooks/use-tiptap-editor.ts +47 -0
  103. package/template/client/src/components/business-ui/tiptap-editor/index.ts +38 -0
  104. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor-complete.tsx +112 -0
  105. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor.tsx +169 -0
  106. package/template/client/src/components/business-ui/types/user.ts +74 -0
  107. package/template/client/src/components/business-ui/user-display/index.tsx +2 -0
  108. package/template/client/src/components/business-ui/user-display/overflow-tooltip-text.tsx +90 -0
  109. package/template/client/src/components/business-ui/user-display/type.ts +27 -0
  110. package/template/client/src/components/business-ui/user-display/user-display.tsx +99 -0
  111. package/template/client/src/components/business-ui/user-display/user-with-avatar.tsx +158 -0
  112. package/template/client/src/components/business-ui/user-display/utils.ts +37 -0
  113. package/template/client/src/components/business-ui/user-profile/error-image.tsx +302 -0
  114. package/template/client/src/components/business-ui/user-profile/user-external-script.ts +73 -0
  115. package/template/client/src/components/business-ui/user-profile/user-profile.tsx +370 -0
  116. package/template/client/src/components/business-ui/user-select/index.tsx +28 -0
  117. package/template/client/src/components/business-ui/user-select/types.ts +109 -0
  118. package/template/client/src/components/business-ui/user-select/use-user-value.ts +160 -0
  119. package/template/client/src/components/business-ui/user-select/user-item.tsx +98 -0
  120. package/template/client/src/components/business-ui/user-select/user-pill.tsx +140 -0
  121. package/template/client/src/components/business-ui/user-select/user-select-tag.tsx +82 -0
  122. package/template/client/src/components/business-ui/user-select/user-select.tsx +359 -0
  123. package/template/client/src/components/business-ui/user-select/utils.tsx +188 -0
  124. package/template/client/src/components/business-ui/utils/user.ts +72 -0
  125. package/template/client/src/components/ui/README.md +134 -0
  126. package/template/client/src/components/ui/accordion.tsx +66 -0
  127. package/template/client/src/components/ui/alert-dialog.tsx +157 -0
  128. package/template/client/src/components/ui/alert.tsx +71 -0
  129. package/template/client/src/components/ui/aspect-ratio.tsx +11 -0
  130. package/template/client/src/components/ui/avatar.tsx +53 -0
  131. package/template/client/src/components/ui/badge.tsx +42 -0
  132. package/template/client/src/components/ui/breadcrumb.tsx +109 -0
  133. package/template/client/src/components/ui/button-group.tsx +83 -0
  134. package/template/client/src/components/ui/button.tsx +69 -0
  135. package/template/client/src/components/ui/calendar.tsx +213 -0
  136. package/template/client/src/components/ui/card.tsx +82 -0
  137. package/template/client/src/components/ui/carousel.tsx +241 -0
  138. package/template/client/src/components/ui/chart.tsx +357 -0
  139. package/template/client/src/components/ui/checkbox.tsx +32 -0
  140. package/template/client/src/components/ui/collapsible.tsx +33 -0
  141. package/template/client/src/components/ui/command.tsx +208 -0
  142. package/template/client/src/components/ui/context-menu.tsx +324 -0
  143. package/template/client/src/components/ui/dialog.tsx +143 -0
  144. package/template/client/src/components/ui/drawer.tsx +135 -0
  145. package/template/client/src/components/ui/dropdown-menu.tsx +329 -0
  146. package/template/client/src/components/ui/empty.tsx +104 -0
  147. package/template/client/src/components/ui/field.tsx +248 -0
  148. package/template/client/src/components/ui/form.tsx +167 -0
  149. package/template/client/src/components/ui/hover-card.tsx +44 -0
  150. package/template/client/src/components/ui/icons/file-ae-colorful-icon.tsx +21 -0
  151. package/template/client/src/components/ui/icons/file-ai-colorful-icon.tsx +36 -0
  152. package/template/client/src/components/ui/icons/file-android-colorful-icon.tsx +33 -0
  153. package/template/client/src/components/ui/icons/file-audio-colorful-icon.tsx +21 -0
  154. package/template/client/src/components/ui/icons/file-code-colorful-icon.tsx +28 -0
  155. package/template/client/src/components/ui/icons/file-csv-colorful-icon.tsx +21 -0
  156. package/template/client/src/components/ui/icons/file-eml-colorful-icon.tsx +29 -0
  157. package/template/client/src/components/ui/icons/file-ios-colorful-icon.tsx +25 -0
  158. package/template/client/src/components/ui/icons/file-keynote-colorful-icon.tsx +29 -0
  159. package/template/client/src/components/ui/icons/file-pages-colorful-icon.tsx +29 -0
  160. package/template/client/src/components/ui/icons/file-ps-colorful-icon.tsx +21 -0
  161. package/template/client/src/components/ui/icons/file-sketch-colorful-icon.tsx +21 -0
  162. package/template/client/src/components/ui/icons/file-slide-colorful-icon.tsx +21 -0
  163. package/template/client/src/components/ui/icons/file-vcf-colorful-icon.tsx +29 -0
  164. package/template/client/src/components/ui/icons/file-wiki-excel-colorful-icon.tsx +23 -0
  165. package/template/client/src/components/ui/icons/file-wiki-image-colorful-icon.tsx +27 -0
  166. package/template/client/src/components/ui/icons/file-wiki-pdf-colorful-icon.tsx +20 -0
  167. package/template/client/src/components/ui/icons/file-wiki-ppt-colorful-icon.tsx +21 -0
  168. package/template/client/src/components/ui/icons/file-wiki-text-colorful-icon.tsx +12 -0
  169. package/template/client/src/components/ui/icons/file-wiki-unknown-colorful-icon.tsx +14 -0
  170. package/template/client/src/components/ui/icons/file-wiki-video-colorful-icon.tsx +23 -0
  171. package/template/client/src/components/ui/icons/file-wiki-word-colorful-icon.tsx +38 -0
  172. package/template/client/src/components/ui/icons/file-wiki-zip-colorful-icon.tsx +21 -0
  173. package/template/client/src/components/ui/image.tsx +183 -0
  174. package/template/client/src/components/ui/input-group.tsx +166 -0
  175. package/template/client/src/components/ui/input-otp.tsx +77 -0
  176. package/template/client/src/components/ui/input.tsx +21 -0
  177. package/template/client/src/components/ui/item.tsx +193 -0
  178. package/template/client/src/components/ui/kbd.tsx +28 -0
  179. package/template/client/src/components/ui/label.tsx +24 -0
  180. package/template/client/src/components/ui/menubar.tsx +348 -0
  181. package/template/client/src/components/ui/native-select.tsx +48 -0
  182. package/template/client/src/components/ui/navigation-menu.tsx +168 -0
  183. package/template/client/src/components/ui/pagination.tsx +127 -0
  184. package/template/client/src/components/ui/popover.tsx +48 -0
  185. package/template/client/src/components/ui/progress.tsx +31 -0
  186. package/template/client/src/components/ui/radio-group.tsx +45 -0
  187. package/template/client/src/components/ui/resizable.tsx +56 -0
  188. package/template/client/src/components/ui/scroll-area.tsx +58 -0
  189. package/template/client/src/components/ui/select.tsx +243 -0
  190. package/template/client/src/components/ui/separator.tsx +28 -0
  191. package/template/client/src/components/ui/sheet.tsx +139 -0
  192. package/template/client/src/components/ui/sidebar.tsx +727 -0
  193. package/template/client/src/components/ui/skeleton.tsx +13 -0
  194. package/template/client/src/components/ui/slider.tsx +87 -0
  195. package/template/client/src/components/ui/sonner.tsx +67 -0
  196. package/template/client/src/components/ui/spinner.tsx +16 -0
  197. package/template/client/src/components/ui/streamdown.tsx +186 -0
  198. package/template/client/src/components/ui/switch.tsx +31 -0
  199. package/template/client/src/components/ui/table.tsx +116 -0
  200. package/template/client/src/components/ui/tabs.tsx +66 -0
  201. package/template/client/src/components/ui/textarea.tsx +18 -0
  202. package/template/client/src/components/ui/toggle-group.tsx +83 -0
  203. package/template/client/src/components/ui/toggle.tsx +47 -0
  204. package/template/client/src/components/ui/tooltip.tsx +61 -0
  205. package/template/client/src/hooks/use-example.ts +52 -0
  206. package/template/client/src/hooks/use-mobile.ts +19 -0
  207. package/template/client/src/index.css +7 -0
  208. package/template/client/src/index.tsx +36 -0
  209. package/template/client/src/lib/shiki.ts +92 -0
  210. package/template/client/src/lib/utils.ts +6 -0
  211. package/template/client/src/pages/ExamplePage/ExamplePage.tsx +37 -0
  212. package/template/client/src/pages/NotFound/NotFound.tsx +11 -0
  213. package/template/client/src/tailwind-theme.css +257 -0
  214. package/template/client/src/types/common.ts +34 -0
  215. package/template/client/src/types/global.d.ts +53 -0
  216. package/template/client/src/types/index.ts +4 -0
  217. package/template/client/src/typography.css +41 -0
  218. package/template/client/src/utils/img-resources/avatar-placeholders.ts +12 -0
  219. package/template/client/src/utils/img-resources/banner-placeholders.ts +29 -0
  220. package/template/client/src/utils/img-resources/cover-placeholders.ts +47 -0
  221. package/template/components.json +21 -0
  222. package/template/eslint.config.js +50 -0
  223. package/template/nest-cli.json +25 -0
  224. package/template/package-lock.json +29195 -0
  225. package/template/package.json +202 -0
  226. package/template/postcss.config.js +10 -0
  227. package/template/scripts/build.sh +120 -0
  228. package/template/scripts/dev.js +295 -0
  229. package/template/scripts/dev.sh +2 -0
  230. package/template/scripts/lint.js +150 -0
  231. package/template/scripts/prune-smart.js +281 -0
  232. package/template/scripts/run.sh +5 -0
  233. package/template/server/app.module.ts +27 -0
  234. package/template/server/common/constants/api_response_code.ts +54 -0
  235. package/template/server/common/filters/exception.filter.ts +78 -0
  236. package/template/server/common/interfaces/api_response.interface.ts +21 -0
  237. package/template/server/common/interfaces/exception.interface.ts +19 -0
  238. package/template/server/database/schema.ts +90 -0
  239. package/template/server/main.ts +31 -0
  240. package/template/server/modules/hello/hello.controller.ts +16 -0
  241. package/template/server/modules/hello/hello.module.ts +10 -0
  242. package/template/server/modules/hello/hello.service.ts +28 -0
  243. package/template/server/modules/view/view.controller.ts +17 -0
  244. package/template/server/modules/view/view.module.ts +8 -0
  245. package/template/shared/api.interface.ts +1 -0
  246. package/template/tailwind.config.ts +9 -0
  247. package/template/tsconfig.app.json +23 -0
  248. package/template/tsconfig.json +8 -0
  249. package/template/tsconfig.node.json +16 -0
  250. package/template/vite.config.ts +10 -0
@@ -0,0 +1,519 @@
1
+ # 业务组件文档
2
+
3
+ 本文档包含用户选择、部门选择、群组选择、用户展示等业务组件的使用说明。
4
+
5
+ ---
6
+
7
+ ## 目录
8
+
9
+ - [类型统一规范](#类型统一规范)
10
+ - [UserSelect 用户选择组件](#userselect-用户选择组件)
11
+ - [DepartmentSelect 部门选择组件](#departmentselect-部门选择组件)
12
+ - [ChatSelect 群组选择组件](#chatselect-群组选择组件)
13
+ - [UserDisplay 用户展示组件](#userdisplay-用户展示组件)
14
+ - [UserProfile 用户详情组件](#userprofile-用户详情组件)
15
+
16
+ ---
17
+
18
+ ## 类型统一规范
19
+
20
+ ### 部门类型
21
+
22
+ ```typescript
23
+ /** 部门数据结构 */
24
+ interface Department {
25
+ /** 唯一标识:用于 key/去重/选中判断,内部对应 departmentID */
26
+ id: string;
27
+ /** 展示名称:内部优先 zh_cn,其次 en_us */
28
+ name: string;
29
+ }
30
+ ```
31
+
32
+ ### 群组类型
33
+
34
+ ```typescript
35
+ /** 群组数据结构 */
36
+ interface Chat {
37
+ /** 唯一标识:对应 chatID */
38
+ id: string;
39
+ /** 群组名称:内部优先 zh_cn,其次 en_us */
40
+ name: string;
41
+ /** 头像:URL 或 16 进制 RGB 颜色 */
42
+ avatar: string;
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## UserSelect 用户选择组件
49
+
50
+ **组件路径**:`@/components/business-ui/user-select`
51
+
52
+ ### 功能概述
53
+
54
+ - 用户字段选择组件,用于表单中录入"人员"字段
55
+ - 通过触发器打开弹层,弹层内置搜索功能(始终可用)
56
+ - 仅支持 **ID 模式**:`value/onChange` 只使用用户 `user_id`(string)
57
+ - 组件在 UI 上可展示用户信息(头像/名称),但对外值仅返回/接收 `id`
58
+ - 组件自带用户检索能力,无需额外传搜索接口/数据源
59
+
60
+ ### 类型定义
61
+
62
+ ```typescript
63
+
64
+ /** 触发器类型 */
65
+ type TriggerType = 'button' | 'search' | 'custom';
66
+
67
+ /** 单选模式 Props */
68
+ interface UserSelectSingleProps {
69
+ /** 当前选中的用户 ID,空值为 null */
70
+ value: string | null;
71
+ /** 选中变化回调 */
72
+ onChange: (value: string | null) => void;
73
+ /** 是否多选,单选时为 false 或不传 */
74
+ multiple?: false;
75
+ /** 触发器类型,仅影响样式,不影响搜索功能 */
76
+ triggerType?: TriggerType;
77
+ /** 是否禁用 */
78
+ disabled?: boolean;
79
+ /** 占位文本 */
80
+ placeholder?: string;
81
+ }
82
+
83
+ /** 多选模式 Props */
84
+ interface UserSelectMultipleProps {
85
+ /** 当前选中的用户 ID 列表,空值为 [] */
86
+ value: string[];
87
+ /** 选中变化回调 */
88
+ onChange: (value: string[]) => void;
89
+ /** 是否多选,多选时为 true */
90
+ multiple: true;
91
+ /** 触发器类型 */
92
+ triggerType?: TriggerType;
93
+ /** 是否禁用 */
94
+ disabled?: boolean;
95
+ /** 占位文本 */
96
+ placeholder?: string;
97
+ }
98
+
99
+ type UserSelectProps = UserSelectSingleProps | UserSelectMultipleProps;
100
+ ```
101
+
102
+ ### Props 说明
103
+
104
+ | 属性 | 类型 | 默认值 | 说明 |
105
+ |------|------|--------|------|
106
+ | `value` | `string \| null` / `string[]` | - | **必填**。单选传 `string \| null`(用户 ID),多选传 `string[]` |
107
+ | `onChange` | `function` | - | **必填**。值变化回调,返回值形态与 `value` 匹配 |
108
+ | `multiple` | `boolean` | `false` | 是否多选 |
109
+ | `triggerType` | `'button' \| 'search' \| 'custom'` | `'button'` | 触发器类型,仅影响样式 |
110
+ | `disabled` | `boolean` | `false` | 是否禁用 |
111
+ | `placeholder` | `string` | `'请选择'` | 占位文本 |
112
+
113
+ ### 受控规范
114
+
115
+ > ⚠️ **强约束**
116
+
117
+ 1. **必须受控**:始终传 `value` + `onChange`
118
+ 2. **禁止传**:`undefined` / `User` 对象 / 其他类型
119
+ 3. **空值规则**:
120
+ - 单选清空:`null`
121
+ - 多选清空:`[]`
122
+
123
+ ### 使用示例
124
+
125
+ ```tsx
126
+ import { useState } from 'react';
127
+ import { UserSelect } from '@/components/business-ui/user-select';
128
+
129
+ // 单选
130
+ function SingleSelectExample() {
131
+ const [userId, setUserId] = useState<string | null>(null);
132
+
133
+ return (
134
+ <UserSelect
135
+ value={userId}
136
+ onChange={setUserId}
137
+ placeholder="请选择用户"
138
+ />
139
+ );
140
+ }
141
+
142
+ // 多选
143
+ function MultipleSelectExample() {
144
+ const [userIds, setUserIds] = useState<string[]>([]);
145
+
146
+ return (
147
+ <UserSelect
148
+ multiple
149
+ value={userIds}
150
+ onChange={setUserIds}
151
+ placeholder="请选择用户"
152
+ getOptionDisabled={(user) => user.id === 'disabled-id'}
153
+ />
154
+ );
155
+ }
156
+ ```
157
+
158
+ ---
159
+
160
+ ## DepartmentSelect 部门选择组件
161
+
162
+ **组件路径**:`@/components/business-ui/department-select`
163
+
164
+ ### 功能概述
165
+
166
+ - 部门字段选择组件,用于表单中录入"部门/组织"字段
167
+ - 通过触发器打开弹层,弹层内置搜索功能(始终可用)
168
+ - 仅支持**对象模式**(不支持传 ID/string)
169
+ - 选择后自动回显已选部门信息:部门名称(多选以 tag 形式展示)
170
+ - 组件自带部门检索能力,无需额外传搜索接口/数据源
171
+
172
+ ### 类型定义
173
+
174
+ ```typescript
175
+
176
+ /** 触发器类型 */
177
+ type TriggerType = 'button' | 'search' | 'custom';
178
+
179
+ /** 单选模式 Props */
180
+ interface DepartmentSelectSingleProps {
181
+ /** 当前选中的部门 */
182
+ value: Department | null;
183
+ /** 选中变化回调 */
184
+ onChange: (value: Department | null) => void;
185
+ /** 是否多选,单选时为 false 或不传 */
186
+ multiple?: false;
187
+ /** 触发器类型,仅影响样式,不影响搜索功能 */
188
+ triggerType?: TriggerType;
189
+ /** 是否禁用 */
190
+ disabled?: boolean;
191
+ /** 占位文本 */
192
+ placeholder?: string;
193
+ /** 判断选项是否禁用 */
194
+ getOptionDisabled?: (department: Department) => boolean;
195
+ }
196
+
197
+ /** 多选模式 Props */
198
+ interface DepartmentSelectMultipleProps {
199
+ /** 当前选中的部门列表 */
200
+ value: Department[];
201
+ /** 选中变化回调 */
202
+ onChange: (value: Department[]) => void;
203
+ /** 是否多选,多选时为 true */
204
+ multiple: true;
205
+ /** 触发器类型 */
206
+ triggerType?: TriggerType;
207
+ /** 是否禁用 */
208
+ disabled?: boolean;
209
+ /** 占位文本 */
210
+ placeholder?: string;
211
+ /** 判断选项是否禁用 */
212
+ getOptionDisabled?: (department: Department) => boolean;
213
+ }
214
+
215
+ type DepartmentSelectProps = DepartmentSelectSingleProps | DepartmentSelectMultipleProps;
216
+ ```
217
+
218
+ ### Props 说明
219
+
220
+ | 属性 | 类型 | 默认值 | 说明 |
221
+ |------|------|--------|------|
222
+ | `value` | `Department \| null` / `Department[]` | - | **必填**。单选传 `Department \| null`,多选传 `Department[]` |
223
+ | `onChange` | `function` | - | **必填**。值变化回调,返回值形态与 `value` 匹配 |
224
+ | `multiple` | `boolean` | `false` | 是否多选 |
225
+ | `triggerType` | `'button' \| 'search' \| 'custom'` | `'button'` | 触发器类型,仅影响样式 |
226
+ | `disabled` | `boolean` | `false` | 是否禁用 |
227
+ | `placeholder` | `string` | `'请选择部门'` | 占位文本 |
228
+ | `getOptionDisabled` | `(department: Department) => boolean` | - | 判断选项是否禁用,被禁用的已选项 tag 不显示删除按钮 |
229
+
230
+ ### 受控规范
231
+
232
+ > ⚠️ **强约束**
233
+
234
+ 1. **必须受控**:始终传 `value` + `onChange`
235
+ 2. **禁止传**:`undefined` / `string` / `string[]`
236
+ 3. **空值规则**:
237
+ - 单选清空:`null`
238
+ - 多选清空:`[]`
239
+
240
+ ### 使用示例
241
+
242
+ ```tsx
243
+ import { useState } from 'react';
244
+ import { DepartmentSelect, type Department } from '@/components/business-ui/department-select';
245
+
246
+ // 单选
247
+ function SingleSelectExample() {
248
+ const [department, setDepartment] = useState<Department | null>(null);
249
+
250
+ return (
251
+ <DepartmentSelect
252
+ value={department}
253
+ onChange={setDepartment}
254
+ placeholder="请选择部门"
255
+ />
256
+ );
257
+ }
258
+
259
+ // 多选
260
+ function MultipleSelectExample() {
261
+ const [departments, setDepartments] = useState<Department[]>([]);
262
+
263
+ return (
264
+ <DepartmentSelect
265
+ multiple
266
+ value={departments}
267
+ onChange={setDepartments}
268
+ placeholder="请选择部门"
269
+ />
270
+ );
271
+ }
272
+ ```
273
+
274
+ ---
275
+
276
+ ## ChatSelect 群组选择组件
277
+
278
+ **组件路径**:`@/components/business-ui/chat-select`
279
+
280
+ ### 功能概述
281
+
282
+ - 群组字段选择组件,用于表单中录入"群组"字段
283
+ - 通过触发器打开弹层,弹层内置搜索功能(始终可用)
284
+ - **仅支持 ID 模式**:`value/onChange` 只使用群组 `chatID`(string),该 ID 为飞书群聊 ID
285
+ - 组件在 UI 上可展示群组信息(头像/名称),但对外值仅返回/接收 `chatID`
286
+ - 选择后自动回显已选群组信息:群头像 + 群名称(多选以 tag 形式展示)
287
+ - 组件自带群组检索能力,无需额外传搜索接口/数据源
288
+
289
+ ### 类型定义
290
+
291
+ ```typescript
292
+ /** 触发器类型 */
293
+ type TriggerType = 'button' | 'search' | 'custom';
294
+
295
+ /** 单选模式 Props */
296
+ interface ChatSelectSingleProps {
297
+ /** 当前选中的群组 ID,空值为 null */
298
+ value: string | null;
299
+ /** 选中变化回调 */
300
+ onChange: (value: string | null) => void;
301
+ /** 是否多选,单选时为 false 或不传 */
302
+ multiple?: false;
303
+ /** 触发器类型,仅影响样式,不影响搜索功能 */
304
+ triggerType?: TriggerType;
305
+ /** 是否禁用 */
306
+ disabled?: boolean;
307
+ /** 占位文本 */
308
+ placeholder?: string;
309
+ }
310
+
311
+ /** 多选模式 Props */
312
+ interface ChatSelectMultipleProps {
313
+ /** 当前选中的群组 ID 列表,空值为 [] */
314
+ value: string[];
315
+ /** 选中变化回调 */
316
+ onChange: (value: string[]) => void;
317
+ /** 是否多选,多选时为 true */
318
+ multiple: true;
319
+ /** 触发器类型 */
320
+ triggerType?: TriggerType;
321
+ /** 是否禁用 */
322
+ disabled?: boolean;
323
+ /** 占位文本 */
324
+ placeholder?: string;
325
+ }
326
+
327
+ type ChatSelectProps = ChatSelectSingleProps | ChatSelectMultipleProps;
328
+ ```
329
+
330
+ ### Props 说明
331
+
332
+ | 属性 | 类型 | 默认值 | 说明 |
333
+ |------|------|--------|------|
334
+ | `value` | `string \| null` / `string[]` | - | **必填**。单选传 `string \| null`(群组 ID),多选传 `string[]` |
335
+ | `onChange` | `function` | - | **必填**。值变化回调,返回值形态与 `value` 匹配 |
336
+ | `multiple` | `boolean` | `false` | 是否多选 |
337
+ | `triggerType` | `'button' \| 'search' \| 'custom'` | `'button'` | 触发器类型,仅影响样式 |
338
+ | `disabled` | `boolean` | `false` | 是否禁用 |
339
+ | `placeholder` | `string` | `'请选择'` | 占位文本 |
340
+ | `getOptionDisabled` | `(chat) => boolean` | - | 判断选项是否禁用 |
341
+
342
+ ### 受控规范
343
+
344
+ > ⚠️ **强约束**
345
+
346
+ 1. **必须受控**:始终传 `value` + `onChange`
347
+ 2. **禁止传**:`undefined` / 群组对象 / 其他类型
348
+ 3. **空值规则**:
349
+ - 单选清空:`null`
350
+ - 多选清空:`[]`
351
+
352
+ ### 使用示例
353
+
354
+ ```tsx
355
+ import { useState } from 'react';
356
+ import { ChatSelect } from '@/components/business-ui/chat-select';
357
+
358
+ // 单选
359
+ function SingleSelectExample() {
360
+ const [chatId, setChatId] = useState<string | null>(null);
361
+
362
+ return (
363
+ <ChatSelect
364
+ value={chatId}
365
+ onChange={setChatId}
366
+ placeholder="请选择群组"
367
+ />
368
+ );
369
+ }
370
+
371
+ // 多选
372
+ function MultipleSelectExample() {
373
+ const [chatIds, setChatIds] = useState<string[]>([]);
374
+
375
+ return (
376
+ <ChatSelect
377
+ multiple
378
+ value={chatIds}
379
+ onChange={setChatIds}
380
+ placeholder="请选择群组"
381
+ getOptionDisabled={(chat) => chat.id === 'disabled-id'}
382
+ />
383
+ );
384
+ }
385
+ ```
386
+
387
+ ---
388
+
389
+ ## UserDisplay 用户展示组件
390
+
391
+ **组件路径**:`@/components/business-ui/user-display`
392
+
393
+ ### 功能概述
394
+
395
+ - 用于所有用户信息的展示场景
396
+ - 显示用户**头像**和**姓名**
397
+ - 支持单用户和多用户展示
398
+ - 点击用户会弹出 `UserProfile` 卡片
399
+ - 当没有用户的 name 和 avatar 时,只要确保传递了 `user_id` 即可,组件会自动查询用户信息
400
+
401
+ > ⚠️ **注意**:当不传递 `showLabel` 时,组件会同时展示用户头像和姓名。如果只需要头像,需要传递 `showLabel={false}`
402
+
403
+ ### 类型定义
404
+
405
+ ```typescript
406
+
407
+ /** 头像尺寸 */
408
+ type AvatarSize = 'small' | 'medium' | 'large';
409
+
410
+ /** UserDisplay Props */
411
+ interface IUserDisplayProps {
412
+ /** 用户 ID */
413
+ value?: string[];
414
+ /** 头像尺寸 */
415
+ size?: AvatarSize;
416
+ /** 自定义样式类名 */
417
+ className?: string;
418
+ /** 自定义内联样式 */
419
+ style?: React.CSSProperties;
420
+ /** 是否显示用户姓名,默认 true。设为 false 则只展示头像 */
421
+ showLabel?: boolean;
422
+ }
423
+ ```
424
+
425
+ ### Props 说明
426
+
427
+ | 属性 | 类型 | 默认值 | 说明 |
428
+ |------|------|--------|------|
429
+ | `value` | `string[]` | - | 用户 ID 数组 |
430
+ | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | 头像尺寸 |
431
+ | `className` | `string` | - | 自定义样式类名 |
432
+ | `style` | `React.CSSProperties` | - | 自定义内联样式 |
433
+ | `showLabel` | `boolean` | `true` | 是否显示用户姓名,设为 `false` 只展示头像 |
434
+
435
+ ### 使用示例
436
+
437
+ ```tsx
438
+ import { UserDisplay } from '@/components/business-ui/user-display';
439
+
440
+ // 单用户展示 - 使用 ID
441
+ function SingleUserExample() {
442
+ return (
443
+ <UserDisplay
444
+ value={["user-123"]}
445
+ size="small"
446
+ />
447
+ );
448
+ }
449
+
450
+ // 多用户展示 - 使用 ID 数组
451
+ function MultipleUsersExample() {
452
+ const userIds: string[] = ['1', '2', '3'];
453
+
454
+ return (
455
+ <UserDisplay
456
+ value={userIds}
457
+ size="medium"
458
+ className="project-members"
459
+ />
460
+ );
461
+ }
462
+
463
+ // 仅展示头像
464
+ function AvatarOnlyExample() {
465
+ return (
466
+ <UserDisplay
467
+ value={["user-123"]}
468
+ showLabel={false}
469
+ />
470
+ );
471
+ }
472
+ ```
473
+
474
+ ### 最佳实践
475
+
476
+ - 根据展示场景选择合适的组件尺寸
477
+ - 对于用户信息`user_id`,**禁止直接展示文本**,总是使用 `UserDisplay` 组件展示
478
+ - 对于需要显示完整用户详情的场景,使用 `UserProfile` 组件
479
+
480
+ ---
481
+
482
+ ## UserProfile 用户详情组件
483
+
484
+ **组件路径**:`@/components/business-ui/user-profile/user-profile`
485
+
486
+ ### 功能概述
487
+
488
+ - 用于显示用户详细信息卡片
489
+ - 支持两种显示模式:简单信息卡片和飞书官方用户卡片
490
+ - 自动处理用户状态和类型标识
491
+ - 提供邮箱链接功能
492
+ - 支持错误重试机制
493
+
494
+ ### 类型定义
495
+
496
+ ```typescript
497
+ /** UserProfile Props */
498
+ interface UserProfileProps {
499
+ /** 用户 ID*/
500
+ value?: string;
501
+ }
502
+ ```
503
+
504
+ ### Props 说明
505
+
506
+ | 属性 | 类型 | 默认值 | 说明 |
507
+ |------|------|--------|------|
508
+ | `value` | `string` | - | 用户 ID |
509
+
510
+ ### 使用示例
511
+
512
+ ```tsx
513
+ import { UserProfile } from '@/components/business-ui/user-profile/user-profile';
514
+
515
+ // 基础用法 - 使用字符串 ID
516
+ function BasicExample() {
517
+ return <UserProfile value="user-123" />;
518
+ }
519
+ ```
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+
3
+ import { useQuery, queryOptions, type QueryClient } from '@tanstack/react-query';
4
+
5
+ import { listChatsByIds, searchChats } from './service';
6
+
7
+ export const chatQueries = {
8
+ all: () => ['chats'] as const,
9
+ search: (query: string, pageSize: number = 100) =>
10
+ queryOptions({
11
+ queryKey: [...chatQueries.all(), 'search', query, pageSize],
12
+ queryFn: () => searchChats({ query, pageSize }),
13
+ staleTime: 1 * 60 * 1000,
14
+ gcTime: 5 * 60 * 1000,
15
+ enabled: query.length > 0,
16
+ }),
17
+ byIds: (chatIds: string[]) =>
18
+ queryOptions({
19
+ queryKey: [...chatQueries.all(), 'byIds', chatIds.join(',')],
20
+ queryFn: () => listChatsByIds(chatIds),
21
+ staleTime: 1 * 60 * 1000,
22
+ gcTime: 5 * 60 * 1000,
23
+ enabled: chatIds.length > 0,
24
+ }),
25
+ };
26
+
27
+ /**
28
+ * 批量获取群组信息的 hook
29
+ */
30
+ export function useChatsByIds(chatIds: string[]) {
31
+ return useQuery(chatQueries.byIds(chatIds));
32
+ }
33
+
34
+ /**
35
+ * 清除群组缓存
36
+ */
37
+ export function clearChatCache(queryClient: QueryClient): void {
38
+ queryClient.invalidateQueries({ queryKey: chatQueries.all() });
39
+ }
@@ -0,0 +1,29 @@
1
+ import {
2
+ ChatService,
3
+ type BatchGetChatsResponse,
4
+ type ChatInfo,
5
+ type SearchChatsParams,
6
+ type SearchChatsResponse,
7
+ } from '@lark-apaas/client-toolkit/tools/services';
8
+
9
+ const chatService = new ChatService();
10
+
11
+ /**
12
+ * 搜索群组
13
+ */
14
+ export async function searchChats(
15
+ params: SearchChatsParams,
16
+ ): Promise<SearchChatsResponse> {
17
+ return chatService.searchChats(params);
18
+ }
19
+
20
+ /**
21
+ * 批量根据群组 ID 查询群组信息
22
+ */
23
+ export async function listChatsByIds(
24
+ chatIds: string[],
25
+ ): Promise<BatchGetChatsResponse> {
26
+ return chatService.listChatsByIds(chatIds);
27
+ }
28
+
29
+ export type { BatchGetChatsResponse, ChatInfo, SearchChatsParams, SearchChatsResponse };
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { useQuery, queryOptions, type QueryClient } from '@tanstack/react-query';
4
+
5
+ import { searchDepartments } from './service';
6
+
7
+ export const departmentQueries = {
8
+ all: () => ['departments'] as const,
9
+ search: (query: string, pageSize: number = 100) =>
10
+ queryOptions({
11
+ queryKey: [...departmentQueries.all(), 'search', query, pageSize],
12
+ queryFn: () => searchDepartments({ query, pageSize }),
13
+ staleTime: 1 * 60 * 1000,
14
+ gcTime: 5 * 60 * 1000,
15
+ enabled: query.length > 0,
16
+ }),
17
+ };
18
+
19
+ export type UseSearchDepartmentsOptions = {
20
+ query: string;
21
+ pageSize?: number;
22
+ enabled?: boolean;
23
+ };
24
+
25
+ /**
26
+ * 搜索部门的 hook
27
+ */
28
+ export function useSearchDepartments(options: UseSearchDepartmentsOptions) {
29
+ const { query, pageSize = 100, enabled = true } = options;
30
+
31
+ return useQuery({
32
+ ...departmentQueries.search(query, pageSize),
33
+ enabled: enabled && query.length > 0,
34
+ });
35
+ }
36
+
37
+ /**
38
+ * 清除部门缓存
39
+ */
40
+ export function clearDepartmentCache(queryClient: QueryClient): void {
41
+ queryClient.invalidateQueries({ queryKey: departmentQueries.all() });
42
+ }
@@ -0,0 +1,18 @@
1
+ import {
2
+ DepartmentService,
3
+ type SearchDepartmentsParams,
4
+ type SearchDepartmentsResponse,
5
+ } from '@lark-apaas/client-toolkit/tools/services';
6
+
7
+ const departmentService = new DepartmentService();
8
+
9
+ /**
10
+ * 搜索部门
11
+ */
12
+ export async function searchDepartments(
13
+ params: SearchDepartmentsParams,
14
+ ): Promise<SearchDepartmentsResponse> {
15
+ return departmentService.searchDepartments(params);
16
+ }
17
+
18
+ export type { SearchDepartmentsParams, SearchDepartmentsResponse };
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+ import { getDataloom } from '@lark-apaas/client-toolkit/dataloom';
3
+ import { getDefaultBucketId } from '@lark-apaas/client-toolkit/tools/storage';
4
+
5
+ export interface UploadFileData {
6
+ id: string;
7
+ filePath: string;
8
+ bucketId: string;
9
+ url: string;
10
+ }
11
+
12
+ export async function uploadFile(file: File): Promise<UploadFileData> {
13
+ const dataloom = await getDataloom();
14
+ const bucket = dataloom.storage.from(getDefaultBucketId());
15
+
16
+ const result = await bucket.uploadFile(file);
17
+
18
+ if (result.error) {
19
+ throw result.error;
20
+ }
21
+
22
+ return {
23
+ id: result.data.id,
24
+ filePath: result.data.file_path,
25
+ bucketId: result.data.bucket_id,
26
+ url: result.data.download_url,
27
+ };
28
+ }