@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.
- package/package.json +1 -1
- package/template/client/src/components/ui/markdown.tsx +43 -0
- package/template/client/src/index.css +0 -1
- package/template/package-lock.json +5783 -13022
- package/template/package.json +5 -68
- package/template/client/src/components/business-ui/README.md +0 -519
- package/template/client/src/components/business-ui/api/chats/queries.ts +0 -39
- package/template/client/src/components/business-ui/api/chats/service.ts +0 -29
- package/template/client/src/components/business-ui/api/departments/queries.ts +0 -42
- package/template/client/src/components/business-ui/api/departments/service.ts +0 -18
- package/template/client/src/components/business-ui/api/files/service.ts +0 -28
- package/template/client/src/components/business-ui/api/user-profiles/queries.ts +0 -42
- package/template/client/src/components/business-ui/api/user-profiles/service.ts +0 -23
- package/template/client/src/components/business-ui/api/users/queries.ts +0 -34
- package/template/client/src/components/business-ui/api/users/service.ts +0 -40
- package/template/client/src/components/business-ui/chat-select/README.md +0 -128
- package/template/client/src/components/business-ui/chat-select/chat-item.tsx +0 -85
- package/template/client/src/components/business-ui/chat-select/chat-select-tag.tsx +0 -83
- package/template/client/src/components/business-ui/chat-select/chat-select.tsx +0 -197
- package/template/client/src/components/business-ui/chat-select/index.tsx +0 -12
- package/template/client/src/components/business-ui/chat-select/types.ts +0 -102
- package/template/client/src/components/business-ui/chat-select/use-chat-value.ts +0 -180
- package/template/client/src/components/business-ui/chat-select/utils.ts +0 -81
- package/template/client/src/components/business-ui/department-select/department-item.tsx +0 -55
- package/template/client/src/components/business-ui/department-select/department-select-field.tsx +0 -86
- package/template/client/src/components/business-ui/department-select/department-select-tag.tsx +0 -56
- package/template/client/src/components/business-ui/department-select/department-select.tsx +0 -168
- package/template/client/src/components/business-ui/department-select/icon-department.tsx +0 -22
- package/template/client/src/components/business-ui/department-select/index.tsx +0 -7
- package/template/client/src/components/business-ui/department-select/types.ts +0 -15
- package/template/client/src/components/business-ui/department-select/utils.ts +0 -17
- package/template/client/src/components/business-ui/entity-combobox/base-combobox-content.tsx +0 -35
- package/template/client/src/components/business-ui/entity-combobox/base-combobox-empty.tsx +0 -28
- package/template/client/src/components/business-ui/entity-combobox/base-combobox-error.tsx +0 -35
- package/template/client/src/components/business-ui/entity-combobox/base-combobox-item.tsx +0 -55
- package/template/client/src/components/business-ui/entity-combobox/base-combobox-list.tsx +0 -174
- package/template/client/src/components/business-ui/entity-combobox/base-combobox-loading.tsx +0 -19
- package/template/client/src/components/business-ui/entity-combobox/base-combobox-search.tsx +0 -31
- package/template/client/src/components/business-ui/entity-combobox/base-combobox-trigger.tsx +0 -281
- package/template/client/src/components/business-ui/entity-combobox/base-combobox.tsx +0 -132
- package/template/client/src/components/business-ui/entity-combobox/context.tsx +0 -19
- package/template/client/src/components/business-ui/entity-combobox/entity-combobox.tsx +0 -145
- package/template/client/src/components/business-ui/entity-combobox/highlight-text.tsx +0 -52
- package/template/client/src/components/business-ui/entity-combobox/hooks.tsx +0 -21
- package/template/client/src/components/business-ui/entity-combobox/index.tsx +0 -31
- package/template/client/src/components/business-ui/entity-combobox/item-pill.tsx +0 -179
- package/template/client/src/components/business-ui/entity-combobox/popover-wrapper.tsx +0 -149
- package/template/client/src/components/business-ui/entity-combobox/search-trigger.tsx +0 -262
- package/template/client/src/components/business-ui/entity-combobox/shared-types.ts +0 -296
- package/template/client/src/components/business-ui/entity-combobox/size-variants.tsx +0 -322
- package/template/client/src/components/business-ui/entity-combobox/types.ts +0 -246
- package/template/client/src/components/business-ui/entity-combobox/use-fetch-data.tsx +0 -81
- package/template/client/src/components/business-ui/entity-combobox/use-infinite-scroll.tsx +0 -99
- package/template/client/src/components/business-ui/entity-combobox/use-popover-outside-click.tsx +0 -32
- package/template/client/src/components/business-ui/form/checkbox-field.tsx +0 -83
- package/template/client/src/components/business-ui/form/context.tsx +0 -38
- package/template/client/src/components/business-ui/form/field-layout.tsx +0 -91
- package/template/client/src/components/business-ui/form/form.tsx +0 -54
- package/template/client/src/components/business-ui/form/hooks/form-context.tsx +0 -4
- package/template/client/src/components/business-ui/form/hooks/form-utils.ts +0 -23
- package/template/client/src/components/business-ui/form/hooks/form.tsx +0 -40
- package/template/client/src/components/business-ui/form/index.tsx +0 -4
- package/template/client/src/components/business-ui/form/input-field.tsx +0 -74
- package/template/client/src/components/business-ui/form/radio-group-field.tsx +0 -138
- package/template/client/src/components/business-ui/form/select-field.tsx +0 -155
- package/template/client/src/components/business-ui/form/switch-field.tsx +0 -66
- package/template/client/src/components/business-ui/form/textarea-field.tsx +0 -72
- package/template/client/src/components/business-ui/form/types.ts +0 -32
- package/template/client/src/components/business-ui/tiptap-editor/README.md +0 -324
- package/template/client/src/components/business-ui/tiptap-editor/components/attachment-toolbar-button.tsx +0 -74
- package/template/client/src/components/business-ui/tiptap-editor/components/blockquote-toolbar-button.tsx +0 -41
- package/template/client/src/components/business-ui/tiptap-editor/components/code-block-toolbar-button.tsx +0 -41
- package/template/client/src/components/business-ui/tiptap-editor/components/color-highlight-toolbar-button.tsx +0 -141
- package/template/client/src/components/business-ui/tiptap-editor/components/heading-toolbar-button.tsx +0 -99
- package/template/client/src/components/business-ui/tiptap-editor/components/horizontal-rule-toolbar-button.tsx +0 -39
- package/template/client/src/components/business-ui/tiptap-editor/components/image-upload-toolbar-button.tsx +0 -65
- package/template/client/src/components/business-ui/tiptap-editor/components/link-edit-form.tsx +0 -127
- package/template/client/src/components/business-ui/tiptap-editor/components/link-hover-toolbar.tsx +0 -380
- package/template/client/src/components/business-ui/tiptap-editor/components/link-toolbar-button.tsx +0 -96
- package/template/client/src/components/business-ui/tiptap-editor/components/list-toolbar-button.tsx +0 -75
- package/template/client/src/components/business-ui/tiptap-editor/components/mark-toolbar-button.tsx +0 -118
- package/template/client/src/components/business-ui/tiptap-editor/components/text-align-toolbar-button.tsx +0 -84
- package/template/client/src/components/business-ui/tiptap-editor/components/undo-redo-toolbar-button.tsx +0 -54
- package/template/client/src/components/business-ui/tiptap-editor/extensions/attachment.tsx +0 -539
- package/template/client/src/components/business-ui/tiptap-editor/extensions/code-block-shiki.tsx +0 -236
- package/template/client/src/components/business-ui/tiptap-editor/extensions/complete-kit.ts +0 -260
- package/template/client/src/components/business-ui/tiptap-editor/extensions/image.tsx +0 -456
- package/template/client/src/components/business-ui/tiptap-editor/hooks/use-tiptap-editor.ts +0 -47
- package/template/client/src/components/business-ui/tiptap-editor/index.ts +0 -38
- package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor-complete.tsx +0 -112
- package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor.tsx +0 -169
- package/template/client/src/components/business-ui/types/user.ts +0 -74
- package/template/client/src/components/business-ui/user-display/index.tsx +0 -2
- package/template/client/src/components/business-ui/user-display/overflow-tooltip-text.tsx +0 -90
- package/template/client/src/components/business-ui/user-display/type.ts +0 -27
- package/template/client/src/components/business-ui/user-display/user-display.tsx +0 -99
- package/template/client/src/components/business-ui/user-display/user-with-avatar.tsx +0 -158
- package/template/client/src/components/business-ui/user-display/utils.ts +0 -37
- package/template/client/src/components/business-ui/user-profile/error-image.tsx +0 -302
- package/template/client/src/components/business-ui/user-profile/user-external-script.ts +0 -73
- package/template/client/src/components/business-ui/user-profile/user-profile.tsx +0 -370
- package/template/client/src/components/business-ui/user-select/index.tsx +0 -28
- package/template/client/src/components/business-ui/user-select/types.ts +0 -109
- package/template/client/src/components/business-ui/user-select/use-user-value.ts +0 -160
- package/template/client/src/components/business-ui/user-select/user-item.tsx +0 -98
- package/template/client/src/components/business-ui/user-select/user-pill.tsx +0 -140
- package/template/client/src/components/business-ui/user-select/user-select-tag.tsx +0 -82
- package/template/client/src/components/business-ui/user-select/user-select.tsx +0 -359
- package/template/client/src/components/business-ui/user-select/utils.tsx +0 -188
- package/template/client/src/components/business-ui/utils/user.ts +0 -72
- package/template/client/src/components/ui/streamdown.tsx +0 -186
- 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
|
-
};
|