vite-uni-dev-tool 1.0.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 (170) hide show
  1. package/README.md +54 -0
  2. package/dist/const.cjs +1 -1
  3. package/dist/const.d.ts +13 -0
  4. package/dist/const.d.ts.map +1 -1
  5. package/dist/const.js +1 -1
  6. package/dist/core-shared.d.ts +1 -1
  7. package/dist/core-shared.d.ts.map +1 -1
  8. package/dist/core-shared.js +1 -1
  9. package/dist/core.d.ts +10 -3
  10. package/dist/core.d.ts.map +1 -1
  11. package/dist/core.js +2 -2
  12. package/dist/i18n/locales/en.cjs +1 -1
  13. package/dist/i18n/locales/en.d.ts +86 -0
  14. package/dist/i18n/locales/en.d.ts.map +1 -1
  15. package/dist/i18n/locales/en.js +1 -1
  16. package/dist/i18n/locales/zh-Hans.cjs +1 -1
  17. package/dist/i18n/locales/zh-Hans.d.ts +87 -1
  18. package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
  19. package/dist/i18n/locales/zh-Hans.js +1 -1
  20. package/dist/modules/devConsole/index.cjs +1 -1
  21. package/dist/modules/devConsole/index.js +3 -3
  22. package/dist/modules/devEvent/index.cjs +3 -3
  23. package/dist/modules/devEvent/index.d.ts +1 -0
  24. package/dist/modules/devEvent/index.d.ts.map +1 -1
  25. package/dist/modules/devEvent/index.js +3 -3
  26. package/dist/modules/devIntercept/index.cjs +14 -13
  27. package/dist/modules/devIntercept/index.d.ts +20 -1
  28. package/dist/modules/devIntercept/index.d.ts.map +1 -1
  29. package/dist/modules/devIntercept/index.js +14 -13
  30. package/dist/modules/devStore/index.cjs +1 -1
  31. package/dist/modules/devStore/index.d.ts +21 -0
  32. package/dist/modules/devStore/index.d.ts.map +1 -1
  33. package/dist/modules/devStore/index.js +1 -1
  34. package/dist/plugins/uniDevTool/transform/transformMain.cjs +3 -3
  35. package/dist/plugins/uniDevTool/transform/transformMain.d.ts +2 -1
  36. package/dist/plugins/uniDevTool/transform/transformMain.d.ts.map +1 -1
  37. package/dist/plugins/uniDevTool/transform/transformMain.js +3 -3
  38. package/dist/plugins/uniDevTool/transform/transformVue.cjs +31 -25
  39. package/dist/plugins/uniDevTool/transform/transformVue.d.ts +2 -1
  40. package/dist/plugins/uniDevTool/transform/transformVue.d.ts.map +1 -1
  41. package/dist/plugins/uniDevTool/transform/transformVue.js +30 -24
  42. package/dist/plugins/uniDevTool/uniDevTool.cjs +3 -3
  43. package/dist/plugins/uniDevTool/uniDevTool.d.ts +3 -1
  44. package/dist/plugins/uniDevTool/uniDevTool.d.ts.map +1 -1
  45. package/dist/plugins/uniDevTool/uniDevTool.js +3 -3
  46. package/dist/type.d.ts +50 -2
  47. package/dist/type.d.ts.map +1 -1
  48. package/dist/utils/language.cjs +1 -1
  49. package/dist/utils/language.d.ts.map +1 -1
  50. package/dist/utils/language.js +1 -1
  51. package/dist/utils/object.cjs +1 -1
  52. package/dist/utils/object.d.ts.map +1 -1
  53. package/dist/utils/object.js +1 -1
  54. package/dist/v3/DevTool/components/BluetoothList/BluetoothItem.vue +199 -0
  55. package/dist/v3/DevTool/components/BluetoothList/BluetoothTool.vue +730 -0
  56. package/dist/v3/DevTool/components/BluetoothList/index.vue +167 -0
  57. package/dist/v3/{CaptureScreen → DevTool/components/CaptureScreen}/index.vue +4 -4
  58. package/dist/v3/{ConsoleList → DevTool/components/ConsoleList}/ConsoleItem.vue +22 -16
  59. package/dist/v3/{ConsoleList → DevTool/components/ConsoleList}/RunJSInput.vue +4 -6
  60. package/dist/v3/{ConsoleList → DevTool/components/ConsoleList}/index.vue +21 -10
  61. package/dist/v3/{DevToolButton → DevTool/components/DevToolButton}/index.vue +7 -4
  62. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/DevToolOverlay.vue +17 -2
  63. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/const.ts +28 -5
  64. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/hooks/dataUtils.ts +1 -1
  65. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/hooks/useDevToolData.ts +55 -6
  66. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/hooks/useDevToolHandlers.ts +85 -5
  67. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/hooks/useDevToolOverlay.ts +25 -8
  68. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/index.vue +67 -16
  69. package/dist/v3/{ElEvent → DevTool/components/ElEvent}/ElEventItem.vue +3 -3
  70. package/dist/v3/{ElEvent → DevTool/components/ElEvent}/index.vue +10 -13
  71. package/dist/v3/{Instance → DevTool/components/Instance}/index.vue +1 -1
  72. package/dist/v3/{Instance → DevTool/components/Instance}/transformTree.ts +1 -1
  73. package/dist/v3/{Instance → DevTool/components/Instance}/transformTreeCtx.ts +1 -1
  74. package/dist/v3/{InstanceDetail → DevTool/components/InstanceDetail}/index.vue +4 -4
  75. package/dist/v3/{JsonDetail → DevTool/components/JsonDetail}/index.vue +4 -4
  76. package/dist/v3/{NFCList → DevTool/components/NFCList}/NFCItem.vue +4 -5
  77. package/dist/v3/{NFCList → DevTool/components/NFCList}/NFCTool.vue +33 -57
  78. package/dist/v3/{NFCList → DevTool/components/NFCList}/index.vue +12 -16
  79. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/InterceptConfig.vue +20 -4
  80. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/InterceptItem.vue +3 -3
  81. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/NetworkDetail.vue +18 -27
  82. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/NetworkIntercept.vue +11 -16
  83. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/NetworkItem.vue +10 -14
  84. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/NetworkSend.vue +67 -34
  85. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/index.vue +8 -8
  86. package/dist/v3/{Performance → DevTool/components/Performance}/index.vue +7 -4
  87. package/dist/v3/{Performance → DevTool/components/Performance}/modules/PerformanceWidget.vue +12 -9
  88. package/dist/v3/{Performance → DevTool/components/Performance}/modules/usePerformanceChart.ts +1 -1
  89. package/dist/v3/{Performance → DevTool/components/Performance}/modules/usePerformanceData.ts +2 -2
  90. package/dist/v3/{PiniaList → DevTool/components/PiniaList}/index.vue +5 -6
  91. package/dist/v3/{RouteList → DevTool/components/RouteList}/index.vue +21 -24
  92. package/dist/v3/{RunJS → DevTool/components/RunJS}/index.vue +3 -3
  93. package/dist/v3/{ScanCodeList → DevTool/components/ScanCodeList}/ScanCodeItem.vue +3 -4
  94. package/dist/v3/{ScanCodeList → DevTool/components/ScanCodeList}/index.vue +12 -16
  95. package/dist/v3/{SettingList → DevTool/components/SettingList}/index.vue +68 -0
  96. package/dist/v3/DevTool/components/SettingList/modules/SettingBarrage.vue +304 -0
  97. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingDevTool.vue +8 -4
  98. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingInfo.vue +47 -9
  99. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingLanguage.vue +2 -2
  100. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingLog.vue +2 -2
  101. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingNetwork.vue +3 -3
  102. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingTheme.vue +37 -7
  103. package/dist/v3/{SourceCode → DevTool/components/SourceCode}/Line.vue +22 -11
  104. package/dist/v3/{SourceCode → DevTool/components/SourceCode}/index.vue +8 -8
  105. package/dist/v3/{SourceCode → DevTool/components/SourceCode}/parseCode.ts +136 -228
  106. package/dist/v3/{StorageList → DevTool/components/StorageList}/index.vue +7 -7
  107. package/dist/v3/{TransferList → DevTool/components/TransferList}/TransferDetail.vue +6 -6
  108. package/dist/v3/{TransferList → DevTool/components/TransferList}/TransferItem.vue +4 -4
  109. package/dist/v3/{TransferList → DevTool/components/TransferList}/index.vue +8 -8
  110. package/dist/v3/{UniEvent → DevTool/components/UniEvent}/UniEventItem.vue +6 -7
  111. package/dist/v3/{UniEvent → DevTool/components/UniEvent}/index.vue +6 -6
  112. package/dist/v3/{VuexList → DevTool/components/VuexList}/index.vue +3 -3
  113. package/dist/v3/{WebSocket → DevTool/components/WebSocket}/WebSocketDetail.vue +8 -8
  114. package/dist/v3/{WebSocket → DevTool/components/WebSocket}/WebSocketItem.vue +4 -4
  115. package/dist/v3/{WebSocket → DevTool/components/WebSocket}/index.vue +8 -8
  116. package/dist/v3/DevTool/index.vue +179 -4
  117. package/dist/v3/{AppTransition → components/AppTransition}/index.vue +6 -0
  118. package/dist/v3/components/Barrage/BarrageItem.vue +137 -0
  119. package/dist/v3/components/Barrage/index.vue +202 -0
  120. package/dist/v3/components/DevErrorBoundary/index.vue +380 -0
  121. package/dist/v3/{DraggableContainer → components/DraggableContainer}/index.vue +1 -1
  122. package/dist/v3/{FilterInput → components/FilterInput}/index.vue +1 -1
  123. package/dist/v3/{JsonPretty → components/JsonPretty}/components/CheckController/index.vue +1 -1
  124. package/dist/v3/{JsonPretty → components/JsonPretty}/components/TreeNode/index.vue +11 -5
  125. package/dist/v3/{JsonPretty → components/JsonPretty}/index.vue +16 -13
  126. package/dist/v3/{JsonPretty → components/JsonPretty}/type.ts +1 -0
  127. package/dist/v3/{JsonPretty → components/JsonPretty}/utils/index.ts +1 -1
  128. package/dist/v3/{MovableContainer → components/MovableContainer}/index.vue +9 -5
  129. package/dist/v3/{Pick → components/Pick}/index.vue +1 -1
  130. package/dist/v3/{Tabs → components/Tabs}/index.vue +30 -4
  131. package/dist/v3/{VirtualList → components/VirtualList}/AutoSize.vue +1 -1
  132. package/dist/v3/{VirtualList → components/VirtualList}/index.vue +4 -0
  133. package/dist/v3/hooks/useBluetooth/index.ts +561 -0
  134. package/dist/v3/hooks/useRequest/index.ts +33 -20
  135. package/dist/v3/hooks/useWebsocket/README.md +79 -0
  136. package/dist/v3/hooks/useWebsocket/index.ts +253 -0
  137. package/dist/v3/styles/theme.css +17 -10
  138. package/package.json +67 -64
  139. package/dist/plugins/uniParseStock/index.d.ts +0 -10
  140. package/dist/plugins/uniParseStock/index.d.ts.map +0 -1
  141. /package/dist/v3/{ConsoleList → DevTool/components/ConsoleList}/staticTips.ts +0 -0
  142. /package/dist/v3/{DevToolTitle → DevTool/components/DevToolTitle}/index.vue +0 -0
  143. /package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/index.css +0 -0
  144. /package/dist/v3/{Instance → DevTool/components/Instance}/components/InstanceTreeNode.vue +0 -0
  145. /package/dist/v3/{Instance → DevTool/components/Instance}/flatten.ts +0 -0
  146. /package/dist/v3/{Instance → DevTool/components/Instance}/registry.ts +0 -0
  147. /package/dist/v3/{Instance → DevTool/components/Instance}/typing.d.ts +0 -0
  148. /package/dist/v3/{NFCList → DevTool/components/NFCList}/const.ts +0 -0
  149. /package/dist/v3/{NetworkList → DevTool/components/NetworkList}/const.ts +0 -0
  150. /package/dist/v3/{NetworkList → DevTool/components/NetworkList}/hooks/useNetworkForm.ts +0 -0
  151. /package/dist/v3/{NetworkList → DevTool/components/NetworkList}/utils.ts +0 -0
  152. /package/dist/v3/{Performance → DevTool/components/Performance}/modules/PerformanceMetrics.vue +0 -0
  153. /package/dist/v3/{SettingButton → DevTool/components/SettingButton}/index.vue +0 -0
  154. /package/dist/v3/{SettingList → DevTool/components/SettingList}/index.css +0 -0
  155. /package/dist/v3/{SettingList → DevTool/components/SettingList}/typing.d.ts +0 -0
  156. /package/dist/v3/{AutoSizer → components/AutoSizer}/index.vue +0 -0
  157. /package/dist/v3/{AutoSizer → components/AutoSizer}/index1.vue +0 -0
  158. /package/dist/v3/{AutoSizer → components/AutoSizer}/utils.ts +0 -0
  159. /package/dist/v3/{CircularButton → components/CircularButton}/index.vue +0 -0
  160. /package/dist/v3/{CustomSwiper → components/CustomSwiper}/CustomSwiperItem.vue +0 -0
  161. /package/dist/v3/{CustomSwiper → components/CustomSwiper}/index.vue +0 -0
  162. /package/dist/v3/{Empty → components/Empty}/empty.png +0 -0
  163. /package/dist/v3/{Empty → components/Empty}/index.vue +0 -0
  164. /package/dist/v3/{FilterSelect → components/FilterSelect}/index.vue +0 -0
  165. /package/dist/v3/{JsonPretty → components/JsonPretty}/components/Brackets/index.vue +0 -0
  166. /package/dist/v3/{JsonPretty → components/JsonPretty}/components/Carets/index.vue +0 -0
  167. /package/dist/v3/{JsonPretty → components/JsonPretty}/hooks/useClipboard.ts +0 -0
  168. /package/dist/v3/{JsonPretty → components/JsonPretty}/hooks/useError.ts +0 -0
  169. /package/dist/v3/{Tag → components/Tag}/index.vue +0 -0
  170. /package/dist/v3/{VirtualList → components/VirtualList}/readme.md +0 -0
