uni-oaview 1.9.5 → 1.9.6

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.
@@ -1,46 +1,74 @@
1
1
  <template>
2
- <uni-collapse>
3
- <uni-collapse-item v-for="log in nativeEventLogs" :title="getTitle(log)" :key="log.key">
2
+ <view class="toolbar">
3
+ <view class="toolbar-input-wrap">
4
+ <input v-model="keyword" class="toolbar-input" placeholder="搜索事件名 / 参数 / 响应" confirm-type="search" />
5
+ <view v-if="keyword" class="toolbar-input-clear" @click="handleClearKeyword">×</view>
6
+ </view>
7
+ <view class="toolbar-clear" @click="handleClear">清除</view>
8
+ </view>
9
+ <uni-collapse v-if="filteredNativeEventLogMetas.length" ref="collapseRef" v-model="activeNames">
10
+ <uni-collapse-item v-for="meta in filteredNativeEventLogMetas" :key="meta.id" :name="meta.id" :title="meta.title">
4
11
  <view style="font-size: 10px">
5
12
  <view style="text-align: right; margin-bottom: 8px; padding: 0 15px">
6
- <text style="color: #007aff; font-size: 12px" @click="handleCopy(log)">复制</text>
7
- </view>
8
- <view style="word-break: break-all; padding: 0 15px">
9
- <text :selectable="true" style="font-weight: bolder">发送数据:</text>
10
- <awesome-display-info :log="log.params" />
11
- </view>
12
- <view style="word-break: break-all; padding: 0 15px">
13
- <text :selectable="true" style="font-weight: bolder">接收数据:</text>
14
- <awesome-display-info :log="log.response" />
13
+ <text style="color: #007aff; font-size: 12px" @click="handleCopy(meta)">复制</text>
15
14
  </view>
15
+ <template v-if="isExpanded(meta.id)">
16
+ <view style="word-break: break-all; padding: 0 15px">
17
+ <text :selectable="true" style="font-weight: bolder">发送数据:</text>
18
+ <awesome-display-info :log="getNativeEventDetail(meta.id)?.params" />
19
+ </view>
20
+ <view style="word-break: break-all; padding: 0 15px">
21
+ <text :selectable="true" style="font-weight: bolder">接收数据:</text>
22
+ <awesome-display-info :log="getNativeEventDetail(meta.id)?.response" />
23
+ </view>
24
+ </template>
16
25
  </view>
17
26
  </uni-collapse-item>
18
27
  </uni-collapse>
28
+ <view v-else class="empty-state">
29
+ {{ emptyText }}
30
+ </view>
19
31
  </template>
20
32
  <script lang="ts" setup>
21
- import { ref, onBeforeUnmount } from 'vue';
33
+ import { computed, nextTick, onBeforeUnmount, ref, shallowRef, watch } from 'vue';
22
34
  import awesomeDisplayInfo from './awesome-display-info.vue';
23
35
  import { nativeEventSubject, getNativeEventLogs } from 'uniapp-log-sdk';
24
36
 
25
37
  interface NativeEventLog {
26
38
  key: string;
27
- params: any;
28
- response: any;
39
+ params: unknown;
40
+ response: unknown;
29
41
  startTime: number;
30
42
  endTime: number;
31
43
  }
32
44
 
45
+ interface NativeEventLogMeta {
46
+ id: string;
47
+ key: string;
48
+ title: string;
49
+ }
50
+
33
51
  const MAX_NATIVE_EVENT_LOGS = 50;
52
+ const FLUSH_DELAY = 16;
34
53
 
35
- const nativeEventLogs = ref<NativeEventLog[]>(getNativeEventLogs().slice(-MAX_NATIVE_EVENT_LOGS));
54
+ const collapseRef = ref<{ resize?: () => void } | null>(null);
55
+ const activeNames = ref<string[] | string>([]);
56
+ const keyword = ref('');
57
+ const clearTimestamp = ref<number | null>(null);
58
+ const hasCleared = ref(false);
59
+ const nativeEventDetailMap = shallowRef<Map<string, NativeEventLog>>(new Map());
60
+ const nativeEventLogMetas = ref<NativeEventLogMeta[]>([]);
36
61
 
