im-ui-mobile 0.0.6 → 0.0.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.
Files changed (41) hide show
  1. package/package.json +13 -9
  2. package/src/components/chat-box/index.vue +148 -112
  3. package/src/components/chat-item/index.vue +131 -51
  4. package/src/components/chat-message-item/index.vue +135 -59
  5. package/src/{index.ts → index.js} +2 -9
  6. package/src/{types → libs}/index.ts +1 -2
  7. package/src/types/components/chat-box/index.vue.d.ts +129 -0
  8. package/src/types/components/chat-item/index.vue.d.ts +64 -0
  9. package/src/types/components/chat-message-item/index.vue.d.ts +73 -0
  10. package/src/types/index.d.ts +9 -0
  11. package/src/types/libs/index.d.ts +166 -0
  12. package/src/types/libs/recorder.d.ts +15 -0
  13. package/src/types/libs/user.d.ts +19 -0
  14. package/src/types/utils/auth.d.ts +7 -0
  15. package/src/types/utils/datetime.d.ts +9 -0
  16. package/src/types/utils/emotion.d.ts +8 -0
  17. package/src/types/utils/enums.d.ts +73 -0
  18. package/src/types/utils/env.d.ts +11 -0
  19. package/src/types/utils/eventBus/EventBusImpl.d.ts +67 -0
  20. package/src/types/utils/eventBus/EventCallback.d.ts +1 -0
  21. package/src/types/utils/eventBus/GlobalEventCallback.d.ts +1 -0
  22. package/src/types/utils/eventBus/IEventBus.d.ts +10 -0
  23. package/src/types/utils/eventBus/IEventListener.d.ts +4 -0
  24. package/src/types/utils/eventBus/index.d.ts +3 -0
  25. package/src/types/utils/htmlEscape.d.ts +50 -0
  26. package/src/types/utils/index.d.ts +15 -0
  27. package/src/types/utils/messageType.d.ts +35 -0
  28. package/src/types/utils/recorderApp.d.ts +8 -0
  29. package/src/types/utils/recorderH5.d.ts +8 -0
  30. package/src/types/utils/request.d.ts +8 -0
  31. package/src/types/utils/requestx.d.ts +24 -0
  32. package/src/types/utils/url.d.ts +5 -0
  33. package/src/types/utils/useDynamicRefs.d.ts +9 -0
  34. package/src/types/utils/websocket.d.ts +35 -0
  35. package/src/utils/index.ts +1 -1
  36. package/src/utils/recorderApp.ts +1 -1
  37. package/src/utils/recorderH5.ts +1 -1
  38. package/src/utils/requestx.ts +1 -1
  39. package/src/utils/websocket.ts +4 -4
  40. /package/src/{types → libs}/recorder.ts +0 -0
  41. /package/src/{types → libs}/user.ts +0 -0
