@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,324 @@
1
+ # Tiptap Editor
2
+
3
+ > 基于 [Tiptap](https://tiptap.dev) 构建的富文本编辑器组件,提供开箱即用和可组合两种使用模式。
4
+
5
+ ## 快速开始
6
+
7
+ ### 推荐:使用 `TiptapEditorComplete`
8
+
9
+ 对于大多数场景,直接使用 `TiptapEditorComplete` 即可。它封装了完整的工具栏和样式,开箱即用。
10
+
11
+ ```tsx
12
+ import { TiptapEditorComplete } from '@/components/business-ui/tiptap-editor';
13
+
14
+ function MyEditor() {
15
+ const [value, setValue] = React.useState('');
16
+
17
+ return (
18
+ <TiptapEditorComplete
19
+ value={value}
20
+ onValueChange={setValue}
21
+ placeholder="请输入内容..."
22
+ />
23
+ );
24
+ }
25
+ ```
26
+
27
+ #### `TiptapEditorComplete` Props
28
+
29
+ | Prop | 类型 | 默认值 | 描述 |
30
+ | :-------------- | :------------------------ | :----- | :----------------------- |
31
+ | `value` | `string` | - | 受控值 (HTML 字符串) |
32
+ | `defaultValue` | `string` | - | 非受控默认值 |
33
+ | `onValueChange` | `(value: string) => void` | - | 内容变更回调 |
34
+ | `placeholder` | `string` | - | 占位符文本 |
35
+ | `readOnly` | `boolean` | false | 只读模式 |
36
+ | `aria-disabled` | `boolean` | - | 禁用状态 |
37
+ | `aria-invalid` | `boolean` | - | 无效状态(用于表单校验) |
38
+ | `className` | `string` | - | 自定义样式类 |
39
+
40
+ #### ⚠️ 样式注意事项
41
+
42
+ **组件已包含完整的边框、内边距、Hover 边框、聚焦环等等丰富且完整的视觉样式,请勿在外层添加额外包装!**
43
+
44
+ ```tsx
45
+ // ❌ 错误:不要用 div 包裹并添加 border、padding、overflow-auto 等样式
46
+ // 例如这段代码会导致展示多层 border,编辑器聚焦时聚焦环被截断等问题
47
+ <div className="border rounded-lg overflow-auto">
48
+ <TiptapEditorComplete ... />
49
+ </div>
50
+
51
+ // ✅ 正确:直接使用,只建议添加 w-full、h-auto 等尺寸类
52
+ // 完整版组件默认具有 max-h-140 min-h-80,必要时可覆盖。
53
+ <TiptapEditorComplete className="w-full max-h-100" ... />
54
+ ```
55
+
56
+ ---
57
+
58
+ ## 组合式 API(高级用法)
59
+
60
+ > ⚠️ **警告**:必须优先使用 `TiptapEditorComplete`!仅当用户**明确要求自定义工具栏**或**添加特殊功能**时,才使用组合式 API。绝大多数场景下,`TiptapEditorComplete` 已能满足需求。
61
+
62
+ 当需要 **自定义工具栏布局** 或 **添加特殊功能** 时,使用组合式 API。
63
+
64
+ ```tsx
65
+ import {
66
+ TiptapEditor,
67
+ TiptapEditorContent,
68
+ TiptapEditorToolbar,
69
+ TiptapEditorToolbarSeparator,
70
+ UndoRedoToolbarButton,
71
+ MarkToolbarButton,
72
+ HeadingToolbarButton,
73
+ ListToolbarButton,
74
+ TextAlignToolbarButton,
75
+ ColorHighlightToolbarButton,
76
+ BlockquoteToolbarButton,
77
+ HorizontalRuleToolbarButton,
78
+ CodeBlockToolbarButton,
79
+ LinkToolbarButton,
80
+ ImageUploadToolbarButton,
81
+ AttachmentToolbarButton,
82
+ CompleteKit,
83
+ } from '@/components/business-ui/tiptap-editor';
84
+
85
+ function CustomEditor() {
86
+ // placeholder 需要通过 CompleteKit 配置
87
+ const extensions = [CompleteKit.configure({ placeholder: { placeholder: '请输入内容...' } })];
88
+
89
+ return (
90
+ <TiptapEditor extensions={extensions}>
91
+ <CustomToolbar />
92
+ <TiptapEditorContent />
93
+ </TiptapEditor>
94
+ );
95
+ }
96
+
97
+ /** 自定义工具栏:可根据需求自由组合按钮 */
98
+ function CustomToolbar() {
99
+ return (
100
+ <TiptapEditorToolbar>
101
+ {/* 撤销/重做 */}
102
+ <UndoRedoToolbarButton action="undo" />
103
+ <UndoRedoToolbarButton action="redo" />
104
+ <TiptapEditorToolbarSeparator />
105
+
106
+ {/* 标题/正文 */}
107
+ <HeadingToolbarButton />
108
+ <TiptapEditorToolbarSeparator />
109
+
110
+ {/* 文本对齐 & 列表 */}
111
+ <TextAlignToolbarButton />
112
+ <ListToolbarButton />
113
+ <TiptapEditorToolbarSeparator />
114
+
115
+ {/* 文本格式化: 加粗、删除线、斜体、下划线、颜色 */}
116
+ <MarkToolbarButton format="bold" />
117
+ <MarkToolbarButton format="strike" />
118
+ <MarkToolbarButton format="italic" />
119
+ <MarkToolbarButton format="underline" />
120
+ <ColorHighlightToolbarButton />
121
+ <TiptapEditorToolbarSeparator />
122
+
123
+ {/* 块级元素: 引用、分割线、代码块 */}
124
+ <BlockquoteToolbarButton />
125
+ <HorizontalRuleToolbarButton />
126
+ <CodeBlockToolbarButton />
127
+
128
+ {/* 链接 & 媒体: 图片、附件 */}
129
+ <LinkToolbarButton />
130
+ <ImageUploadToolbarButton />
131
+ <AttachmentToolbarButton />
132
+ </TiptapEditorToolbar>
133
+ );
134
+ }
135
+ ```
136
+
137
+ ### 核心组件
138
+
139
+ | 组件 | 描述 |
140
+ | :----------------------------- | :--------------------------------------- |
141
+ | `TiptapEditor` | 根组件,提供 Editor Context 和编辑器实例 |
142
+ | `TiptapEditorToolbar` | 工具栏容器,用于放置工具栏按钮 |
143
+ | `TiptapEditorContent` | 编辑区域,渲染可编辑内容 |
144
+ | `TiptapEditorToolbarSeparator` | 工具栏分隔符,用于视觉分组 |
145
+
146
+ ### `TiptapEditor` Props
147
+
148
+ | Prop | 类型 | 默认值 | 描述 |
149
+ | :-------------- | :------------------------ | :-------------- | :--------------------- |
150
+ | `value` | `string` | - | 受控值 (HTML) |
151
+ | `defaultValue` | `string` | - | 非受控默认值 |
152
+ | `onValueChange` | `(value: string) => void` | - | 内容变更回调 |
153
+ | `extensions` | `Extensions` | `[CompleteKit]` | 自定义 Tiptap 扩展 |
154
+ | `readOnly` | `boolean` | `false` | 只读模式 |
155
+ | `autoFocus` | `boolean` | - | 自动聚焦 |
156
+ | `asChild` | `boolean` | `false` | 使用子元素作为渲染容器 |
157
+
158
+ ---
159
+
160
+ ## 工具栏按钮参考
161
+
162
+ 所有工具栏按钮均已在 `index.ts` 导出,可直接组合使用:
163
+
164
+ | 组件 | 功能 | Props / 说明 |
165
+ | :---------------------------- | :------------- | :---------------------------------------------------------------- |
166
+ | `UndoRedoToolbarButton` | 撤销 / 重做 | `action: "undo" \| "redo"` |
167
+ | `HeadingToolbarButton` | 标题级别选择 | 下拉菜单,支持正文和 H1-H6 |
168
+ | `MarkToolbarButton` | 文本格式 | `format: "bold" \| "italic" \| "underline" \| "strike" \| "code"` |
169
+ | `ListToolbarButton` | 列表 | 下拉菜单,支持无序、有序、任务列表 |
170
+ | `TextAlignToolbarButton` | 文本对齐 | 下拉菜单,支持左、中、右、两端对齐 |
171
+ | `ColorHighlightToolbarButton` | 文字颜色与高亮 | 颜色选择器 |
172
+ | `LinkToolbarButton` | 超链接 | 点击弹出链接编辑表单 |
173
+ | `BlockquoteToolbarButton` | 引用块 | 切换引用样式 |
174
+ | `CodeBlockToolbarButton` | 代码块 | 插入代码块(Shiki 语法高亮) |
175
+ | `HorizontalRuleToolbarButton` | 水平分割线 | 插入分割线 |
176
+ | `ImageUploadToolbarButton` | 图片上传 | 需配置 `image.upload` 函数 |
177
+ | `AttachmentToolbarButton` | 附件上传 | 需配置 `attachment.upload` 函数,支持 `accept` prop |
178
+
179
+ ---
180
+
181
+ ## 扩展 (Extensions)
182
+
183
+ ### CompleteKit
184
+
185
+ `CompleteKit` 是预配置的扩展集合,包含所有常用编辑功能。它是 `TiptapEditor` 的默认扩展。
186
+
187
+ ```tsx
188
+ import { TiptapEditor, TiptapEditorContent, CompleteKit } from '@/components/business-ui/tiptap-editor';
189
+
190
+ // 使用默认配置
191
+ <TiptapEditor extensions={[CompleteKit]}>
192
+ <TiptapEditorContent />
193
+ </TiptapEditor>
194
+
195
+ // 自定义配置
196
+ <TiptapEditor
197
+ extensions={[
198
+ CompleteKit.configure({
199
+ // 禁用图片扩展
200
+ image: false,
201
+ // 自定义 placeholder
202
+ placeholder: { placeholder: '开始输入...' },
203
+ // 限制标题级别
204
+ heading: { levels: [1, 2, 3] },
205
+ // 禁用链接
206
+ link: false,
207
+ }),
208
+ ]}
209
+ >
210
+ <TiptapEditorContent />
211
+ </TiptapEditor>
212
+ ```
213
+
214
+ #### CompleteKit 包含的扩展
215
+
216
+ | 类别 | 扩展 |
217
+ | :------- | :---------------------------------------------------------- |
218
+ | **基础** | Document, Paragraph, Text, HardBreak, Dropcursor, Gapcursor |
219
+ | **历史** | Undo, Redo |
220
+ | **标题** | Heading (H1-H6) |
221
+ | **文本** | Bold, Italic, Underline, Strike, Code |
222
+ | **颜色** | TextStyle, Color, Highlight |
223
+ | **对齐** | TextAlign |
224
+ | **列表** | BulletList, OrderedList, ListItem, TaskList, TaskItem |
225
+ | **块级** | Blockquote, HorizontalRule, CodeBlock (Shiki) |
226
+ | **链接** | Link (autolink, openOnClick) |
227
+ | **媒体** | Image, Attachment |
228
+ | **其他** | Placeholder |
229
+
230
+ #### CompleteKit 配置选项
231
+
232
+ ```typescript
233
+ interface CompleteKitOptions {
234
+ // 设置为 false 可禁用对应扩展
235
+ heading?: { levels: number[] } | false;
236
+ link?: { autolink?: boolean; openOnClick?: boolean } | false;
237
+ codeBlock?: false;
238
+ taskList?: Partial<TaskListOptions> | false;
239
+ taskItem?: { nested?: boolean } | false;
240
+ textStyle?: Partial<TextStyleOptions> | false;
241
+ color?: Partial<ColorOptions> | false;
242
+ highlight?: { multicolor?: boolean } | false;
243
+ textAlign?: { types: string[] } | false;
244
+ image?: Partial<ImageOptions> | false;
245
+ attachment?: Partial<AttachmentOptions> | false;
246
+ placeholder?: { placeholder?: string } | false;
247
+ // ... 以及所有 StarterKit 选项
248
+ }
249
+ ```
250
+
251
+ ---
252
+
253
+ ## 目录结构
254
+
255
+ ```text
256
+ tiptap-editor/
257
+ ├── index.ts # 统一导出入口
258
+ ├── tiptap-editor.tsx # 核心组件 (TiptapEditor, TiptapEditorToolbar, TiptapEditorContent)
259
+ ├── tiptap-editor-complete.tsx # 完整封装版 (TiptapEditorComplete)
260
+ ├── components/ # 富文本依赖的组件,其中工具栏按钮以 toolbar-button 结尾
261
+ ├── extensions/ # 自定义 Tiptap 扩展
262
+ │ ├── ...
263
+ │ └── complete-kit.ts # 完整扩展集合
264
+ └── hooks/
265
+ └── use-tiptap-editor.ts # 编辑器实例 Hook
266
+ ```
267
+
268
+ ---
269
+
270
+ ## 开发指南
271
+
272
+ ### 添加新的工具栏按钮
273
+
274
+ 1. 在 `components/` 目录创建新组件:
275
+
276
+ ```tsx
277
+ // components/business-ui/tiptap-editor/components/my-toolbar-button.tsx
278
+ 'use client';
279
+
280
+ import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
281
+ import { Button } from '@/components/ui/button';
282
+
283
+ export function MyToolbarButton() {
284
+ const { editor } = useTiptapEditor();
285
+
286
+ if (!editor) return null;
287
+
288
+ return (
289
+ <Button
290
+ variant="ghost"
291
+ size="sm"
292
+ className="size-6 px-0"
293
+ disabled={!editor.can().myCommand()}
294
+ onClick={() => editor.chain().focus().myCommand().run()}
295
+ >
296
+ {/* Icon */}
297
+ </Button>
298
+ );
299
+ }
300
+ ```
301
+
302
+ 2. 在 `index.ts` 中导出:
303
+
304
+ ```tsx
305
+ export { MyToolbarButton } from '@/components/business-ui/tiptap-editor/components/my-toolbar-button';
306
+ ```
307
+
308
+ ### 添加自定义扩展
309
+
310
+ 1. 在 `extensions/` 目录创建扩展文件
311
+ 2. 如需集成到 CompleteKit,修改 `extensions/complete-kit.ts`
312
+ 3. 通过 `extensions` prop 传入:
313
+
314
+ ```tsx
315
+ import { MyExtension } from './extensions/my-extension';
316
+
317
+ <TiptapEditor extensions={[CompleteKit, MyExtension]} />;
318
+ ```
319
+
320
+ ---
321
+
322
+ ## 相关资源
323
+
324
+ - [Tiptap LLM 文档](https://tiptap.dev/llms.txt)
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { Paperclip } from 'lucide-react';
5
+ import { toast } from 'sonner';
6
+
7
+ import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
8
+ import { Button } from '@/components/ui/button';
9
+ import {
10
+ Tooltip,
11
+ TooltipContent,
12
+ TooltipProvider,
13
+ TooltipTrigger,
14
+ } from '@/components/ui/tooltip';
15
+
16
+ interface AttachmentToolbarButtonProps {
17
+ accept?: string;
18
+ }
19
+
20
+ export function AttachmentToolbarButton({
21
+ accept,
22
+ }: AttachmentToolbarButtonProps) {
23
+ const { editor } = useTiptapEditor();
24
+ const fileInputRef = React.useRef<HTMLInputElement>(null);
25
+
26
+ if (!editor) return null;
27
+
28
+ const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
29
+ const files = e.target.files;
30
+ if (!files || files.length === 0) return;
31
+
32
+ const ok = editor
33
+ .chain()
34
+ .focus()
35
+ .insertAttachments(Array.from(files))
36
+ .run();
37
+ if (!ok) {
38
+ toast.error('插入附件失败(请确认 attachment 扩展已启用且提供 upload)');
39
+ }
40
+
41
+ if (fileInputRef.current) {
42
+ fileInputRef.current.value = '';
43
+ }
44
+ };
45
+
46
+ return (
47
+ <TooltipProvider>
48
+ <Tooltip delayDuration={700}>
49
+ <TooltipTrigger asChild>
50
+ <Button
51
+ variant="ghost"
52
+ size="sm"
53
+ className="size-6 px-0"
54
+ onClick={() => fileInputRef.current?.click()}
55
+ >
56
+ <Paperclip className="size-4" />
57
+ <span className="sr-only">上传附件</span>
58
+ </Button>
59
+ </TooltipTrigger>
60
+ <TooltipContent>
61
+ <p>上传附件</p>
62
+ </TooltipContent>
63
+ </Tooltip>
64
+ <input
65
+ type="file"
66
+ multiple
67
+ accept={accept}
68
+ ref={fileInputRef}
69
+ className="hidden"
70
+ onChange={handleFileChange}
71
+ />
72
+ </TooltipProvider>
73
+ );
74
+ }
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { Quote } from 'lucide-react';
4
+
5
+ import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
6
+ import { Toggle } from '@/components/ui/toggle';
7
+ import {
8
+ Tooltip,
9
+ TooltipContent,
10
+ TooltipProvider,
11
+ TooltipTrigger,
12
+ } from '@/components/ui/tooltip';
13
+
14
+ export function BlockquoteToolbarButton() {
15
+ const { editor } = useTiptapEditor();
16
+ if (!editor) return null;
17
+
18
+ return (
19
+ <TooltipProvider>
20
+ <Tooltip delayDuration={700}>
21
+ <TooltipTrigger asChild>
22
+ <Toggle
23
+ size="sm"
24
+ pressed={editor.isActive('blockquote')}
25
+ onPressedChange={() =>
26
+ editor.chain().focus().toggleBlockquote().run()
27
+ }
28
+ disabled={!editor.can().toggleBlockquote()}
29
+ aria-label="引用"
30
+ className="size-6 p-0"
31
+ >
32
+ <Quote className="size-4" />
33
+ </Toggle>
34
+ </TooltipTrigger>
35
+ <TooltipContent>
36
+ <p>引用(⌘+Shift+&gt;)</p>
37
+ </TooltipContent>
38
+ </Tooltip>
39
+ </TooltipProvider>
40
+ );
41
+ }
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { CodeXml } from 'lucide-react';
4
+
5
+ import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
6
+ import { Toggle } from '@/components/ui/toggle';
7
+ import {
8
+ Tooltip,
9
+ TooltipContent,
10
+ TooltipProvider,
11
+ TooltipTrigger,
12
+ } from '@/components/ui/tooltip';
13
+
14
+ export function CodeBlockToolbarButton() {
15
+ const { editor } = useTiptapEditor();
16
+ if (!editor) return null;
17
+
18
+ return (
19
+ <TooltipProvider>
20
+ <Tooltip delayDuration={700}>
21
+ <TooltipTrigger asChild>
22
+ <Toggle
23
+ size="sm"
24
+ pressed={editor.isActive('codeBlock')}
25
+ onPressedChange={() =>
26
+ editor.chain().focus().toggleCodeBlock().run()
27
+ }
28
+ disabled={!editor.can().toggleCodeBlock()}
29
+ aria-label="代码块"
30
+ className="size-6 p-0"
31
+ >
32
+ <CodeXml className="size-4" />
33
+ </Toggle>
34
+ </TooltipTrigger>
35
+ <TooltipContent>
36
+ <p>代码块</p>
37
+ </TooltipContent>
38
+ </Tooltip>
39
+ </TooltipProvider>
40
+ );
41
+ }
@@ -0,0 +1,141 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { Baseline, Check, ChevronDown } from 'lucide-react';
5
+
6
+ import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
7
+ import { cn } from '@/lib/utils';
8
+ import { Button } from '@/components/ui/button';
9
+ import {
10
+ Popover,
11
+ PopoverContent,
12
+ PopoverTrigger,
13
+ } from '@/components/ui/popover';
14
+ import {
15
+ Tooltip,
16
+ TooltipContent,
17
+ TooltipProvider,
18
+ TooltipTrigger,
19
+ } from '@/components/ui/tooltip';
20
+
21
+ // Design system colors matching Figma
22
+ const TEXT_COLORS = [
23
+ { value: 'var(--foreground)', label: '默认', className: 'bg-foreground' },
24
+ {
25
+ value: 'var(--muted-foreground)',
26
+ label: '次要',
27
+ className: 'bg-muted-foreground',
28
+ },
29
+ { value: 'var(--color-red-600)', label: '红色', className: 'bg-red-600' },
30
+ {
31
+ value: 'var(--color-orange-500)',
32
+ label: '橙色',
33
+ className: 'bg-orange-500',
34
+ },
35
+ {
36
+ value: 'var(--color-yellow-500)',
37
+ label: '黄色',
38
+ className: 'bg-yellow-500',
39
+ },
40
+ { value: 'var(--color-green-500)', label: '绿色', className: 'bg-green-500' },
41
+ { value: 'var(--color-blue-600)', label: '蓝色', className: 'bg-blue-600' },
42
+ {
43
+ value: 'var(--color-purple-600)',
44
+ label: '紫色',
45
+ className: 'bg-purple-600',
46
+ },
47
+ ];
48
+
49
+ export function ColorHighlightToolbarButton() {
50
+ const { editor } = useTiptapEditor();
51
+ const [open, setOpen] = React.useState(false);
52
+
53
+ if (!editor) return null;
54
+
55
+ const currentColor =
56
+ editor.getAttributes('textStyle').color || 'var(--foreground)';
57
+
58
+ const disabled = !editor.can().setColor(TEXT_COLORS[0]!.value);
59
+
60
+ const handleColorSelect = (color: string) => {
61
+ if (color === 'var(--foreground)') {
62
+ editor.chain().focus().unsetColor().run();
63
+ } else {
64
+ editor.chain().focus().setColor(color).run();
65
+ }
66
+ };
67
+
68
+ const handleResetColor = () => {
69
+ editor.chain().focus().unsetColor().run();
70
+ };
71
+
72
+ const trigger = (
73
+ <Button
74
+ variant="ghost"
75
+ size="sm"
76
+ className="h-6 gap-0.5 px-2"
77
+ disabled={disabled}
78
+ >
79
+ <Baseline className="size-4" style={{ color: currentColor }} />
80
+ <ChevronDown className="size-3.5 text-muted-foreground" />
81
+ <span className="sr-only">文字颜色</span>
82
+ </Button>
83
+ );
84
+
85
+ return (
86
+ <Popover open={open} onOpenChange={setOpen}>
87
+ <TooltipProvider>
88
+ <Tooltip delayDuration={700}>
89
+ <TooltipTrigger asChild>
90
+ <PopoverTrigger asChild>{trigger}</PopoverTrigger>
91
+ </TooltipTrigger>
92
+ {!open && (
93
+ <TooltipContent>
94
+ <p>文字颜色</p>
95
+ </TooltipContent>
96
+ )}
97
+ </Tooltip>
98
+ </TooltipProvider>
99
+ <PopoverContent className="w-auto p-4" align="start">
100
+ <div className="flex flex-col items-center gap-3">
101
+ {/* Color grid */}
102
+ <div className="flex gap-1">
103
+ {TEXT_COLORS.map((color) => {
104
+ const isSelected = currentColor === color.value;
105
+
106
+ return (
107
+ <button
108
+ key={color.value}
109
+ className={cn(
110
+ 'relative flex size-6 items-center justify-center rounded-md p-0.5 transition-colors',
111
+ 'hover:ring-1 hover:ring-ring/20',
112
+ isSelected && 'ring-1 ring-ring',
113
+ )}
114
+ onClick={() => handleColorSelect(color.value)}
115
+ title={color.label}
116
+ >
117
+ <span
118
+ className={cn('size-5 rounded-[5px]', color.className)}
119
+ />
120
+ {isSelected && (
121
+ <Check className="absolute size-3.5 text-white" />
122
+ )}
123
+ </button>
124
+ );
125
+ })}
126
+ </div>
127
+
128
+ {/* Reset button */}
129
+ <Button
130
+ variant="outline"
131
+ size="sm"
132
+ className="w-full"
133
+ onClick={handleResetColor}
134
+ >
135
+ 恢复默认
136
+ </Button>
137
+ </div>
138
+ </PopoverContent>
139
+ </Popover>
140
+ );
141
+ }