37
- const stop = nativeEventSubject.subscribe((data: NativeEventLog[]) => {
38
- if (data.length > MAX_NATIVE_EVENT_LOGS) {
39
- nativeEventLogs.value = data.slice(-MAX_NATIVE_EVENT_LOGS);
40
- } else {
41
- nativeEventLogs.value = data;
42
- }
43
- });
62
+ let pendingLogs: NativeEventLog[] | null = null;
63
+ let flushTimer: ReturnType<typeof setTimeout> | null = null;
64
+
65
+ const buildNativeEventLogId = (log: NativeEventLog): string => {
66
+ return `${log.startTime}|${log.endTime}|${log.key}`;
67
+ };
68
+
69
+ const buildNativeEventLogMatchKey = (log: NativeEventLog): string => {
70
+ return `${log.startTime}|${log.key}`;
71
+ };
44
72
 
45
73
  const formatStartTime = (timestamp: number): string => {
46
74
  const date = new Date(timestamp);
@@ -57,18 +85,206 @@
57
85
  return key;
58
86
  };
59
87
 
88
+ const buildMetaFromLog = (log: NativeEventLog): NativeEventLogMeta => {
89
+ return {
90
+ id: buildNativeEventLogId(log),
91
+ key: log.key,
92
+ title: getTitle(log),
93
+ };
94
+ };
95
+
96
+ const migrateActiveNames = (
97
+ previousDetailMap: Map<string, NativeEventLog>,
98
+ nextMetas: NativeEventLogMeta[],
99
+ ): string[] | string => {
100
+ const activeList = getActiveNameList();
101
+ if (!activeList.length) return activeNames.value;
102
+
103
+ const nextMetaIdByMatchKey = new Map<string, string>();
104
+ for (const meta of nextMetas) {
105
+ const detail = nativeEventDetailMap.value.get(meta.id);
106
+ if (!detail) continue;
107
+ nextMetaIdByMatchKey.set(buildNativeEventLogMatchKey(detail), meta.id);
108
+ }
109
+
110
+ const migratedIds = activeList
111
+ .map((id) => {
112
+ if (nextMetaIdByMatchKey.has(id)) return id;
113
+ const previousDetail = previousDetailMap.get(id);
114
+ if (!previousDetail) return null;
115
+ return nextMetaIdByMatchKey.get(buildNativeEventLogMatchKey(previousDetail)) ?? null;
116
+ })
117
+ .filter((id): id is string => Boolean(id));
118
+
119
+ if (Array.isArray(activeNames.value)) return migratedIds;
120
+ return migratedIds[0] ?? '';
121
+ };
122
+
123
+ const getActiveNameList = (): string[] => {
124
+ const value = activeNames.value;
125
+ return Array.isArray(value) ? value : value ? [value] : [];
126
+ };
127
+
128
+ const isExpanded = (id: string): boolean => getActiveNameList().includes(id);
129
+
130
+ const getNativeEventDetail = (id: string): NativeEventLog | null => {
131
+ return nativeEventDetailMap.value.get(id) ?? null;
132
+ };
133
+
134
+ const normalizeKeyword = (value: string): string => value.trim().toLowerCase();
135
+
136
+ const safeStringify = (value: unknown): string => {
137
+ if (value === null || value === undefined) return '';
138
+ if (typeof value === 'string') return value;
139
+ try {
140
+ return JSON.stringify(value);
141
+ } catch (error) {
142
+ return String(value);
143
+ }
144
+ };
145
+
146
+ const matchesNativeEventLog = (meta: NativeEventLogMeta): boolean => {
147
+ const normalizedKeyword = normalizeKeyword(keyword.value);
148
+ if (!normalizedKeyword) return true;
149
+ const detail = getNativeEventDetail(meta.id);
150
+ return [meta.title, meta.key, safeStringify(detail?.params), safeStringify(detail?.response)]
151
+ .join(' ')
152
+ .toLowerCase()
153
+ .includes(normalizedKeyword);
154
+ };
155
+
156
+ const filteredNativeEventLogMetas = computed(() => nativeEventLogMetas.value.filter(matchesNativeEventLog));
157
+
158
+ const emptyText = computed(() => {
159
+ if (keyword.value) return '无匹配结果';
160
+ if (hasCleared.value) return '已清空';
161
+ return '暂无日志';
162
+ });
163
+
164
+ const applyLogs = (logs: NativeEventLog[]): void => {
165
+ const visibleLogs = clearTimestamp.value ? logs.filter((log) => log.startTime > clearTimestamp.value!) : logs;
166
+
167
+ if (
168
+ !visibleLogs.length &&
169
+ clearTimestamp.value &&
170
+ logs.length &&
171
+ logs[logs.length - 1].startTime < clearTimestamp.value
172
+ ) {
173
+ clearTimestamp.value = null;
174
+ hasCleared.value = false;
175
+ }
176
+
177
+ if (!visibleLogs.length && logs.length && clearTimestamp.value) {
178
+ nativeEventDetailMap.value = new Map();
179
+ nativeEventLogMetas.value = [];
180
+ activeNames.value = [];
181
+ return;
182
+ }
183
+
184
+ const nextLogs = visibleLogs.slice(-MAX_NATIVE_EVENT_LOGS);
185
+ const previousDetailMap = new Map(nativeEventDetailMap.value);
186
+ const detailMap = new Map<string, NativeEventLog>();
187
+ const nextMetas: NativeEventLogMeta[] = [];
188
+
189
+ for (const log of nextLogs) {
190
+ const id = buildNativeEventLogId(log);
191
+ detailMap.set(id, log);
192
+ nextMetas.push(buildMetaFromLog(log));
193
+ }
194
+
195
+ nativeEventDetailMap.value = detailMap;
196
+ nativeEventLogMetas.value = nextMetas;
197
+ activeNames.value = migrateActiveNames(previousDetailMap, nextMetas);
198
+ };
199
+
200
+ const flushPendingLogs = (): void => {
201
+ flushTimer = null;
202
+ if (!pendingLogs) return;
203
+ applyLogs(pendingLogs);
204
+ pendingLogs = null;
205
+ };
206
+
207
+ const scheduleFlush = (): void => {
208
+ if (flushTimer) return;
209
+ flushTimer = setTimeout(() => {
210
+ flushPendingLogs();
211
+ }, FLUSH_DELAY);
212
+ };
213
+
214
+ const enqueueLogs = (logs: NativeEventLog[]): void => {
215
+ pendingLogs = logs;
216
+ scheduleFlush();
217
+ };
218
+
219
+ const handleClear = (): void => {
220
+ clearTimestamp.value = Date.now();
221
+ hasCleared.value = true;
222
+ nativeEventLogMetas.value = [];
223
+ nativeEventDetailMap.value = new Map();
224
+ activeNames.value = [];
225
+ };
226
+
227
+ const handleClearKeyword = (): void => {
228
+ keyword.value = '';
229
+ };
230
+
231
+ applyLogs(getNativeEventLogs().filter((log): log is NativeEventLog => Boolean(log)));
232
+
233
+ const stop = nativeEventSubject.subscribe((data: NativeEventLog[]) => {
234
+ enqueueLogs(data);
235
+ });
236
+
237
+ watch(
238
+ () => getActiveNameList().join('|'),
239
+ () => {
240
+ // #ifdef MP
241
+ nextTick(() => collapseRef.value?.resize?.());
242
+ // #endif
243
+ },
244
+ );
245
+
246
+ watch(
247
+ () => nativeEventLogMetas.value,
248
+ () => {
249
+ // #ifdef MP
250
+ if (getActiveNameList().length) nextTick(() => collapseRef.value?.resize?.());
251
+ // #endif
252
+ },
253
+ { deep: false },
254
+ );
255
+
256
+ watch(keyword, () => {
257
+ const keepIds = new Set(filteredNativeEventLogMetas.value.map((meta) => meta.id));
258
+ const currentActive = activeNames.value;
259
+ if (Array.isArray(currentActive)) {
260
+ activeNames.value = currentActive.filter((id) => keepIds.has(id));
261
+ } else if (currentActive && !keepIds.has(currentActive)) {
262
+ activeNames.value = '';
263
+ }
264
+
265
+ // #ifdef MP
266
+ if (getActiveNameList().length) nextTick(() => collapseRef.value?.resize?.());
267
+ // #endif
268
+ });
269
+
60
270
  onBeforeUnmount(() => {
271
+ if (flushTimer) {
272
+ clearTimeout(flushTimer);
273
+ flushTimer = null;
274
+ }
275
+ pendingLogs = null;
61
276
  stop?.unsubscribe();
62
277
  });