@@ -33,13 +33,29 @@
33
33
  :active="isPageActive"
34
34
  :FPS="0"></view>
35
35
  <!-- #endif -->
36
+ <!-- SafeArea Preview Mask -->
37
+ <view
38
+ v-if="showingSafeArea"
39
+ class="dev-tool-safe-area-preview"
40
+ :style="safeAreaStyle"></view>
41
+ <!-- Barrage -->
42
+ <Barrage
43
+ ref="barrageRef"
44
+ :zIndex="zIndex + 100"
45
+ :maxTracks="windowData.barrageMaxTracks"
46
+ :trackHeight="windowData.barrageTrackHeight"
47
+ :startTop="windowData.barrageStartTop"
48
+ :duration="windowData.barrageDuration"
49
+ :maxLength="windowData.barrageMaxLength"
50
+ :visible="barrageFinalVisible" />
36
51
  </view>
37
52
  </template>
38
53
 
39
54
  <script lang="ts">
40
- import DevToolButton from '../DevToolButton/index.vue';
41
- import DevToolWindow from '../DevToolWindow/index.vue';
42
- import PerformanceWidget from '../Performance/modules/PerformanceWidget.vue';
55
+ import DevToolButton from './components/DevToolButton/index.vue';
56
+ import DevToolWindow from './components/DevToolWindow/index.vue';
57
+ import PerformanceWidget from './components/Performance/modules/PerformanceWidget.vue';
58
+ import Barrage from '../components/Barrage/index.vue';
43
59
 
