@lark-apaas/coding-template-nestjs-react-fullstack 0.1.36-alpha.20260903095202 → 0.1.36-alpha.20260903185731

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 (112) hide show
  1. package/package.json +1 -1
  2. package/template/client/src/components/ui/markdown.tsx +43 -0
  3. package/template/client/src/index.css +0 -1
  4. package/template/package-lock.json +5783 -13022
  5. package/template/package.json +5 -68
  6. package/template/client/src/components/business-ui/README.md +0 -519
  7. package/template/client/src/components/business-ui/api/chats/queries.ts +0 -39
  8. package/template/client/src/components/business-ui/api/chats/service.ts +0 -29
  9. package/template/client/src/components/business-ui/api/departments/queries.ts +0 -42
  10. package/template/client/src/components/business-ui/api/departments/service.ts +0 -18
  11. package/template/client/src/components/business-ui/api/files/service.ts +0 -28
  12. package/template/client/src/components/business-ui/api/user-profiles/queries.ts +0 -42
  13. package/template/client/src/components/business-ui/api/user-profiles/service.ts +0 -23
  14. package/template/client/src/components/business-ui/api/users/queries.ts +0 -34
  15. package/template/client/src/components/business-ui/api/users/service.ts +0 -40
  16. package/template/client/src/components/business-ui/chat-select/README.md +0 -128
  17. package/template/client/src/components/business-ui/chat-select/chat-item.tsx +0 -85
  18. package/template/client/src/components/business-ui/chat-select/chat-select-tag.tsx +0 -83
  19. package/template/client/src/components/business-ui/chat-select/chat-select.tsx +0 -197
  20. package/template/client/src/components/business-ui/chat-select/index.tsx +0 -12
  21. package/template/client/src/components/business-ui/chat-select/types.ts +0 -102
  22. package/template/client/src/components/business-ui/chat-select/use-chat-value.ts +0 -180
  23. package/template/client/src/components/business-ui/chat-select/utils.ts +0 -81
  24. package/template/client/src/components/business-ui/department-select/department-item.tsx +0 -55
  25. package/template/client/src/components/business-ui/department-select/department-select-field.tsx +0 -86
  26. package/template/client/src/components/business-ui/department-select/department-select-tag.tsx +0 -56
  27. package/template/client/src/components/business-ui/department-select/department-select.tsx +0 -168
  28. package/template/client/src/components/business-ui/department-select/icon-department.tsx +0 -22
  29. package/template/client/src/components/business-ui/department-select/index.tsx +0 -7
  30. package/template/client/src/components/business-ui/department-select/types.ts +0 -15
  31. package/template/client/src/components/business-ui/department-select/utils.ts +0 -17
  32. package/template/client/src/components/business-ui/entity-combobox/base-combobox-content.tsx +0 -35
  33. package/template/client/src/components/business-ui/entity-combobox/base-combobox-empty.tsx +0 -28
  34. package/template/client/src/components/business-ui/entity-combobox/base-combobox-error.tsx +0 -35
  35. package/template/client/src/components/business-ui/entity-combobox/base-combobox-item.tsx +0 -55
  36. package/template/client/src/components/business-ui/entity-combobox/base-combobox-list.tsx +0 -174
  37. package/template/client/src/components/business-ui/entity-combobox/base-combobox-loading.tsx +0 -19
  38. package/template/client/src/components/business-ui/entity-combobox/base-combobox-search.tsx +0 -31
  39. package/template/client/src/components/business-ui/entity-combobox/base-combobox-trigger.tsx +0 -281
  40. package/template/client/src/components/business-ui/entity-combobox/base-combobox.tsx +0 -132
  41. package/template/client/src/components/business-ui/entity-combobox/context.tsx +0 -19
  42. package/template/client/src/components/business-ui/entity-combobox/entity-combobox.tsx +0 -145
  43. package/template/client/src/components/business-ui/entity-combobox/highlight-text.tsx +0 -52
  44. package/template/client/src/components/business-ui/entity-combobox/hooks.tsx +0 -21
  45. package/template/client/src/components/business-ui/entity-combobox/index.tsx +0 -31
  46. package/template/client/src/components/business-ui/entity-combobox/item-pill.tsx +0 -179
  47. package/template/client/src/components/business-ui/entity-combobox/popover-wrapper.tsx +0 -149
  48. package/template/client/src/components/business-ui/entity-combobox/search-trigger.tsx +0 -262
  49. package/template/client/src/components/business-ui/entity-combobox/shared-types.ts +0 -296
  50. package/template/client/src/components/business-ui/entity-combobox/size-variants.tsx +0 -322
  51. package/template/client/src/components/business-ui/entity-combobox/types.ts +0 -246
  52. package/template/client/src/components/business-ui/entity-combobox/use-fetch-data.tsx +0 -81
  53. package/template/client/src/components/business-ui/entity-combobox/use-infinite-scroll.tsx +0 -99
  54. package/template/client/src/components/business-ui/entity-combobox/use-popover-outside-click.tsx +0 -32
  55. package/template/client/src/components/business-ui/form/checkbox-field.tsx +0 -83
  56. package/template/client/src/components/business-ui/form/context.tsx +0 -38
  57. package/template/client/src/components/business-ui/form/field-layout.tsx +0 -91
  58. package/template/client/src/components/business-ui/form/form.tsx +0 -54
  59. package/template/client/src/components/business-ui/form/hooks/form-context.tsx +0 -4
  60. package/template/client/src/components/business-ui/form/hooks/form-utils.ts +0 -23
  61. package/template/client/src/components/business-ui/form/hooks/form.tsx +0 -40
  62. package/template/client/src/components/business-ui/form/index.tsx +0 -4
  63. package/template/client/src/components/business-ui/form/input-field.tsx +0 -74
  64. package/template/client/src/components/business-ui/form/radio-group-field.tsx +0 -138
  65. package/template/client/src/components/business-ui/form/select-field.tsx +0 -155
  66. package/template/client/src/components/business-ui/form/switch-field.tsx +0 -66
  67. package/template/client/src/components/business-ui/form/textarea-field.tsx +0 -72
  68. package/template/client/src/components/business-ui/form/types.ts +0 -32
  69. package/template/client/src/components/business-ui/tiptap-editor/README.md +0 -324
  70. package/template/client/src/components/business-ui/tiptap-editor/components/attachment-toolbar-button.tsx +0 -74
  71. package/template/client/src/components/business-ui/tiptap-editor/components/blockquote-toolbar-button.tsx +0 -41
  72. package/template/client/src/components/business-ui/tiptap-editor/components/code-block-toolbar-button.tsx +0 -41
  73. package/template/client/src/components/business-ui/tiptap-editor/components/color-highlight-toolbar-button.tsx +0 -141
  74. package/template/client/src/components/business-ui/tiptap-editor/components/heading-toolbar-button.tsx +0 -99
  75. package/template/client/src/components/business-ui/tiptap-editor/components/horizontal-rule-toolbar-button.tsx +0 -39
  76. package/template/client/src/components/business-ui/tiptap-editor/components/image-upload-toolbar-button.tsx +0 -65
  77. package/template/client/src/components/business-ui/tiptap-editor/components/link-edit-form.tsx +0 -127
  78. package/template/client/src/components/business-ui/tiptap-editor/components/link-hover-toolbar.tsx +0 -380
  79. package/template/client/src/components/business-ui/tiptap-editor/components/link-toolbar-button.tsx +0 -96
  80. package/template/client/src/components/business-ui/tiptap-editor/components/list-toolbar-button.tsx +0 -75
  81. package/template/client/src/components/business-ui/tiptap-editor/components/mark-toolbar-button.tsx +0 -118
  82. package/template/client/src/components/business-ui/tiptap-editor/components/text-align-toolbar-button.tsx +0 -84
  83. package/template/client/src/components/business-ui/tiptap-editor/components/undo-redo-toolbar-button.tsx +0 -54
  84. package/template/client/src/components/business-ui/tiptap-editor/extensions/attachment.tsx +0 -539
  85. package/template/client/src/components/business-ui/tiptap-editor/extensions/code-block-shiki.tsx +0 -236
  86. package/template/client/src/components/business-ui/tiptap-editor/extensions/complete-kit.ts +0 -260
  87. package/template/client/src/components/business-ui/tiptap-editor/extensions/image.tsx +0 -456
  88. package/template/client/src/components/business-ui/tiptap-editor/hooks/use-tiptap-editor.ts +0 -47
  89. package/template/client/src/components/business-ui/tiptap-editor/index.ts +0 -38
  90. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor-complete.tsx +0 -112
  91. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor.tsx +0 -169
  92. package/template/client/src/components/business-ui/types/user.ts +0 -74
  93. package/template/client/src/components/business-ui/user-display/index.tsx +0 -2
  94. package/template/client/src/components/business-ui/user-display/overflow-tooltip-text.tsx +0 -90
  95. package/template/client/src/components/business-ui/user-display/type.ts +0 -27
  96. package/template/client/src/components/business-ui/user-display/user-display.tsx +0 -99
  97. package/template/client/src/components/business-ui/user-display/user-with-avatar.tsx +0 -158
  98. package/template/client/src/components/business-ui/user-display/utils.ts +0 -37
  99. package/template/client/src/components/business-ui/user-profile/error-image.tsx +0 -302
  100. package/template/client/src/components/business-ui/user-profile/user-external-script.ts +0 -73
  101. package/template/client/src/components/business-ui/user-profile/user-profile.tsx +0 -370
  102. package/template/client/src/components/business-ui/user-select/index.tsx +0 -28
  103. package/template/client/src/components/business-ui/user-select/types.ts +0 -109
  104. package/template/client/src/components/business-ui/user-select/use-user-value.ts +0 -160
  105. package/template/client/src/components/business-ui/user-select/user-item.tsx +0 -98
  106. package/template/client/src/components/business-ui/user-select/user-pill.tsx +0 -140
  107. package/template/client/src/components/business-ui/user-select/user-select-tag.tsx +0 -82
  108. package/template/client/src/components/business-ui/user-select/user-select.tsx +0 -359
  109. package/template/client/src/components/business-ui/user-select/utils.tsx +0 -188
  110. package/template/client/src/components/business-ui/utils/user.ts +0 -72
  111. package/template/client/src/components/ui/streamdown.tsx +0 -186
  112. package/template/client/src/lib/shiki.ts +0 -92
