im-ui-mobile 0.0.43 → 0.0.44

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 (42) hide show
  1. package/components/im-arrow-bar/im-arrow-bar.vue +59 -0
  2. package/components/im-bar-group/im-bar-group.vue +17 -0
  3. package/components/im-btn-bar/im-btn-bar.vue +60 -0
  4. package/components/im-chat-at-box/im-chat-at-box.vue +189 -0
  5. package/components/im-chat-group-readed/im-chat-group-readed.vue +172 -0
  6. package/components/im-chat-item/im-chat-item.vue +215 -0
  7. package/components/im-chat-message-item/im-chat-message-item.vue +559 -0
  8. package/components/im-chat-record/im-chat-record.vue +303 -0
  9. package/components/im-file-upload/im-file-upload.vue +300 -0
  10. package/components/im-friend-item/im-friend-item.vue +74 -0
  11. package/components/im-group-item/im-group-item.vue +60 -0
  12. package/components/im-group-member-selector/im-group-member-selector.vue +199 -0
  13. package/components/im-group-rtc-join/im-group-rtc-join.vue +112 -0
  14. package/components/im-head-image/im-head-image.vue +122 -0
  15. package/components/im-image-upload/im-image-upload.vue +90 -0
  16. package/components/im-loading/im-loading.vue +63 -0
  17. package/components/im-long-press-menu/im-long-press-menu.vue +137 -0
  18. package/components/im-nav-bar/im-nav-bar.vue +99 -0
  19. package/components/im-switch-bar/im-switch-bar.vue +60 -0
  20. package/components/im-virtual-scroller/im-virtual-scroller.vue +52 -0
  21. package/index.js +13 -3
  22. package/package.json +8 -2
  23. package/plugins/pinia.js +19 -0
  24. package/types/components/arrow-bar.d.ts +14 -0
  25. package/types/components/bar-group.d.ts +14 -0
  26. package/types/components/btn-bar.d.ts +16 -0
  27. package/types/components/chat-at-box.d.ts +22 -0
  28. package/types/components/chat-group-readed.d.ts +30 -0
  29. package/types/components/chat-item.d.ts +21 -0
  30. package/types/components/chat-message-item.d.ts +28 -0
  31. package/types/components/chat-record.d.ts +14 -0
  32. package/types/components/chat-upload.d.ts +58 -0
  33. package/types/components/friend-item.d.ts +19 -0
  34. package/types/components/group-item.d.ts +18 -0
  35. package/types/components/group-member-selector.d.ts +31 -0
  36. package/types/components/group-rtc-join.d.ts +31 -0
  37. package/types/components/head-image.d.ts +18 -0
  38. package/types/components/im-loading.d.ts +20 -0
  39. package/types/components/long-press-menu.d.ts +23 -0
  40. package/types/components/sample.d.ts +1 -3
  41. package/types/components/switch-bar.d.ts +19 -0
  42. package/types/components/virtual-scroller.d.ts +20 -0
