uni-oaview 1.2.4 → 1.3.0
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.
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="dev-tools">
|
|
3
|
+
<view class="section">
|
|
4
|
+
<text class="section-title">快捷入口</text>
|
|
5
|
+
<view class="page-item" @click="handleNavigateToMock">
|
|
6
|
+
<text class="page-title">Mock 页面</text>
|
|
7
|
+
<text class="page-path">pages/mock/index</text>
|
|
8
|
+
</view>
|
|
9
|
+
</view>
|
|
10
|
+
</view>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script lang="ts" setup>
|
|
14
|
+
const handleNavigateToMock = () => {
|
|
15
|
+
uni.navigateTo({ url: '/pages/mock/index' });
|
|
16
|
+
};
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<style lang="scss" scoped>
|
|
20
|
+
.dev-tools {
|
|
21
|
+
font-size: 12px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.section {
|
|
25
|
+
margin-bottom: 16px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.section-title {
|
|
29
|
+
display: block;
|
|
30
|
+
margin-bottom: 8px;
|
|
31
|
+
font-size: 14px;
|
|
32
|
+
font-weight: 500;
|
|
33
|
+
color: #333;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.page-item {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
gap: 4px;
|
|
40
|
+
padding: 12px 16px;
|
|
41
|
+
background: #f5f5f5;
|
|
42
|
+
border-radius: 8px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.page-title {
|
|
46
|
+
font-weight: 500;
|
|
47
|
+
color: #333;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.page-path {
|
|
51
|
+
font-size: 10px;
|
|
52
|
+
color: #999;
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -1,65 +1,86 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<uni-collapse>
|
|
2
|
+
<uni-collapse ref="collapseRef" v-model="activeNames">
|
|
3
3
|
<uni-collapse-item
|
|
4
|
-
v-for="
|
|
5
|
-
:
|
|
6
|
-
:
|
|
4
|
+
v-for="meta in networkLogMetas"
|
|
5
|
+
:key="meta.id"
|
|
6
|
+
:name="meta.id"
|
|
7
|
+
:title="meta.title"
|
|
7
8
|
:class="{
|
|
8
|
-
'uni-collapse-item-failed':
|
|
9
|
+
'uni-collapse-item-failed': meta.failed,
|
|
10
|
+
'uni-collapse-item-pending': meta.pending,
|
|
9
11
|
}"
|
|
10
12
|
>
|
|
11
13
|
<view style="font-size: 10px; padding: 10px 15px">
|
|
12
14
|
<view style="word-break: break-all; padding: 8px 0; margin-bottom: 10px">
|
|
13
15
|
<text :selectable="true" style="font-weight: bolder">状态码:</text>
|
|
14
|
-
{{
|
|
15
|
-
</view>
|
|
16
|
-
<view style="word-break: break-all; padding: 8px 0; margin-bottom: 10px">
|
|
17
|
-
<text :selectable="true" style="font-weight: bolder">请求头:</text>
|
|
18
|
-
<awesome-display-info :log="log.request.header" />
|
|
19
|
-
</view>
|
|
20
|
-
<view style="word-break: break-all; padding: 8px 0; margin-bottom: 10px">
|
|
21
|
-
<text :selectable="true" style="font-weight: bolder">请求参数:</text>
|
|
22
|
-
<awesome-display-info :log="log.request.data" />
|
|
23
|
-
</view>
|
|
24
|
-
<view style="word-break: break-all; padding: 8px 0">
|
|
25
|
-
<text :selectable="true" style="font-weight: bolder">响应数据:</text>
|
|
26
|
-
<awesome-display-info :log="log.response" />
|
|
16
|
+
{{ meta.method }} {{ meta.statusCode ?? '-' }}
|
|
27
17
|
</view>
|
|
18
|
+
<template v-if="isExpanded(meta.id)">
|
|
19
|
+
<view style="word-break: break-all; padding: 8px 0; margin-bottom: 10px">
|
|
20
|
+
<text :selectable="true" style="font-weight: bolder">请求头:</text>
|
|
21
|
+
<awesome-display-info :log="getNetworkLogDetail(meta.id)?.request?.header" />
|
|
22
|
+
</view>
|
|
23
|
+
<view style="word-break: break-all; padding: 8px 0; margin-bottom: 10px">
|
|
24
|
+
<text :selectable="true" style="font-weight: bolder">请求参数:</text>
|
|
25
|
+
<awesome-display-info :log="getNetworkLogDetail(meta.id)?.request?.data" />
|
|
26
|
+
</view>
|
|
27
|
+
<view style="word-break: break-all; padding: 8px 0">
|
|
28
|
+
<text :selectable="true" style="font-weight: bolder">响应数据:</text>
|
|
29
|
+
<awesome-display-info :log="getNetworkLogDetail(meta.id)?.response" />
|
|
30
|
+
</view>
|
|
31
|
+
</template>
|
|
28
32
|
</view>
|
|
29
33
|
</uni-collapse-item>
|
|
30
34
|
</uni-collapse>
|
|
31
35
|
</template>
|
|
32
36
|
|
|
33
37
|
<script lang="ts" setup>
|
|
34
|
-
import {
|
|
38
|
+
import { nextTick, onBeforeUnmount, ref, shallowRef, watch } from 'vue';
|
|
35
39
|
import awesomeDisplayInfo from './awesome-display-info.vue';
|
|
36
40
|
import { networkSubject, getNetworkLogs } from 'uniapp-log-sdk';
|
|
37
41
|
|
|
42
|
+
interface NetworkLogRequest {
|
|
43
|
+
url: string;
|
|
44
|
+
method?: string;
|
|
45
|
+
header?: unknown;
|
|
46
|
+
data?: unknown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface NetworkLogResponseData {
|
|
50
|
+
errno?: number;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface NetworkLogResponse {
|
|
55
|
+
statusCode?: number;
|
|
56
|
+
data?: NetworkLogResponseData;
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
}
|
|
59
|
+
|
|
38
60
|
interface NetworkLog {
|
|
39
|
-
request:
|
|
40
|
-
response
|
|
61
|
+
request: NetworkLogRequest;
|
|
62
|
+
response?: NetworkLogResponse;
|
|
41
63
|
startTime: number;
|
|
42
|
-
endTime
|
|
64
|
+
endTime?: number;
|
|
43
65
|
}
|
|
44
66
|
|
|
45
|
-
|
|
67
|
+
interface NetworkLogMeta {
|
|
68
|
+
id: string;
|
|
69
|
+
title: string;
|
|
70
|
+
method: string;
|
|
71
|
+
statusCode: number | null;
|
|
72
|
+
pending: boolean;
|
|
73
|
+
failed: boolean;
|
|
74
|
+
}
|
|
46
75
|
|
|
47
|
-
const
|
|
76
|
+
const MAX_NETWORK_LOGS = 100;
|
|
48
77
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} else {
|
|
53
|
-
networkLogs.value = data;
|
|
54
|
-
}
|
|
55
|
-
});
|
|
78
|
+
const collapseRef = ref<{ resize?: () => void } | null>(null);
|
|
79
|
+
// uni-collapse 的 v-model 在不同模式下可能是 string 或 string[]
|
|
80
|
+
const activeNames = ref<string[] | string>([]);
|
|
56
81
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const timeStr = formatTime(startTime);
|
|
60
|
-
const duration = endTime && startTime ? `${((endTime - startTime) / 1000).toFixed(2)}s` : 'pending';
|
|
61
|
-
return `[${timeStr}] ${request.url};${duration}`;
|
|
62
|
-
};
|
|
82
|
+
const networkLogDetailMap = shallowRef<Map<string, NetworkLog>>(new Map());
|
|
83
|
+
const networkLogMetas = ref<NetworkLogMeta[]>([]);
|
|
63
84
|
|
|
64
85
|
const formatTime = (timestamp: number): string => {
|
|
65
86
|
const date = new Date(timestamp);
|
|
@@ -73,6 +94,99 @@
|
|
|
73
94
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
|
74
95
|
};
|
|
75
96
|
|
|
97
|
+
const buildNetworkLogId = (log: NetworkLog): string => {
|
|
98
|
+
const url = log.request?.url ?? '';
|
|
99
|
+
const method = log.request?.method ?? '';
|
|
100
|
+
const startTime = log.startTime ?? 0;
|
|
101
|
+
return `${startTime}|${method}|${url}`;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const buildMetaFromLog = (log: NetworkLog): NetworkLogMeta => {
|
|
105
|
+
const { startTime, endTime, request, response } = log;
|
|
106
|
+
const timeStr = formatTime(startTime);
|
|
107
|
+
const duration = endTime && startTime ? `${((endTime - startTime) / 1000).toFixed(2)}s` : 'pending';
|
|
108
|
+
const statusCode = typeof response?.statusCode === 'number' ? response.statusCode : null;
|
|
109
|
+
const errno = response?.data?.errno;
|
|
110
|
+
const pending = !endTime;
|
|
111
|
+
const failed = !pending && Boolean(response && (statusCode !== 200 || (typeof errno === 'number' && errno !== 0)));
|
|
112
|
+
const url = request?.url ?? '';
|
|
113
|
+
const method = request?.method ?? '';
|
|
114
|
+
return {
|
|
115
|
+
id: buildNetworkLogId(log),
|
|
116
|
+
title: `[${timeStr}] ${url};${duration}`,
|
|
117
|
+
method,
|
|
118
|
+
statusCode,
|
|
119
|
+
pending,
|
|
120
|
+
failed,
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const getActiveNameList = (): string[] => {
|
|
125
|
+
const value = activeNames.value;
|
|
126
|
+
return Array.isArray(value) ? value : value ? [value] : [];
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const isExpanded = (id: string): boolean => getActiveNameList().includes(id);
|
|
130
|
+
|
|
131
|
+
const getNetworkLogDetail = (id: string): NetworkLog | null => {
|
|
132
|
+
return networkLogDetailMap.value.get(id) ?? null;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const upsertLogs = (logs: NetworkLog[]) => {
|
|
136
|
+
const nextLogs = logs.slice(-MAX_NETWORK_LOGS);
|
|
137
|
+
const detailMap = networkLogDetailMap.value;
|
|
138
|
+
|
|
139
|
+
const nextMetas: NetworkLogMeta[] = [];
|
|
140
|
+
for (const log of nextLogs) {
|
|
141
|
+
const id = buildNetworkLogId(log);
|
|
142
|
+
detailMap.set(id, log);
|
|
143
|
+
nextMetas.push(buildMetaFromLog(log));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
networkLogMetas.value = nextMetas;
|
|
147
|
+
|
|
148
|
+
// 清理 detailMap,避免无限增长
|
|
149
|
+
const keepIds = new Set(nextMetas.map((m) => m.id));
|
|
150
|
+
for (const key of detailMap.keys()) {
|
|
151
|
+
if (!keepIds.has(key)) detailMap.delete(key);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 清理 activeNames(如果展开项已被裁剪掉)
|
|
155
|
+
const currentActive = activeNames.value;
|
|
156
|
+
if (Array.isArray(currentActive)) {
|
|
157
|
+
if (currentActive.length) activeNames.value = currentActive.filter((id) => keepIds.has(id));
|
|
158
|
+
} else if (currentActive && !keepIds.has(currentActive)) {
|
|
159
|
+
activeNames.value = '';
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
upsertLogs(getNetworkLogs());
|
|
164
|
+
|
|
165
|
+
const stop = networkSubject.subscribe((data: NetworkLog[]) => {
|
|
166
|
+
upsertLogs(data);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// 展开项变化时刷新 collapse 高度缓存(小程序端必须手动调用,否则内容高度可能为 0)
|
|
170
|
+
watch(
|
|
171
|
+
() => getActiveNameList().join('|'),
|
|
172
|
+
() => {
|
|
173
|
+
// #ifdef MP
|
|
174
|
+
nextTick(() => collapseRef.value?.resize?.());
|
|
175
|
+
// #endif
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// 列表更新时,如果有已展开项(内容高度可能变化),同步刷新高度缓存
|
|
180
|
+
watch(
|
|
181
|
+
() => networkLogMetas.value,
|
|
182
|
+
() => {
|
|
183
|
+
// #ifdef MP
|
|
184
|
+
if (getActiveNameList().length) nextTick(() => collapseRef.value?.resize?.());
|
|
185
|
+
// #endif
|
|
186
|
+
},
|
|
187
|
+
{ deep: false },
|
|
188
|
+
);
|
|
189
|
+
|
|
76
190
|
onBeforeUnmount(() => {
|
|
77
191
|
stop?.unsubscribe();
|
|
78
192
|
});
|
|
@@ -84,11 +198,11 @@
|
|
|
84
198
|
}
|
|
85
199
|
::v-deep {
|
|
86
200
|
.uni-collapse-item__title-text {
|
|
87
|
-
white-space: normal !important;
|
|
88
|
-
overflow: visible !important;
|
|
89
|
-
text-overflow: clip !important;
|
|
90
|
-
line-height: normal !important;
|
|
91
|
-
word-break: break-all !important;
|
|
201
|
+
white-space: normal !important;
|
|
202
|
+
overflow: visible !important;
|
|
203
|
+
text-overflow: clip !important;
|
|
204
|
+
line-height: normal !important;
|
|
205
|
+
word-break: break-all !important;
|
|
92
206
|
font-size: 10px;
|
|
93
207
|
font-weight: normal;
|
|
94
208
|
}
|
|
@@ -97,5 +211,10 @@
|
|
|
97
211
|
color: red !important;
|
|
98
212
|
}
|
|
99
213
|
}
|
|
214
|
+
.uni-collapse-item-pending {
|
|
215
|
+
.uni-collapse-item__title-text {
|
|
216
|
+
color: #f90 !important;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
100
219
|
}
|
|
101
220
|
</style>
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<uni-popup ref="popupRef" background-color="#fff">
|
|
3
3
|
<view style="height: 500px; background-color: white; width: 100vw">
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
<scroll-view scroll-x class="tab-scroll" :show-scrollbar="false">
|
|
5
|
+
<view class="tab-container">
|
|
6
|
+
<view
|
|
7
|
+
v-for="(item, index) in list"
|
|
8
|
+
:key="item"
|
|
9
|
+
class="tab-item"
|
|
10
|
+
:class="{ active: current === index }"
|
|
11
|
+
@click="handleTabClick(index)"
|
|
12
|
+
>
|
|
13
|
+
<text class="tab-text">{{ item }}</text>
|
|
14
|
+
</view>
|
|
15
|
+
</view>
|
|
16
|
+
</scroll-view>
|
|
10
17
|
<view style="height: calc(100% - 48px); overflow-y: auto; padding: 8px">
|
|
11
18
|
<template v-if="current === 0">
|
|
12
19
|
<Console />
|
|
@@ -26,6 +33,9 @@
|
|
|
26
33
|
<template v-if="current === 5">
|
|
27
34
|
<System />
|
|
28
35
|
</template>
|
|
36
|
+
<template v-if="current === 6">
|
|
37
|
+
<DevTools />
|
|
38
|
+
</template>
|
|
29
39
|
</view>
|
|
30
40
|
</view>
|
|
31
41
|
</uni-popup>
|
|
@@ -51,17 +61,14 @@
|
|
|
51
61
|
import Storage from './storage.vue';
|
|
52
62
|
import Console from './console.vue';
|
|
53
63
|
import System from './system.vue';
|
|
54
|
-
|
|
55
|
-
interface SegmentedControlClickEvent {
|
|
56
|
-
currentIndex: number;
|
|
57
|
-
}
|
|
64
|
+
import DevTools from './dev-tools.vue';
|
|
58
65
|
|
|
59
66
|
const popupRef = ref<any>();
|
|
60
|
-
const list = ref(['console', 'network', 'event', 'error', 'storage', 'system']);
|
|
67
|
+
const list = ref(['console', 'network', 'event', 'error', 'storage', 'system', 'dev-tools']);
|
|
61
68
|
const current = ref(0);
|
|
62
69
|
|
|
63
|
-
const handleTabClick = (
|
|
64
|
-
current.value =
|
|
70
|
+
const handleTabClick = (index: number) => {
|
|
71
|
+
current.value = index;
|
|
65
72
|
};
|
|
66
73
|
|
|
67
74
|
const openDebug = () => {
|
|
@@ -103,4 +110,49 @@
|
|
|
103
110
|
box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
|
|
104
111
|
background-color: white;
|
|
105
112
|
}
|
|
113
|
+
|
|
114
|
+
.tab-scroll {
|
|
115
|
+
height: 48px;
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.tab-container {
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: row;
|
|
122
|
+
height: 100%;
|
|
123
|
+
padding: 0 8px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.tab-item {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
justify-content: center;
|
|
130
|
+
padding: 0 6px;
|
|
131
|
+
height: 100%;
|
|
132
|
+
flex-shrink: 0;
|
|
133
|
+
position: relative;
|
|
134
|
+
|
|
135
|
+
&.active {
|
|
136
|
+
.tab-text {
|
|
137
|
+
color: #007aff;
|
|
138
|
+
font-weight: 500;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
&::after {
|
|
142
|
+
content: '';
|
|
143
|
+
position: absolute;
|
|
144
|
+
bottom: 0;
|
|
145
|
+
left: 6px;
|
|
146
|
+
right: 6px;
|
|
147
|
+
height: 2px;
|
|
148
|
+
background-color: #007aff;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.tab-text {
|
|
154
|
+
font-size: 14px;
|
|
155
|
+
color: #666;
|
|
156
|
+
white-space: nowrap;
|
|
157
|
+
}
|
|
106
158
|
</style>
|