@@ -1,39 +0,0 @@
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
- }
@@ -1,29 +0,0 @@
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 };
@@ -1,42 +0,0 @@
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
- }
@@ -1,18 +0,0 @@
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 };
@@ -1,28 +0,0 @@
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
- }
@@ -1,42 +0,0 @@
1
- 'use client';
2
-
3
- import { useQuery, queryOptions, type QueryClient } from '@tanstack/react-query';
4
-
5
- import { fetchUserProfile, type AccountType } from './service';
6
-
7
- export const userProfileQueries = {
8
- all: () => ['userProfiles'] as const,
9
- byId: (userId: string, accountType: AccountType = 'apaas') =>
10
- queryOptions({
11
- queryKey: [...userProfileQueries.all(), 'byId', accountType, userId],
12
- queryFn: ({ signal }) => fetchUserProfile(userId, accountType, signal),
13
- staleTime: 1 * 60 * 1000,
14
- gcTime: 5 * 60 * 1000,
15
- enabled: !!userId,
16
- }),
17
- };
18
-
19
- export type UseUserProfileOptions = {
20
- userId: string;
21
- accountType?: AccountType;
22
- enabled?: boolean;
23
- };
24
-
25
- /**
26
- * 获取用户 Profile 卡片数据的 hook
27
- */
28
- export function useUserProfile(options: UseUserProfileOptions) {
29
- const { userId, accountType = 'apaas', enabled = true } = options;
30
-
31
- return useQuery({
32
- ...userProfileQueries.byId(userId, accountType),
33
- enabled: enabled && !!userId,
34
- });
35
- }
36
-
37
- /**
38
- * 清除用户 Profile 缓存
39
- */
40
- export function clearUserProfileCache(queryClient: QueryClient): void {
41
- queryClient.invalidateQueries({ queryKey: userProfileQueries.all() });
42
- }
@@ -1,23 +0,0 @@
1
- import {
2
- UserProfileService,
3
- getAssetsUrl,
4
- type AccountType,
5
- type UserProfileData,
6
- } from '@lark-apaas/client-toolkit/tools/services';
7
-
8
- const userProfileService = new UserProfileService();
9
-
10
- /**
11
- * 获取用户 Profile 卡片数据
12
- */
13
- export async function fetchUserProfile(
14
- userId: string,
15
- accountType: AccountType = 'apaas',
16
- signal?: AbortSignal,
17
- ): Promise<UserProfileData> {
18
- return userProfileService.getUserProfile(userId, accountType, signal);
19
- }
20
-
21
- // Re-export for convenience
22
- export { getAssetsUrl };
23
- export type { AccountType, UserProfileData };
@@ -1,34 +0,0 @@
1
- 'use client';
2
-
3
- import { useQuery, queryOptions, type QueryClient } from '@tanstack/react-query';
4
-
5
- import { listUsersByIds, type AccountType } from './service';
6
-
7
- export const userQueries = {
8
- all: () => ['users'] as const,
9
- byIds: (userIds: string[], accountType: AccountType = 'apaas') =>
10
- queryOptions({
11
- queryKey: [...userQueries.all(), 'byIds', accountType, userIds.join(',')],
12
- queryFn: () => listUsersByIds(userIds),
13
- staleTime: 1 * 60 * 1000,
14
- gcTime: 5 * 60 * 1000,
15
- enabled: userIds.length > 0,
16
- }),
17
- };
18
-
19
- /**
20
- * 批量获取用户信息的 hook
21
- */
22
- export function useUsersByIds(
23
- userIds: string[],
24
- accountType: AccountType = 'apaas',
25
- ) {
26
- return useQuery(userQueries.byIds(userIds, accountType));
27
- }
28
-
29
- /**
30
- * 清除用户缓存
31
- */
32
- export function clearUserCache(queryClient: QueryClient): void {
33
- queryClient.invalidateQueries({ queryKey: userQueries.all() });
34
- }
@@ -1,40 +0,0 @@
1
- import {
2
- UserService,
3
- type AccountType,
4
- type SearchUsersParams,
5
- type SearchUsersResponse,
6
- type BatchGetUsersResponse,
7
- type ConvertExternalContactResponse,
8
- } from '@lark-apaas/client-toolkit/tools/services';
9
-
10
- const userService = new UserService();
11
-
12
- /**
13
- * 搜索用户
14
- */
15
- export async function searchUsers(
16
- params: SearchUsersParams,
17
- ): Promise<SearchUsersResponse> {
18
- return userService.searchUsers({ ...params, searchExternalContact: true });
19
- }
20
-
21
- /**
22
- * 批量根据用户 ID 查询用户信息
23
- */
24
- export async function listUsersByIds(
25
- userIds: string[],
26
- ): Promise<BatchGetUsersResponse> {
27
- const numericIds = userIds.filter((id) => !isNaN(Number(id)));
28
- return userService.listUsersByIds(numericIds);
29
- }
30
-
31
- /**
32
- * 调用开户接口,将外部联系人的 larkUserID 转换为已注册用户
33
- */
34
- export async function convertExternalContact(
35
- larkUserID: string,
36
- ): Promise<ConvertExternalContactResponse> {
37
- return userService.convertExternalContact(larkUserID);
38
- }
39
-
40
- export type { AccountType, SearchUsersParams, SearchUsersResponse, BatchGetUsersResponse, ConvertExternalContactResponse };
@@ -1,128 +0,0 @@
1
- # ChatSelect 群组选择组件
2
-
3
- **组件路径**:`@/components/business-ui/chat-select`
4
-
5
- ---
6
-
7
- ## 群组类型
8
-
9
- ```typescript
10
- /** 群组数据结构 */
11
- interface Chat {
12
- /** 唯一标识:对应 chatID */
13
- id: string;
14
- /** 群组名称:内部优先 zh_cn,其次 en_us */
15
- name: string;
16
- /** 头像:URL 或 16 进制 RGB 颜色 */
17
- avatar: string;
18
- }
19
- ```
20
-
21
- ---
22
-
23
- ## 功能概述
24
-
25
- - 群组字段选择组件,用于表单中录入"群组"字段
26
- - 通过触发器打开弹层,弹层内置搜索功能(始终可用)
27
- - **仅支持 ID 模式**:`value/onChange` 只使用群组 `chatID`(string),该 ID 为飞书群聊 ID。
28
- - 组件在 UI 上可展示群组信息(头像/名称),但对外值仅返回/接收 `chatID`
29
- - 选择后自动回显已选群组信息:群头像 + 群名称(多选以 tag 形式展示)
30
- - 组件自带群组检索能力,无需额外传搜索接口/数据源
31
-
32
- ## 类型定义
33
-
34
- ```typescript
35
- /** 触发器类型 */
36
- type TriggerType = 'button' | 'search' | 'custom';
37
-
38
- /** 单选模式 Props */
39
- interface ChatSelectSingleProps {
40
- /** 当前选中的群组 ID,空值为 null */
41
- value: string | null;
42
- /** 选中变化回调 */
43
- onChange: (value: string | null) => void;
44
- /** 是否多选,单选时为 false 或不传 */
45
- multiple?: false;
46
- /** 触发器类型,仅影响样式,不影响搜索功能 */
47
- triggerType?: TriggerType;
48
- /** 是否禁用 */
49
- disabled?: boolean;
50
- /** 占位文本 */
51
- placeholder?: string;
52
- }
53
-
54
- /** 多选模式 Props */
55
- interface ChatSelectMultipleProps {
56
- /** 当前选中的群组 ID 列表,空值为 [] */
57
- value: string[];
58
- /** 选中变化回调 */
59
- onChange: (value: string[]) => void;
60
- /** 是否多选,多选时为 true */
61
- multiple: true;
62
- /** 触发器类型 */
63
- triggerType?: TriggerType;
64
- /** 是否禁用 */
65
- disabled?: boolean;
66
- /** 占位文本 */
67
- placeholder?: string;
68
- }
69
-
70
- type ChatSelectProps = ChatSelectSingleProps | ChatSelectMultipleProps;
71
- ```
72
-
73
- ## Props 说明
74
-
75
- | 属性 | 类型 | 默认值 | 说明 |
76
- |------|------|--------|------|
77
- | `value` | `string \| null` / `string[]` | - | **必填**。单选传 `string \| null`(群组 ID),多选传 `string[]` |
78
- | `onChange` | `function` | - | **必填**。值变化回调,返回值形态与 `value` 匹配 |
79
- | `multiple` | `boolean` | `false` | 是否多选 |
80
- | `triggerType` | `'button' \| 'search' \| 'custom'` | `'button'` | 触发器类型,仅影响样式 |
81
- | `disabled` | `boolean` | `false` | 是否禁用 |
82
- | `placeholder` | `string` | `'请选择'` | 占位文本 |
83
- | `getOptionDisabled` | `(chat) => boolean` | - | 判断选项是否禁用 |
84
-
85
- ## 受控规范
86
-
87
- > ⚠️ **强约束**
88
-
89
- 1. **必须受控**:始终传 `value` + `onChange`
90
- 2. **禁止传**:`undefined` / 群组对象 / 其他类型
91
- 3. **空值规则**:
92
- - 单选清空:`null`
93
- - 多选清空:`[]`
94
-
95
- ## 使用示例
96
-
97
- ```tsx
98
- import { useState } from 'react';
99
- import { ChatSelect } from '@/components/business-ui/chat-select';
100
-
101
- // 单选
102
- function SingleSelectExample() {
103
- const [chatId, setChatId] = useState<string | null>(null);
104
-
105
- return (
106
- <ChatSelect
107
- value={chatId}
108
- onChange={setChatId}
109
- placeholder="请选择群组"
110
- />
111
- );
112
- }
113
-
114
- // 多选
115
- function MultipleSelectExample() {
116
- const [chatIds, setChatIds] = useState<string[]>([]);
117
-
118
- return (
119
- <ChatSelect
120
- multiple
121
- value={chatIds}
122
- onChange={setChatIds}
123
- placeholder="请选择群组"
124
- getOptionDisabled={(chat) => chat.id === 'disabled-id'}
125
- />
126
- );
127
- }
128
- ```
@@ -1,85 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
-
5
- import type { Chat } from '@client/src/components/business-ui/chat-select/types';
6
- import { BaseComboboxItem } from '@client/src/components/business-ui/entity-combobox/base-combobox-item';
7
- import { HighlightText } from '@client/src/components/business-ui/entity-combobox/highlight-text';
8
- import { isHexColor } from '@client/src/components/business-ui/entity-combobox/item-pill';
9
- import {
10
- listItemAvatarVariants,
11
- listItemNameVariants,
12
- listItemSubTextVariants,
13
- listItemVariants,
14
- type ComboboxSize,
15
- } from '@client/src/components/business-ui/entity-combobox/size-variants';
16
- import { cn } from '@/lib/utils';
17
- import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
18
-
19
- const ExternalChatTag = () => (
20
- <span className="inline-flex shrink-0 items-center rounded-sm bg-blue-500/20 px-1.5 py-0.5 text-xs leading-4 text-blue-900 dark:text-blue-200">
21
- 外部
22
- </span>
23
- );
24
-
25
- export const ChatItem = ({
26
- chatValue,
27
- isSelected,
28
- className,
29
- size = 'medium',
30
- searchKeyword,
31
- disabled,
32
- }: {
33
- chatValue: Chat;
34
- isSelected: boolean;
35
- className?: string;
36
- size?: ComboboxSize;
37
- searchKeyword?: string;
38
- disabled?: boolean;
39
- }) => {
40
- const chat = chatValue.raw;
41
- const displayName = chat?.name?.zh_cn || chat?.name?.en_us || chatValue.name;
42
- const userCount = chat?.userCount;
43
- const isExternal = chat?.isExternal;
44
-
45
- const isHex = isHexColor(chat?.avatar);
46
-
47
- const avatarImage = !isHex ? (
48
- <AvatarImage src={chat?.avatar} alt={displayName} loading="eager" />
49
- ) : (
50
- <AvatarFallback style={{ backgroundColor: chat?.avatar }}>
51
- {displayName?.charAt?.(0).toUpperCase()}
52
- </AvatarFallback>
53
- );
54
-
55
- return (
56
- <BaseComboboxItem
57
- item={chatValue}
58
- getItemValue={(item) => item}
59
- isSelected={isSelected}
60
- className={cn(listItemVariants({ size }), className)}
61
- disabled={disabled}
62
- >
63
- <Avatar className={listItemAvatarVariants({ size })}>
64
- {chat?.avatar ? (
65
- avatarImage
66
- ) : (
67
- <AvatarFallback>{displayName?.charAt?.(0)}</AvatarFallback>
68
- )}
69
- </Avatar>
70
- <div className="flex min-w-0 flex-1 items-center gap-1">
71
- <HighlightText
72
- text={chatValue.name}
73
- highlight={searchKeyword}
74
- className={cn(listItemNameVariants({ size }), 'truncate')}
75
- />
76
- {userCount != null && (
77
- <span className={cn(listItemSubTextVariants({ size }), 'shrink-0')}>
78
- ({userCount})
79
- </span>
80
- )}
81
- {isExternal && <ExternalChatTag />}
82
- </div>
83
- </BaseComboboxItem>
84
- );
85
- };
@@ -1,83 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
-
5
- import { X } from 'lucide-react';
6
-
7
- import type { Chat } from '@client/src/components/business-ui/chat-select/types';
8
- import {
9
- ItemPill,
10
- renderPillAvatar,
11
- } from '@client/src/components/business-ui/entity-combobox/item-pill';
12
- import {
13
- tagCloseIconVariants,
14
- type ComboboxSize,
15
- } from '@client/src/components/business-ui/entity-combobox/size-variants';
16
- import { Skeleton } from '@/components/ui/skeleton';
17
-
18
- export const ChatSelectTag = ({
19
- chatValue,
20
- onClose,
21
- maxTextLength,
22
- className,
23
- size = 'medium',
24
- disabled,
25
- isLoading,
26
- }: {
27
- chatValue: Chat;
28
- onClose: (value: Chat, e: React.MouseEvent) => void;
29
- maxTextLength?: number;
30
- className?: string;
31
- size?: ComboboxSize;
32
- disabled?: boolean;
33
- /**
34
- * 是否正在加载群组信息(ID 模式下查询中)
35
- */
36
- isLoading?: boolean;
37
- }) => {
38
- const chat = chatValue.raw;
39
-
40
- const handleClose = (e: React.MouseEvent) => {
41
- e.stopPropagation();
42
- onClose(chatValue, e);
43
- };
44
-
45
- const closeButton = !disabled ? (
46
- <button
47
- type="button"
48
- className="rounded-sm text-foreground opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none"
49
- onClick={handleClose}
50
- >
51
- <X className={tagCloseIconVariants({ size })} />
52
- <span className="sr-only">移除 {chatValue.name}</span>
53
- </button>
54
- ) : null;
55
-
56
- // 加载中状态显示 Skeleton
57
- if (isLoading && !chat) {
58
- return (
59
- <div className="inline-flex items-center gap-1 rounded-md bg-secondary px-1.5 py-0.5">
60
- <Skeleton className="h-4 w-4 rounded-full" />
61
- <Skeleton className="h-3 w-12" />
62
- </div>
63
- );
64
- }
65
-
66
- const avatarElement = renderPillAvatar({
67
- avatarUrl: chatValue.avatar || chat?.avatar,
68
- label: chatValue.name,
69
- size,
70
- });
71
-
72
- return (
73
- <ItemPill
74
- label={chatValue.name}
75
- avatar={avatarElement}
76
- className={className}
77
- maxTextLength={maxTextLength}
78
- suffix={closeButton}
79
- size={size}
80
- disabled={disabled}
81
- />
82
- );
83
- };