63
278
 
64
- const handleCopy = (log: NativeEventLog) => {
279
+ const handleCopy = (meta: NativeEventLogMeta) => {
280
+ const detail = getNativeEventDetail(meta.id);
65
281
  const text = JSON.stringify(
66
282
  {
67
- key: log.key,
68
- params: log.params,
69
- response: log.response,
70
- startTime: log.startTime,
71
- endTime: log.endTime,
283
+ key: detail?.key ?? meta.key,
284
+ params: detail?.params,
285
+ response: detail?.response,
286
+ startTime: detail?.startTime,
287
+ endTime: detail?.endTime,
72
288
  },
73
289
  null,
74
290
  2,
@@ -83,6 +299,65 @@
83
299
  </script>
84
300
 
85
301
  <style lang="scss" scoped>
302
+ .toolbar {
303
+ position: sticky;
304
+ top: 0;
305
+ z-index: 1;
306
+ display: flex;
307
+ align-items: center;
308
+ gap: 8px;
309
+ margin-bottom: 8px;
310
+ padding-bottom: 8px;
311
+ background-color: #fff;
312
+ }
313
+
314
+ .toolbar-input {
315
+ width: 100%;
316
+ height: 32px;
317
+ padding: 0 28px 0 10px;
318
+ font-size: 12px;
319
+ border: 1px solid #dcdfe6;
320
+ border-radius: 4px;
321
+ background-color: #fff;
322
+ box-sizing: border-box;
323
+ }
324
+
325
+ .toolbar-input-wrap {
326
+ position: relative;
327
+ flex: 1;
328
+ min-width: 0;
329
+ }
330
+
331
+ .toolbar-input-clear {
332
+ position: absolute;
333
+ top: 50%;
334
+ right: 8px;
335
+ transform: translateY(-50%);
336
+ width: 16px;
337
+ height: 16px;
338
+ line-height: 16px;
339
+ font-size: 12px;
340
+ color: #999;
341
+ text-align: center;
342
+ border-radius: 50%;
343
+ }
344
+
345
+ .toolbar-clear {
346
+ flex-shrink: 0;
347
+ padding: 6px 10px;
348
+ font-size: 12px;
349
+ color: #119af5;
350
+ border: 1px solid #119af5;
351
+ border-radius: 4px;
352
+ }
353
+
354
+ .empty-state {
355
+ padding: 16px 0;
356
+ font-size: 12px;
357
+ color: #999;
358
+ text-align: center;
359
+ }
360
+
86
361
  .uni-collapse-content {
87
362
  font-size: 12px;
88
363
  }
@@ -1,6 +1,6 @@
1
1
  const MAX_DEPTH = 10;
2
- const MAX_ARRAY_LENGTH = 10;
3
- const MAX_STRING_LENGTH = 300;
2
+ const MAX_ARRAY_LENGTH = 100;
3
+ const MAX_STRING_LENGTH = 10000;
4
4
 
5
5
  interface RichTextNode {
6
6
  type: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uni-oaview",
3
- "version": "1.9.5",
3
+ "version": "1.9.6",
4
4
  "description": "uniapp小程序组件库",
5
5
  "main": "dist/index.esm.js",
6
6
  "typings": "dist/index.d.ts",