44
60
  export default {
45
61
  __devTool: true,
@@ -47,6 +63,7 @@ export default {
47
63
  DevToolButton,
48
64
  DevToolWindow,
49
65
  PerformanceWidget,
66
+ Barrage,
50
67
  },
51
68
  };
52
69
  </script>
@@ -81,12 +98,17 @@ const sourceFileServers = ref<string[]>([]);
81
98
 
82
99
  const mode = ref<string>();
83
100
 
101
+ const showingSafeArea = ref(false);
102
+ const safeAreaStyle = ref('');
103
+
84
104
  const zIndex = ref(998);
85
105
 
86
106
  const useDevSource = ref(false);
87
107
 
88
108
  const windowOrigin = ref({ x: 0, y: 0 });
89
109
 
110
+ const barrageRef = ref<any>(null);
111
+
90
112
  const theme = ref<string>('dark');
91
113
 
92
114
  const devToolVersion = ref<string>('v0.0.0');
@@ -106,6 +128,14 @@ const baseFontSizePx = computed(() => baseFontSize.value + 'px');
106
128
  const tagFontSizePx = computed(() => tagFontSize.value + 'px');
107
129
  const tipsFontSizePx = computed(() => tipsFontSize.value + 'px');
108
130
 
131
+ const barrageFinalVisible = computed(() => {
132
+ if (!windowData.value.barrageVisible) return false;
133
+ // 如果主窗口打开了,且配置为“打开时不显示”,则隐藏
134
+ if (devToolWindowVisible.value && !windowData.value.barrageShowWhenOpen)
135
+ return false;
136
+ return true;
137
+ });
138
+
109
139
  function updateWindowOrigin(pos?: { x: number; y: number }) {
110
140
  const { left = 300, top = 500 } = getDevToolInfo();
111
141
  let x = left + 50;
@@ -133,6 +163,36 @@ function sendCommand(type: string, data: any = {}) {
133
163
  uni.$emit(DEV_UI_MESSAGE, { type, data });
134
164
  }
135
165
 
166
+ function handleUIMessage(msg: any) {
167
+ let type, data;
168
+ try {
169
+ const payload = typeof msg === 'string' ? JSON.parse(msg) : msg;
170
+ type = payload.type;
171
+ data = payload.data;
172
+ } catch (e) {
173
+ return;
174
+ }
175
+
176
+ if (type === 'TOGGLE_SAFE_AREA') {
177
+ renderSafeArea(data);
178
+ }
179
+ }
180
+
181
+ function renderSafeArea(show: boolean) {
182
+ showingSafeArea.value = show;
183
+ if (show) {
184
+ if (uni.getWindowInfo) {
185
+ const info = uni.getWindowInfo();
186
+ const safeArea = info.safeArea;
187
+ if (safeArea) {
188
+ safeAreaStyle.value = `top: ${safeArea.top}px; left: ${safeArea.left}px; width: ${safeArea.width}px; height: ${safeArea.height}px; border: 3px solid #00ff00; background: rgba(0, 255, 0, 0.15);`;
189
+ return;
190
+ }
191
+ }
192
+ safeAreaStyle.value = `top: env(safe-area-inset-top); bottom: env(safe-area-inset-bottom); left: env(safe-area-inset-left); right: env(safe-area-inset-right); border: 3px solid #00ff00; background: rgba(0, 255, 0, 0.15);`;
193
+ }
194
+ }
195
+
136
196
  function onDevToolButtonClick(pos: { x: number; y: number }) {
137
197
  updateWindowOrigin(pos);
138
198
  onShowDevToolWindow(true);
@@ -173,7 +233,8 @@ function onSendMessage(param: { type: string; data: Record<string, any> }) {
173
233
 
174
234
  function onDevOptionSend(data: DevTool.WindowData) {
175
235
  if (data) {
176
- windowData.value = data;
236
+ // 采用合并策略而不是直接替换,防止丢失部分本地状态
237
+ windowData.value = { ...windowData.value, ...data };
177
238
 
178
239
  if (data.devToolVersion !== undefined)
179
240
  devToolVersion.value = data.devToolVersion;
@@ -195,6 +256,33 @@ function onDevOptionSend(data: DevTool.WindowData) {
195
256
  if (data.fontWeight !== undefined) fontWeight.value = data.fontWeight;
196
257
  if (data.performanceVisible !== undefined)
197
258
  performanceVisible.value = data.performanceVisible;
259
+
260
+ // 弹幕配置
261
+ if (data.barrageVisible !== undefined)
262
+ windowData.value.barrageVisible = data.barrageVisible;
263
+ if (data.barrageShowWhenOpen !== undefined)
264
+ windowData.value.barrageShowWhenOpen = data.barrageShowWhenOpen;
265
+ if (data.barrageMaxTracks !== undefined)
266
+ windowData.value.barrageMaxTracks = data.barrageMaxTracks;
267
+ if (data.barrageTrackHeight !== undefined)
268
+ windowData.value.barrageTrackHeight = data.barrageTrackHeight;
269
+ if (data.barrageStartTop !== undefined)
270
+ windowData.value.barrageStartTop = data.barrageStartTop;
271
+ if (data.barrageDuration !== undefined)
272
+ windowData.value.barrageDuration = data.barrageDuration;
273
+ if (data.barrageMaxLength !== undefined)
274
+ windowData.value.barrageMaxLength = data.barrageMaxLength;
275
+
276
+ if (data.barrageTypes !== undefined) {
277
+ if (Array.isArray(data.barrageTypes)) {
278
+ windowData.value.barrageTypes = data.barrageTypes;
279
+ } else {
280
+ console.warn(
281
+ '[DevTool] Received invalid barrageTypes in windowData:',
282
+ data.barrageTypes,
283
+ );
284
+ }
285
+ }
198
286
  }
199
287
  }
200
288
 
@@ -230,9 +318,57 @@ function handleCoreMessage(msg: any) {
230
318
  case MSG_TYPE.PERF_VISIBLE:
231
319
  performanceVisible.value = data;
232
320
  break;
321
+ case MSG_TYPE.DATA_UPDATE:
233
322
  case MSG_TYPE.OPTION_UPDATE:
234
323
  onDevOptionSend(data);
235
324
  break;
325
+ case MSG_TYPE.BARRAGE_VISIBLE:
326
+ windowData.value.barrageVisible = data;
327
+ break;
328
+ case MSG_TYPE.BARRAGE_SHOW_WHEN_OPEN:
329
+ windowData.value.barrageShowWhenOpen = data;
330
+ break;
331
+ case MSG_TYPE.BARRAGE_ADD:
332
+ if (barrageFinalVisible.value && barrageRef.value) {
333
+ const itemsList = data.items || [];
334
+ itemsList.forEach((item: any) => {
335
+ // 检查类型是否在允许列表中
336
+ if (!windowData.value.barrageTypes?.includes(item.type || 'log'))
337
+ return;
338
+
339
+ if (item.type === 'network') {
340
+ const status = item.status;
341
+ // 异常定义:已完成(非 pending)且 (状态码非 2xx 或 为 'ERR')
342
+ const isPending = status === 'pending';
343
+ const isSuccess =
344
+ typeof status === 'number'
345
+ ? status >= 200 && status < 300
346
+ : typeof status === 'string' && /^[2]\d{2}$/.test(status);
347
+
348
+ if (!isPending && !isSuccess) {
349
+ barrageRef.value.add(
350
+ `[${item.method || 'GET'}] ${status} - ${item.url}`,
351
+ 'network',
352
+ );
353
+ }
354
+ } else if (item.type === 'transfer') {
355
+ barrageRef.value.add(`Transfer Failed: ${item.url}`, 'error');
356
+ } else if (item.type === 'websocket') {
357
+ barrageRef.value.add(`WS: ${item.data}`, 'info');
358
+ } else {
359
+ const args = item.args || [];
360
+ const text = args
361
+ .map((arg: any) =>
362
+ typeof arg?.value === 'string'
363
+ ? arg.value
364
+ : JSON.stringify(arg?.value ?? ''),
365
+ )
366
+ .join(' ');
367
+ barrageRef.value.add(text, item.type || 'log');
368
+ }
369
+ });
370
+ }
371
+ break;
236
372
  // ... 其他消息处理
237
373
  }
238
374
  }
@@ -256,6 +392,23 @@ onBeforeMount(() => {
256
392
  fontFamily: fontF = '',
257
393
  fontWeight: weightF = 'normal',
258
394
  theme: themeF = 'light',
395
+ showingSafeArea: safeAreaF = false,
396
+ barrageVisible: barrageVisibleF = true,
397
+ barrageShowWhenOpen: barrageShowWhenOpenF = false,
398
+ barrageMaxTracks: maxTracksF = 10,
399
+ barrageTrackHeight: trackHeightF = 36,
400
+ barrageStartTop: startTopF = 40,
401
+ barrageDuration: durationF = 6,
402
+ barrageMaxLength: maxLengthF = 100,
403
+ barrageTypes: typesF = [
404
+ 'log',
405
+ 'info',
406
+ 'warn',
407
+ 'error',
408
+ 'network',
409
+ 'transfer',
410
+ 'websocket',
411
+ ],
259
412
  } = getDevToolInfo();
260
413
 
261
414
  isActive = true;
@@ -264,6 +417,10 @@ onBeforeMount(() => {
264
417
  devToolWindowVisible.value = false; // 始终从关闭状态开始
265
418
  performanceVisible.value = false; // 始终从关闭状态开始
266
419
  windowData.value.performanceVisible = false;
420
+
421
+ if (safeAreaF) {
422
+ renderSafeArea(safeAreaF);
423
+ }
267
424
  baseFontSize.value = baseF;
268
425
  tagFontSize.value = tagF;
269
426
  tipsFontSize.value = tipsF;
@@ -274,10 +431,20 @@ onBeforeMount(() => {
274
431
  windowData.value.tipsFontSize = tipsF;
275
432
  windowData.value.fontFamily = fontF;
276
433
  windowData.value.fontWeight = weightF;
434
+ windowData.value.barrageVisible = barrageVisibleF;
435
+ windowData.value.barrageShowWhenOpen = barrageShowWhenOpenF;
436
+ windowData.value.barrageMaxTracks = maxTracksF;
437
+ windowData.value.barrageTrackHeight = trackHeightF;
438
+ windowData.value.barrageStartTop = startTopF;
439
+ windowData.value.barrageDuration = durationF;
440
+ windowData.value.barrageMaxLength = maxLengthF;
441
+ windowData.value.barrageTypes = typesF;
277
442
  theme.value = themeF;
278
443
 
279
444
  // 监听核心模块消息
280
445
  uni.$on(DEV_CORE_MESSAGE, handleCoreMessage);
446
+ // 自己也监听部分内网通讯用于控制外边框等全局状态
447
+ uni.$on(DEV_UI_MESSAGE, handleUIMessage);
281
448
 
282
449
  // #ifdef H5
283
450
  setTimeout(() => {
@@ -295,6 +462,7 @@ onBeforeMount(() => {
295
462
  onUnmounted(() => {
296
463
  sendCommand(CMD_TYPE.WINDOW_CLOSE, {});
297
464
  uni.$off(DEV_CORE_MESSAGE, handleCoreMessage);
465
+ uni.$off(DEV_UI_MESSAGE, handleUIMessage);
298
466
  });
299
467
 
300
468
  onShow(() => {
@@ -342,4 +510,11 @@ onHide(() => {
342
510
  font-family: var(--dev-tool-font-family);
343
511
  font-weight: var(--dev-tool-font-weight);
344
512
  }
513
+
514
+ .dev-tool-safe-area-preview {
515
+ position: fixed;
516
+ pointer-events: none;
517
+ z-index: 999999;
518
+ box-sizing: border-box;
519
+ }
345
520
  </style>
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <view
3
3
  v-if="renderShow"
4
+ class="transition-root"
4
5
  :class="[showContainer ? 'transition-fixed' : '']"
5
6
  :style="[showContainer ? { zIndex } : {}]">
6
7
  <view
@@ -140,6 +141,11 @@ watch(
140
141
  </script>
141
142
 
142
143
  <style scoped>
144
+ .transition-root {
145
+ width: 100%;
146
+ height: 100%;
147
+ }
148
+
143
149
  .transition-container {
144
150
  display: block;
145
151
  position: relative;
@@ -0,0 +1,137 @@
1
+ <template>
2
+ <view class="barrage-item" :style="itemStyle" @animationend="onAnimationEnd">
3
+ <view class="barrage-content" :class="type">
4
+ <view class="barrage-type-tag">
5
+ {{ typeTag }}
6
+ </view>
7
+ <view class="barrage-text">{{ text }}</view>
8
+ </view>
9
+ </view>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed } from 'vue';
14
+
15
+ const props = defineProps<{
16
+ text: string;
17
+ type: string;
18
+ top: number;
19
+ duration: number;
20
+ delay?: number; // 新增延迟属性,毫秒单位
21
+ maxLength?: number;
22
+ color?: string;
23
+ }>();
24
+
25
+ const emit = defineEmits<{
26
+ (e: 'remove'): void;
27
+ }>();
28
+
29
+ const typeTag = computed(() => {
30
+ const t = props.type.toLowerCase();
31
+ if (t === 'network') return 'NET';
32
+ return t.toUpperCase();
33
+ });
34
+
35
+ const itemStyle = computed(() => ({
36
+ top: `${props.top}px`,
37
+ '--barrage-duration': `${props.duration}s`,
38
+ '--barrage-delay': `${props.delay || 0}ms`,
39
+ '--barrage-max-width': props.maxLength ? 'none' : '300px',
40
+ color: props.color || 'inherit',
41
+ }));
42
+
43
+ function onAnimationEnd() {
44
+ emit('remove');
45
+ }
46
+ </script>
47
+
48
+ <style scoped>
49
+ .barrage-item {
50
+ position: fixed;
51
+ left: 100%;
52
+ top: 0;
53
+ white-space: nowrap;
54
+ pointer-events: none;
55
+ z-index: 10001;
56
+ display: flex;
57
+ will-change: transform;
58
+ animation: barrage-move var(--barrage-duration) linear var(--barrage-delay)
59
+ forwards;
60
+ }
61
+
62
+ .barrage-item:hover {
63
+ animation-play-state: paused;
64
+ z-index: 20000;
65
+ }
66
+
67
+ @keyframes barrage-move {
68
+ from {
69
+ transform: translateX(0);
70
+ }
71
+
72
+ to {
73
+ transform: translateX(calc(-100vw - 100%));
74
+ }
75
+ }
76
+
77
+ .barrage-content {
78
+ display: flex;
79
+ align-items: center;
80
+ padding: 4px 12px;
81
+ border-radius: 16px;
82
+ background: rgb(0 0 0 / 60%);
83
+ backdrop-filter: blur(4px);
84
+ color: #fff;
85
+ font-size: 12px;
86
+ box-shadow: 0 2px 8px rgb(0 0 0 / 20%);
87
+ pointer-events: auto;
88
+ cursor: pointer;
89
+ }
90
+
91
+ /* Log & Info: 白色/中性系 */
92
+ .barrage-content.log,
93
+ .barrage-content.info {
94
+ background: rgb(255 255 255 / 90%);
95
+ color: #333;
96
+ box-shadow: 0 2px 10px rgb(0 0 0 / 10%);
97
+ }
98
+
99
+ .barrage-content.log .barrage-type-tag,
100
+ .barrage-content.info .barrage-type-tag {
101
+ background: rgb(0 0 0 / 10%);
102
+ }
103
+
104
+ /* Warn: 黄橙色系 */
105
+ .barrage-content.warn {
106
+ background: rgb(180 100 0 / 75%);
107
+ box-shadow: 0 0 10px rgb(255 149 0 / 30%);
108
+ }
109
+
110
+ /* Error & Network: 红色系 */
111
+ .barrage-content.error,
112
+ .barrage-content.network {
113
+ background: rgb(180 30 20 / 75%);
114
+ box-shadow: 0 0 10px rgb(255 59 48 / 30%);
115
+ }
116
+
117
+ /* Debug: 灰色系 */
118
+ .barrage-content.debug {
119
+ background: rgb(80 80 80 / 75%);
120
+ box-shadow: 0 0 10px rgb(142 142 147 / 30%);
121
+ }
122
+
123
+ .barrage-type-tag {
124
+ font-weight: bold;
125
+ margin-right: 6px;
126
+ font-size: 10px;
127
+ padding: 2px 4px;
128
+ background: rgb(0 0 0 / 5%);
129
+ border-radius: 4px;
130
+ }
131
+
132
+ .barrage-text {
133
+ max-width: var(--barrage-max-width, 300px);
134
+ overflow: hidden;
135
+ text-overflow: ellipsis;
136
+ }
137
+ </style>
@@ -0,0 +1,202 @@
1
+ <template>
2
+ <view
3
+ v-if="actualVisible"
4
+ class="barrage-container"
5
+ :style="{ zIndex: props.zIndex }">
6
+ <BarrageItem
7
+ v-for="item in items"
8
+ :key="item.id"
9
+ :text="item.text"
10
+ :type="item.type"
11
+ :top="item.top"
12
+ :duration="item.duration"
13
+ :delay="item.delay"
14
+ :maxLength="props.maxLength"
15
+ @remove="removeItem(item.id)" />
16
+ </view>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { ref, onMounted, watch, computed } from 'vue';
21
+ import { onShow, onHide } from '@dcloudio/uni-app';
22
+ import BarrageItem from './BarrageItem.vue';
23
+
24
+ const props = withDefaults(
25
+ defineProps<{
26
+ maxTracks?: number;
27
+ trackHeight?: number;
28
+ startTop?: number;
29
+ duration?: number;
30
+ maxLength?: number;
31
+ visible?: boolean;
32
+ zIndex?: number;
33
+ }>(),
34
+ {
35
+ maxTracks: 10,
36
+ trackHeight: 36,
37
+ startTop: 40,
38
+ duration: 6,
39
+ maxLength: 100,
40
+ visible: true,
41
+ zIndex: 10000,
42
+ },
43
+ );
44
+
45
+ const actualVisible = computed(() => props.visible);
46
+
47
+ const items = ref<
48
+ {
49
+ id: number;
50
+ text: string;
51
+ type: string;
52
+ top: number;
53
+ duration: number;
54
+ delay: number;
55
+ }[]
56
+ >([]);
57
+ let nextId = 0;
58
+
59
+ // 页面是否在前台(用于防止页面隐藏时弹幕堆积)
60
+ let isPageActive = true;
61
+
62
+ // 记录每个轨道的最后一条弹幕开始的时间 + 安全间隔
63
+ const laneNextAvailableTime = ref<number[]>(new Array(props.maxTracks).fill(0));
64
+ const laneLastWidth = ref<number[]>(new Array(props.maxTracks).fill(0));
65
+
66
+ watch(
67
+ () => props.maxTracks,
68
+ (val: number) => {
69
+ laneNextAvailableTime.value = new Array(val).fill(0);
70
+ laneLastWidth.value = new Array(val).fill(0);
71
+ },
72
+ );
73
+
74
+ const screenWidth = ref(375);
75
+
76
+ function getBestLane(
77
+ text: string,
78
+ duration: number,
79
+ ): { index: number; delay: number } {
80
+ const now = Date.now();
81
+ const chineseCount = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
82
+ const emojiCount = (
83
+ text.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|\u2600-\u27BF/g) || []
84
+ ).length;
85
+ const otherCount = text.length - chineseCount - emojiCount;
86
+ const estimatedWidth =
87
+ chineseCount * 14 + emojiCount * 20 + otherCount * 8 + 80;
88
+
89
+ let bestLane = 0;
90
+ let minStartTime = Infinity;
91
+
92
+ for (let i = 0; i < props.maxTracks; i++) {
93
+ const nextCanStart = laneNextAvailableTime.value[i] || 0;
94
+ if (nextCanStart <= now) {
95
+ bestLane = i;
96
+ minStartTime = now;
97
+ break;
98
+ }
99
+ if (nextCanStart < minStartTime) {
100
+ minStartTime = nextCanStart;
101
+ bestLane = i;
102
+ }
103
+ }
104
+
105
+ const startTime = Math.max(now, minStartTime);
106
+ const delay = startTime - now;
107
+ const lastWidth = laneLastWidth.value[bestLane] || 0;
108
+ const s = screenWidth.value || 375;
109
+
110
+ const enterTimeNew =
111
+ (estimatedWidth / ((s + estimatedWidth) / duration)) * 1000;
112
+ const enterTimePrev = (lastWidth / ((s + lastWidth) / duration)) * 1000;
113
+ const safeInterval = Math.max(enterTimeNew, enterTimePrev) + 300;
114
+
115
+ laneNextAvailableTime.value[bestLane] = startTime + safeInterval;
116
+ laneLastWidth.value[bestLane] = estimatedWidth;
117
+
118
+ return { index: bestLane, delay };
119
+ }
120
+
121
+ /**
122
+ * 添加一条弹幕 (通用接口)
123
+ * @param text 文字内容
124
+ * @param type 类型,用于区分颜色样式
125
+ */
126
+ function add(text: string, type: string = 'log') {
127
+ if (!isPageActive || !actualVisible.value) return;
128
+
129
+ const id = nextId++;
130
+ const duration = props.duration;
131
+
132
+ let finalText = text;
133
+ if (finalText.length > props.maxLength) {
134
+ finalText = finalText.slice(0, props.maxLength) + '...';
135
+ }
136
+
137
+ const { index: laneIndex, delay } = getBestLane(finalText, duration);
138
+ const top = props.startTop + laneIndex * props.trackHeight;
139
+
140
+ items.value.push({
141
+ id,
142
+ text: finalText,
143
+ type,
144
+ top,
145
+ duration,
146
+ delay,
147
+ });
148
+ }
149
+
150
+ function removeItem(id: number) {
151
+ const index = items.value.findIndex((item) => item.id === id);
152
+ if (index !== -1) {
153
+ items.value.splice(index, 1);
154
+ }
155
+ }
156
+
157
+ /**
158
+ * 清空弹幕
159
+ */
160
+ function clear() {
161
+ items.value = [];
162
+ laneNextAvailableTime.value = new Array(props.maxTracks).fill(0);
163
+ laneLastWidth.value = new Array(props.maxTracks).fill(0);
164
+ }
165
+
166
+ onMounted(() => {
167
+ try {
168
+ const sys = uni.getSystemInfoSync();
169
+ if (sys.windowWidth) screenWidth.value = sys.windowWidth;
170
+ } catch (_err) {
171
+ // 降级使用 375 默认宽度
172
+ }
173
+ });
174
+
175
+ onShow(() => {
176
+ isPageActive = true;
177
+ clear();
178
+ });
179
+
180
+ onHide(() => {
181
+ isPageActive = false;
182
+ items.value = [];
183
+ });
184
+
185
+ defineExpose({
186
+ add,
187
+ clear,
188
+ });
189
+ </script>
190
+
191
+ <style scoped>
192
+ .barrage-container {
193
+ position: fixed;
194
+ top: 0;
195
+ left: 0;
196
+ width: 100%;
197
+ height: 100%;
198
+ pointer-events: none;
199
+ z-index: 10000;
200
+ overflow: hidden;
201
+ }
202
+ </style>