im-ui-mobile 0.1.6 → 0.1.8

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
  // 定义类型
@@ -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);
@@ -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'
@@ -13,8 +13,6 @@
13
13
  </template>
14
14
 
15
15
  <script setup lang="ts">
16
- import ImAvatar from '../im-avatar/im-avatar.vue'
17
-
18
16
  interface Props {
19
17
  friend?: any;
20
18
  detail?: boolean;
@@ -40,8 +40,6 @@
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'
45
43
 
46
44
  interface Props {
47
45
  group?: any;
@@ -522,10 +522,6 @@ defineExpose({
522
522
  width: 100%;
523
523
  height: 100%;
524
524
  fill: currentColor;
525
-
526
- * {
527
- fill: inherit;
528
- }
529
525
  }
530
526
  }
531
527
  }
@@ -36,8 +36,6 @@
36
36
 
37
37
  <script setup lang="ts">
38
38
  import { ref, computed } from 'vue'
39
- import ImAvatar from '../im-avatar/im-avatar.vue'
40
- import ImVirtualList from '../im-virtual-list/im-virtual-list.vue'
41
39
  import { GroupMember } from '../../libs';
42
40
 
43
41
  interface Props {
@@ -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,37 @@
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'
105
71
 
106
72
  // 定义 Props
107
73
  interface Props {
108
74
  // 值
109
75
  modelValue?: string
110
-
76
+
111
77
  // 样式配置
112
78
  shape?: 'square' | 'round'
113
79
  size?: 'small' | 'medium' | 'large'
@@ -115,7 +81,7 @@ interface Props {
115
81
  borderColor?: string
116
82
  textColor?: string
117
83
  placeholderColor?: string
118
-
84
+
119
85
  // 输入框配置
120
86
  placeholder?: string
121
87
  placeholderStyle?: string
@@ -127,7 +93,7 @@ interface Props {
127
93
  adjustPosition?: boolean
128
94
  cursorSpacing?: number
129
95
  showConfirmBar?: boolean
130
-
96
+
131
97
  // 功能配置
132
98
  showClear?: boolean
133
99
  showCancel?: boolean
@@ -135,14 +101,14 @@ interface Props {
135
101
  cancelText?: string
136
102
  showHistory?: boolean
137
103
  showSuggestions?: boolean
138
-
104
+
139
105
  // 搜索相关
140
106
  searchDelay?: number
141
107
  minLength?: number
142
108
  maxHistory?: number
143
109
  suggestions?: any[]
144
110
  suggestionFormatter?: (item: any) => string
145
-
111
+
146
112
  // 自定义事件
147
113
  onSearch?: (value: string) => void
148
114
  onClear?: () => void
@@ -167,14 +133,14 @@ interface Emits {
167
133
  // 定义 Props 默认值
168
134
  const props = withDefaults(defineProps<Props>(), {
169
135
  modelValue: '',
170
-
136
+
171
137
  shape: 'round',
172
138
  size: 'medium',
173
139
  bgColor: '#f5f5f5',
174
140
  borderColor: '#e0e0e0',
175
141
  textColor: '#333333',
176
142
  placeholderColor: '#999999',
177
-
143
+
178
144
  placeholder: '请输入搜索内容',
179
145
  placeholderStyle: '',
180
146
  inputType: 'text',
@@ -185,19 +151,19 @@ const props = withDefaults(defineProps<Props>(), {
185
151
  adjustPosition: true,
186
152
  cursorSpacing: 0,
187
153
  showConfirmBar: true,
188
-
154
+
189
155
  showClear: true,
190
156
  showCancel: true,
191
157
  alwaysShowCancel: false,
192
158
  cancelText: '取消',
193
159
  showHistory: true,
194
160
  showSuggestions: false,
195
-
161
+
196
162
  searchDelay: 300,
197
163
  minLength: 1,
198
164
  maxHistory: 10,
199
165
  suggestions: () => [],
200
- suggestionFormatter: (item:any) => typeof item === 'string' ? item : item.text || item.name || JSON.stringify(item)
166
+ suggestionFormatter: (item: any) => typeof item === 'string' ? item : item.text || item.name || JSON.stringify(item)
201
167
  })
202
168
 
203
169
  const emit = defineEmits<Emits>()
@@ -228,21 +194,21 @@ const initHistory = () => {
228
194
  // 保存搜索历史
229
195
  const saveHistory = (value: string) => {
230
196
  if (!value.trim()) return
231
-
197
+
232
198
  // 移除重复项
233
199
  const index = searchHistory.value.indexOf(value)
234
200
  if (index > -1) {
235
201
  searchHistory.value.splice(index, 1)
236
202
  }
237
-
203
+
238
204
  // 添加到开头
239
205
  searchHistory.value.unshift(value)
240
-
206
+
241
207
  // 限制历史记录数量
242
208
  if (searchHistory.value.length > props.maxHistory) {
243
209
  searchHistory.value = searchHistory.value.slice(0, props.maxHistory)
244
210
  }
245
-
211
+
246
212
  // 保存到本地存储
247
213
  try {
248
214
  uni.setStorageSync('im_search_history', JSON.stringify(searchHistory.value))
@@ -276,12 +242,12 @@ const handleInput = (event: any) => {
276
242
  const value = event.detail?.value || ''
277
243
  emit('update:modelValue', value)
278
244
  emit('input', value)
279
-
245
+
280
246
  // 防抖搜索
281
247
  if (searchTimer.value) {
282
248
  clearTimeout(searchTimer.value)
283
249
  }
284
-
250
+
285
251
  searchTimer.value = setTimeout(() => {
286
252
  if (value.trim().length >= props.minLength) {
287
253
  emit('search', value)
@@ -289,7 +255,7 @@ const handleInput = (event: any) => {
289
255
  props.onSearch(value)
290
256
  }
291
257
  }
292
-
258
+
293
259
  // 显示/隐藏建议
294
260
  suggestionsVisible.value = value.trim().length > 0 && props.showSuggestions
295
261
  historyVisible.value = value.trim().length === 0 && props.showHistory
@@ -328,7 +294,7 @@ const handleClear = () => {
328
294
  emit('clear')
329
295
  historyVisible.value = props.showHistory
330
296
  suggestionsVisible.value = false
331
-
297
+
332
298
  // 聚焦输入框
333
299
  nextTick(() => {
334
300
  if (inputRef.value) {
@@ -345,12 +311,12 @@ const handleCancel = () => {
345
311
  historyVisible.value = false
346
312
  suggestionsVisible.value = false
347
313
  isFocused.value = false
348
-
314
+
349
315
  // 失去焦点
350
316
  if (inputRef.value) {
351
317
  blur()
352
318
  }
353
-
319
+
354
320
  if (props.onCancel) {
355
321
  props.onCancel()
356
322
  }
@@ -370,7 +336,7 @@ const handleHistoryClick = (value: string) => {
370
336
  emit('history-click', value)
371
337
  saveHistory(value)
372
338
  historyVisible.value = false
373
-
339
+
374
340
  // 触发搜索
375
341
  if (value.trim().length >= props.minLength) {
376
342
  emit('search', value)
@@ -378,7 +344,7 @@ const handleHistoryClick = (value: string) => {
378
344
  props.onSearch(value)
379
345
  }
380
346
  }
381
-
347
+
382
348
  // 聚焦输入框
383
349
  nextTick(() => {
384
350
  if (inputRef.value) {
@@ -395,7 +361,7 @@ const handleSuggestionClick = (item: any) => {
395
361
  emit('suggestion-click', item)
396
362
  saveHistory(value)
397
363
  suggestionsVisible.value = false
398
-
364
+
399
365
  // 触发搜索
400
366
  if (value.trim().length >= props.minLength) {
401
367
  emit('search', value)
@@ -424,7 +390,7 @@ const setResults = (results: any[]) => {
424
390
  const focus = () => {
425
391
  if (!props.disabled) {
426
392
  isFocused.value = true
427
-
393
+
428
394
  nextTick(() => {
429
395
  // 使用 uniapp 的选择器 API
430
396
  uni.createSelectorQuery()
@@ -439,7 +405,7 @@ const focus = () => {
439
405
  })
440
406
  }
441
407
  })
442
-
408
+
443
409
  // 直接设置焦点(需要确保组件已渲染)
444
410
  setTimeout(() => {
445
411
  if (inputRef.value) {
@@ -494,23 +460,23 @@ defineExpose({
494
460
  border-radius: 8rpx;
495
461
  transition: all 0.3s ease;
496
462
  position: relative;
497
-
463
+
498
464
  &--focus {
499
465
  border-color: #409eff;
500
466
  background-color: #ffffff;
501
467
  box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.1);
502
468
  }
503
-
469
+
504
470
  &--disabled {
505
471
  opacity: 0.6;
506
472
  cursor: not-allowed;
507
473
  }
508
-
474
+
509
475
  // 圆角样式
510
476
  &--round {
511
477
  border-radius: 100rpx;
512
478
  }
513
-
479
+
514
480
  // 尺寸样式
515
481
  &--small {
516
482
  padding: 12rpx 20rpx;
@@ -519,32 +485,37 @@ defineExpose({
519
485
  font-size: 26rpx;
520
486
  height: 40rpx;
521
487
  }
488
+
522
489
  .im-search__icon {
523
490
  width: 36rpx;
524
491
  height: 36rpx;
525
492
  margin-right: 12rpx;
526
493
  }
527
494
  }
528
-
495
+
529
496
  &--medium {
530
497
  padding: 16rpx 24rpx;
498
+
531
499
  .im-search__input {
532
500
  font-size: 28rpx;
533
501
  height: 44rpx;
534
502
  }
503
+
535
504
  .im-search__icon {
536
505
  width: 40rpx;
537
506
  height: 40rpx;
538
507
  margin-right: 16rpx;
539
508
  }
540
509
  }
541
-
510
+
542
511
  &--large {
543
512
  padding: 20rpx 28rpx;
513
+
544
514
  .im-search__input {
545
515
  font-size: 32rpx;
546
516
  height: 48rpx;
547
517
  }
518
+
548
519
  .im-search__icon {
549
520
  width: 44rpx;
550
521
  height: 44rpx;
@@ -559,7 +530,7 @@ defineExpose({
559
530
  align-items: center;
560
531
  justify-content: center;
561
532
  cursor: pointer;
562
-
533
+
563
534
  &-text {
564
535
  font-size: 32rpx;
565
536
  color: v-bind('props.placeholderColor');
@@ -574,11 +545,11 @@ defineExpose({
574
545
  background-color: transparent;
575
546
  border: none;
576
547
  outline: none;
577
-
548
+
578
549
  &::placeholder {
579
550
  color: v-bind('props.placeholderColor');
580
551
  }
581
-
552
+
582
553
  &:disabled {
583
554
  cursor: not-allowed;
584
555
  }
@@ -596,12 +567,12 @@ defineExpose({
596
567
  border-radius: 50%;
597
568
  cursor: pointer;
598
569
  transition: all 0.3s;
599
-
570
+
600
571
  &:active {
601
572
  background-color: #c0c0c0;
602
573
  transform: scale(0.95);
603
574
  }
604
-
575
+
605
576
  &-text {
606
577
  font-size: 20rpx;
607
578
  color: #666666;
@@ -616,19 +587,19 @@ defineExpose({
616
587
  transform: translateX(20rpx);
617
588
  transition: all 0.3s ease;
618
589
  pointer-events: none;
619
-
590
+
620
591
  &--show {
621
592
  opacity: 1;
622
593
  transform: translateX(0);
623
594
  pointer-events: auto;
624
595
  }
625
-
596
+
626
597
  &-text {
627
598
  font-size: 28rpx;
628
599
  color: #409eff;
629
600
  cursor: pointer;
630
601
  white-space: nowrap;
631
-
602
+
632
603
  &:active {
633
604
  opacity: 0.8;
634
605
  }
@@ -665,11 +636,11 @@ defineExpose({
665
636
  padding: 8rpx 16rpx;
666
637
  border-radius: 6rpx;
667
638
  cursor: pointer;
668
-
639
+
669
640
  &:active {
670
641
  background-color: #f5f5f5;
671
642
  }
672
-
643
+
673
644
  &-text {
674
645
  font-size: 24rpx;
675
646
  color: #666666;
@@ -689,11 +660,11 @@ defineExpose({
689
660
  border-bottom: 2rpx solid #f5f5f5;
690
661
  cursor: pointer;
691
662
  transition: background-color 0.2s;
692
-
663
+
693
664
  &:last-child {
694
665
  border-bottom: none;
695
666
  }
696
-
667
+
697
668
  &:active {
698
669
  background-color: #f9f9f9;
699
670
  }
@@ -716,11 +687,11 @@ defineExpose({
716
687
  color: #999999;
717
688
  opacity: 0;
718
689
  transition: opacity 0.3s;
719
-
690
+
720
691
  .im-search__history-item:hover & {
721
692
  opacity: 1;
722
693
  }
723
-
694
+
724
695
  &:active {
725
696
  color: #ff4d4f;
726
697
  }
@@ -745,11 +716,11 @@ defineExpose({
745
716
  border-bottom: 2rpx solid #f5f5f5;
746
717
  cursor: pointer;
747
718
  transition: background-color 0.2s;
748
-
719
+
749
720
  &:last-child {
750
721
  border-bottom: none;
751
722
  }
752
-
723
+
753
724
  &:active {
754
725
  background-color: #f9f9f9;
755
726
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "im-ui-mobile",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "A Vue3.0 + Typescript instant messaging component library for Uniapp",
5
5
  "type": "module",
6
6
  "main": "index.js",