vite-uni-dev-tool 1.1.0 → 1.2.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.
Files changed (135) hide show
  1. package/README.md +8 -0
  2. package/dist/const.cjs +1 -1
  3. package/dist/const.d.ts +1 -0
  4. package/dist/const.d.ts.map +1 -1
  5. package/dist/const.js +1 -1
  6. package/dist/core.d.ts.map +1 -1
  7. package/dist/core.js +2 -2
  8. package/dist/i18n/locales/en.cjs +1 -1
  9. package/dist/i18n/locales/en.d.ts +5 -0
  10. package/dist/i18n/locales/en.d.ts.map +1 -1
  11. package/dist/i18n/locales/en.js +1 -1
  12. package/dist/i18n/locales/zh-Hans.cjs +1 -1
  13. package/dist/i18n/locales/zh-Hans.d.ts +5 -0
  14. package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
  15. package/dist/i18n/locales/zh-Hans.js +1 -1
  16. package/dist/modules/devIntercept/index.cjs +9 -9
  17. package/dist/modules/devIntercept/index.d.ts +1 -1
  18. package/dist/modules/devIntercept/index.d.ts.map +1 -1
  19. package/dist/modules/devIntercept/index.js +9 -9
  20. package/dist/modules/devStore/index.cjs +1 -1
  21. package/dist/modules/devStore/index.d.ts.map +1 -1
  22. package/dist/modules/devStore/index.js +1 -1
  23. package/dist/plugins/uniDevTool/transform/transformMain.cjs +3 -3
  24. package/dist/plugins/uniDevTool/transform/transformMain.d.ts +2 -1
  25. package/dist/plugins/uniDevTool/transform/transformMain.d.ts.map +1 -1
  26. package/dist/plugins/uniDevTool/transform/transformMain.js +3 -3
  27. package/dist/plugins/uniDevTool/transform/transformVue.cjs +31 -25
  28. package/dist/plugins/uniDevTool/transform/transformVue.d.ts +2 -1
  29. package/dist/plugins/uniDevTool/transform/transformVue.d.ts.map +1 -1
  30. package/dist/plugins/uniDevTool/transform/transformVue.js +30 -24
  31. package/dist/plugins/uniDevTool/uniDevTool.cjs +3 -3
  32. package/dist/plugins/uniDevTool/uniDevTool.d.ts +3 -1
  33. package/dist/plugins/uniDevTool/uniDevTool.d.ts.map +1 -1
  34. package/dist/plugins/uniDevTool/uniDevTool.js +3 -3
  35. package/dist/type.d.ts +3 -0
  36. package/dist/type.d.ts.map +1 -1
  37. package/dist/v3/DevTool/components/BluetoothList/BluetoothItem.vue +199 -199
  38. package/dist/v3/DevTool/components/BluetoothList/BluetoothTool.vue +730 -730
  39. package/dist/v3/DevTool/components/BluetoothList/index.vue +167 -167
  40. package/dist/v3/DevTool/components/CaptureScreen/index.vue +109 -109
  41. package/dist/v3/DevTool/components/ConsoleList/ConsoleItem.vue +230 -225
  42. package/dist/v3/DevTool/components/ConsoleList/RunJSInput.vue +247 -247
  43. package/dist/v3/DevTool/components/ConsoleList/index.vue +171 -171
  44. package/dist/v3/DevTool/components/ConsoleList/staticTips.ts +1145 -1145
  45. package/dist/v3/DevTool/components/DevToolTitle/index.vue +24 -24
  46. package/dist/v3/DevTool/components/DevToolWindow/DevToolOverlay.vue +197 -197
  47. package/dist/v3/DevTool/components/DevToolWindow/hooks/dataUtils.ts +48 -48
  48. package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolData.ts +387 -387
  49. package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolHandlers.ts +629 -629
  50. package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolOverlay.ts +201 -197
  51. package/dist/v3/DevTool/components/ElEvent/ElEventItem.vue +105 -105
  52. package/dist/v3/DevTool/components/ElEvent/index.vue +106 -106
  53. package/dist/v3/DevTool/components/Instance/components/InstanceTreeNode.vue +265 -265
  54. package/dist/v3/DevTool/components/Instance/flatten.ts +226 -226
  55. package/dist/v3/DevTool/components/Instance/index.vue +94 -94
  56. package/dist/v3/DevTool/components/Instance/registry.ts +49 -49
  57. package/dist/v3/DevTool/components/Instance/transformTree.ts +375 -375
  58. package/dist/v3/DevTool/components/Instance/transformTreeCtx.ts +268 -268
  59. package/dist/v3/DevTool/components/Instance/typing.d.ts +43 -43
  60. package/dist/v3/DevTool/components/InstanceDetail/index.vue +485 -485
  61. package/dist/v3/DevTool/components/JsonDetail/index.vue +70 -70
  62. package/dist/v3/DevTool/components/NFCList/NFCItem.vue +112 -112
  63. package/dist/v3/DevTool/components/NFCList/NFCTool.vue +454 -454
  64. package/dist/v3/DevTool/components/NFCList/const.ts +56 -56
  65. package/dist/v3/DevTool/components/NFCList/index.vue +94 -94
  66. package/dist/v3/DevTool/components/NetworkList/InterceptConfig.vue +624 -624
  67. package/dist/v3/DevTool/components/NetworkList/InterceptItem.vue +140 -140
  68. package/dist/v3/DevTool/components/NetworkList/NetworkDetail.vue +287 -287
  69. package/dist/v3/DevTool/components/NetworkList/NetworkIntercept.vue +88 -88
  70. package/dist/v3/DevTool/components/NetworkList/NetworkItem.vue +163 -163
  71. package/dist/v3/DevTool/components/NetworkList/NetworkSend.vue +589 -589
  72. package/dist/v3/DevTool/components/NetworkList/const.ts +4 -4
  73. package/dist/v3/DevTool/components/NetworkList/hooks/useNetworkForm.ts +86 -86
  74. package/dist/v3/DevTool/components/NetworkList/index.vue +160 -160
  75. package/dist/v3/DevTool/components/NetworkList/utils.ts +101 -101
  76. package/dist/v3/DevTool/components/Performance/index.vue +498 -498
  77. package/dist/v3/DevTool/components/Performance/modules/PerformanceMetrics.vue +153 -153
  78. package/dist/v3/DevTool/components/Performance/modules/usePerformanceChart.ts +460 -460
  79. package/dist/v3/DevTool/components/Performance/modules/usePerformanceData.ts +258 -258
  80. package/dist/v3/DevTool/components/PiniaList/index.vue +93 -93
  81. package/dist/v3/DevTool/components/RunJS/index.vue +148 -148
  82. package/dist/v3/DevTool/components/ScanCodeList/ScanCodeItem.vue +97 -97
  83. package/dist/v3/DevTool/components/ScanCodeList/index.vue +100 -100
  84. package/dist/v3/DevTool/components/SettingButton/index.vue +45 -45
  85. package/dist/v3/DevTool/components/SettingList/index.vue +218 -218
  86. package/dist/v3/DevTool/components/SettingList/modules/SettingBarrage.vue +304 -304
  87. package/dist/v3/DevTool/components/SettingList/modules/SettingDevTool.vue +212 -212
  88. package/dist/v3/DevTool/components/SettingList/modules/SettingInfo.vue +157 -157
  89. package/dist/v3/DevTool/components/SettingList/modules/SettingLanguage.vue +74 -74
  90. package/dist/v3/DevTool/components/SettingList/modules/SettingLog.vue +230 -230
  91. package/dist/v3/DevTool/components/SettingList/typing.d.ts +2 -2
  92. package/dist/v3/DevTool/components/SourceCode/Line.vue +127 -127
  93. package/dist/v3/DevTool/components/SourceCode/parseCode.ts +609 -609
  94. package/dist/v3/DevTool/components/StorageList/index.vue +174 -174
  95. package/dist/v3/DevTool/components/TransferList/TransferDetail.vue +268 -268
  96. package/dist/v3/DevTool/components/VuexList/index.vue +84 -84
  97. package/dist/v3/DevTool/index.vue +1 -0
  98. package/dist/v3/components/AppTransition/index.vue +176 -176
  99. package/dist/v3/components/AutoSizer/index.vue +192 -192
  100. package/dist/v3/components/AutoSizer/index1.vue +184 -184
  101. package/dist/v3/components/AutoSizer/utils.ts +49 -49
  102. package/dist/v3/components/Barrage/BarrageItem.vue +137 -137
  103. package/dist/v3/components/Barrage/index.vue +202 -202
  104. package/dist/v3/components/CircularButton/index.vue +84 -84
  105. package/dist/v3/components/CustomSwiper/CustomSwiperItem.vue +49 -49
  106. package/dist/v3/components/CustomSwiper/index.vue +104 -104
  107. package/dist/v3/components/DevErrorBoundary/index.vue +380 -0
  108. package/dist/v3/components/Empty/index.vue +29 -29
  109. package/dist/v3/components/FilterSelect/index.vue +179 -179
  110. package/dist/v3/components/JsonPretty/components/Brackets/index.vue +27 -27
  111. package/dist/v3/components/JsonPretty/components/Carets/index.vue +59 -59
  112. package/dist/v3/components/JsonPretty/components/CheckController/index.vue +136 -136
  113. package/dist/v3/components/JsonPretty/components/TreeNode/index.vue +387 -387
  114. package/dist/v3/components/JsonPretty/hooks/useClipboard.ts +21 -21
  115. package/dist/v3/components/JsonPretty/hooks/useError.ts +21 -21
  116. package/dist/v3/components/JsonPretty/type.ts +127 -127
  117. package/dist/v3/components/JsonPretty/utils/index.ts +169 -169
  118. package/dist/v3/components/MovableContainer/index.vue +8 -4
  119. package/dist/v3/components/Pick/index.vue +322 -322
  120. package/dist/v3/components/Tag/index.vue +113 -113
  121. package/dist/v3/components/VirtualList/AutoSize.vue +40 -40
  122. package/dist/v3/components/VirtualList/index.vue +416 -416
  123. package/dist/v3/hooks/useBluetooth/index.ts +561 -561
  124. package/dist/v3/hooks/useContainerStyle.ts +153 -153
  125. package/dist/v3/hooks/useNFC/index.ts +107 -107
  126. package/dist/v3/hooks/useNFC/typing.d.ts +396 -396
  127. package/dist/v3/hooks/useNFC/useNFCAndroid.ts +966 -966
  128. package/dist/v3/hooks/useNFC/useNFCMpWeiXin.ts +812 -812
  129. package/dist/v3/hooks/useNFC/utils.ts +754 -754
  130. package/dist/v3/hooks/useRequest/index.ts +586 -586
  131. package/dist/v3/hooks/useRequest/utils.ts +267 -267
  132. package/dist/v3/hooks/useScanCode/index.ts +206 -206
  133. package/dist/v3/hooks/useWebsocket/index.ts +253 -253
  134. package/dist/v3/styles/theme.ts +12 -12
  135. package/package.json +9 -1
