im-ui-mobile 0.1.7 → 0.1.9

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/README.md CHANGED
@@ -1,16 +1,39 @@
1
- # im-ui-mobile
1
+ # im-ui-mobile 移动端组件库
2
2
 
3
- 一个基于 Vue 3 + TypeScript 的即时通信 Uniapp 组件库
3
+ <p align="center">
4
+ <img src="https://img.shields.io/badge/version-1.0.0-blue.svg" alt="version">
5
+ <img src="https://img.shields.io/badge/license-MIT-green.svg" alt="license">
6
+ <img src="https://img.shields.io/badge/uni--app-3.0+-orange.svg" alt="uniapp">
7
+ <img src="https://img.shields.io/badge/vue-3.0+-brightgreen.svg" alt="vue">
8
+ </p>
4
9
 
5
- ## 安装
10
+ <p align="center">
11
+ <strong>一个基于 Vue 3 + TypeScript 的即时通信 Uniapp 组件库</strong>
12
+ </p>
13
+
14
+ ## ✨ 特性
15
+
16
+ - 🚀 **基于 Vue 3 Composition API** - 现代化的组件开发方式
17
+ - 📱 **uniapp 多端支持** - 一套代码,多端运行
18
+ - 🎨 **TypeScript 支持** - 完整的类型定义,开发更友好
19
+ - 🎯 **主题定制** - 支持自定义主题和样式
20
+ - 📦 **按需引入** - 支持组件按需加载,减小包体积
21
+ - 📖 **完整文档** - 详细的文档和示例
22
+ - 🎮 **流畅动画** - 精心设计的交互动画
23
+
24
+ ## 📦 安装
25
+
26
+ ### NPM 安装
6
27
 
7
28
  ```bash
8
- npm install im-ui-mobile
29
+ npm install im-ui-mobile --save
30
+ # 或
31
+ yarn add im-ui-mobile
9
32
  ```
10
33
 
11
34
  ## 演示
12
35
 
13
- 微信扫码,查看演示。
36
+ 微信扫码,查看演示
14
37
 
15
38
  ![微信小程序码](https://fdm-shop-25.oss-cn-shenzhen.aliyuncs.com/fdm26010502453644377ae6e77.jpg)
16
39
 
@@ -20,12 +20,6 @@
20
20
  @chooseavatar="onChooseAvatar">
21
21
  <!-- 加载状态 -->
22
22
  <view v-if="loading && showLoading" class="im-button__loading">
23
- <!-- <view class="im-button__loading-spinner" :style="loadingStyle">
24
- <view v-for="n in loadingDotCount" :key="n" class="im-button__loading-dot" :style="{
25
- animationDelay: `${(n - 1) * 0.15}s`,
26
- backgroundColor: loadingColor
27
- }" />
28
- </view> -->
29
23
  <text v-if="loadingText" class="im-button__loading-text">
30
24
  {{ loadingText }}
31
25
  </text>
@@ -63,7 +57,7 @@
63
57
  </template>
64
58
 
65
59
  <script setup lang="ts">
66
- import { computed, withDefaults } from 'vue'
60
+ import { computed } from 'vue'
67
61
  import { validator } from '../../index'
68
62
 
69
63
  // 定义类型