@@ -0,0 +1,137 @@
1
+ <template>
2
+ <view class="long-press-menu none-pointer-events">
3
+ <view @longpress.prevent.stop="onLongPress($event)" @touchmove="onTouchMove" @touchend="onTouchEnd">
4
+ <slot></slot>
5
+ </view>
6
+ <view v-if="isShowMenu" class="menu-mask" @touchstart="onClose()" @contextmenu.prevent=""></view>
7
+ <view v-if="isShowMenu" class="menu" :style="menuStyle">
8
+ <view class="menu-item" v-for="(item) in items" :key="item.key" @click.prevent="onSelectMenu(item)">
9
+ <text :style="itemStyle(item)"> {{ item.name }}</text>
10
+ </view>
11
+ </view>
12
+ </view>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ interface Props {
17
+ items?: any[];
18
+ }
19
+
20
+ interface Emits {
21
+ (e: 'select', item: any): void;
22
+ }
23
+
24
+ interface TouchPoint {
25
+ clientX: number;
26
+ clientY: number;
27
+ }
28
+
29
+ const props = defineProps<Props>();
30
+ const emit = defineEmits<Emits>();
31
+
32
+ const isShowMenu = ref(false);
33
+ const isTouchMove = ref(false);
34
+ const menuStyle = ref("");
35
+ const menuTouch = ref<TouchPoint>({ clientX: 0, clientY: 0 });
36
+
37
+ const onLongPress = (e: any) => {
38
+ if (isTouchMove.value) {
39
+ // 屏幕移动时不弹出
40
+ return;
41
+ }
42
+
43
+ uni.getSystemInfo({
44
+ success: (res) => {
45
+ const touches = e.touches[0];
46
+ let style = "";
47
+ /* 因 非H5端不兼容 style 属性绑定 Object ,所以拼接字符 */
48
+ if (touches.clientY > (res.windowHeight / 2)) {
49
+ style = `bottom:${res.windowHeight - touches.clientY + 20}px;`;
50
+ } else {
51
+ style = `top:${touches.clientY + 20}px;`;
52
+ }
53
+ if (touches.clientX > (res.windowWidth / 2)) {
54
+ style += `right:${res.windowWidth - touches.clientX}px;`;
55
+ } else {
56
+ style += `left:${touches.clientX}px;`;
57
+ }
58
+ menuStyle.value = style;
59
+
60
+ menuTouch.value = touches;
61
+ isShowMenu.value = true;
62
+ }
63
+ });
64
+ };
65
+
66
+ const onTouchMove = (e: any) => {
67
+ isTouchMove.value = true;
68
+ const touches = e.touches[0];
69
+ // 屏幕拖动大于50px时,取消菜单
70
+ if (Math.abs(touches.clientX - menuTouch.value.clientX) > 50 ||
71
+ Math.abs(touches.clientY - menuTouch.value.clientY) > 50) {
72
+ onClose();
73
+ }
74
+ };
75
+
76
+ const onTouchEnd = () => {
77
+ isTouchMove.value = false;
78
+ };
79
+
80
+ const onSelectMenu = (item: any) => {
81
+ emit("select", item);
82
+ isShowMenu.value = false;
83
+ };
84
+
85
+ const onClose = () => {
86
+ isShowMenu.value = false;
87
+ };
88
+
89
+ const itemStyle = (item: any) => {
90
+ if (item.color) {
91
+ return `color:${item.color};`;
92
+ }
93
+ return `color:#000;`;
94
+ };
95
+ </script>
96
+
97
+ <style lang="scss" scoped>
98
+ .long-press-menu {
99
+ .menu-mask {
100
+ position: fixed;
101
+ left: 0;
102
+ top: 0;
103
+ right: 0;
104
+ bottom: 0;
105
+ width: 100%;
106
+ height: 100%;
107
+ z-index: 999;
108
+ }
109
+
110
+ .menu {
111
+ position: fixed;
112
+ border-radius: 4px;
113
+ overflow: hidden;
114
+ background-color: #fff;
115
+ z-index: 1000;
116
+ box-shadow: $im-box-shadow-dark;
117
+
118
+ .menu-item {
119
+ height: 28px;
120
+ min-width: 120rpx;
121
+ line-height: 28px;
122
+ font-size: $im-font-size-small;
123
+ display: flex;
124
+ padding: 6px 20px;
125
+ justify-content: flex-start;
126
+
127
+ &:hover {
128
+ background: $im-bg-active;
129
+ }
130
+
131
+ .menu-icon {
132
+ margin-right: 10rpx;
133
+ }
134
+ }
135
+ }
136
+ }
137
+ </style>
@@ -0,0 +1,99 @@
1
+ <template>
2
+ <view class="nav-bar">
3
+ <!-- #ifdef APP-PLUS -->
4
+ <view style="height: var(--status-bar-height)"></view>
5
+ <!-- #endif -->
6
+ <view class="nav-bar-content">
7
+ <!-- #ifndef MP-WEIXIN -->
8
+ <view class="back" @click="handleBackClick" v-if="back">
9
+ <u-icon name="arrow-left" :size="iconFontSize"></u-icon>
10
+ </view>
11
+ <!-- #endif -->
12
+ <view class="title" v-if="title">
13
+ <slot></slot>
14
+ </view>
15
+ <view class="btn">
16
+ <u-icon class="btn-item" v-if="search" name="search" :size="iconFontSize"
17
+ @click="$emit('search')"></u-icon>
18
+ <u-icon class="btn-item" v-if="add" name="plus" :size="iconFontSize" @click="$emit('add')"></u-icon>
19
+ <u-icon class="btn-item" v-if="more" name="more-dot-fill" :size="iconFontSize"
20
+ @click="$emit('more')"></u-icon>
21
+ </view>
22
+ </view>
23
+ </view>
24
+ </template>
25
+
26
+ <script setup lang="ts">
27
+ interface Props {
28
+ back?: boolean;
29
+ title?: boolean;
30
+ search?: boolean;
31
+ add?: boolean;
32
+ more?: boolean;
33
+ iconFontSize?: number;
34
+ }
35
+
36
+ const props = withDefaults(defineProps<Props>(), {
37
+ back: false,
38
+ title: true,
39
+ search: false,
40
+ add: false,
41
+ more: false,
42
+ iconFontSize: 24
43
+ });
44
+
45
+ const handleBackClick = () => {
46
+ uni.navigateBack({
47
+ delta: 1
48
+ });
49
+ };
50
+ </script>
51
+
52
+ <style scoped lang="scss">
53
+ .nav-bar {
54
+ background: $im-bg-linear;
55
+ position: fixed;
56
+ top: 0;
57
+ width: 100%;
58
+ color: $im-text-color;
59
+ border-bottom: 1px solid $im-border-light;
60
+ font-size: $im-font-size-large;
61
+ z-index: 99;
62
+
63
+ .nav-bar-content {
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ box-sizing: border-box;
68
+ height: $im-nav-bar-height;
69
+
70
+ .title {}
71
+
72
+ .back {
73
+ position: absolute;
74
+ left: 0;
75
+ height: 100%;
76
+ display: flex;
77
+ align-items: center;
78
+ padding: 12px;
79
+ font-size: 22px;
80
+ box-sizing: border-box;
81
+ }
82
+
83
+ .btn {
84
+ position: absolute;
85
+ right: 0;
86
+ height: 100%;
87
+ display: flex;
88
+ padding: 12px;
89
+ align-items: center;
90
+ box-sizing: border-box;
91
+
92
+ .btn-item {
93
+ margin-left: 8px;
94
+ }
95
+ }
96
+ }
97
+
98
+ }
99
+ </style>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <view class="switch-bar">
3
+ <text class="title">{{ title }}</text>
4
+ <switch class="switch" :checked="checked" color="#18bc37" @change="onChange"></switch>
5
+ </view>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ interface Props {
10
+ title: string;
11
+ checked?: boolean;
12
+ }
13
+
14
+ interface Emits {
15
+ (e: 'change', value: boolean): void;
16
+ }
17
+
18
+ const props = withDefaults(defineProps<Props>(), {
19
+ checked: false
20
+ });
21
+
22
+ const emit = defineEmits<Emits>();
23
+
24
+ const value = ref(props.checked);
25
+
26
+ watch(() => props.checked, (newVal) => {
27
+ value.value = newVal;
28
+ });
29
+
30
+ const onChange = (e: any) => {
31
+ value.value = true;
32
+ setTimeout(() => {
33
+ value.value = false;
34
+ }, 100);
35
+
36
+ emit('change', e);
37
+ };
38
+ </script>F
39
+
40
+ <style lang="scss" scoped>
41
+ .switch-bar {
42
+ width: 100%;
43
+ height: 100rpx;
44
+ font-size: 34rpx;
45
+ color: black;
46
+ margin-top: 5rpx;
47
+ background-color: white;
48
+ line-height: 100rpx;
49
+ display: flex;
50
+
51
+ .title {
52
+ flex: 1;
53
+ margin-left: 40rpx;
54
+ }
55
+
56
+ .switch {
57
+ margin-right: 40rpx;
58
+ }
59
+ }
60
+ </style>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <scroll-view scroll-y="true" :style="{ height: height }" upper-threshold="200" @scrolltolower="onScrollToBottom"
3
+ scroll-with-animation="true">
4
+ <view class="virtual-scroller" v-for="(item, idx) in showItems" :key="idx">
5
+ <slot :item="item">
6
+ </slot>
7
+ </view>
8
+ </scroll-view>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ interface Props {
13
+ height?: string;
14
+ items?: any[];
15
+ size?: number;
16
+ }
17
+
18
+ const props = withDefaults(defineProps<Props>(), {
19
+ height: '60vh',
20
+ items: () => [],
21
+ size: 30
22
+ });
23
+
24
+ const page = ref(1);
25
+ const isInitEvent = ref(false);
26
+ const lockTip = ref(false);
27
+
28
+ const onScrollToBottom = (e: any) => {
29
+ if (showMaxIdx.value >= props.items.length) {
30
+ showTip();
31
+ } else {
32
+ page.value++;
33
+ }
34
+ };
35
+
36
+ const showTip = () => {
37
+ uni.showToast({
38
+ title: "已滚动至底部",
39
+ icon: 'none'
40
+ });
41
+ };
42
+
43
+ const showMaxIdx = computed(() => {
44
+ return Math.min(page.value * props.size, props.items.length);
45
+ });
46
+
47
+ const showItems = computed(() => {
48
+ return props.items.slice(0, showMaxIdx.value);
49
+ });
50
+ </script>
51
+
52
+ <style scoped></style>
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { Pinia } from './plugins/pinia.js'
1
2
  import { UViewPlusPlugin } from './plugins/uview-plus.js'
