af-mobile-client-vue3 1.5.84 → 1.5.85

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/.env.development CHANGED
@@ -1,2 +1 @@
1
1
  NODE_ENV=development
2
- VITE_RSA_PRIVATE_KEY=MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIRrmftLDHCQqREEJ132Onu+W3vmFbdF7QD751SrcDDGDTfzuz1zBuElvkHhuDBb7KZkXrCIe+MhvX2IvxcLObl3faX+evYlnfj2HRbF0hIpQLuIq22tL06ZcV5w7wqLxUZRpFElIFm8gZTkUvfKXVuHw89e4daDVhU5hK3GHNGTAgMBAAECgYABiINrFaE1E8pkBYx1JJA5yuhL73aUktfd2TeCU00vFg6kyrWCI85Sa2RKu/6CJNZWeOFgdubEUv7a22tRrNIZb3yUMaqtTwSso78mspIOJqjWXTkTH9WPElfTcdpdIse/lgZtPz6egxkuhadSvwrM9Y6NgusiW/5+x95Ct08iOQJBAN5aK+7uISURvGQj2EaRtgGEd8+d4oHl+BYvvTeG3qSgUikHQW3j0sp4gXPw2kxw6sjVgLFOc4FB6LGqwzOTzokCQQCYdYG8ty3Uo/ebUlNzeJFxHXjy/KvBSytAUzAXkRu3nZrkEaPQsi3dgOkZgk+F1fMDzfQ4EbDIU6xvqOoZXHg7AkATCW9XfoXR8anKfRMoP5Nwn9HOMbtR2cmaxK2TknV/bMZ8AsYETYwfj5+tuIJIJybC2RyykX/sIiN1CqS5xr7ZAkArj19rMRdaKyMi8MnBM1Cy9g3Jt2HHj5ejAGG8SgyWUOShh1y70z0BjcSMMkxQXAncK2s83ekZw7aADM4eQupjAkARRgTwwMOnn3IoKmQusKhZk0uxilZ4Zc2LH6Z4GiWnvteM0W8Zw4Z1lJUcjgQq3dGqL2RdmzeQZ+HgPIOXrZVK
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.5.84",
4
+ "version": "1.5.85",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -1,326 +1,326 @@
1
- <script setup lang="ts">
2
- import { post } from '@af-mobile-client-vue3/services/restTools'
3
- import useUserStore from '@af-mobile-client-vue3/stores/modules/user'
4
- import { mobileUtil } from '@af-mobile-client-vue3/utils/mobileUtil'
5
- import { Badge as VanBadge, Icon as VanIcon } from 'vant'
6
- import { onMounted, onUnmounted, ref } from 'vue'
7
- import { closeWebSocket, initWebSocket } from './webSocket'
8
- import 'vant/es/badge/style'
9
- import 'vant/es/icon/style'
10
-
11
- interface Props {
12
- iconColor?: string
13
- }
14
-
15
- const props = withDefaults(defineProps<Props>(), {
16
- iconColor: '#666',
17
- })
18
-
19
- const userInfo = useUserStore().getUserInfo()
20
-
21
- // 未读消息数量
22
- const unreadCount = ref(0)
23
-
24
- // 打开消息下拉菜单
25
- const isShowMessageDropdown = ref(false)
26
-
27
- // 消息列表数据
28
- const messages = ref<any[]>([])
29
-
30
- // 组件根元素的引用
31
- const messageNotificationRef = ref<HTMLElement | null>(null)
32
-
33
- // 获取消息数据
34
- async function fetchMessages() {
35
- const param = {
36
- userId: `${userInfo.id}:phone`, // |phone
37
- typeList: '\'phone\'', // phone
38
- pageNo: 1,
39
- pageSize: 100,
40
- }
41
- const res = await post('/af-system/logic/getNotificationListByType', param)
42
- messages.value = (res || []).map((item: any) => {
43
- const event: any = JSON.parse(item.f_event || '{}')
44
- return {
45
- id: item.id,
46
- type: event.type,
47
- time: item.f_input_date,
48
- title: event.title || '',
49
- status: item.f_state,
50
- content: event.description || '',
51
- }
52
- })
53
- }
54
-
55
- // 获取未读消息数量
56
- async function fetchMessagesCount() {
57
- const param = {
58
- userId: `${userInfo.id}:phone`, // |phone
59
- typeList: '\'phone\'', // phone
60
- }
61
- const count = await post('/af-system/logic/getNotificationCount', param)
62
- unreadCount.value = count.count
63
- }
64
-
65
- // 处理 WebSocket 消息
66
- function createMessageCallback(message: any) {
67
- fetchMessagesCount()
68
- fetchMessages()
69
- }
70
- // 处理 不同的业务
71
- function sendMessageCallback(message: any) {
72
- console.log('>>>> 处理业务', JSON.stringify(message))
73
- if (message.businessType === 'audio') {
74
- // 播放响铃, 响铃文件为 message.audioName
75
- mobileUtil.execute({
76
- funcName: 'playAudio',
77
- param: {
78
- fileName: message.audioName,
79
- },
80
- callbackFunc: (result: any) => {
81
- // {"status":"success","data":{"message":"音频播放成功","fileName":"anjian.mp3","path":"assets/audio/anjian.mp3"}}
82
- },
83
- })
84
- }
85
- }
86
-
87
- // 全部标记为已读(清空消息列表)
88
- function markAllAsRead() {
89
- if (messages.value.length === 0)
90
- return
91
-
92
- const messageIds = messages.value.map(msg => msg.id).join(',')
93
- markAsRead(messageIds)
94
- }
95
-
96
- // 单条消息标记为已读
97
- function markAsRead(id: string | number) {
98
- post('/af-system/logic/confirmMessage', {
99
- id,
100
- f_state: 2,
101
- }).then(() => {
102
- fetchMessages()
103
- fetchMessagesCount()
104
- })
105
- }
106
-
107
- function handleIconClick(message: any) {
108
- // 乐观更新为已读状态,使图标立刻变绿
109
- message.status = 2
110
- setTimeout(() => {
111
- markAsRead(message.id)
112
- }, 200)
113
- }
114
-
115
- function showMessageDropdown() {
116
- isShowMessageDropdown.value = !isShowMessageDropdown.value
117
- if (isShowMessageDropdown.value) {
118
- fetchMessages()
119
- }
120
- }
121
-
122
- // 处理全局点击事件
123
- function handleDocumentClick(event: MouseEvent) {
124
- if (messageNotificationRef.value && !messageNotificationRef.value.contains(event.target as Node)) {
125
- isShowMessageDropdown.value = false
126
- }
127
- }
128
-
129
- onMounted(() => {
130
- fetchMessages()
131
- fetchMessagesCount()
132
- // 初始化 WebSocket 连接
133
- initWebSocket(userInfo.id, sendMessageCallback, createMessageCallback)
134
- // 添加全局点击事件监听器
135
- document.addEventListener('click', handleDocumentClick)
136
- })
137
-
138
- onUnmounted(() => {
139
- closeWebSocket()
140
- // 移除全局点击事件监听器
141
- document.removeEventListener('click', handleDocumentClick)
142
- })
143
- </script>
144
-
145
- <template>
146
- <div ref="messageNotificationRef" class="message_icon" @click="showMessageDropdown">
147
- <VanBadge v-if="unreadCount > 0" :content="unreadCount" max="99">
148
- <VanIcon name="bell" :color="props.iconColor" />
149
- </VanBadge>
150
- <VanIcon v-else name="bell" :color="props.iconColor" />
151
- </div>
152
-
153
- <div v-if="isShowMessageDropdown" class="message_overlay" @click="isShowMessageDropdown = false" />
154
-
155
- <div v-show="isShowMessageDropdown" class="message_dropdown" @click.stop>
156
- <div class="message_header">
157
- <span class="label">消息通知</span>
158
- <span class="action" @click="markAllAsRead">全部已读</span>
159
- </div>
160
- <div class="message_list">
161
- <template v-if="messages.length > 0">
162
- <div
163
- v-for="message in messages"
164
- :key="message.id"
165
- class="message_item"
166
- :class="{ info: message.type === 'info', warning: message.type === 'warning' }"
167
- >
168
- <!-- 第一行:div1 + div2 -->
169
- <div class="message_row">
170
- <!-- div1:标题+时间 -->
171
- <div class="message_left">
172
- <p class="message_title">
173
- {{ message.title }}
174
- </p>
175
- <p class="message_time">
176
- {{ message.time }}
177
- </p>
178
- </div>
179
- <!-- div2:图标 -->
180
- <div class="message_right">
181
- <VanIcon
182
- name="checked"
183
- :color="message.status === 2 ? '#52c41a' : '#999'"
184
- size="28"
185
- @click.stop="handleIconClick(message)"
186
- />
187
- </div>
188
- </div>
189
- <!-- 第二行:div3 内容 -->
190
- <div class="message_content">
191
- <p class="message_text">
192
- {{ message.content }}
193
- </p>
194
- </div>
195
- </div>
196
- </template>
197
- <div v-else class="empty_message">
198
- 暂无未读消息
199
- </div>
200
- </div>
201
- </div>
202
- </template>
203
-
204
- <style scoped lang="less">
205
- .message_dropdown {
206
- position: absolute;
207
- top: 56px;
208
- left: 0;
209
- right: 0;
210
- background-color: #fff;
211
- box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
212
- z-index: 10001;
213
- max-height: 400px;
214
- overflow-y: auto;
215
-
216
- .message_header {
217
- display: flex;
218
- justify-content: space-between;
219
- align-items: center;
220
- padding: 12px 16px;
221
- border-bottom: 1px solid #eee;
222
-
223
- .label {
224
- font-size: 14px;
225
- color: #999;
226
- }
227
- .action {
228
- font-size: 14px;
229
- color: #1989fa;
230
- cursor: pointer;
231
- }
232
- }
233
-
234
- .message_list {
235
- .message_item {
236
- padding: 12px 16px;
237
- border-bottom: 1px solid #f5f5f5;
238
-
239
- &.info {
240
- background-color: #eff6ff;
241
- }
242
- &.warning {
243
- background-color: #fff7ed;
244
- }
245
-
246
- .message_row {
247
- display: flex;
248
- justify-content: space-between;
249
- align-items: flex-start;
250
- }
251
-
252
- .message_left {
253
- flex: 1;
254
- min-width: 0;
255
-
256
- .message_title {
257
- font-size: 14px;
258
- font-weight: 500;
259
- color: #333;
260
- margin: 0;
261
- white-space: nowrap;
262
- overflow: hidden;
263
- text-overflow: ellipsis;
264
- }
265
-
266
- .message_time {
267
- font-size: 12px;
268
- color: #999;
269
- margin: 4px 0 0 0;
270
- }
271
- }
272
-
273
- .message_right {
274
- flex: 0 0 auto;
275
- display: flex;
276
- align-items: center;
277
- padding-left: 8px;
278
-
279
- .van-icon {
280
- font-size: 18px;
281
- color: #999;
282
- }
283
- }
284
-
285
- .message_content {
286
- margin-top: 8px;
287
-
288
- .message_text {
289
- border-radius: 4px;
290
- font-size: 13px;
291
- color: #666;
292
- background-color: #fafafc;
293
- margin: 0;
294
- padding: 8px 12px;
295
- line-height: 1.4;
296
- }
297
- }
298
- }
299
-
300
- .empty_message {
301
- padding: 60px 0;
302
- text-align: center;
303
- color: #999;
304
- font-size: 14px;
305
- }
306
- }
307
- }
308
-
309
- .message_overlay {
310
- position: fixed;
311
- inset: 0;
312
- background: rgba(0, 0, 0, 0.45);
313
- z-index: 10001;
314
- margin-top: 56px;
315
- margin-bottom: 50px;
316
- }
317
- .message_icon {
318
- padding: 8px;
319
- position: relative;
320
- cursor: pointer;
321
-
322
- .van-icon {
323
- font-size: 20px;
324
- }
325
- }
326
- </style>
1
+ <script setup lang="ts">
2
+ import { post } from '@af-mobile-client-vue3/services/restTools'
3
+ import useUserStore from '@af-mobile-client-vue3/stores/modules/user'
4
+ import { mobileUtil } from '@af-mobile-client-vue3/utils/mobileUtil'
5
+ import { Badge as VanBadge, Icon as VanIcon } from 'vant'
6
+ import { onMounted, onUnmounted, ref } from 'vue'
7
+ import { closeWebSocket, initWebSocket } from './webSocket'
8
+ import 'vant/es/badge/style'
9
+ import 'vant/es/icon/style'
10
+
11
+ interface Props {
12
+ iconColor?: string
13
+ }
14
+
15
+ const props = withDefaults(defineProps<Props>(), {
16
+ iconColor: '#666',
17
+ })
18
+
19
+ const userInfo = useUserStore().getUserInfo()
20
+
21
+ // 未读消息数量
22
+ const unreadCount = ref(0)
23
+
24
+ // 打开消息下拉菜单
25
+ const isShowMessageDropdown = ref(false)
26
+
27
+ // 消息列表数据
28
+ const messages = ref<any[]>([])
29
+
30
+ // 组件根元素的引用
31
+ const messageNotificationRef = ref<HTMLElement | null>(null)
32
+
33
+ // 获取消息数据
34
+ async function fetchMessages() {
35
+ const param = {
36
+ userId: `${userInfo.id}:phone`, // |phone
37
+ typeList: '\'phone\'', // phone
38
+ pageNo: 1,
39
+ pageSize: 100,
40
+ }
41
+ const res = await post('/af-system/logic/getNotificationListByType', param)
42
+ messages.value = (res || []).map((item: any) => {
43
+ const event: any = JSON.parse(item.f_event || '{}')
44
+ return {
45
+ id: item.id,
46
+ type: event.type,
47
+ time: item.f_input_date,
48
+ title: event.title || '',
49
+ status: item.f_state,
50
+ content: event.description || '',
51
+ }
52
+ })
53
+ }
54
+
55
+ // 获取未读消息数量
56
+ async function fetchMessagesCount() {
57
+ const param = {
58
+ userId: `${userInfo.id}:phone`, // |phone
59
+ typeList: '\'phone\'', // phone
60
+ }
61
+ const count = await post('/af-system/logic/getNotificationCount', param)
62
+ unreadCount.value = count.count
63
+ }
64
+
65
+ // 处理 WebSocket 消息
66
+ function createMessageCallback(message: any) {
67
+ fetchMessagesCount()
68
+ fetchMessages()
69
+ }
70
+ // 处理 不同的业务
71
+ function sendMessageCallback(message: any) {
72
+ console.log('>>>> 处理业务', JSON.stringify(message))
73
+ if (message.businessType === 'audio') {
74
+ // 播放响铃, 响铃文件为 message.audioName
75
+ mobileUtil.execute({
76
+ funcName: 'playAudio',
77
+ param: {
78
+ fileName: message.audioName,
79
+ },
80
+ callbackFunc: (result: any) => {
81
+ // {"status":"success","data":{"message":"音频播放成功","fileName":"anjian.mp3","path":"assets/audio/anjian.mp3"}}
82
+ },
83
+ })
84
+ }
85
+ }
86
+
87
+ // 全部标记为已读(清空消息列表)
88
+ function markAllAsRead() {
89
+ if (messages.value.length === 0)
90
+ return
91
+
92
+ const messageIds = messages.value.map(msg => msg.id).join(',')
93
+ markAsRead(messageIds)
94
+ }
95
+
96
+ // 单条消息标记为已读
97
+ function markAsRead(id: string | number) {
98
+ post('/af-system/logic/confirmMessage', {
99
+ id,
100
+ f_state: 2,
101
+ }).then(() => {
102
+ fetchMessages()
103
+ fetchMessagesCount()
104
+ })
105
+ }
106
+
107
+ function handleIconClick(message: any) {
108
+ // 乐观更新为已读状态,使图标立刻变绿
109
+ message.status = 2
110
+ setTimeout(() => {
111
+ markAsRead(message.id)
112
+ }, 200)
113
+ }
114
+
115
+ function showMessageDropdown() {
116
+ isShowMessageDropdown.value = !isShowMessageDropdown.value
117
+ if (isShowMessageDropdown.value) {
118
+ fetchMessages()
119
+ }
120
+ }
121
+
122
+ // 处理全局点击事件
123
+ function handleDocumentClick(event: MouseEvent) {
124
+ if (messageNotificationRef.value && !messageNotificationRef.value.contains(event.target as Node)) {
125
+ isShowMessageDropdown.value = false
126
+ }
127
+ }
128
+
129
+ onMounted(() => {
130
+ fetchMessages()
131
+ fetchMessagesCount()
132
+ // 初始化 WebSocket 连接
133
+ initWebSocket(userInfo.id, sendMessageCallback, createMessageCallback)
134
+ // 添加全局点击事件监听器
135
+ document.addEventListener('click', handleDocumentClick)
136
+ })
137
+
138
+ onUnmounted(() => {
139
+ closeWebSocket()
140
+ // 移除全局点击事件监听器
141
+ document.removeEventListener('click', handleDocumentClick)
142
+ })
143
+ </script>
144
+
145
+ <template>
146
+ <div ref="messageNotificationRef" class="message_icon" @click="showMessageDropdown">
147
+ <VanBadge v-if="unreadCount > 0" :content="unreadCount" max="99">
148
+ <VanIcon name="bell" :color="props.iconColor" />
149
+ </VanBadge>
150
+ <VanIcon v-else name="bell" :color="props.iconColor" />
151
+ </div>
152
+
153
+ <div v-if="isShowMessageDropdown" class="message_overlay" @click="isShowMessageDropdown = false" />
154
+
155
+ <div v-show="isShowMessageDropdown" class="message_dropdown" @click.stop>
156
+ <div class="message_header">
157
+ <span class="label">消息通知</span>
158
+ <span class="action" @click="markAllAsRead">全部已读</span>
159
+ </div>
160
+ <div class="message_list">
161
+ <template v-if="messages.length > 0">
162
+ <div
163
+ v-for="message in messages"
164
+ :key="message.id"
165
+ class="message_item"
166
+ :class="{ info: message.type === 'info', warning: message.type === 'warning' }"
167
+ >
168
+ <!-- 第一行:div1 + div2 -->
169
+ <div class="message_row">
170
+ <!-- div1:标题+时间 -->
171
+ <div class="message_left">
172
+ <p class="message_title">
173
+ {{ message.title }}
174
+ </p>
175
+ <p class="message_time">
176
+ {{ message.time }}
177
+ </p>
178
+ </div>
179
+ <!-- div2:图标 -->
180
+ <div class="message_right">
181
+ <VanIcon
182
+ name="checked"
183
+ :color="message.status === 2 ? '#52c41a' : '#999'"
184
+ size="28"
185
+ @click.stop="handleIconClick(message)"
186
+ />
187
+ </div>
188
+ </div>
189
+ <!-- 第二行:div3 内容 -->
190
+ <div class="message_content">
191
+ <p class="message_text">
192
+ {{ message.content }}
193
+ </p>
194
+ </div>
195
+ </div>
196
+ </template>
197
+ <div v-else class="empty_message">
198
+ 暂无未读消息
199
+ </div>
200
+ </div>
201
+ </div>
202
+ </template>
203
+
204
+ <style scoped lang="less">
205
+ .message_dropdown {
206
+ position: absolute;
207
+ top: 56px;
208
+ left: 0;
209
+ right: 0;
210
+ background-color: #fff;
211
+ box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
212
+ z-index: 10001;
213
+ max-height: 400px;
214
+ overflow-y: auto;
215
+
216
+ .message_header {
217
+ display: flex;
218
+ justify-content: space-between;
219
+ align-items: center;
220
+ padding: 12px 16px;
221
+ border-bottom: 1px solid #eee;
222
+
223
+ .label {
224
+ font-size: 14px;
225
+ color: #999;
226
+ }
227
+ .action {
228
+ font-size: 14px;
229
+ color: #1989fa;
230
+ cursor: pointer;
231
+ }
232
+ }
233
+
234
+ .message_list {
235
+ .message_item {
236
+ padding: 12px 16px;
237
+ border-bottom: 1px solid #f5f5f5;
238
+
239
+ &.info {
240
+ background-color: #eff6ff;
241
+ }
242
+ &.warning {
243
+ background-color: #fff7ed;
244
+ }
245
+
246
+ .message_row {
247
+ display: flex;
248
+ justify-content: space-between;
249
+ align-items: flex-start;
250
+ }
251
+
252
+ .message_left {
253
+ flex: 1;
254
+ min-width: 0;
255
+
256
+ .message_title {
257
+ font-size: 14px;
258
+ font-weight: 500;
259
+ color: #333;
260
+ margin: 0;
261
+ white-space: nowrap;
262
+ overflow: hidden;
263
+ text-overflow: ellipsis;
264
+ }
265
+
266
+ .message_time {
267
+ font-size: 12px;
268
+ color: #999;
269
+ margin: 4px 0 0 0;
270
+ }
271
+ }
272
+
273
+ .message_right {
274
+ flex: 0 0 auto;
275
+ display: flex;
276
+ align-items: center;
277
+ padding-left: 8px;
278
+
279
+ .van-icon {
280
+ font-size: 18px;
281
+ color: #999;
282
+ }
283
+ }
284
+
285
+ .message_content {
286
+ margin-top: 8px;
287
+
288
+ .message_text {
289
+ border-radius: 4px;
290
+ font-size: 13px;
291
+ color: #666;
292
+ background-color: #fafafc;
293
+ margin: 0;
294
+ padding: 8px 12px;
295
+ line-height: 1.4;
296
+ }
297
+ }
298
+ }
299
+
300
+ .empty_message {
301
+ padding: 60px 0;
302
+ text-align: center;
303
+ color: #999;
304
+ font-size: 14px;
305
+ }
306
+ }
307
+ }
308
+
309
+ .message_overlay {
310
+ position: fixed;
311
+ inset: 0;
312
+ background: rgba(0, 0, 0, 0.45);
313
+ z-index: 10001;
314
+ margin-top: 56px;
315
+ margin-bottom: 50px;
316
+ }
317
+ .message_icon {
318
+ padding: 8px;
319
+ position: relative;
320
+ cursor: pointer;
321
+
322
+ .van-icon {
323
+ font-size: 20px;
324
+ }
325
+ }
326
+ </style>