package/package.json CHANGED
@@ -1,27 +1,31 @@
1
1
  {
2
2
  "name": "im-ui-mobile",
3
- "version": "0.0.6",
4
- "description": "A Uniapp instant messaging component library based on Vue3.0+typescript",
5
- "main": "index.ts",
6
- "module": "dist/im-ui-mobile.es.js",
7
- "types": "dist/index.d.ts",
3
+ "version": "0.0.8",
4
+ "description": "A Vue3.0 + typescript instant messaging component library for Uniapp",
5
+ "main": "index.js",
6
+ "types": "types/index.d.ts",
8
7
  "files": [
9
8
  "src/components",
9
+ "src/libs",
10
10
  "src/types",
11
11
  "src/utils",
12
- "src/index.ts"
12
+ "index.js"
13
13
  ],
14
14
  "scripts": {
15
- "build": "vite build",
16
- "preview": "vite preview"
15
+ "dev": "cd examples && npm run dev:h5",
16
+ "build": "npm run build:types",
17
+ "build:types": "vue-tsc -p tsconfig.build.json",
18
+ "clean:types": "rmdir /s /q src/types 2>nul # '删除 types 目录'",
19
+ "prepublishOnly": "npm run build"
17
20
  },
18
21
  "devDependencies": {
22
+ "@dcloudio/uni-app": "3.0.0-4070520250711001",
19
23
  "@dcloudio/types": "^3.4.8",
20
24
  "@dcloudio/uni-cli-shared": "3.0.0-4070520250711001",
21
25
  "@dcloudio/vite-plugin-uni": "3.0.0-4070520250711001",
22
26
  "@types/node": "^24.10.1",
23
27
  "@vitejs/plugin-vue": "^4.0.0",
24
- "typescript": "^5.0.0",
28
+ "typescript": "~5.4.5",
25
29
  "vite": "^4.0.0",
26
30
  "vite-plugin-dts": "^3.0.0",
27
31
  "vue-tsc": "^1.0.0"
@@ -1,120 +1,132 @@
1
- <!-- packages/chat-box/index.vue -->
2
1
  <template>
3
- <div class="u-im-chat-box">
4
- <div class="u-im-chat-box__header">
5
- <div class="u-im-chat-box__title">{{ title }}</div>
6
- <div class="u-im-chat-box__actions">
2
+ <view class="u-im-chat-box">
3
+ <view class="u-im-chat-box__header">
4
+ <view class="u-im-chat-box__title">{{ title }}</view>
5
+ <view class="u-im-chat-box__actions">
7
6
  <slot name="header-actions"></slot>
8
- </div>
9
- </div>
10
-
11
- <div class="u-im-chat-box__messages" ref="messagesRef">
12
- <div class="u-im-chat-box__loading" v-if="loading">
13
- <u-loading-icon size="24" />
14
- </div>
15
- <chat-message-item v-for="(message, index) in messages" :key="message.id || index"
16
- :position="message.position" :type="message.type" :content="message.content" :avatar="message.avatar"
17
- :show-avatar="shouldShowAvatar(message, index)" :status="message.status" :duration="message.duration" />
18
- </div>
19
-
20
- <div class="u-im-chat-box__input">
21
- <div class="u-im-chat-box__tools">
7
+ </view>
8
+ </view>
9
+
10
+ <view class="u-im-chat-box__messages" ref="messagesRef">
11
+ <view class="u-im-chat-box__loading" v-if="loading">
12
+ <!-- 替换 uview 组件 -->
13
+ <view class="loading-icon">加载中...</view>
14
+ </view>
15
+ <chat-message-item
16
+ v-for="(message, index) in messages"
17
+ :key="message.id || index"
18
+ :position="message.position"
19
+ :type="message.type"
20
+ :content="message.content"
21
+ :avatar="message.avatar"
22
+ :show-avatar="shouldShowAvatar(message, index)"
23
+ :status="message.status"
24
+ :duration="message.duration"
25
+ />
26
+ </view>
27
+
28
+ <view class="u-im-chat-box__input">
29
+ <view class="u-im-chat-box__tools">
22
30
  <slot name="input-tools"></slot>
23
- </div>
24
- <div class="u-im-chat-box__textarea">
25
- <u-textarea v-model="inputText" :placeholder="placeholder" :maxlength="maxlength" :auto-height="true"
26
- @confirm="handleSend" />
27
- </div>
28
- <div class="u-im-chat-box__send">
29
- <u-button type="primary" size="small" @click="handleSend">发送</u-button>
30
- </div>
31
- </div>
32
- </div>
31
+ </view>
32
+ <view class="u-im-chat-box__textarea">
33
+ <!-- 替换 u-textarea -->
34
+ <textarea
35
+ v-model="inputText"
36
+ :placeholder="placeholder"
37
+ :maxlength="maxlength"
38
+ :auto-height="true"
39
+ @confirm="handleSend"
40
+ class="chat-textarea"
41
+ />
42
+ </view>
43
+ <view class="u-im-chat-box__send">
44
+ <!-- 替换 u-button -->
45
+ <button type="primary" size="mini" @click="handleSend" class="send-button">发送</button>
46
+ </view>
47
+ </view>
48
+ </view>
33
49
  </template>
34
50
 
35
- <script setup lang="ts">
36
- import { ref, nextTick, watch, onMounted } from 'vue'
51
+ <script>
37
52
  import ChatMessageItem from '../chat-message-item/index.vue'
38
53
 
39
- interface Message {
40
- id?: string | number
41
- position: 'left' | 'right'
42
- type: 'text' | 'image' | 'voice' | 'file'
43
- content: string
44
- avatar?: string
45
- status?: 'sending' | 'success' | 'failed'
46
- duration?: number
47
- timestamp?: number
48
- }
49
-
50
- interface ChatBoxProps {
51
- title?: string
52
- messages?: Message[]
53
- placeholder?: string
54
- maxlength?: number
55
- loading?: boolean
56
- }
57
-
58
- const props = withDefaults(defineProps<ChatBoxProps>(), {
59
- title: '聊天',
60
- messages: () => [],
61
- placeholder: '请输入消息...',
62
- maxlength: 1000,
63
- loading: false
64
- })
65
-
66
- const emit = defineEmits<{
67
- send: [message: string]
68
- loadMore: []
69
- }>()
70
-
71
- const inputText = ref('')
72
- const messagesRef = ref<HTMLElement>()
73
-
74
- const handleSend = () => {
75
- if (inputText.value.trim()) {
76
- emit('send', inputText.value.trim())
77
- inputText.value = ''
78
- }
79
- }
80
-
81
- const shouldShowAvatar = (message: Message, index: number): boolean => {
82
- if (index === 0) return true
83
- const prevMessage = props.messages[index - 1]
84
- if (prevMessage.position !== message.position ||
85
- prevMessage.avatar !== message.avatar ||
86
- (message.timestamp && prevMessage.timestamp &&
87
- message.timestamp - prevMessage.timestamp > 300000)) // 5分钟
88
- {
89
- return true
90
- }
91
- return false
92
- }
93
-
94
- const scrollToBottom = () => {
95
- nextTick(() => {
96
- if (messagesRef.value) {
97
- messagesRef.value.scrollTop = messagesRef.value.scrollHeight
54
+ export default {
55
+ name: 'ChatBox',
56
+ components: {
57
+ ChatMessageItem
58
+ },
59
+ props: {
60
+ title: {
61
+ type: String,
62
+ default: '聊天'
63
+ },
64
+ messages: {
65
+ type: Array,
66
+ default: () => []
67
+ },
68
+ placeholder: {
69
+ type: String,
70
+ default: '请输入消息...'
71
+ },
72
+ maxlength: {
73
+ type: Number,
74
+ default: 1000
75
+ },
76
+ loading: {
77
+ type: Boolean,
78
+ default: false
79
+ }
80
+ },
81
+ data() {
82
+ return {
83
+ inputText: ''
84
+ }
85
+ },
86
+ watch: {
87
+ messages: {
88
+ handler() {
89
+ this.scrollToBottom()
90
+ },
91
+ deep: true
98
92
  }
99
- })
93
+ },
94
+ mounted() {
95
+ this.scrollToBottom()
96
+ },
97
+ methods: {
98
+ handleSend() {
99
+ if (this.inputText.trim()) {
100
+ this.$emit('send', this.inputText.trim())
101
+ this.inputText = ''
102
+ }
103
+ },
104
+ shouldShowAvatar(message, index) {
105
+ if (index === 0) return true
106
+ const prevMessage = this.messages[index - 1]
107
+ return prevMessage.position !== message.position ||
108
+ prevMessage.avatar !== message.avatar ||
109
+ (message.timestamp && prevMessage.timestamp &&
110
+ message.timestamp - prevMessage.timestamp > 300000) // 5分钟
111
+ },
112
+ scrollToBottom() {
113
+ this.$nextTick(() => {
114
+ if (this.$refs.messagesRef) {
115
+ this.$refs.messagesRef.scrollTop = this.$refs.messagesRef.scrollHeight
116
+ }
117
+ })
118
+ }
119
+ }
100
120
  }
101
-
102
- watch(() => props.messages, () => {
103
- scrollToBottom()
104
- }, { deep: true })
105
-
106
- onMounted(() => {
107
- scrollToBottom()
108
- })
109
121
  </script>
110
122
 
111
123
  <style scoped>
112
124
  .u-im-chat-box {
113
125
  display: flex;
114
126
  flex-direction: column;
115
- height: 600px;
116
- border: 1px solid #e4e7ed;
117
- border-radius: 8px;
127
+ height: 1200rpx;
128
+ border: 2rpx solid #e4e7ed;
129
+ border-radius: 16rpx;
118
130
  overflow: hidden;
119
131
  }
120
132
 
@@ -122,20 +134,20 @@ onMounted(() => {
122
134
  display: flex;
123
135
  justify-content: space-between;
124
136
  align-items: center;
125
- padding: 12px 16px;
137
+ padding: 24rpx 32rpx;
126
138
  background-color: #f5f7fa;
127
- border-bottom: 1px solid #e4e7ed;
139
+ border-bottom: 2rpx solid #e4e7ed;
128
140
  }
129
141
 
130
142
  .u-im-chat-box__title {
131
- font-size: 16px;
143
+ font-size: 32rpx;
132
144
  font-weight: 500;
133
145
  color: #303133;
134
146
  }
135
147
 
136
148
  .u-im-chat-box__messages {
137
149
  flex: 1;
138
- padding: 16px;
150
+ padding: 32rpx;
139
151
  overflow-y: auto;
140
152
  background-color: #fafafa;
141
153
  }
@@ -143,25 +155,49 @@ onMounted(() => {
143
155
  .u-im-chat-box__loading {
144
156
  display: flex;
145
157
  justify-content: center;
146
- padding: 16px;
158
+ padding: 32rpx;
159
+ }
160
+
161
+ .loading-icon {
162
+ font-size: 28rpx;
163
+ color: #909399;
147
164
  }
148
165
 
149
166
  .u-im-chat-box__input {
150
- border-top: 1px solid #e4e7ed;
167
+ border-top: 2rpx solid #e4e7ed;
151
168
  background-color: #fff;
152
169
  }
153
170
 
154
171
  .u-im-chat-box__tools {
155
- padding: 8px 16px;
156
- border-bottom: 1px solid #e4e7ed;
172
+ padding: 16rpx 32rpx;
173
+ border-bottom: 2rpx solid #e4e7ed;
157
174
  }
158
175
 
159
176
  .u-im-chat-box__textarea {
160
- padding: 12px 16px;
177
+ padding: 24rpx 32rpx;
178
+ }
179
+
180
+ .chat-textarea {
181
+ width: 100%;
182
+ min-height: 80rpx;
183
+ padding: 16rpx;
184
+ border: 2rpx solid #dcdfe6;
185
+ border-radius: 8rpx;
186
+ font-size: 28rpx;
187
+ background-color: #fff;
161
188
  }
162
189
 
163
190
  .u-im-chat-box__send {
164
- padding: 0 16px 12px;
191
+ padding: 0 32rpx 24rpx;
165
192
  text-align: right;
166
193
  }
194
+
195
+ .send-button {
196
+ background-color: #409EFF;
197
+ color: #fff;
198
+ border: none;
199
+ border-radius: 8rpx;
200
+ padding: 16rpx 32rpx;
201
+ font-size: 28rpx;
202
+ }
167
203
  </style>
@@ -1,64 +1,119 @@
1
- <!-- packages/chat-item/index.vue -->
2
1
  <template>
3
- <div class="u-im-chat-item" :class="[`u-im-chat-item--${type}`]" @click="handleClick">
4
- <div class="u-im-chat-item__avatar">
5
- <u-avatar :src="avatar" size="40" />
6
- </div>
7
- <div class="u-im-chat-item__content">
8
- <div class="u-im-chat-item__header">
9
- <span class="u-im-chat-item__name">{{ name }}</span>
10
- <span class="u-im-chat-item__time">{{ time }}</span>
11
- </div>
12
- <div class="u-im-chat-item__message">
13
- <span class="u-im-chat-item__text">{{ lastMessage }}</span>
14
- <u-badge v-if="unreadCount > 0" :value="unreadCount" max="99" class="u-im-chat-item__badge" />
15
- </div>
16
- </div>
17
- </div>
2
+ <view class="u-im-chat-item" :class="[`u-im-chat-item--${type}`]" @click="handleClick">
3
+ <view class="u-im-chat-item__avatar">
4
+ <!-- 替换 u-avatar -->
5
+ <view class="avatar-fallback">
6
+ <image v-if="avatar" :src="avatar" class="avatar-image" mode="aspectFill" />
7
+ <text v-else class="avatar-text">{{ displayName }}</text>
8
+ </view>
9
+ </view>
10
+ <view class="u-im-chat-item__content">
11
+ <view class="u-im-chat-item__header">
12
+ <text class="u-im-chat-item__name">{{ name }}</text>
13
+ <text class="u-im-chat-item__time">{{ time }}</text>
14
+ </view>
15
+ <view class="u-im-chat-item__message">
16
+ <text class="u-im-chat-item__text">{{ lastMessage }}</text>
17
+ <!-- 替换 u-badge -->
18
+ <view v-if="unreadCount > 0" class="badge-fallback">
19
+ <text class="badge-text">{{ displayUnreadCount }}</text>
20
+ </view>
21
+ </view>
22
+ </view>
23
+ </view>
18
24
  </template>
19
25
 
20
- <script setup lang="ts">
21
- import { withDefaults } from 'vue'
22
-
23
- interface ChatItemProps {
24
- type?: 'default' | 'group'
25
- avatar?: string
26
- name: string
27
- time: string
28
- lastMessage: string
29
- unreadCount?: number
30
- }
31
-
32
- // const props =
33
- withDefaults(defineProps<ChatItemProps>(), {
34
- type: 'default',
35
- avatar: '',
36
- unreadCount: 0
37
- })
38
-
39
- const emit = defineEmits<{
40
- click: [event: MouseEvent]
41
- }>()
42
-
43
- const handleClick = (event: MouseEvent) => {
44
- emit('click', event)
26
+ <script>
27
+ export default {
28
+ name: 'ChatItem',
29
+ props: {
30
+ type: {
31
+ type: String,
32
+ default: 'default',
33
+ validator: function (value) {
34
+ return ['default', 'group'].includes(value)
35
+ }
36
+ },
37
+ avatar: {
38
+ type: String,
39
+ default: ''
40
+ },
41
+ name: {
42
+ type: String,
43
+ required: true
44
+ },
45
+ time: {
46
+ type: String,
47
+ required: true
48
+ },
49
+ lastMessage: {
50
+ type: String,
51
+ required: true
52
+ },
53
+ unreadCount: {
54
+ type: Number,
55
+ default: 0
56
+ }
57
+ },
58
+ computed: {
59
+ displayName() {
60
+ return this.name ? this.name.charAt(0) : ''
61
+ },
62
+ displayUnreadCount() {
63
+ return this.unreadCount > 99 ? '99+' : this.unreadCount
64
+ }
65
+ },
66
+ methods: {
67
+ handleClick() {
68
+ this.showToast('恭喜发财')
69
+ this.$emit('click')
70
+ },
71
+ showToast(message) {
72
+ uni.showToast({
73
+ title: message,
74
+ icon: 'none'
75
+ })
76
+ },
77
+ }
45
78
  }
46
79
  </script>
47
80
 
48
81
  <style scoped>
49
82
  .u-im-chat-item {
50
83
  display: flex;
51
- padding: 12px 16px;
52
- cursor: pointer;
84
+ padding: 24rpx 32rpx;
53
85
  transition: background-color 0.3s;
86
+ border-bottom: 2rpx solid #f5f7fa;
54
87
  }
55
88
 
56
- .u-im-chat-item:hover {
89
+ .u-im-chat-item:active {
57
90
  background-color: #f5f7fa;
58
91
  }
59
92
 
60
93
  .u-im-chat-item__avatar {
61
- margin-right: 12px;
94
+ margin-right: 24rpx;
95
+ }
96
+
97
+ .avatar-fallback {
98
+ width: 80rpx;
99
+ height: 80rpx;
100
+ border-radius: 50%;
101
+ background-color: #409EFF;
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ overflow: hidden;
106
+ }
107
+
108
+ .avatar-image {
109
+ width: 100%;
110
+ height: 100%;
111
+ }
112
+
113
+ .avatar-text {
114
+ color: white;
115
+ font-size: 32rpx;
116
+ font-weight: bold;
62
117
  }
63
118
 
64
119
  .u-im-chat-item__content {
@@ -70,18 +125,24 @@ const handleClick = (event: MouseEvent) => {
70
125
  display: flex;
71
126
  justify-content: space-between;
72
127
  align-items: center;
73
- margin-bottom: 4px;
128
+ margin-bottom: 8rpx;
74
129
  }
75
130
 
76
131
  .u-im-chat-item__name {
77
- font-size: 16px;
132
+ font-size: 32rpx;
78
133
  font-weight: 500;
79
134
  color: #303133;
135
+ overflow: hidden;
136
+ text-overflow: ellipsis;
137
+ white-space: nowrap;
138
+ flex: 1;
80
139
  }
81
140
 
82
141
  .u-im-chat-item__time {
83
- font-size: 12px;
142
+ font-size: 24rpx;
84
143
  color: #909399;
144
+ flex-shrink: 0;
145
+ margin-left: 16rpx;
85
146
  }
86
147
 
87
148
  .u-im-chat-item__message {
@@ -91,7 +152,7 @@ const handleClick = (event: MouseEvent) => {
91
152
  }
92
153
 
93
154
  .u-im-chat-item__text {
94
- font-size: 14px;
155
+ font-size: 28rpx;
95
156
  color: #606266;
96
157
  overflow: hidden;
97
158
  text-overflow: ellipsis;
@@ -99,7 +160,26 @@ const handleClick = (event: MouseEvent) => {
99
160
  flex: 1;
100
161
  }
101
162
 
102
- .u-im-chat-item__badge {
103
- margin-left: 8px;
163
+ .badge-fallback {
164
+ background-color: #FA3534;
165
+ border-radius: 40rpx;
166
+ min-width: 36rpx;
167
+ height: 36rpx;
168
+ display: flex;
169
+ align-items: center;
170
+ justify-content: center;
171
+ margin-left: 16rpx;
172
+ }
173
+
174
+ .badge-text {
175
+ color: white;
176
+ font-size: 20rpx;
177
+ font-weight: bold;
178
+ padding: 0 8rpx;
179
+ }
180
+
181
+ /* 群聊样式 */
182
+ .u-im-chat-item--group .u-im-chat-item__name::before {
183
+ content: "👥 ";
104
184
  }
105
185
  </style>