@@ -109,7 +103,7 @@ interface Props {
109
103
  color?: string
110
104
  bgColor?: string
111
105
  borderColor?: string
112
- border: boolean
106
+ border?: boolean
113
107
  textColor?: string
114
108
  textSize?: TextSize
115
109
  width?: string
@@ -377,7 +371,7 @@ uni-button:after {
377
371
  left: 0;
378
372
  right: 0;
379
373
  bottom: 0;
380
- border: 1rpx solid currentColor;//
374
+ border: 1rpx solid currentColor; //
381
375
  border-radius: inherit;
382
376
  pointer-events: none;
383
377
  transform: scale(0.5);
@@ -93,8 +93,7 @@
93
93
  <script setup lang="ts">
94
94
  import { computed } from 'vue'
95
95
  import type { CSSProperties } from 'vue'
96
- import ImIcon from '../im-icon/im-icon.vue'
97
- import ImBadge from '../im-badge/im-badge.vue'
96
+ import ImButton from '../im-button/im-button.vue'
98
97
 
99
98
  // 类型定义
100
99
  interface CardAction {
@@ -96,7 +96,7 @@
96
96
  </template>
97
97
 
98
98
  <script setup lang="ts">
99
- import { computed, withDefaults, useSlots } from 'vue'
99
+ import { computed, useSlots } from 'vue'
100
100
 
101
101
  // 定义类型
102
102
  type CellType = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info'
@@ -98,6 +98,7 @@
98
98
  <script setup lang="ts">
99
99
  import { ref, computed, watch, nextTick } from 'vue'
100
100
  import type { Modal as ModalType } from 'im-ui-mobile'
101
+ import ImModal from '../im-modal/im-modal.vue'
101
102
 
102
103
  // 定义对话框类型
103
104
  type DialogType = 'alert' | 'confirm' | 'prompt' | 'success' | 'error' | 'warning' | 'info' | 'html' | 'custom'
@@ -179,6 +179,7 @@
179
179
 
180
180
  <script setup lang="ts">
181
181
  import { ref, computed, watch, onMounted } from 'vue'
182
+ import ImButton from '../im-button/im-button.vue'
182
183
 
183
184
  // 类型定义
184
185
  interface Emoji {
@@ -40,8 +40,7 @@
40
40
 
41
41
  <script setup lang="ts">
42
42
  import { ref, computed } from 'vue'
43
- import ImAvatar from '../im-avatar/im-avatar.vue'
44
- import ImVirtualList from '../im-virtual-list/im-virtual-list.vue'
43
+ import ImPopup from '../im-popup/im-popup.vue'
45
44
 
46
45
  interface Props {
47
46
  group?: any;
@@ -23,6 +23,8 @@
23
23
 
24
24
  <script setup lang="ts">
25
25
  import ImAvatar from '../im-avatar/im-avatar.vue'
26
+ import ImPopup from '../im-popup/im-popup.vue'
27
+ import ImModal from '../im-modal/im-modal.vue'
26
28
  import { ref } from 'vue';
27
29
  import { UserInfo } from '../../libs';
28
30
 
@@ -113,6 +113,7 @@
113
113
 
114
114
  <script setup lang="ts">
115
115
  import { computed } from 'vue'
116
+ import ImBadge from '../im-badge/im-badge.vue'
116
117
 
117
118
  // 定义 Props
118
119
  interface Props {
@@ -10,6 +10,7 @@
10
10
 
11
11
  <script setup lang="ts">
12
12
  import { computed } from 'vue'
13
+ import ImIcon from '../im-icon/im-icon.vue'
13
14
 
14
15
  interface Props {
15
16
  size?: number;
@@ -36,9 +36,11 @@
36
36
 
37
37
  <script setup lang="ts">
38
38
  import { ref, computed } from 'vue'
39
+ import { GroupMember } from '../../libs';
40
+ import ImPopup from '../im-popup/im-popup.vue'
39
41
  import ImAvatar from '../im-avatar/im-avatar.vue'
42
+ import ImSearch from '../im-search/im-search.vue'
40
43
  import ImVirtualList from '../im-virtual-list/im-virtual-list.vue'
41
- import { GroupMember } from '../../libs';
42
44
 
43
45
  interface Props {
44
46
  ownerId?: number;
@@ -63,6 +63,8 @@
63
63
 
64
64
  <script setup lang="ts">
65
65
  import { ref, computed, watch, nextTick } from 'vue'
66
+ import ImIcon from '../im-icon/im-icon.vue'
67
+ import ImBadge from '../im-badge/im-badge.vue'
66
68
 
67
69
  // 类型定义
68
70
  interface MessageAction {
@@ -117,7 +117,7 @@
117
117
  <script setup lang="ts">
118
118
  import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
119
119
  import { useDynamicRefs, datetime } from 'im-ui-mobile'
120
- import { MESSAGE_TYPE, MESSAGE_STATUS } from '../..'
120
+ import type { MESSAGE_TYPE, MESSAGE_STATUS } from 'im-ui-mobile'
121
121
  import { Message, GroupMember } from '../../libs'
122
122
 
123
123
  // 使用动态引用
@@ -1,29 +1,11 @@
1
1
  <template>
2
- <im-popup
3
- v-model:show="visible"
4
- ref="popupRef"
5
- :position="position"
6
- :animation="animation"
7
- :duration="duration"
8
- :show-overlay="showOverlay"
9
- :overlay-color="overlayColor"
10
- :overlay-opacity="overlayOpacity"
11
- :close-on-click-overlay="closeOnClickOverlay"
12
- :lock-scroll="lockScroll"
13
- :width="computedWidth"
14
- :border-radius="borderRadius"
15
- :background-color="backgroundColor"
16
- :z-index="zIndex"
17
- :show-close="showClose && showHeader"
18
- :close-position="closePosition"
19
- :safe-area-inset-bottom="safeAreaInsetBottom"
20
- :custom-class="customClass"
21
- @open="handleOpen"
22
- @opened="handleOpened"
23
- @close="handleClose"
24
- @closed="handleClosed"
25
- @click-overlay="handleClickOverlay"
26
- >
2
+ <im-popup v-model:show="visible" ref="popupRef" :position="position" :animation="animation" :duration="duration"
3
+ :show-overlay="showOverlay" :overlay-color="overlayColor" :overlay-opacity="overlayOpacity"
4
+ :close-on-click-overlay="closeOnClickOverlay" :lock-scroll="lockScroll" :width="computedWidth"
5
+ :border-radius="borderRadius" :background-color="backgroundColor" :z-index="zIndex"
6
+ :show-close="showClose && showHeader" :close-position="closePosition" :safe-area-inset-bottom="safeAreaInsetBottom"
7
+ :custom-class="customClass" @open="handleOpen" @opened="handleOpened" @close="handleClose" @closed="handleClosed"
8
+ @click-overlay="handleClickOverlay">
27
9
  <!-- 头部 -->
28
10
  <view v-if="showHeader" class="im-modal__header">
29
11
  <slot name="header">
@@ -48,30 +30,14 @@
48
30
  <slot name="footer">
49
31
  <view class="im-modal__footer-buttons">
50
32
  <!-- 取消按钮 -->
51
- <im-button
52
- v-if="showCancel"
53
- class="im-modal__button"
54
- :type="cancelType"
55
- :size="buttonSize"
56
- :plain="cancelPlain"
57
- :disabled="cancelDisabled"
58
- :loading="cancelLoading"
59
- @click="handleCancel"
60
- >
33
+ <im-button v-if="showCancel" class="im-modal__button" :type="cancelType" :size="buttonSize"
34
+ :plain="cancelPlain" :disabled="cancelDisabled" :loading="cancelLoading" @click="handleCancel">
61
35
  {{ cancelText }}
62
36
  </im-button>
63
37
 
64
38
  <!-- 确认按钮 -->
65
- <im-button
66
- v-if="showConfirm"
67
- class="im-modal__button"
68
- :type="confirmType"
69
- :size="buttonSize"
70
- :plain="confirmPlain"
71
- :disabled="confirmDisabled"
72
- :loading="confirmLoading"
73
- @click="handleConfirm"
74
- >
39
+ <im-button v-if="showConfirm" class="im-modal__button" :type="confirmType" :size="buttonSize"
40
+ :plain="confirmPlain" :disabled="confirmDisabled" :loading="confirmLoading" @click="handleConfirm">
75
41
  {{ confirmText }}
76
42
  </im-button>
77
43
  </view>
@@ -83,25 +49,27 @@
83
49
  <script setup lang="ts">
84
50
  import { ref, computed } from 'vue'
85
51
  import type { Popup as PopupType } from 'im-ui-mobile'
52
+ import ImPopup from '../im-popup/im-popup.vue'
53
+ import ImButton from '../im-button/im-button.vue'
86
54
 
87
55
  // 定义 Props
88
56
  interface Props {
89
57
  // 显示控制
90
58
  show?: boolean
91
59
  modelValue?: boolean
92
-
60
+
93
61
  // 标题和内容
94
62
  title?: string
95
63
  subtitle?: string
96
64
  content?: string
97
-
65
+
98
66
  // 头部配置
99
67
  showHeader?: boolean
100
68
  showSubtitle?: boolean
101
-
69
+
102
70
  // 内容配置
103
71
  bodyStyle?: Record<string, string>
104
-
72
+
105
73
  // 底部按钮配置
106
74
  showFooter?: boolean
107
75
  showCancel?: boolean
@@ -117,7 +85,7 @@ interface Props {
117
85
  cancelLoading?: boolean
118
86
  confirmLoading?: boolean
119
87
  buttonSize?: 'small' | 'medium' | 'large'
120
-
88
+
121
89
  // 弹窗配置(传递给 im-popup)
122
90
  position?: 'center' | 'top' | 'bottom' | 'left' | 'right'
123
91
  animation?: 'fade' | 'slide' | 'zoom' | 'none'
@@ -137,7 +105,7 @@ interface Props {
137
105
  closePosition?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
138
106
  safeAreaInsetBottom?: boolean
139
107
  customClass?: string
140
-
108
+
141
109
  // 行为控制
142
110
  closeOnClickAction?: boolean
143
111
  showCloseAfterConfirm?: boolean
@@ -165,16 +133,16 @@ interface Emits {
165
133
  const props = withDefaults(defineProps<Props>(), {
166
134
  show: false,
167
135
  modelValue: false,
168
-
136
+
169
137
  title: '提示',
170
138
  subtitle: '',
171
139
  content: '',
172
-
140
+
173
141
  showHeader: true,
174
142
  showSubtitle: true,
175
-
143
+
176
144
  bodyStyle: () => ({}),
177
-
145
+
178
146
  showFooter: true,
179
147
  showCancel: true,
180
148
  showConfirm: true,
@@ -189,7 +157,7 @@ const props = withDefaults(defineProps<Props>(), {
189
157
  cancelLoading: false,
190
158
  confirmLoading: false,
191
159
  buttonSize: 'medium',
192
-
160
+
193
161
  position: 'center',
194
162
  animation: 'zoom',
195
163
  duration: 300,
@@ -208,7 +176,7 @@ const props = withDefaults(defineProps<Props>(), {
208
176
  closePosition: 'top-right',
209
177
  safeAreaInsetBottom: true,
210
178
  customClass: '',
211
-
179
+
212
180
  closeOnClickAction: true,
213
181
  showCloseAfterConfirm: true,
214
182
  showCloseAfterCancel: true
@@ -231,17 +199,17 @@ const visible = computed({
231
199
  // 计算宽度
232
200
  const computedWidth = computed(() => {
233
201
  let width = props.width
234
-
202
+
235
203
  // 添加最大宽度限制
236
204
  if (props.maxWidth) {
237
205
  width = `min(${props.width}, ${props.maxWidth})`
238
206
  }
239
-
207
+
240
208
  // 添加最小宽度限制
241
209
  if (props.minWidth) {
242
210
  width = `max(${width}, ${props.minWidth})`
243
211
  }
244
-
212
+
245
213
  return width
246
214
  })
247
215
 
@@ -292,7 +260,7 @@ const handleCancel = () => {
292
260
 
293
261
  // 打开模态框
294
262
  const open = () => {
295
- popupRef.value.open()
263
+ popupRef.value.open()
296
264
  }
297
265
 
298
266
  // 关闭模态框
@@ -318,12 +286,12 @@ defineExpose({
318
286
  &__header {
319
287
  padding: 40rpx 40rpx 20rpx;
320
288
  border-bottom: 2rpx solid #f5f5f5;
321
-
289
+
322
290
  &-content {
323
291
  text-align: center;
324
292
  }
325
293
  }
326
-
294
+
327
295
  &__title {
328
296
  display: block;
329
297
  font-size: 36rpx;
@@ -331,17 +299,17 @@ defineExpose({
331
299
  color: #333;
332
300
  line-height: 1.4;
333
301
  }
334
-
302
+
335
303
  &__subtitle {
336
304
  margin-top: 12rpx;
337
-
305
+
338
306
  text {
339
307
  font-size: 28rpx;
340
308
  color: #999;
341
309
  line-height: 1.4;
342
310
  }
343
311
  }
344
-
312
+
345
313
  &__body {
346
314
  padding: 40rpx;
347
315
  font-size: 30rpx;
@@ -350,22 +318,22 @@ defineExpose({
350
318
  overflow-y: auto;
351
319
  max-height: 60vh;
352
320
  }
353
-
321
+
354
322
  &__content {
355
323
  display: block;
356
324
  text-align: center;
357
325
  }
358
-
326
+
359
327
  &__footer {
360
328
  padding: 20rpx 40rpx 40rpx;
361
-
329
+
362
330
  &-buttons {
363
331
  display: flex;
364
332
  gap: 20rpx;
365
333
  justify-content: center;
366
334
  }
367
335
  }
368
-
336
+
369
337
  &__button {
370
338
  flex: 1;
371
339
  }
@@ -99,6 +99,7 @@
99
99
  import { ref, computed, onMounted, watch } from 'vue'
100
100
  import { onLoad, onShow } from '@dcloudio/uni-app'
101
101
  import type { IconType } from '../../types/components/icon'
102
+ import ImIcon from '../im-icon/im-icon.vue'
102
103
 
103
104
  // 类型定义
104
105
  interface NavBarAction {
@@ -34,9 +34,11 @@
34
34
 
35
35
  <script setup lang="ts">
36
36
  import { ref } from 'vue'
37
+ import { Chat, Message } from '../../libs';
37
38
  import ImAvatar from '../im-avatar/im-avatar.vue'
38
39
  import ImVirtualList from '../im-virtual-list/im-virtual-list.vue'
39
- import { Chat, Message } from '../../libs';
40
+ import ImTabs from '../im-tabs/im-tabs.vue'
41
+ import ImPopup from '../im-popup/im-popup.vue'
40
42
 
41
43
  interface Props {
42
44
  msgInfo: {
@@ -1,9 +1,9 @@
1
1
  <template>
2
2
  <view class="im-search" :class="[
3
- isFocused?'im-search--focus': '',
4
- disabled?'im-search--disabled': '',
5
- `im-search--${size}`
6
- ]">
3
+ isFocused ? 'im-search--focus' : '',
4
+ disabled ? 'im-search--disabled' : '',
5
+ `im-search--${size}`
6
+ ]">
7
7
  <!-- 搜索图标 -->
8
8
  <view class="im-search__icon" @tap="handleIconClick">
9
9
  <slot name="icon">
@@ -11,52 +11,29 @@
11
11
  <im-icon size="38" name="search" />
12
12
  </slot>
13
13
  </view>
14
-
14
+
15
15
  <!-- 搜索输入框 -->
16
- <input
17
- ref="inputRef"
18
- class="im-search__input"
19
- :type="inputType"
20
- :value="modelValue"
21
- :placeholder="placeholder"
22
- :placeholder-style="placeholderStyle"
23
- :maxlength="maxlength"
24
- :disabled="disabled"
25
- :focus="autoFocus"
26
- :confirm-type="confirmType"
27
- :adjust-position="adjustPosition"
28
- :cursor-spacing="cursorSpacing"
29
- :show-confirm-bar="showConfirmBar"
30
- @input="handleInput"
31
- @focus="handleFocus"
32
- @blur="handleBlur"
33
- @confirm="handleConfirm"
34
- @keyboardheightchange="handleKeyboardHeightChange"
35
- />
36
-
16
+ <input ref="inputRef" class="im-search__input" :type="inputType" :value="modelValue" :placeholder="placeholder"
17
+ :placeholder-style="placeholderStyle" :maxlength="maxlength" :disabled="disabled" :focus="autoFocus"
18
+ :confirm-type="confirmType" :adjust-position="adjustPosition" :cursor-spacing="cursorSpacing"
19
+ :show-confirm-bar="showConfirmBar" @input="handleInput" @focus="handleFocus" @blur="handleBlur"
20
+ @confirm="handleConfirm" @keyboardheightchange="handleKeyboardHeightChange" />
21
+
37
22
  <!-- 清除按钮 -->
38
- <view
39
- v-if="showClear && modelValue && !disabled"
40
- class="im-search__clear"
41
- @tap="handleClear"
42
- >
23
+ <view v-if="showClear && modelValue && !disabled" class="im-search__clear" @tap="handleClear">
43
24
  <slot name="clear-icon">
44
25
  <text class="im-search__clear-text">✕</text>
45
26
  </slot>
46
27
  </view>
47
-
28
+
48
29
  <!-- 取消按钮 -->
49
- <view
50
- v-if="showCancel"
51
- class="im-search__cancel"
52
- :class="{ 'im-search__cancel--show': modelValue || isFocused || alwaysShowCancel }"
53
- @tap="handleCancel"
54
- >
30
+ <view v-if="showCancel" class="im-search__cancel"
31
+ :class="{ 'im-search__cancel--show': modelValue || isFocused || alwaysShowCancel }" @tap="handleCancel">
55
32
  <slot name="cancel-text">
56
33
  <text class="im-search__cancel-text">{{ cancelText }}</text>
57
34
  </slot>
58
35
  </view>
59
-
36
+
60
37
  <!-- 搜索历史 -->
61
38
  <view v-if="showHistory && historyVisible && searchHistory.length > 0" class="im-search__history">
62
39
  <view class="im-search__history-header">
@@ -66,48 +43,38 @@
66
43
  </view>
67
44
  </view>
68
45
  <view class="im-search__history-list">
69
- <view
70
- v-for="(item, index) in searchHistory"
71
- :key="index"
72
- class="im-search__history-item"
73
- @tap="handleHistoryClick(item)"
74
- >
46
+ <view v-for="(item, index) in searchHistory" :key="index" class="im-search__history-item"
47
+ @tap="handleHistoryClick(item)">
75
48
  <text class="im-search__history-item-text">{{ item }}</text>
76
- <text
77
- class="im-search__history-item-delete"
78
- @tap.stop="deleteHistoryItem(index)"
79
- >✕</text>
49
+ <text class="im-search__history-item-delete" @tap.stop="deleteHistoryItem(index)">✕</text>
80
50
  </view>
81
51
  </view>
82
52
  </view>
83
-
53
+
84
54
  <!-- 搜索建议 -->
85
55
  <view v-if="showSuggestions && suggestionsVisible && suggestions.length > 0" class="im-search__suggestions">
86
- <view
87
- v-for="(item, index) in suggestions"
88
- :key="index"
89
- class="im-search__suggestion-item"
90
- @tap="handleSuggestionClick(item)"
91
- >
56
+ <view v-for="(item, index) in suggestions" :key="index" class="im-search__suggestion-item"
57
+ @tap="handleSuggestionClick(item)">
92
58
  <slot name="suggestion" :item="item" :index="index">
93
59
  <text class="im-search__suggestion-text">{{ formatSuggestion(item) }}</text>
94
60
  </slot>
95
61
  </view>
96
62
  </view>
97
-
63
+
98
64
  <!-- 搜索结果 -->
99
65
  <slot name="results" :results="searchResults"></slot>
100
66
  </view>
101
67
  </template>
102
68
 
103
69
  <script setup lang="ts">
104
- import { ref, computed, watch, nextTick } from 'vue'
70
+ import { ref, nextTick } from 'vue'
71
+ import ImIcon from '../im-icon/im-icon.vue'
105
72
 
106
73
  // 定义 Props
107
74
  interface Props {
108
75
  // 值
109
76
  modelValue?: string
110
-
77
+
111
78
  // 样式配置
112
79
  shape?: 'square' | 'round'
113
80
  size?: 'small' | 'medium' | 'large'
@@ -115,7 +82,7 @@ interface Props {
115
82
  borderColor?: string
116
83
  textColor?: string
117
84
  placeholderColor?: string
118
-
85
+
119
86
  // 输入框配置
120
87
  placeholder?: string
121
88
  placeholderStyle?: string
@@ -127,7 +94,7 @@ interface Props {
127
94
  adjustPosition?: boolean
128
95
  cursorSpacing?: number
129
96
  showConfirmBar?: boolean
130
-
97
+
131
98
  // 功能配置
132
99
  showClear?: boolean
133
100
  showCancel?: boolean
@@ -135,14 +102,14 @@ interface Props {
135
102
  cancelText?: string
136
103
  showHistory?: boolean
137
104
  showSuggestions?: boolean
138
-
105
+
139
106
  // 搜索相关
140
107
  searchDelay?: number
141
108
  minLength?: number
142
109
  maxHistory?: number
143
110
  suggestions?: any[]
144
111
  suggestionFormatter?: (item: any) => string
145
-
112
+
146
113
  // 自定义事件
147
114
  onSearch?: (value: string) => void
148
115
  onClear?: () => void
@@ -167,14 +134,14 @@ interface Emits {
167
134
  // 定义 Props 默认值
168
135
  const props = withDefaults(defineProps<Props>(), {
169
136
  modelValue: '',
170
-
137
+
171
138
  shape: 'round',
172
139
  size: 'medium',
173
140
  bgColor: '#f5f5f5',
174
141
  borderColor: '#e0e0e0',
175
142
  textColor: '#333333',
176
143
  placeholderColor: '#999999',
177
-
144
+
178
145
  placeholder: '请输入搜索内容',
179
146
  placeholderStyle: '',
180
147
  inputType: 'text',
@@ -185,19 +152,19 @@ const props = withDefaults(defineProps<Props>(), {
185
152
  adjustPosition: true,
186
153
  cursorSpacing: 0,
187
154
  showConfirmBar: true,
188
-
155
+
189
156
  showClear: true,
190
157
  showCancel: true,
191
158
  alwaysShowCancel: false,
192
159
  cancelText: '取消',
193
160
  showHistory: true,
194
161
  showSuggestions: false,
195
-
162
+
196
163
  searchDelay: 300,
197
164
  minLength: 1,
198
165
  maxHistory: 10,
199
166
  suggestions: () => [],
200
- suggestionFormatter: (item:any) => typeof item === 'string' ? item : item.text || item.name || JSON.stringify(item)
167
+ suggestionFormatter: (item: any) => typeof item === 'string' ? item : item.text || item.name || JSON.stringify(item)
201
168
  })
202
169
 
203
170
  const emit = defineEmits<Emits>()
@@ -228,21 +195,21 @@ const initHistory = () => {
228
195
  // 保存搜索历史
229
196
  const saveHistory = (value: string) => {
230
197
  if (!value.trim()) return
231
-
198
+
232
199
  // 移除重复项
233
200
  const index = searchHistory.value.indexOf(value)
234
201
  if (index > -1) {
235
202
  searchHistory.value.splice(index, 1)
236
203
  }
237
-
204
+
238
205
  // 添加到开头
239
206
  searchHistory.value.unshift(value)
240
-
207
+
241
208
  // 限制历史记录数量
242
209
  if (searchHistory.value.length > props.maxHistory) {
243
210
  searchHistory.value = searchHistory.value.slice(0, props.maxHistory)
244
211
  }
245
-
212
+
246
213
  // 保存到本地存储
247
214
  try {
248
215
  uni.setStorageSync('im_search_history', JSON.stringify(searchHistory.value))
@@ -276,12 +243,12 @@ const handleInput = (event: any) => {
276
243
  const value = event.detail?.value || ''
277
244
  emit('update:modelValue', value)
278
245
  emit('input', value)
279
-
246
+
280
247
  // 防抖搜索
281
248
  if (searchTimer.value) {
282
249
  clearTimeout(searchTimer.value)
283
250
  }
284
-
251
+
285
252
  searchTimer.value = setTimeout(() => {
286
253
  if (value.trim().length >= props.minLength) {
287
254
  emit('search', value)
@@ -289,7 +256,7 @@ const handleInput = (event: any) => {
289
256
  props.onSearch(value)
290
257
  }
291
258
  }
292
-
259
+
293
260
  // 显示/隐藏建议
294
261
  suggestionsVisible.value = value.trim().length > 0 && props.showSuggestions
295
262
  historyVisible.value = value.trim().length === 0 && props.showHistory
@@ -328,7 +295,7 @@ const handleClear = () => {
328
295
  emit('clear')
329
296
  historyVisible.value = props.showHistory
330
297
  suggestionsVisible.value = false
331
-
298
+
332
299
  // 聚焦输入框
333
300
  nextTick(() => {
334
301
  if (inputRef.value) {
@@ -345,12 +312,12 @@ const handleCancel = () => {
345
312
  historyVisible.value = false
346
313
  suggestionsVisible.value = false
347
314
  isFocused.value = false
348
-
315
+
349
316
  // 失去焦点
350
317
  if (inputRef.value) {
351
318
  blur()
352
319
  }
353
-
320
+
354
321
  if (props.onCancel) {
355
322
  props.onCancel()
356
323
  }
@@ -370,7 +337,7 @@ const handleHistoryClick = (value: string) => {
370
337
  emit('history-click', value)
371
338
  saveHistory(value)
372
339
  historyVisible.value = false
373
-
340
+
374
341
  // 触发搜索
375
342
  if (value.trim().length >= props.minLength) {
376
343
  emit('search', value)
@@ -378,7 +345,7 @@ const handleHistoryClick = (value: string) => {
378
345
  props.onSearch(value)
379
346
  }
380
347
  }
381
-
348
+
382
349
  // 聚焦输入框
383
350
  nextTick(() => {
384
351
  if (inputRef.value) {
@@ -395,7 +362,7 @@ const handleSuggestionClick = (item: any) => {
395
362
  emit('suggestion-click', item)
396
363
  saveHistory(value)
397
364
  suggestionsVisible.value = false
398
-
365
+
399
366
  // 触发搜索
400
367
  if (value.trim().length >= props.minLength) {
401
368
  emit('search', value)
@@ -424,7 +391,7 @@ const setResults = (results: any[]) => {
424
391
  const focus = () => {
425
392
  if (!props.disabled) {
426
393
  isFocused.value = true
427
-
394
+
428
395
  nextTick(() => {
429
396
  // 使用 uniapp 的选择器 API
430
397
  uni.createSelectorQuery()
@@ -439,7 +406,7 @@ const focus = () => {
439
406
  })
440
407
  }
441
408
  })
442
-
409
+
443
410
  // 直接设置焦点(需要确保组件已渲染)
444
411
  setTimeout(() => {
445
412
  if (inputRef.value) {
@@ -494,23 +461,23 @@ defineExpose({
494
461
  border-radius: 8rpx;
495
462
  transition: all 0.3s ease;
496
463
  position: relative;
497
-
464
+
498
465
  &--focus {
499
466
  border-color: #409eff;
500
467
  background-color: #ffffff;
501
468
  box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.1);
502
469
  }
503
-
470
+
504
471
  &--disabled {
505
472
  opacity: 0.6;
506
473
  cursor: not-allowed;
507
474
  }
508
-
475
+
509
476
  // 圆角样式
510
477
  &--round {
511
478
  border-radius: 100rpx;
512
479
  }
513
-
480
+
514
481
  // 尺寸样式
515
482
  &--small {
516
483
  padding: 12rpx 20rpx;
@@ -519,32 +486,37 @@ defineExpose({
519
486
  font-size: 26rpx;
520
487
  height: 40rpx;
521
488
  }
489
+
522
490
  .im-search__icon {
523
491
  width: 36rpx;
524
492
  height: 36rpx;
525
493
  margin-right: 12rpx;
526
494
  }
527
495
  }
528
-
496
+
529
497
  &--medium {
530
498
  padding: 16rpx 24rpx;
499
+
531
500
  .im-search__input {
532
501
  font-size: 28rpx;
533
502
  height: 44rpx;
534
503
  }
504
+
535
505
  .im-search__icon {
536
506
  width: 40rpx;
537
507
  height: 40rpx;
538
508
  margin-right: 16rpx;
539
509
  }
540
510
  }
541
-
511
+
542
512
  &--large {
543
513
  padding: 20rpx 28rpx;
514
+
544
515
  .im-search__input {
545
516
  font-size: 32rpx;
546
517
  height: 48rpx;
547
518
  }
519
+
548
520
  .im-search__icon {
549
521
  width: 44rpx;
550
522
  height: 44rpx;
@@ -559,7 +531,7 @@ defineExpose({
559
531
  align-items: center;
560
532
  justify-content: center;
561
533
  cursor: pointer;
562
-
534
+
563
535
  &-text {
564
536
  font-size: 32rpx;
565
537
  color: v-bind('props.placeholderColor');
@@ -574,11 +546,11 @@ defineExpose({
574
546
  background-color: transparent;
575
547
  border: none;
576
548
  outline: none;
577
-
549
+
578
550
  &::placeholder {
579
551
  color: v-bind('props.placeholderColor');
580
552
  }
581
-
553
+
582
554
  &:disabled {
583
555
  cursor: not-allowed;
584
556
  }
@@ -596,12 +568,12 @@ defineExpose({
596
568
  border-radius: 50%;
597
569
  cursor: pointer;
598
570
  transition: all 0.3s;
599
-
571
+
600
572
  &:active {
601
573
  background-color: #c0c0c0;
602
574
  transform: scale(0.95);
603
575
  }
604
-
576
+
605
577
  &-text {
606
578
  font-size: 20rpx;
607
579
  color: #666666;
@@ -616,19 +588,19 @@ defineExpose({
616
588
  transform: translateX(20rpx);
617
589
  transition: all 0.3s ease;
618
590
  pointer-events: none;
619
-
591
+
620
592
  &--show {
621
593
  opacity: 1;
622
594
  transform: translateX(0);
623
595
  pointer-events: auto;
624
596
  }
625
-
597
+
626
598
  &-text {
627
599
  font-size: 28rpx;
628
600
  color: #409eff;
629
601
  cursor: pointer;
630
602
  white-space: nowrap;
631
-
603
+
632
604
  &:active {
633
605
  opacity: 0.8;
634
606
  }
@@ -665,11 +637,11 @@ defineExpose({
665
637
  padding: 8rpx 16rpx;
666
638
  border-radius: 6rpx;
667
639
  cursor: pointer;
668
-
640
+
669
641
  &:active {
670
642
  background-color: #f5f5f5;
671
643
  }
672
-
644
+
673
645
  &-text {
674
646
  font-size: 24rpx;
675
647
  color: #666666;
@@ -689,11 +661,11 @@ defineExpose({
689
661
  border-bottom: 2rpx solid #f5f5f5;
690
662
  cursor: pointer;
691
663
  transition: background-color 0.2s;
692
-
664
+
693
665
  &:last-child {
694
666
  border-bottom: none;
695
667
  }
696
-
668
+
697
669
  &:active {
698
670
  background-color: #f9f9f9;
699
671
  }
@@ -716,11 +688,11 @@ defineExpose({
716
688
  color: #999999;
717
689
  opacity: 0;
718
690
  transition: opacity 0.3s;
719
-
691
+
720
692
  .im-search__history-item:hover & {
721
693
  opacity: 1;
722
694
  }
723
-
695
+
724
696
  &:active {
725
697
  color: #ff4d4f;
726
698
  }
@@ -745,11 +717,11 @@ defineExpose({
745
717
  border-bottom: 2rpx solid #f5f5f5;
746
718
  cursor: pointer;
747
719
  transition: background-color 0.2s;
748
-
720
+
749
721
  &:last-child {
750
722
  border-bottom: none;
751
723
  }
752
-
724
+
753
725
  &:active {
754
726
  background-color: #f9f9f9;
755
727
  }
@@ -147,6 +147,8 @@ import type {
147
147
  SkuEmits,
148
148
  SelectedSku
149
149
  } from '../../types/components/sku'
150
+ import ImPopup from '../im-popup/im-popup.vue'
151
+ import ImIcon from '../im-icon/im-icon.vue'
150
152
 
151
153
  const props = withDefaults(defineProps<SkuProps>(), {
152
154
  current: undefined,
@@ -58,6 +58,7 @@
58
58
 
59
59
  <script lang="ts" setup>
60
60
  import type { StepperProps, StepperEmits } from '../../types/components/stepper'
61
+ import ImIcon from '../im-icon/im-icon.vue'
61
62
 
62
63
  const props = withDefaults(defineProps<StepperProps>(), {
63
64
  modelValue: 0,
@@ -211,6 +211,7 @@
211
211
 
212
212
  <script setup lang="ts">
213
213
  import { ref, computed, watch } from 'vue'
214
+ import ImButton from '../im-button/im-button.vue'
214
215
 
215
216
  // 定义文件类型
216
217
  interface UploadFile {
@@ -50,6 +50,7 @@
50
50
 
51
51
  <script setup lang="ts">
52
52
  import { ref, computed, watch, nextTick, onMounted, onUnmounted, getCurrentInstance } from 'vue'
53
+ import ImLoading from '../im-loading/im-loading.vue'
53
54
 
54
55
  // 定义类型
55
56
  export interface VirtualListItem<T = any> {
@@ -28,6 +28,7 @@
28
28
  <script setup lang="ts">
29
29
  import { ref, computed, onUnmounted } from 'vue'
30
30
  import { RecorderApp, RecorderH5 } from '../../index'
31
+ import ImIcon from '../im-icon/im-icon.vue'
31
32
 
32
33
  const recording = ref(false);
33
34
  const moveToCancel = ref(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "im-ui-mobile",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "A Vue3.0 + Typescript instant messaging component library for Uniapp",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -26,7 +26,7 @@ declare interface ButtonProps {
26
26
  color?: string
27
27
  bgColor?: string
28
28
  borderColor?: string
29
- border: boolean
29
+ border?: boolean
30
30
  textColor?: string
31
31
  textSize?: 'small' | 'medium' | 'large'
32
32
  width?: string