@@ -1,589 +1,589 @@
1
- <template>
2
- <view class="network-send" :style="customStyle">
3
- <view class="network-send-control">
4
- <DevToolTitle>{{ t('network.sendRequest') }}</DevToolTitle>
5
- <CircularButton style="margin-left: auto" text="×" @click="onClose" />
6
- </view>
7
- <scroll-view scroll-y class="network-send-form">
8
- <view class="network-send-control">
9
- <DevToolTitle>{{ t('network.timeout') }}</DevToolTitle>
10
- </view>
11
- <input
12
- class="network-input"
13
- :value="state.timeout"
14
- @input="onChangeState($event, 'timeout')" />
15
- <view class="network-send-control">
16
- <DevToolTitle>{{ t('network.requestMethod') }}</DevToolTitle>
17
- </view>
18
- <radio-group class="radio-group" @change="onChangeRequestMethod">
19
- <label
20
- class="radio-group-item"
21
- v-for="item in requestMethods"
22
- :key="item.value">
23
- <radio
24
- size
25
- style="transform: scale(0.8)"
26
- :color="themeColor"
27
- :value="item.value"
28
- :checked="item.value === state.method" />
29
-
30
- <view>{{ item.name }}</view>
31
- </label>
32
- </radio-group>
33
- <view class="network-send-control">
34
- <DevToolTitle>{{ t('network.requestHeaders') }}</DevToolTitle>
35
- <CircularButton
36
- style="margin-left: auto"
37
- text="+"
38
- @click="onAddHeader" />
39
- </view>
40
-
41
- <view class="network-header-list">
42
- <checkbox-group
43
- v-if="headerList.length > 0"
44
- @change="onChangeHeaderChecked">
45
- <view
46
- class="network-header-item"
47
- v-for="(item, index) in headerList"
48
- :key="item.id">
49
- <checkbox color="#9254de" :checked="item.checked" />
50
- <input
51
- class="network-input"
52
- :placeholder="t('network.placeholderHeaderName')"
53
- :value="item.key"
54
- @input="onChangeHeader($event, 'key', index)" />
55
- <input
56
- class="network-input"
57
- :placeholder="t('network.placeholderHeaderValue')"
58
- :value="item.value"
59
- @input="onChangeHeader($event, 'value', index)" />
60
- <CircularButton text="一" @click="onDeleteHeader(item)" />
61
- </view>
62
- </checkbox-group>
63
-
64
- <view class="network-empty" v-else>
65
- {{ t('network.noRequestHeaders') }}
66
- </view>
67
- </view>
68
- <DevToolTitle>{{ t('network.requestUrl') }}</DevToolTitle>
69
- <textarea
70
- :placeholder="t('network.placeholderUrl')"
71
- class="network-textarea"
72
- :value="state.url"
73
- @input="onChangeUrl" />
74
- <view class="form-error-message">
75
- {{ hasUrl ? '' : t('network.toastEnterUrl') }}
76
- </view>
77
-
78
- <view class="network-send-control">
79
- <DevToolTitle>{{ t('network.requestParams') }}</DevToolTitle>
80
- <CircularButton
81
- style="margin-left: auto"
82
- text="+"
83
- @click="onAddQuery" />
84
- </view>
85
- <view class="network-param-list">
86
- <checkbox-group
87
- v-if="queryList.length > 0"
88
- @change="handleQueryCheckedChange">
89
- <view
90
- class="network-param-item"
91
- v-for="(item, index) in queryList"
92
- :key="item.id">
93
- <checkbox
94
- :color="themeColor"
95
- :checked="item.checked"
96
- :value="item.id" />
97
- <input
98
- class="network-input"
99
- :placeholder="t('network.placeholderParamName')"
100
- :value="item.key"
101
- @input="onChangeQuery($event, 'key', index)" />
102
- <input
103
- class="network-input"
104
- :placeholder="t('network.placeholderParamValue')"
105
- :value="item.value"
106
- @input="onChangeQuery($event, 'value', index)" />
107
- <CircularButton text="一" @click="onDeleteQuery(item)" />
108
- </view>
109
- </checkbox-group>
110
-
111
- <view class="network-empty" v-else>
112
- {{ t('network.emptyParams') }}
113
- </view>
114
- </view>
115
-
116
- <template v-if="!noBodyRequestMethods.includes(state.method)">
117
- <view class="network-send-control">
118
- <DevToolTitle>{{ t('network.requestBody') }}</DevToolTitle>
119
- </view>
120
-
121
- <textarea
122
- :placeholder="t('network.placeholderBody')"
123
- class="network-textarea"
124
- :value="state.bodyText"
125
- @input="onChangeState($event, 'bodyText')" />
126
- </template>
127
- </scroll-view>
128
- <view class="network-send-buttons">
129
- <button
130
- size="mini"
131
- :disabled="!isFinished"
132
- class="network-send-button"
133
- @click="emit('openDetail', currentNetwork)">
134
- {{ t('network.detail') }}
135
- </button>
136
- <button size="mini" class="network-send-button" @click="onReset">
137
- {{ t('network.reset') }}
138
- </button>
139
- <button size="mini" class="network-send-button-last" @click="onSend">
140
- {{ t('network.send') }}
141
- </button>
142
- </view>
143
- </view>
144
- </template>
145
- <script lang="ts" setup>
146
- import { computed, ref, reactive, onMounted } from 'vue';
147
- import CircularButton from '../../../components/CircularButton/index.vue';
148
- import DevToolTitle from '../DevToolTitle/index.vue';
149
- import type { DevTool } from '../../../../type';
150
- import { useI18n } from '../../../../i18n';
151
- import { getThemeMainColor } from '../../../styles/theme';
152
- const { t } = useI18n();
153
- const themeColor = computed(() => getThemeMainColor(props.theme));
154
-
155
- const props = defineProps<{
156
- theme?: string;
157
- network: DevTool.NetworkItem;
158
- zIndex?: number;
159
- interceptNetworkList?: DevTool.NetworkItem[];
160
- customStyle?: Record<string, any>;
161
- }>();
162
-
163
- const emit = defineEmits<{
164
- (e: 'close'): void;
165
- (e: 'openDetail', v: DevTool.NetworkItem): void;
166
- }>();
167
-
168
- import {
169
- requestMethods,
170
- noBodyRequestMethods,
171
- defaultHeader,
172
- arrayToObject,
173
- objectToArray,
174
- arrayToUrlParams,
175
- urlParamsToObject,
176
- unionArrayByKey,
177
- } from './utils';
178
- import { useKeyValueList, useNetworkState } from './hooks/useNetworkForm';
179
-
180
- const { state, hasUrl, onChangeState, parseUrlToList, parseListToUrl } =
181
- useNetworkState({
182
- method: 'GET',
183
- url: 'http://192.168.0.1:8080/a?a=123',
184
- bodyText: '',
185
- timeout: 3 * 1000,
186
- });
187
-
188
- const {
189
- list: headerList,
190
- onAdd: onAddHeader,
191
- onDelete: onDeleteHeader,
192
- onChangeChecked: onChangeHeaderChecked,
193
- onChangeValue: onChangeHeaderItem,
194
- setList: setHeaderList,
195
- } = useKeyValueList(defaultHeader);
196
-
197
- const {
198
- list: queryList,
199
- onAdd: onAddQuery,
200
- onDelete: onDeleteQuery,
201
- onChangeChecked: onChangeQueryChecked,
202
- onChangeValue: onChangeQueryItem,
203
- setList: setQueryList,
204
- } = useKeyValueList([
205
- {
206
- id: '1',
207
- key: 'a',
208
- value: '123',
209
- checked: true,
210
- },
211
- ]);
212
-
213
- const isFinished = ref(false);
214
-
215
- const currentNetwork = reactive<DevTool.NetworkItem>({
216
- index: 0,
217
- url: '',
218
- name: '',
219
- method: '',
220
- status: '',
221
- time: '',
222
- startTime: 0,
223
- endTime: 0,
224
- size: '',
225
- headers: {
226
- requestHeader: [],
227
- responseHeader: [],
228
- },
229
- response: {
230
- data: undefined,
231
- statusCode: 0,
232
- header: {},
233
- cookies: [],
234
- errMsg: '',
235
- },
236
- payload: undefined,
237
- intercepted: false,
238
- sponsor: 'dev_tool',
239
- });
240
-
241
- function onChangeHeader(e: any, key: 'key' | 'value', index: number) {
242
- onChangeHeaderItem(index, key, e.detail.value);
243
- }
244
-
245
- function onChangeUrl(e: any) {
246
- const url = e.detail.value;
247
- state.url = url;
248
- const list = parseUrlToList(url);
249
- setQueryList(list);
250
- }
251
-
252
- function onReset() {
253
- isFinished.value = false;
254
- hasUrl.value = true;
255
- state.method = 'GET';
256
- state.url = '';
257
- state.bodyText = '';
258
- setHeaderList(defaultHeader);
259
- setQueryList([]);
260
- }
261
-
262
- function onClose() {
263
- emit('close');
264
- onReset();
265
- }
266
-
267
- function onSend() {
268
- if (!state.url) {
269
- hasUrl.value = false;
270
- uni.showToast({
271
- title: t('network.toastEnterUrl'),
272
- icon: 'none',
273
- });
274
- return;
275
- }
276
- hasUrl.value = true;
277
-
278
- const header = arrayToObject(headerList);
279
- const query = arrayToUrlParams(queryList.filter((q) => q.checked));
280
-
281
- const requestOptions: UniNamespace.RequestOptions = {
282
- url: state.url,
283
- method: state.method,
284
- timeout: state.timeout,
285
- header: {
286
- ...header,
287
- /** 标记请求发起人 devTool */
288
- 'dev-tool-sponsor': Date.now(),
289
- },
290
- };
291
-
292
- if (noBodyRequestMethods.includes(state.method)) {
293
- // 不支持携带 body 参数
294
- requestOptions.data = {};
295
- state.url = parseListToUrl(state.url, queryList);
296
- } else {
297
- // 支持 url 中携带参数
298
-
299
- if (state.url.includes('?')) {
300
- requestOptions.url = state.url + '&' + query;
301
- } else {
302
- requestOptions.url = state.url + '?' + query;
303
- }
304
-
305
- // 支持携带 body 参数
306
- if (state.bodyText?.trim()) {
307
- try {
308
- const bodyJson = JSON.parse(state.bodyText);
309
- requestOptions.data = bodyJson;
310
- } catch (_error) {
311
- uni.showToast({
312
- title: t('network.toastBodyJson'),
313
- icon: 'none',
314
- });
315
- return;
316
- }
317
- }
318
- }
319
-
320
- isFinished.value = false;
321
-
322
- uni.showLoading({
323
- title: t('network.sending'),
324
- mask: true,
325
- });
326
-
327
- const baseUrl = requestOptions.url?.split('?')[0] || '';
328
- const lastSlashIndex = baseUrl.lastIndexOf('/');
329
- const name = lastSlashIndex !== -1 ? baseUrl.slice(lastSlashIndex + 1) : '';
330
-
331
- currentNetwork.startTime = Date.now();
332
-
333
- uni.request({
334
- ...requestOptions,
335
- complete: (res: any) => {
336
- // 赋值结果
337
- currentNetwork.url = requestOptions.url;
338
- currentNetwork.name = name;
339
- currentNetwork.method = requestOptions.method as string;
340
- currentNetwork.payload = requestOptions.data
341
- ? JSON.stringify(requestOptions.data)
342
- : '';
343
- currentNetwork.endTime = Date.now();
344
- currentNetwork.status = res.statusCode ?? 'error';
345
- const diff = currentNetwork.endTime - currentNetwork.startTime;
346
- currentNetwork.time = diff < 1000 ? diff + 'ms' : diff / 1000 + 's';
347
- const len =
348
- res?.header?.['Content-Length'] || res?.header?.['content-length'] || 0;
349
- currentNetwork.size =
350
- len > 1024 ? (len / 1024).toFixed(2) + 'k' : len + 'b';
351
- currentNetwork.response = res;
352
- currentNetwork.headers.requestHeader = objectToArray(
353
- requestOptions?.header ?? {},
354
- );
355
- currentNetwork.headers.responseHeader = objectToArray(res.header ?? {});
356
-
357
- currentNetwork.intercepted =
358
- props.interceptNetworkList?.some((item) => {
359
- const key = `${item.method?.toUpperCase()}|${item.url?.split('?')[0]}`;
360
- const curKey = `${currentNetwork.method?.toUpperCase()}|${currentNetwork.url?.split('?')[0]}`;
361
- return key === curKey;
362
- }) ?? false;
363
-
364
- // 完成
365
- uni.hideLoading();
366
- isFinished.value = true;
367
- uni.showToast({
368
- title: t('network.toastRequestSuccess'),
369
- icon: 'none',
370
- });
371
- },
372
- });
373
- }
374
-
375
- function onChangeRequestMethod(e: any) {
376
- const method = e.detail.value;
377
- state.method = method;
378
- }
379
-
380
- function onChangeQuery(e: any, key: 'key' | 'value', index: number) {
381
- onChangeQueryItem(index, key, e.detail.value);
382
- state.url = parseListToUrl(state.url, queryList);
383
- }
384
-
385
- function handleQueryCheckedChange(e: any) {
386
- onChangeQueryChecked(e);
387
- state.url = parseListToUrl(state.url, queryList);
388
- }
389
-
390
- /**
391
- * 处理重发参数
392
- */
393
- function handleResendNetwork() {
394
- // 如果没有网络数据或尚未开始,则直接返回
395
- if (!props.network || props.network.startTime === 0) return;
396
-
397
- // 设置基础请求信息
398
- state.url = props.network.url;
399
- state.method = props.network.method?.toUpperCase() ?? 'GET';
400
-
401
- // 初始化请求头
402
- const requestHeaders =
403
- props.network.headers?.requestHeader?.map((item, index) => {
404
- return {
405
- ...item,
406
- checked: true,
407
- id: index.toString(),
408
- };
409
- }) ?? [];
410
-
411
- setHeaderList(requestHeaders);
412
-
413
- setQueryList([]);
414
-
415
- // 解析参数
416
- if (noBodyRequestMethods.includes(state.method)) {
417
- // 参数在 query 中
418
- if (props.network.url.includes('?')) {
419
- const urlParams = objectToArray(
420
- urlParamsToObject(props.network.url.split('?')?.[1] ?? ''),
421
- );
422
- const payloadParams = objectToArray(props.network.payload);
423
-
424
- const list = unionArrayByKey(urlParams, payloadParams, 'key');
425
-
426
- setQueryList(list);
427
- }
428
- } else {
429
- // 参数在 query 中
430
- if (props.network.url.includes('?')) {
431
- const urlParams = objectToArray(
432
- urlParamsToObject(props.network.url.split('?')?.[1] ?? ''),
433
- );
434
- setQueryList(urlParams);
435
- }
436
- state.bodyText = JSON.stringify(props.network.payload);
437
- }
438
- }
439
-
440
- onMounted(() => {
441
- handleResendNetwork();
442
- });
443
- </script>
444
-
445
- <style scoped>
446
- .network-send {
447
- position: fixed;
448
- width: 100vw;
449
- height: 100%;
450
- z-index: 5;
451
- top: 0;
452
- left: 0;
453
- padding: 0 16px;
454
-
455
- /* #ifdef H5 || APP-PLUS */
456
- padding: 50px 16px;
457
-
458
- /* #endif */
459
- overflow: auto;
460
- background-color: var(--dev-tool-bg3-color);
461
- box-sizing: border-box;
462
- }
463
-
464
- .network-send-form {
465
- height: calc(100% - 32px - 56px);
466
- overflow: auto;
467
- }
468
-
469
- .network-send-control {
470
- display: flex;
471
- align-items: center;
472
- gap: 12px;
473
- height: 32px;
474
- border-bottom: 1px solid transparent;
475
- box-sizing: border-box;
476
- }
477
-
478
- .network-send-control:first-child {
479
- margin-top: 0;
480
- }
481
-
482
- .radio-group {
483
- display: flex;
484
- flex-wrap: wrap;
485
- gap: 8px 0; /* Add gap between rows if wrapped */
486
- }
487
-
488
- .radio-group-item {
489
- display: flex;
490
- align-items: center;
491
- width: 25%;
492
- font-size: 14px;
493
- }
494
-
495
- .network-input {
496
- width: 100%;
497
- box-sizing: border-box;
498
- padding: 0 8px;
499
- height: 24px;
500
- font-size: 14px;
501
- border-radius: 4px;
502
- border: 1px solid var(--dev-tool-border-color);
503
- background-color: transparent;
504
- transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
505
- color: var(--dev-tool-text-color, inherit);
506
- }
507
-
508
- .network-input:focus {
509
- border-color: var(--dev-tool-primary-color, #9254de);
510
- box-shadow: 0 0 0 2px rgb(146 84 222 / 10%);
511
- }
512
-
513
- .network-textarea {
514
- padding: 8px;
515
- width: 100%;
516
- box-sizing: border-box;
517
- font-size: 14px;
518
- border-radius: 4px;
519
- border: 1px solid var(--dev-tool-border-color);
520
- background-color: transparent;
521
- transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
522
- color: var(--dev-tool-text-color, inherit);
523
- min-height: 80px;
524
- }
525
-
526
- .network-textarea:focus {
527
- border-color: var(--dev-tool-primary-color, #9254de);
528
- box-shadow: 0 0 0 2px rgb(146 84 222 / 10%);
529
- }
530
-
531
- .form-error-message {
532
- padding: 4px 6px;
533
- height: 24px;
534
- line-height: 24px;
535
- color: #ff4d4f; /* softer red */
536
- font-size: 12px;
537
- }
538
-
539
- .network-header-list,
540
- .network-param-list {
541
- display: flex;
542
- flex-direction: column;
543
- gap: 8px;
544
- margin-bottom: 8px;
545
- }
546
-
547
- .network-header-item,
548
- .network-param-item {
549
- display: flex;
550
- align-items: center;
551
- gap: 8px;
552
- }
553
-
554
- .network-header-item .network-input,
555
- .network-param-item .network-input {
556
- flex: 1;
557
- min-width: 0; /* Prevents flex item from overflowing */
558
- }
559
-
560
- .network-empty {
561
- color: var(--dev-tool-info-color);
562
- text-align: center;
563
- padding: 12px 0;
564
- font-size: 13px;
565
- }
566
-
567
- .network-body-data-item {
568
- display: flex;
569
- align-items: center;
570
- }
571
-
572
- .network-send-buttons {
573
- position: sticky;
574
- display: flex;
575
- justify-content: space-between;
576
- gap: 12px;
577
- width: 100%;
578
- margin-top: 16px;
579
- padding-bottom: 16px;
580
- background-color: var(--dev-tool-bg3-color);
581
- }
582
-
583
- .network-send-button,
584
- .network-send-button-last {
585
- flex: 1;
586
- margin: 0 !important; /* override default margin */
587
- border-radius: 6px;
588
- }
589
- </style>
1
+ <template>
2
+ <view class="network-send" :style="customStyle">
3
+ <view class="network-send-control">
4
+ <DevToolTitle>{{ t('network.sendRequest') }}</DevToolTitle>
5
+ <CircularButton style="margin-left: auto" text="×" @click="onClose" />
6
+ </view>
7
+ <scroll-view scroll-y class="network-send-form">
8
+ <view class="network-send-control">
9
+ <DevToolTitle>{{ t('network.timeout') }}</DevToolTitle>
10
+ </view>
11
+ <input
12
+ class="network-input"
13
+ :value="state.timeout"
14
+ @input="onChangeState($event, 'timeout')" />
15
+ <view class="network-send-control">
16
+ <DevToolTitle>{{ t('network.requestMethod') }}</DevToolTitle>
17
+ </view>
18
+ <radio-group class="radio-group" @change="onChangeRequestMethod">
19
+ <label
20
+ class="radio-group-item"
21
+ v-for="item in requestMethods"
22
+ :key="item.value">
23
+ <radio
24
+ size
25
+ style="transform: scale(0.8)"
26
+ :color="themeColor"
27
+ :value="item.value"
28
+ :checked="item.value === state.method" />
29
+
30
+ <view>{{ item.name }}</view>
31
+ </label>
32
+ </radio-group>
33
+ <view class="network-send-control">
34
+ <DevToolTitle>{{ t('network.requestHeaders') }}</DevToolTitle>
35
+ <CircularButton
36
+ style="margin-left: auto"
37
+ text="+"
38
+ @click="onAddHeader" />
39
+ </view>
40
+
41
+ <view class="network-header-list">
42
+ <checkbox-group
43
+ v-if="headerList.length > 0"
44
+ @change="onChangeHeaderChecked">
45
+ <view
46
+ class="network-header-item"
47
+ v-for="(item, index) in headerList"
48
+ :key="item.id">
49
+ <checkbox color="#9254de" :checked="item.checked" />
50
+ <input
51
+ class="network-input"
52
+ :placeholder="t('network.placeholderHeaderName')"
53
+ :value="item.key"
54
+ @input="onChangeHeader($event, 'key', index)" />
55
+ <input
56
+ class="network-input"
57
+ :placeholder="t('network.placeholderHeaderValue')"
58
+ :value="item.value"
59
+ @input="onChangeHeader($event, 'value', index)" />
60
+ <CircularButton text="一" @click="onDeleteHeader(item)" />
61
+ </view>
62
+ </checkbox-group>
63
+
64
+ <view class="network-empty" v-else>
65
+ {{ t('network.noRequestHeaders') }}
66
+ </view>
67
+ </view>
68
+ <DevToolTitle>{{ t('network.requestUrl') }}</DevToolTitle>
69
+ <textarea
70
+ :placeholder="t('network.placeholderUrl')"
71
+ class="network-textarea"
72
+ :value="state.url"
73
+ @input="onChangeUrl" />
74
+ <view class="form-error-message">
75
+ {{ hasUrl ? '' : t('network.toastEnterUrl') }}
76
+ </view>
77
+
78
+ <view class="network-send-control">
79
+ <DevToolTitle>{{ t('network.requestParams') }}</DevToolTitle>
80
+ <CircularButton
81
+ style="margin-left: auto"
82
+ text="+"
83
+ @click="onAddQuery" />
84
+ </view>
85
+ <view class="network-param-list">
86
+ <checkbox-group
87
+ v-if="queryList.length > 0"
88
+ @change="handleQueryCheckedChange">
89
+ <view
90
+ class="network-param-item"
91
+ v-for="(item, index) in queryList"
92
+ :key="item.id">
93
+ <checkbox
94
+ :color="themeColor"
95
+ :checked="item.checked"
96
+ :value="item.id" />
97
+ <input
98
+ class="network-input"
99
+ :placeholder="t('network.placeholderParamName')"
100
+ :value="item.key"
101
+ @input="onChangeQuery($event, 'key', index)" />
102
+ <input
103
+ class="network-input"
104
+ :placeholder="t('network.placeholderParamValue')"
105
+ :value="item.value"
106
+ @input="onChangeQuery($event, 'value', index)" />
107
+ <CircularButton text="一" @click="onDeleteQuery(item)" />
108
+ </view>
109
+ </checkbox-group>
110
+
111
+ <view class="network-empty" v-else>
112
+ {{ t('network.emptyParams') }}
113
+ </view>
114
+ </view>
115
+
116
+ <template v-if="!noBodyRequestMethods.includes(state.method)">
117
+ <view class="network-send-control">
118
+ <DevToolTitle>{{ t('network.requestBody') }}</DevToolTitle>
119
+ </view>
120
+
121
+ <textarea
122
+ :placeholder="t('network.placeholderBody')"
123
+ class="network-textarea"
124
+ :value="state.bodyText"
125
+ @input="onChangeState($event, 'bodyText')" />
126
+ </template>
127
+ </scroll-view>
128
+ <view class="network-send-buttons">
129
+ <button
130
+ size="mini"
131
+ :disabled="!isFinished"
132
+ class="network-send-button"
133
+ @click="emit('openDetail', currentNetwork)">
134
+ {{ t('network.detail') }}
135
+ </button>
136
+ <button size="mini" class="network-send-button" @click="onReset">
137
+ {{ t('network.reset') }}
138
+ </button>
139
+ <button size="mini" class="network-send-button-last" @click="onSend">
140
+ {{ t('network.send') }}
141
+ </button>
142
+ </view>
143
+ </view>
144
+ </template>
145
+ <script lang="ts" setup>
146
+ import { computed, ref, reactive, onMounted } from 'vue';
147
+ import CircularButton from '../../../components/CircularButton/index.vue';
148
+ import DevToolTitle from '../DevToolTitle/index.vue';
149
+ import type { DevTool } from '../../../../type';
150
+ import { useI18n } from '../../../../i18n';
151
+ import { getThemeMainColor } from '../../../styles/theme';
152
+ const { t } = useI18n();
153
+ const themeColor = computed(() => getThemeMainColor(props.theme));
154
+
155
+ const props = defineProps<{
156
+ theme?: string;
157
+ network: DevTool.NetworkItem;
158
+ zIndex?: number;
159
+ interceptNetworkList?: DevTool.NetworkItem[];
160
+ customStyle?: Record<string, any>;
161
+ }>();
162
+
163
+ const emit = defineEmits<{
164
+ (e: 'close'): void;
165
+ (e: 'openDetail', v: DevTool.NetworkItem): void;
166
+ }>();
167
+
168
+ import {
169
+ requestMethods,
170
+ noBodyRequestMethods,
171
+ defaultHeader,
172
+ arrayToObject,
173
+ objectToArray,
174
+ arrayToUrlParams,
175
+ urlParamsToObject,
176
+ unionArrayByKey,
177
+ } from './utils';
178
+ import { useKeyValueList, useNetworkState } from './hooks/useNetworkForm';
179
+
180
+ const { state, hasUrl, onChangeState, parseUrlToList, parseListToUrl } =
181
+ useNetworkState({
182
+ method: 'GET',
183
+ url: 'http://192.168.0.1:8080/a?a=123',
184
+ bodyText: '',
185
+ timeout: 3 * 1000,
186
+ });
187
+
188
+ const {
189
+ list: headerList,
190
+ onAdd: onAddHeader,
191
+ onDelete: onDeleteHeader,
192
+ onChangeChecked: onChangeHeaderChecked,
193
+ onChangeValue: onChangeHeaderItem,
194
+ setList: setHeaderList,
195
+ } = useKeyValueList(defaultHeader);
196
+
197
+ const {
198
+ list: queryList,
199
+ onAdd: onAddQuery,
200
+ onDelete: onDeleteQuery,
201
+ onChangeChecked: onChangeQueryChecked,
202
+ onChangeValue: onChangeQueryItem,
203
+ setList: setQueryList,
204
+ } = useKeyValueList([
205
+ {
206
+ id: '1',
207
+ key: 'a',
208
+ value: '123',
209
+ checked: true,
210
+ },
211
+ ]);
212
+
213
+ const isFinished = ref(false);
214
+
215
+ const currentNetwork = reactive<DevTool.NetworkItem>({
216
+ index: 0,
217
+ url: '',
218
+ name: '',
219
+ method: '',
220
+ status: '',
221
+ time: '',
222
+ startTime: 0,
223
+ endTime: 0,
224
+ size: '',
225
+ headers: {
226
+ requestHeader: [],
227
+ responseHeader: [],
228
+ },
229
+ response: {
230
+ data: undefined,
231
+ statusCode: 0,
232
+ header: {},
233
+ cookies: [],
234
+ errMsg: '',
235
+ },
236
+ payload: undefined,
237
+ intercepted: false,
238
+ sponsor: 'dev_tool',
239
+ });
240
+
241
+ function onChangeHeader(e: any, key: 'key' | 'value', index: number) {
242
+ onChangeHeaderItem(index, key, e.detail.value);
243
+ }
244
+
245
+ function onChangeUrl(e: any) {
246
+ const url = e.detail.value;
247
+ state.url = url;
248
+ const list = parseUrlToList(url);
249
+ setQueryList(list);
250
+ }
251
+
252
+ function onReset() {
253
+ isFinished.value = false;
254
+ hasUrl.value = true;
255
+ state.method = 'GET';
256
+ state.url = '';
257
+ state.bodyText = '';
258
+ setHeaderList(defaultHeader);
259
+ setQueryList([]);
260
+ }
261
+
262
+ function onClose() {
263
+ emit('close');
264
+ onReset();
265
+ }
266
+
267
+ function onSend() {
268
+ if (!state.url) {
269
+ hasUrl.value = false;
270
+ uni.showToast({
271
+ title: t('network.toastEnterUrl'),
272
+ icon: 'none',
273
+ });
274
+ return;
275
+ }
276
+ hasUrl.value = true;
277
+
278
+ const header = arrayToObject(headerList);
279
+ const query = arrayToUrlParams(queryList.filter((q) => q.checked));
280
+
281
+ const requestOptions: UniNamespace.RequestOptions = {
282
+ url: state.url,
283
+ method: state.method,
284
+ timeout: state.timeout,
285
+ header: {
286
+ ...header,
287
+ /** 标记请求发起人 devTool */
288
+ 'dev-tool-sponsor': Date.now(),
289
+ },
290
+ };
291
+
292
+ if (noBodyRequestMethods.includes(state.method)) {
293
+ // 不支持携带 body 参数
294
+ requestOptions.data = {};
295
+ state.url = parseListToUrl(state.url, queryList);
296
+ } else {
297
+ // 支持 url 中携带参数
298
+
299
+ if (state.url.includes('?')) {
300
+ requestOptions.url = state.url + '&' + query;
301
+ } else {
302
+ requestOptions.url = state.url + '?' + query;
303
+ }
304
+
305
+ // 支持携带 body 参数
306
+ if (state.bodyText?.trim()) {
307
+ try {
308
+ const bodyJson = JSON.parse(state.bodyText);
309
+ requestOptions.data = bodyJson;
310
+ } catch (_error) {
311
+ uni.showToast({
312
+ title: t('network.toastBodyJson'),
313
+ icon: 'none',
314
+ });
315
+ return;
316
+ }
317
+ }
318
+ }
319
+
320
+ isFinished.value = false;
321
+
322
+ uni.showLoading({
323
+ title: t('network.sending'),
324
+ mask: true,
325
+ });
326
+
327
+ const baseUrl = requestOptions.url?.split('?')[0] || '';
328
+ const lastSlashIndex = baseUrl.lastIndexOf('/');
329
+ const name = lastSlashIndex !== -1 ? baseUrl.slice(lastSlashIndex + 1) : '';
330
+
331
+ currentNetwork.startTime = Date.now();
332
+
333
+ uni.request({
334
+ ...requestOptions,
335
+ complete: (res: any) => {
336
+ // 赋值结果
337
+ currentNetwork.url = requestOptions.url;
338
+ currentNetwork.name = name;
339
+ currentNetwork.method = requestOptions.method as string;
340
+ currentNetwork.payload = requestOptions.data
341
+ ? JSON.stringify(requestOptions.data)
342
+ : '';
343
+ currentNetwork.endTime = Date.now();
344
+ currentNetwork.status = res.statusCode ?? 'error';
345
+ const diff = currentNetwork.endTime - currentNetwork.startTime;
346
+ currentNetwork.time = diff < 1000 ? diff + 'ms' : diff / 1000 + 's';
347
+ const len =
348
+ res?.header?.['Content-Length'] || res?.header?.['content-length'] || 0;
349
+ currentNetwork.size =
350
+ len > 1024 ? (len / 1024).toFixed(2) + 'k' : len + 'b';
351
+ currentNetwork.response = res;
352
+ currentNetwork.headers.requestHeader = objectToArray(
353
+ requestOptions?.header ?? {},
354
+ );
355
+ currentNetwork.headers.responseHeader = objectToArray(res.header ?? {});
356
+
357
+ currentNetwork.intercepted =
358
+ props.interceptNetworkList?.some((item) => {
359
+ const key = `${item.method?.toUpperCase()}|${item.url?.split('?')[0]}`;
360
+ const curKey = `${currentNetwork.method?.toUpperCase()}|${currentNetwork.url?.split('?')[0]}`;
361
+ return key === curKey;
362
+ }) ?? false;
363
+
364
+ // 完成
365
+ uni.hideLoading();
366
+ isFinished.value = true;
367
+ uni.showToast({
368
+ title: t('network.toastRequestSuccess'),
369
+ icon: 'none',
370
+ });
371
+ },
372
+ });
373
+ }
374
+
375
+ function onChangeRequestMethod(e: any) {
376
+ const method = e.detail.value;
377
+ state.method = method;
378
+ }
379
+
380
+ function onChangeQuery(e: any, key: 'key' | 'value', index: number) {
381
+ onChangeQueryItem(index, key, e.detail.value);
382
+ state.url = parseListToUrl(state.url, queryList);
383
+ }
384
+
385
+ function handleQueryCheckedChange(e: any) {
386
+ onChangeQueryChecked(e);
387
+ state.url = parseListToUrl(state.url, queryList);
388
+ }
389
+
390
+ /**
391
+ * 处理重发参数
392
+ */
393
+ function handleResendNetwork() {
394
+ // 如果没有网络数据或尚未开始,则直接返回
395
+ if (!props.network || props.network.startTime === 0) return;
396
+
397
+ // 设置基础请求信息
398
+ state.url = props.network.url;
399
+ state.method = props.network.method?.toUpperCase() ?? 'GET';
400
+
401
+ // 初始化请求头
402
+ const requestHeaders =
403
+ props.network.headers?.requestHeader?.map((item, index) => {
404
+ return {
405
+ ...item,
406
+ checked: true,
407
+ id: index.toString(),
408
+ };
409
+ }) ?? [];
410
+
411
+ setHeaderList(requestHeaders);
412
+
413
+ setQueryList([]);
414
+
415
+ // 解析参数
416
+ if (noBodyRequestMethods.includes(state.method)) {
417
+ // 参数在 query 中
418
+ if (props.network.url.includes('?')) {
419
+ const urlParams = objectToArray(
420
+ urlParamsToObject(props.network.url.split('?')?.[1] ?? ''),
421
+ );
422
+ const payloadParams = objectToArray(props.network.payload);
423
+
424
+ const list = unionArrayByKey(urlParams, payloadParams, 'key');
425
+
426
+ setQueryList(list);
427
+ }
428
+ } else {
429
+ // 参数在 query 中
430
+ if (props.network.url.includes('?')) {
431
+ const urlParams = objectToArray(
432
+ urlParamsToObject(props.network.url.split('?')?.[1] ?? ''),
433
+ );
434
+ setQueryList(urlParams);
435
+ }
436
+ state.bodyText = JSON.stringify(props.network.payload);
437
+ }
438
+ }
439
+
440
+ onMounted(() => {
441
+ handleResendNetwork();
442
+ });
443
+ </script>
444
+
445
+ <style scoped>
446
+ .network-send {
447
+ position: fixed;
448
+ width: 100vw;
449
+ height: 100%;
450
+ z-index: 5;
451
+ top: 0;
452
+ left: 0;
453
+ padding: 0 16px;
454
+
455
+ /* #ifdef H5 || APP-PLUS */
456
+ padding: 50px 16px;
457
+
458
+ /* #endif */
459
+ overflow: auto;
460
+ background-color: var(--dev-tool-bg3-color);
461
+ box-sizing: border-box;
462
+ }
463
+
464
+ .network-send-form {
465
+ height: calc(100% - 32px - 56px);
466
+ overflow: auto;
467
+ }
468
+
469
+ .network-send-control {
470
+ display: flex;
471
+ align-items: center;
472
+ gap: 12px;
473
+ height: 32px;
474
+ border-bottom: 1px solid transparent;
475
+ box-sizing: border-box;
476
+ }
477
+
478
+ .network-send-control:first-child {
479
+ margin-top: 0;
480
+ }
481
+
482
+ .radio-group {
483
+ display: flex;
484
+ flex-wrap: wrap;
485
+ gap: 8px 0; /* Add gap between rows if wrapped */
486
+ }
487
+
488
+ .radio-group-item {
489
+ display: flex;
490
+ align-items: center;
491
+ width: 25%;
492
+ font-size: 14px;
493
+ }
494
+
495
+ .network-input {
496
+ width: 100%;
497
+ box-sizing: border-box;
498
+ padding: 0 8px;
499
+ height: 24px;
500
+ font-size: 14px;
501
+ border-radius: 4px;
502
+ border: 1px solid var(--dev-tool-border-color);
503
+ background-color: transparent;
504
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
505
+ color: var(--dev-tool-text-color, inherit);
506
+ }
507
+
508
+ .network-input:focus {
509
+ border-color: var(--dev-tool-primary-color, #9254de);
510
+ box-shadow: 0 0 0 2px rgb(146 84 222 / 10%);
511
+ }
512
+
513
+ .network-textarea {
514
+ padding: 8px;
515
+ width: 100%;
516
+ box-sizing: border-box;
517
+ font-size: 14px;
518
+ border-radius: 4px;
519
+ border: 1px solid var(--dev-tool-border-color);
520
+ background-color: transparent;
521
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
522
+ color: var(--dev-tool-text-color, inherit);
523
+ min-height: 80px;
524
+ }
525
+
526
+ .network-textarea:focus {
527
+ border-color: var(--dev-tool-primary-color, #9254de);
528
+ box-shadow: 0 0 0 2px rgb(146 84 222 / 10%);
529
+ }
530
+
531
+ .form-error-message {
532
+ padding: 4px 6px;
533
+ height: 24px;
534
+ line-height: 24px;
535
+ color: #ff4d4f; /* softer red */
536
+ font-size: 12px;
537
+ }
538
+
539
+ .network-header-list,
540
+ .network-param-list {
541
+ display: flex;
542
+ flex-direction: column;
543
+ gap: 8px;
544
+ margin-bottom: 8px;
545
+ }
546
+
547
+ .network-header-item,
548
+ .network-param-item {
549
+ display: flex;
550
+ align-items: center;
551
+ gap: 8px;
552
+ }
553
+
554
+ .network-header-item .network-input,
555
+ .network-param-item .network-input {
556
+ flex: 1;
557
+ min-width: 0; /* Prevents flex item from overflowing */
558
+ }
559
+
560
+ .network-empty {
561
+ color: var(--dev-tool-info-color);
562
+ text-align: center;
563
+ padding: 12px 0;
564
+ font-size: 13px;
565
+ }
566
+
567
+ .network-body-data-item {
568
+ display: flex;
569
+ align-items: center;
570
+ }
571
+
572
+ .network-send-buttons {
573
+ position: sticky;
574
+ display: flex;
575
+ justify-content: space-between;
576
+ gap: 12px;
577
+ width: 100%;
578
+ margin-top: 16px;
579
+ padding-bottom: 16px;
580
+ background-color: var(--dev-tool-bg3-color);
581
+ }
582
+
583
+ .network-send-button,
584
+ .network-send-button-last {
585
+ flex: 1;
586
+ margin: 0 !important; /* override default margin */
587
+ border-radius: 6px;
588
+ }
589
+ </style>