2
3
  // import ImSample from './components/im-sample/im-sample.vue'
3
4
 
@@ -13,13 +14,13 @@ const components = [];
13
14
 
14
15
  for (const key in importFn) {
15
16
  const component = importFn[key].default;
16
-
17
+
17
18
  // 1. 从文件路径中提取组件名(更可靠的方法)
18
19
  const fileName = key.split('/').pop(); // 获取文件名,如 im-button.vue
19
20
  const componentName = fileName
20
21
  .replace('.vue', '') // 去掉 .vue 后缀
21
22
  .replace(/\b\w/g, l => l.toUpperCase()) // 首字母大写,如 ImButton
22
-
23
+
23
24
  // 2. 添加到组件列表
24
25
  if (component) {
25
26
  // 如果组件本身没有 name,给它设置一个
@@ -31,13 +32,22 @@ for (const key in importFn) {
31
32
  }
32
33
  }
33
34
 
34
- const install = (app) => {
35
+ const install = (app, options = {}) => {
36
+ // 安装pinia插件
37
+ if (options.usePinia !== false) {
38
+ app.use(Pinia, options.piniaOptions)
39
+ }
40
+
35
41
  // 安装 uview-plus
36
42
  app.use(UViewPlusPlugin)
37
43
 
44
+ // 注册组件
38
45
  components.forEach(component => {
39
46
  app.component(component.name || '', component)
40
47
  })
48
+
49
+ // 提供全局配置
50
+ app.provide('im-config', options.config || {})
41
51
  }
42
52
 
43
53
  import eventBus from "./utils/eventBus.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "im-ui-mobile",
3
- "version": "0.0.43",
3
+ "version": "0.0.44",
4
4
  "description": "A Vue3.0 + typescript instant messaging component library for Uniapp",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -36,7 +36,13 @@
36
36
  "peerDependencies": {
37
37
  "@dcloudio/uni-app": "3.0.0-4070520250711001",
38
38
  "uview-plus": "^3.1.0",
39
- "vue": "^3.3.0"
39
+ "vue": "^3.3.0",
40
+ "pinia": "^2.1.7"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "pinia": {
44
+ "optional": true
45
+ }
40
46
  },
41
47
  "keywords": [
42
48
  "vue3",
@@ -0,0 +1,19 @@
1
+ import { createPinia } from 'pinia'
2
+
3
+ export const Pinia = {
4
+ install(app, options = {}) {
5
+ // 创建pinia实例
6
+ const pinia = createPinia()
7
+
8
+ // 可选:添加插件
9
+ if (options.persist) {
10
+ pinia.use(piniaPluginPersistedstate)
11
+ }
12
+
13
+ // 注册pinia
14
+ app.use(pinia)
15
+
16
+ // 提供injection key
17
+ app.provide('imPinia', pinia)
18
+ }
19
+ }
@@ -0,0 +1,14 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+
3
+ declare interface ArrowBarProps {
4
+ title: string
5
+ icon?: string
6
+ }
7
+
8
+ declare interface _ArrowBar {
9
+ new(): {
10
+ $props: AllowedComponentProps & VNodeProps & ArrowBarProps
11
+ }
12
+ }
13
+
14
+ export declare const ArrowBar: _ArrowBar
@@ -0,0 +1,14 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+
3
+ declare interface BarGroupSlots {
4
+ default?: () => any
5
+ }
6
+
7
+ declare interface _BarGroup {
8
+ new(): {
9
+ $props: AllowedComponentProps & VNodeProps
10
+ $slots: BarGroupSlots
11
+ }
12
+ }
13
+
14
+ export declare const BarGroup: _BarGroup
@@ -0,0 +1,16 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+
3
+ declare interface BtnBarProps {
4
+ title: string
5
+ icon?: string
6
+ type?: 'normal' | 'danger' | 'primary'
7
+ color?: string
8
+ }
9
+
10
+ declare interface _BtnBar {
11
+ new(): {
12
+ $props: AllowedComponentProps & VNodeProps & BtnBarProps
13
+ }
14
+ }
15
+
16
+ export declare const BtnBar: _BtnBar
@@ -0,0 +1,22 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+ import { GroupMember } from '../../libs'
3
+
4
+ declare interface ChatAtBoxProps {
5
+ ownerId?: number
6
+ members?: any[]
7
+ }
8
+
9
+ declare interface ChatAtBoxEmits {
10
+ (e: 'complete', userIds: number[]): void
11
+ }
12
+
13
+ declare interface _ChatAtBox {
14
+ new(): {
15
+ $props: AllowedComponentProps & VNodeProps & ChatAtBoxProps
16
+ $emit: ChatAtBoxEmits
17
+ }
18
+ init: (userId: number, atUserIds: number[]) => void
19
+ open: () => void
20
+ }
21
+
22
+ export declare const ChatAtBox: _ChatAtBox
@@ -0,0 +1,30 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+ import { Chat, Message } from '../../libs'
3
+
4
+ declare interface ChatGroupReadedProps {
5
+ msgInfo: {
6
+ groupId: number
7
+ id: number
8
+ sendId: number
9
+ }
10
+ groupMembers: any[]
11
+ }
12
+
13
+ declare interface ChatGroupReadedEmits {
14
+ (e: 'loaded', chatInfo: Chat, msgInfo: Message): void
15
+ }
16
+
17
+ declare interface Member {
18
+ userId: number
19
+ quit?: boolean
20
+ }
21
+
22
+ declare interface _ChatGroupReaded {
23
+ new(): {
24
+ $props: AllowedComponentProps & VNodeProps & ChatGroupReadedProps
25
+ $emit: ChatGroupReadedEmits
26
+ }
27
+ open: (userIds: number[]) => void
28
+ }
29
+
30
+ export declare const ChatGroupReaded: _ChatGroupReaded
@@ -0,0 +1,21 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+
3
+ declare interface ChatItemProps {
4
+ chat?: any
5
+ index?: number
6
+ active?: boolean
7
+ online: boolean
8
+ }
9
+
10
+ declare interface ChatItemEmits {
11
+ (e: 'click', targetId: number): void
12
+ }
13
+
14
+ declare interface _ChatItem {
15
+ new(): {
16
+ $props: AllowedComponentProps & VNodeProps & ChatItemProps
17
+ $emit: ChatItemEmits
18
+ }
19
+ }
20
+
21
+ export declare const ChatItem: _ChatItem
@@ -0,0 +1,28 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+
3
+ declare interface ChatMessageItemProps {
4
+ avatar?: string
5
+ showName: string
6
+ msgInfo: any
7
+ groupMembers?: any[]
8
+ }
9
+
10
+ declare interface ChatMessageItemEmits {
11
+ (e: 'call'): void
12
+ (e: 'longPressHead'): void
13
+ (e: 'resend', msgInfo: any): void
14
+ (e: 'audioStateChange', state: string, msgInfo: any): void
15
+ (e: 'copy', msgInfo: any): void
16
+ (e: 'recall', msgInfo: any): void
17
+ (e: 'delete', msgInfo: any): void
18
+ (e: 'download', msgInfo: any): void
19
+ }
20
+
21
+ declare interface _ChatMessageItem {
22
+ new(): {
23
+ $props: AllowedComponentProps & VNodeProps & ChatMessageItemProps
24
+ $emit: ChatMessageItemEmits
25
+ }
26
+ }
27
+
28
+ export declare const ChatMessageItem: _ChatMessageItem
@@ -0,0 +1,14 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+
3
+ declare interface ChatRecordEmits {
4
+ (e: 'send', data: any): void
5
+ }
6
+
7
+ declare interface _ChatRecord {
8
+ new(): {
9
+ $props: AllowedComponentProps & VNodeProps
10
+ $emit: ChatRecordEmits
11
+ }
12
+ }
13
+
14
+ export declare const ChatRecord: _ChatRecord
@@ -0,0 +1,58 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+
3
+ declare interface UploadFile {
4
+ url: string
5
+ name?: string
6
+ size?: number
7
+ type?: string
8
+ progress?: number
9
+ status?: 'uploading' | 'success' | 'error'
10
+ response?: any
11
+ }
12
+
13
+ declare interface ChatUploadProps {
14
+ modelValue?: UploadFile[]
15
+ maxCount?: number
16
+ maxSize?: number
17
+ sizeType?: string[]
18
+ sourceType?: string[]
19
+ deletable?: boolean
20
+ previewFullImage?: boolean
21
+ multiple?: boolean
22
+ disabled?: boolean
23
+ customBtn?: boolean
24
+ showProgress?: boolean
25
+ uploadText?: string
26
+ width?: number | string
27
+ height?: number | string
28
+ action?: string
29
+ name?: string
30
+ headers?: Record<string, string>
31
+ data?: Record<string, any>
32
+ autoUpload?: boolean
33
+ token?: string
34
+ }
35
+
36
+ declare interface ChatUploadEmits {
37
+ (e: 'update:modelValue', files: UploadFile[]): void
38
+ (e: 'change', files: UploadFile[]): void
39
+ (e: 'before', file: UploadFile): void
40
+ (e: 'success', file: UploadFile, response: any): void
41
+ (e: 'fail', file: UploadFile, error: any): void
42
+ (e: 'progress', file: UploadFile, progress: number): void
43
+ (e: 'delete', file: UploadFile, index: number): void
44
+ (e: 'oversize', file: UploadFile): void
45
+ }
46
+
47
+ declare interface _ChatUpload {
48
+ new(): {
49
+ $props: AllowedComponentProps & VNodeProps & ChatUploadProps
50
+ $emit: ChatUploadEmits
51
+ }
52
+ submit: () => Promise<UploadFile[]>
53
+ clearFiles: () => void
54
+ getFiles: () => UploadFile[]
55
+ hide: () => void
56
+ }
57
+
58
+ export declare const ChatUpload: _ChatUpload
@@ -0,0 +1,19 @@
1
+ import { AllowedComponentProps, VNodeProps } from './_common'
2
+
3
+ declare interface FriendItemProps {
4
+ friend?: any
5
+ detail?: boolean
6
+ }
7
+
8
+ declare interface FriendItemSlots {
9
+ default?: () => any
10
+ }
11
+
12
+ declare interface _FriendItem {
13
+ new(): {
14
+ $props: AllowedComponentProps & VNodeProps & FriendItemProps
15
+ $slots: FriendItemSlots
16
+ }
17
+ }
18
+
19
+ export declare const FriendItem: _FriendItem