vite-uni-dev-tool 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -151,6 +151,14 @@ uni.__dev__console.log('hello vite-uni-dev-tool');
151
151
 
152
152
  ## 更新日志
153
153
 
154
+ ### 0.0.10
155
+
156
+ - 修复 console 过滤异常
157
+
158
+ ### 0.0.9
159
+
160
+ - 修复 template 多节点造成的 createIntersectionObserver 异常
161
+
154
162
  ### 0.0.8
155
163
 
156
164
  - 修复多 template 注入调试工具异常
@@ -0,0 +1,193 @@
1
+ <template>
2
+ <view class="auto-sizer">
3
+ <!-- 大小和slot 内容宽度一致 -->
4
+ <view class="listener-slot">
5
+ <slot></slot>
6
+ </view>
7
+
8
+ <!-- 变大监听固定在 listener-slot 右下角大1px位置 变大时 相交触发监听 -->
9
+ <view
10
+ class="anchor-height-add"
11
+ :style="{
12
+ top: listenerSlot.height + 1 + 'px',
13
+ }"
14
+ >
15
+ </view>
16
+
17
+ <!-- 变小监听固定在 listener-slot 右下角小1px位置 减小时 离开触发监听 -->
18
+ <view
19
+ class="anchor-height-sub"
20
+ :style="{
21
+ top: listenerSlot.height - 1 + 'px',
22
+ }"
23
+ >
24
+ </view>
25
+
26
+ <!-- 变大监听固定在 listener-slot 右下角大1px位置 变大时 相交触发监听 -->
27
+ <view
28
+ class="anchor-width-add"
29
+ :style="{
30
+ left: listenerSlot.width + 1 + 'px',
31
+ top: '0px',
32
+ }"
33
+ >
34
+ </view>
35
+
36
+ <!-- 变小监听固定在 listener-slot 右下角小1px位置 减小时 离开触发监听 -->
37
+ <view
38
+ class="anchor-width-sub"
39
+ :style="{
40
+ left: listenerSlot.width - 1 + 'px',
41
+ top: '0px',
42
+ }"
43
+ >
44
+ </view>
45
+ </view>
46
+ </template>
47
+ <script lang="ts" setup>
48
+ import {
49
+ getCurrentInstance,
50
+ onMounted,
51
+ inject,
52
+ reactive,
53
+ onUnmounted,
54
+ } from 'vue';
55
+ import { debounce } from '../../utils';
56
+
57
+ type Size = {
58
+ width: number;
59
+ height: number;
60
+ top: number;
61
+ left: number;
62
+ right: number;
63
+ bottom: number;
64
+ };
65
+
66
+ const props = defineProps<{
67
+ index: number;
68
+ immediate?: boolean;
69
+ }>();
70
+
71
+ const emit = defineEmits<{ (e: 'resize', size: Size): void }>();
72
+
73
+ let isMounted = false;
74
+
75
+ const listenerSlot = reactive({
76
+ width: 0,
77
+ height: 0,
78
+ });
79
+
80
+ const size: Size = {
81
+ width: 0,
82
+ height: 0,
83
+ top: 0,
84
+ left: 0,
85
+ right: 0,
86
+ bottom: 0,
87
+ };
88
+
89
+ const instance = getCurrentInstance();
90
+
91
+ const query = uni.createSelectorQuery().in(instance);
92
+
93
+ const onSizeChange =
94
+ inject<(index: number, height: number) => void>('onSizeChange');
95
+
96
+ /** 外界记录的高度 */
97
+ const itemsHeight = inject<number[]>('itemsHeight');
98
+
99
+ const debounceReact = debounce(
100
+ (rect: UniApp.NodeInfo | UniApp.NodeInfo[]) => {
101
+ if (Array.isArray(rect)) return;
102
+
103
+ // 获取屏幕尺寸
104
+ const { windowWidth, windowHeight } = uni.getSystemInfoSync();
105
+
106
+ // 在尺寸更新时限制最大值
107
+ listenerSlot.height = Math.min(rect.height ?? 0, windowHeight);
108
+ listenerSlot.width = Math.min(rect.width ?? 0, windowWidth);
109
+
110
+ onSizeChange?.(props.index, rect.height ?? 0);
111
+
112
+ size.width = rect.width ?? 0;
113
+ size.height = rect.height ?? 0;
114
+ size.top = rect.top ?? 0;
115
+ size.left = rect.left ?? 0;
116
+ size.right = rect.right ?? 0;
117
+ size.bottom = rect.bottom ?? 0;
118
+
119
+ if (props.immediate && !isMounted) {
120
+ emit('resize', size);
121
+ }
122
+
123
+ if (isMounted) {
124
+ emit('resize', size);
125
+ }
126
+
127
+ isMounted = true;
128
+ },
129
+ 100,
130
+ true,
131
+ );
132
+ function getSize() {
133
+ query.select('.listener-slot').boundingClientRect(debounceReact).exec();
134
+ }
135
+
136
+ const debounceGetSize = debounce(getSize, 100, true);
137
+
138
+ let observer: UniNamespace.IntersectionObserver;
139
+ onMounted(() => {
140
+ // 防止重复获取
141
+ // TODO
142
+ // if (itemsHeight && itemsHeight?.[props.index]) return;
143
+
144
+ // 初始大小
145
+ debounceGetSize();
146
+
147
+ // TODO 只有可视范围之内才获取大小
148
+ observer = uni.createIntersectionObserver(instance, { thresholds: [0, 1] });
149
+ // 变大监听
150
+ observer
151
+ .relativeTo('.auto-sizer')
152
+ .observe('.anchor-height-add', debounceGetSize);
153
+ // 变小监听
154
+ observer
155
+ .relativeTo('.auto-sizer')
156
+ .observe('.anchor-height-sub', debounceGetSize);
157
+
158
+ // 变大监听
159
+ observer
160
+ .relativeTo('.auto-sizer')
161
+ .observe('.anchor-width-add', debounceGetSize);
162
+ // 变小监听
163
+ observer
164
+ .relativeTo('.auto-sizer')
165
+ .observe('.anchor-width-sub', debounceGetSize);
166
+ });
167
+
168
+ onUnmounted(() => {
169
+ // 销毁监听
170
+ observer.disconnect();
171
+ });
172
+ </script>
173
+
174
+ <style scoped>
175
+ .auto-sizer {
176
+ position: relative;
177
+ overflow: hidden;
178
+ width: min-content;
179
+ height: min-content;
180
+ }
181
+
182
+ .anchor-height-add,
183
+ .anchor-height-sub,
184
+ .anchor-width-add,
185
+ .anchor-width-sub {
186
+ position: absolute;
187
+ width: 1px;
188
+ height: 1px;
189
+
190
+ background-color: skyblue;
191
+ visibility: hidden;
192
+ }
193
+ </style>
@@ -0,0 +1,186 @@
1
+ <template>
2
+ <view
3
+ class="l-resize l-class"
4
+ :class="{ 'l-resize-target': hasNoDefaultSlot }"
5
+ :style="[styles, lStyle]"
6
+ >
7
+ <!--插槽需要脱离父容器文档流,防止父容器固宽固高,进而导致插槽大小被被父容器限制-->
8
+ <view
9
+ ref="l-resize__container"
10
+ class="l-resize__container l-container-class"
11
+ :style="[lStyle]"
12
+ >
13
+ <!--被监听的插槽-->
14
+ <slot></slot>
15
+ <!--监听插槽变大-->
16
+ <scroll-view
17
+ class="l-resize__wrapper"
18
+ :scroll-y="true"
19
+ :scroll-x="true"
20
+ :scroll-top="expandScrollTop"
21
+ :scroll-left="expandScrollLeft"
22
+ @scroll="onScrollHandler"
23
+ >
24
+ <view
25
+ class="l-resize__wrapper--placeholder"
26
+ style="height: 100000px; width: 100000px"
27
+ ></view>
28
+ </scroll-view>
29
+ <!--监听插槽变小-->
30
+ <scroll-view
31
+ class="l-resize__wrapper"
32
+ :scroll-y="true"
33
+ :scroll-x="true"
34
+ :scroll-top="shrinkScrollTop"
35
+ :scroll-left="shrinkScrollLeft"
36
+ @scroll="onScrollHandler"
37
+ >
38
+ <view
39
+ class="l-resize__wrapper--placeholder"
40
+ style="height: 250%; width: 250%"
41
+ ></view>
42
+ </scroll-view>
43
+ </view>
44
+ </view>
45
+ </template>
46
+ <script lang="ts" setup>
47
+ import { ref, onMounted, getCurrentInstance, computed } from 'vue';
48
+ import { getRect, addUnit } from './utils';
49
+
50
+ interface ResizeResult {
51
+ bottom?: number;
52
+ top?: number;
53
+ left?: number;
54
+ right?: number;
55
+ height?: number;
56
+ width?: number;
57
+ }
58
+
59
+ defineProps<{
60
+ lStyle?: any;
61
+ }>();
62
+
63
+ const emit = defineEmits(['resize']);
64
+
65
+ const context = getCurrentInstance();
66
+ const width = ref(0);
67
+ const hasNoDefaultSlot = ref(false);
68
+ const height = ref(0);
69
+ const expandScrollTop = ref(0);
70
+ const expandScrollLeft = ref(0);
71
+ const shrinkScrollTop = ref(0);
72
+ const shrinkScrollLeft = ref(0);
73
+
74
+ let scrollEventCount = 0;
75
+ let lastHeight = 0;
76
+ let lastWidth = 0;
77
+ const querySelector = '.l-resize__container';
78
+ let onScrollHandlerClone = () => {
79
+ getRect(querySelector, context).then((res: ResizeResult) => {
80
+ // 前两次滚动事件被触发,说明 created 的修改已渲染,通知用户代码当前容器大小
81
+ if (scrollEventCount++ === 0) {
82
+ resizeEvent(res);
83
+ }
84
+ // 滚动条拉到底部会触发两次多余的事件,屏蔽掉。
85
+ if (scrollEventCount < 3) return;
86
+ // 手动设置父容器高宽,防止父容器坍塌
87
+ // 滚动完,重新获取容器新的高度
88
+ const newHeight = res.height;
89
+ const newWidth = res.width;
90
+ // 立即填充父容器高宽
91
+ height.value = newHeight ?? 0;
92
+ width.value = newWidth ?? 0;
93
+ // 宽高都改变时,只需要触发一次 size 事件
94
+ const emitStack = [];
95
+ if (newHeight !== lastHeight) {
96
+ lastHeight = newHeight ?? 0;
97
+ emitStack.push(1);
98
+ }
99
+ if (newWidth !== lastWidth) {
100
+ lastWidth = newWidth ?? 0;
101
+ emitStack.push(1);
102
+ }
103
+ if (emitStack.length !== 0) {
104
+ resizeEvent(res);
105
+ }
106
+ // 滚动条拉到底部(如果使用 nextTick 效果更佳)
107
+ scrollToBottom({ width: lastWidth, height: lastHeight });
108
+ });
109
+ };
110
+ let onScrollHandler = () => {
111
+ if (onScrollHandlerClone) {
112
+ onScrollHandlerClone();
113
+ }
114
+ };
115
+ const scrollToBottom = (res: Record<string, number>) => {
116
+ expandScrollTop.value = 100000 + res.height;
117
+ shrinkScrollTop.value = 3 * height.value + res.height;
118
+ expandScrollLeft.value = 100000 + res.width;
119
+ shrinkScrollLeft.value = 3 * width.value + res.width;
120
+ };
121
+ const resizeEvent = (res: ResizeResult) => {
122
+ const result: any = {};
123
+ ['bottom', 'top', 'left', 'right', 'height', 'width'].forEach((propName) => {
124
+ result[propName] = res[propName as keyof ResizeResult];
125
+ });
126
+ emit('resize', result);
127
+ };
128
+ const styles = computed(() => {
129
+ if (context && context.slots?.default && (width.value || height.value)) {
130
+ return {
131
+ width: addUnit(width.value),
132
+ height: addUnit(height.value),
133
+ };
134
+ }
135
+ return {};
136
+ });
137
+ onMounted(() => {
138
+ // const options = { context };
139
+ hasNoDefaultSlot.value = !context?.slots.default;
140
+ getRect(querySelector, context).then((res) => {
141
+ // 闭包记录容器高度
142
+ lastHeight = res.height ?? 0;
143
+ lastWidth = res.width ?? 0;
144
+ // 立即填充父容器高宽
145
+ width.value = lastWidth;
146
+ height.value = lastHeight;
147
+
148
+ scrollToBottom({ width: lastWidth, height: lastHeight });
149
+ });
150
+ });
151
+ </script>
152
+ <style lang="scss" scoped>
153
+ .l-resize {
154
+ &__container {
155
+ position: absolute;
156
+ min-width: 1px;
157
+ min-height: 1px;
158
+ }
159
+ &-target,
160
+ &__wrapper {
161
+ background-color: aqua;
162
+ position: absolute;
163
+ top: 0;
164
+ bottom: 0;
165
+ left: 0;
166
+ right: 0;
167
+ z-index: -9999;
168
+ overflow: hidden;
169
+
170
+ visibility: hidden;
171
+ }
172
+ &-target {
173
+ .l-resize__container {
174
+ width: 100%;
175
+ height: 100%;
176
+ }
177
+ }
178
+ &__wrapper {
179
+ &--placeholder {
180
+ transition: 0s;
181
+
182
+ animation: none;
183
+ }
184
+ }
185
+ }
186
+ </style>
@@ -0,0 +1,49 @@
1
+ import type { ComponentInternalInstance, ComponentPublicInstance } from 'vue';
2
+
3
+ export function getRect(
4
+ selector: string,
5
+ context: ComponentInternalInstance | null | any,
6
+ node: boolean = false,
7
+ ) {
8
+ // 之前是个对象,现在改成实例,防止旧版会报错
9
+ if (context == null) {
10
+ return Promise.reject('context is null');
11
+ }
12
+ if (context?.context) {
13
+ context = context.context;
14
+ }
15
+
16
+ return new Promise<UniNamespace.NodeInfo>((resolve, reject) => {
17
+ const dom = uni.createSelectorQuery().in(context).select(selector);
18
+ const result: any = (rect: UniNamespace.NodeInfo) => {
19
+ if (rect) {
20
+ resolve(rect);
21
+ } else {
22
+ reject('no rect');
23
+ }
24
+ };
25
+
26
+ if (!node) {
27
+ dom.boundingClientRect(result).exec();
28
+ } else {
29
+ dom
30
+ .fields(
31
+ {
32
+ node: true,
33
+ size: true,
34
+ rect: true,
35
+ },
36
+ result,
37
+ )
38
+ .exec();
39
+ }
40
+ });
41
+ }
42
+
43
+ export function addUnit(value?: string | number): string {
44
+ if (!value) {
45
+ return '0px';
46
+ }
47
+
48
+ return typeof value === 'number' ? `${value}px` : value;
49
+ }
@@ -12,26 +12,29 @@
12
12
  :key="item.value"
13
13
  :active="currentConsoleType === item.value"
14
14
  @click="emit('choose', item.value)"
15
- >{{ item.label }}</Tag
16
15
  >
16
+ {{ item.label }}
17
+ </Tag>
17
18
  </view>
18
- <scroll-view
19
- class="console-list"
20
- scroll-y
21
- scroll-with-animation="true"
22
- :scroll-into-view="scrollIntoView"
23
- >
24
- <ConsoleItem
25
- v-for="(item, index) in consoleList"
26
- :consoleItem="item"
27
- :key="index"
28
- :sourceFileServers="sourceFileServers"
29
- :mode="mode"
30
- :useDevSource="useDevSource"
31
- :id="`dev-console-${index}`"
32
- />
33
- <Empty v-if="!consoleList || consoleList.length === 0" />
34
- </scroll-view>
19
+
20
+ <VirtualListPro :data="consoleList" :pageSize="10" className="console-list">
21
+ <template v-slot="{ list, start }">
22
+ <AutoSize
23
+ v-for="(item, index) in list"
24
+ :index="start + index"
25
+ :key="start + index"
26
+ >
27
+ <ConsoleItem
28
+ :consoleItem="item"
29
+ :sourceFileServers="sourceFileServers"
30
+ :mode="mode"
31
+ :useDevSource="useDevSource"
32
+ :id="`dev-console-${index}`"
33
+ />
34
+ </AutoSize>
35
+ <Empty v-if="!consoleList || consoleList.length === 0" />
36
+ </template>
37
+ </VirtualListPro>
35
38
  <RunJSInput @run="emit('run', $event)" />
36
39
  </view>
37
40
  </template>
@@ -44,6 +47,9 @@ import RunJSInput from './RunJSInput.vue';
44
47
  import type { DevTool } from '../../type';
45
48
  import { ref, watch } from 'vue';
46
49
  import { debounce } from '../../utils';
50
+ import VirtualListPro from '../VirtualListPro/index.vue';
51
+ import AutoSize from '../VirtualListPro/AutoSize.vue';
52
+
47
53
  type ConsoleType = 'all' | 'log' | 'info' | 'warn' | 'error' | 'clear';
48
54
  const props = defineProps<{
49
55
  consoleList: DevTool.ConsoleItem[];
@@ -93,7 +99,6 @@ const scrollIntoView = ref('');
93
99
 
94
100
  const debounceWatch = debounce(() => {
95
101
  scrollIntoView.value = `dev-console-${props.consoleList.length - 1}`;
96
- console.log('props.consoleList: ', props.consoleList);
97
102
  }, 200);
98
103
 
99
104
  watch(() => props.consoleList, debounceWatch);
@@ -105,7 +110,6 @@ watch(() => props.consoleList, debounceWatch);
105
110
  }
106
111
  .console-list {
107
112
  height: calc(100% - 32px - 32px);
108
- overflow: auto;
109
113
  }
110
114
  .console-control {
111
115
  display: flex;
@@ -319,7 +319,11 @@ const listenPostMessage = (data: DevTool.WindowData) => {
319
319
  })
320
320
  .filter((item) => {
321
321
  return (
322
- item.args?.some((arg) => arg?.includes?.(searchConsole.value)) ||
322
+ item.args?.some(
323
+ (arg) =>
324
+ arg.type === 'string' &&
325
+ arg.value?.includes?.(searchConsole.value),
326
+ ) ||
323
327
  item.position.includes(searchConsole.value) ||
324
328
  item?.stack?.includes(searchConsole.value)
325
329
  );
@@ -329,7 +333,15 @@ const listenPostMessage = (data: DevTool.WindowData) => {
329
333
  ...item,
330
334
  position: hightLight(item.position, searchConsole.value),
331
335
  stack: hightLight(item.stack, searchConsole.value),
332
- args: item.args.map((arg) => hightLight(arg, searchConsole.value)),
336
+ args: item.args.map((arg) => {
337
+ if (arg.type === 'string') {
338
+ return {
339
+ type: 'string',
340
+ value: hightLight(arg.value, searchConsole.value),
341
+ };
342
+ }
343
+ return arg;
344
+ }),
333
345
  };
334
346
  });
335
347
 
@@ -556,7 +568,9 @@ function onSearchConsole(value: string) {
556
568
  consoleList.value = backup.consoleList
557
569
  .filter((item) => {
558
570
  return (
559
- item.args.some((arg) => arg.includes(value)) ||
571
+ item.args.some(
572
+ (arg) => arg.type === 'string' && arg.value.includes(value),
573
+ ) ||
560
574
  item.position.includes(value) ||
561
575
  item?.stack?.includes(value)
562
576
  );
@@ -566,7 +580,15 @@ function onSearchConsole(value: string) {
566
580
  ...item,
567
581
  position: hightLight(item.position, searchConsole.value),
568
582
  stack: hightLight(item.stack, searchConsole.value),
569
- args: item.args.map((arg) => hightLight(arg, value)),
583
+ args: item.args.map((arg) => {
584
+ if (arg.type === 'string') {
585
+ return {
586
+ type: 'string',
587
+ value: hightLight(arg.value, value),
588
+ };
589
+ }
590
+ return arg;
591
+ }),
570
592
  };
571
593
  });
572
594
  }
@@ -16,51 +16,55 @@
16
16
  : style
17
17
  "
18
18
  >
19
- <VirtualList
19
+ <VirtualListPro
20
20
  v-if="autoVirtual"
21
21
  :height="state.height"
22
- :itemHeight="itemHeight"
22
+ :pageSize="pageSize"
23
23
  :data="flatData"
24
24
  >
25
- <template v-slot="{ list }">
26
- <TreeNode
27
- v-for="item in list"
28
- :key="item.id"
29
- :data="data"
30
- :rootPath="rootPath"
31
- :indent="indent"
32
- :node="item"
33
- :collapsed="!!state.hiddenPaths[item.path]"
34
- :theme="theme"
35
- :showDoubleQuotes="showDoubleQuotes"
36
- :showLength="showLength"
37
- :checked="selectedPaths.includes(item.path)"
38
- :selectableType="selectableType"
39
- :showLine="showLine"
40
- :showLineNumber="showLineNumber"
41
- :showSelectController="showSelectController"
42
- :selectOnClickNode="selectOnClickNode"
43
- :nodeSelectable="nodeSelectable"
44
- :highlightSelectedNode="highlightSelectedNode"
45
- :editable="editable"
46
- :editableTrigger="editableTrigger"
47
- :showIcon="showIcon"
48
- :showKeyValueSpace="showKeyValueSpace"
49
- :showNodeActions="showNodeActions"
50
- @nodeClick="handleNodeClick"
51
- @nodeMouseover="handleNodeMouseover"
52
- @bracketsClick="handleBracketsClick"
53
- @iconClick="handleIconClick"
54
- @selectedChange="handleSelectedChange"
55
- @valueChange="handleValueChange"
56
- :style="
57
- itemHeight && itemHeight !== 20
58
- ? { lineHeight: `${itemHeight}px` }
59
- : {}
60
- "
61
- />
25
+ <template v-slot="{ list, start }">
26
+ <AutoSize
27
+ v-for="(item, index) in list"
28
+ :index="start + index"
29
+ :key="start + index"
30
+ >
31
+ <TreeNode
32
+ :data="data"
33
+ :rootPath="rootPath"
34
+ :indent="indent"
35
+ :node="item"
36
+ :collapsed="!!state.hiddenPaths[item.path]"
37
+ :theme="theme"
38
+ :showDoubleQuotes="showDoubleQuotes"
39
+ :showLength="showLength"
40
+ :checked="selectedPaths.includes(item.path)"
41
+ :selectableType="selectableType"
42
+ :showLine="showLine"
43
+ :showLineNumber="showLineNumber"
44
+ :showSelectController="showSelectController"
45
+ :selectOnClickNode="selectOnClickNode"
46
+ :nodeSelectable="nodeSelectable"
47
+ :highlightSelectedNode="highlightSelectedNode"
48
+ :editable="editable"
49
+ :editableTrigger="editableTrigger"
50
+ :showIcon="showIcon"
51
+ :showKeyValueSpace="showKeyValueSpace"
52
+ :showNodeActions="showNodeActions"
53
+ @nodeClick="handleNodeClick"
54
+ @nodeMouseover="handleNodeMouseover"
55
+ @bracketsClick="handleBracketsClick"
56
+ @iconClick="handleIconClick"
57
+ @selectedChange="handleSelectedChange"
58
+ @valueChange="handleValueChange"
59
+ :style="
60
+ itemHeight && itemHeight !== 20
61
+ ? { lineHeight: `${itemHeight}px` }
62
+ : {}
63
+ "
64
+ />
65
+ </AutoSize>
62
66
  </template>
63
- </VirtualList>
67
+ </VirtualListPro>
64
68
  <template v-else>
65
69
  <TreeNode
66
70
  v-for="item in state?.visibleData"
@@ -115,7 +119,8 @@ import {
115
119
  } from 'vue';
116
120
  import TreeNode from './components/TreeNode/index.vue';
117
121
 
118
- import VirtualList from '../VirtualList/index.vue';
122
+ import VirtualListPro from '../VirtualListPro/index.vue';
123
+ import AutoSize from '../VirtualListPro/AutoSize.vue';
119
124
 
120
125
  import { emitError, jsonFlatten, cloneDeep, isFunction } from './utils';
121
126
 
@@ -162,6 +167,10 @@ const emits = defineEmits<Emits>();
162
167
 
163
168
  const originFlatData = computed(() => jsonFlatten(props.data, props.rootPath));
164
169
 
170
+ const pageSize = computed(() => {
171
+ return Math.ceil(props.height / props.itemHeight) + 5;
172
+ });
173
+
165
174
  // 初始化折叠路径
166
175
  const initHiddenPaths = (
167
176
  deep: number,
@@ -17,14 +17,19 @@
17
17
  {{ item.label }}
18
18
  </Tag>
19
19
  </view>
20
- <view class="network-list">
21
- <NetworkItem
22
- v-for="item in networkList"
23
- :network="item"
24
- :key="item.index"
25
- />
26
- <Empty v-if="!networkList || networkList.length === 0" />
27
- </view>
20
+
21
+ <VirtualListPro :data="networkList" :pageSize="10" className="network-list">
22
+ <template v-slot="{ list, start }">
23
+ <AutoSize
24
+ v-for="(item, index) in list"
25
+ :index="start + index"
26
+ :key="start + index"
27
+ >
28
+ <NetworkItem :network="item" />
29
+ </AutoSize>
30
+ <Empty v-if="!networkList || networkList.length === 0" />
31
+ </template>
32
+ </VirtualListPro>
28
33
  </view>
29
34
  </template>
30
35
  <script lang="ts" setup>
@@ -33,6 +38,9 @@ import NetworkItem from './NetworkItem.vue';
33
38
  import Empty from '../Empty/index.vue';
34
39
  import FilterInput from '../FilterInput/index.vue';
35
40
  import type { DevTool } from '../../type';
41
+ import VirtualListPro from '../VirtualListPro/index.vue';
42
+ import AutoSize from '../VirtualListPro/AutoSize.vue';
43
+
36
44
  defineProps<{
37
45
  currentNetworkType: string;
38
46
  networkList: DevTool.NetworkItem[];
@@ -93,7 +101,6 @@ function onChoose(type: string) {
93
101
  }
94
102
  .network-list {
95
103
  height: calc(100% - 32px);
96
- overflow: auto;
97
104
  }
98
105
  .network-control {
99
106
  display: flex;
@@ -10,25 +10,34 @@
10
10
  />
11
11
  <Tag mode="clear" @click="onRefresh">刷新</Tag>
12
12
  </view>
13
- <view class="route-list">
14
- <view class="route-item" v-for="item in routeList" :key="item.path">
15
- <view class="route-item-name">
16
- <view v-html="item.name" />
17
- <Tag v-if="item.type" mode="info">{{ item.type }}</Tag>
18
- <Tag mode="warn" v-if="item.index === 4 || item.index === 3">
19
- 当前页
20
- </Tag>
21
- <Tag mode="primary" v-if="item.index === 4 || item.index === 2">
22
- 启动页
23
- </Tag>
24
- </view>
25
- <view class="route-item-path">
26
- <view v-html="item.path" />
27
- <Tag mode="main" @click="onGoTo(item)">跳转</Tag>
28
- </view>
29
- </view>
30
- <Empty v-if="!routeList || routeList.length === 0" text="暂无路由信息" />
31
- </view>
13
+
14
+ <VirtualListPro :data="routeList" :pageSize="10" className="route-list">
15
+ <template v-slot="{ list, start }">
16
+ <AutoSize
17
+ v-for="(item, index) in list"
18
+ :index="start + index"
19
+ :key="start + index"
20
+ >
21
+ <view class="route-item">
22
+ <view class="route-item-name">
23
+ <view v-html="item.name" />
24
+ <Tag v-if="item.type" mode="info">{{ item.type }}</Tag>
25
+ <Tag mode="warn" v-if="item.index === 4 || item.index === 3">
26
+ 当前页
27
+ </Tag>
28
+ <Tag mode="primary" v-if="item.index === 4 || item.index === 2">
29
+ 启动页
30
+ </Tag>
31
+ </view>
32
+ <view class="route-item-path">
33
+ <view v-html="item.path" />
34
+ <Tag mode="main" @click="onGoTo(item)">跳转</Tag>
35
+ </view>
36
+ </view>
37
+ </AutoSize>
38
+ <Empty v-if="!routeList || routeList.length === 0" />
39
+ </template>
40
+ </VirtualListPro>
32
41
  </view>
33
42
  </template>
34
43
  <script lang="ts" setup>
@@ -36,6 +45,9 @@ import Tag from '../Tag/index.vue';
36
45
  import Empty from '../Empty/index.vue';
37
46
  import FilterInput from '../FilterInput/index.vue';
38
47
  import type { DevTool } from '../../type';
48
+ import VirtualListPro from '../VirtualListPro/index.vue';
49
+ import AutoSize from '../VirtualListPro/AutoSize.vue';
50
+
39
51
  defineProps<{ routeList: DevTool.Page[]; modelValue?: string }>();
40
52
  const emit = defineEmits<{
41
53
  (e: 'goTo', params: DevTool.Page): void;
@@ -17,10 +17,18 @@
17
17
  {{ item.label }}
18
18
  </Tag>
19
19
  </view>
20
- <view class="upload-list">
21
- <UploadItem v-for="item in uploadList" :upload="item" :key="item.index" />
22
- <Empty v-if="!uploadList || uploadList.length === 0" />
23
- </view>
20
+ <VirtualListPro :data="uploadList" :pageSize="10" className="upload-list">
21
+ <template v-slot="{ list, start }">
22
+ <AutoSize
23
+ v-for="(item, index) in list"
24
+ :index="start + index"
25
+ :key="start + index"
26
+ >
27
+ <UploadItem :upload="item" />
28
+ </AutoSize>
29
+ <Empty v-if="!uploadList || uploadList.length === 0" />
30
+ </template>
31
+ </VirtualListPro>
24
32
  </view>
25
33
  </template>
26
34
  <script lang="ts" setup>
@@ -29,6 +37,9 @@ import UploadItem from './UploadItem.vue';
29
37
  import Empty from '../Empty/index.vue';
30
38
  import FilterInput from '../FilterInput/index.vue';
31
39
  import type { DevTool } from '../../type';
40
+ import VirtualListPro from '../VirtualListPro/index.vue';
41
+ import AutoSize from '../VirtualListPro/AutoSize.vue';
42
+
32
43
  defineProps<{
33
44
  currentUploadType: string;
34
45
  uploadList: DevTool.UploadItem[];
@@ -79,7 +90,6 @@ function onChoose(type: string) {
79
90
  }
80
91
  .upload-list {
81
92
  height: calc(100% - 32px);
82
- overflow: auto;
83
93
  }
84
94
  .upload-control {
85
95
  display: flex;
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <view :class="className">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+ <script lang="ts" setup>
7
+ import { getCurrentInstance, onMounted, inject, nextTick } from 'vue';
8
+ import { uniqueId } from '../../utils';
9
+
10
+ const props = defineProps<{
11
+ index: number;
12
+ }>();
13
+
14
+ const className = uniqueId('virtual-');
15
+
16
+ const instance = getCurrentInstance();
17
+
18
+ const query = uni.createSelectorQuery().in(instance);
19
+
20
+ const onSizeChange =
21
+ inject<(index: number, height: number) => void>('onSizeChange');
22
+
23
+ const itemsHeight = inject<number[]>('itemsHeight');
24
+
25
+ onMounted(() => {
26
+ // TODO 可能会存在异步高度的问题
27
+ // TODO image 加载完成之后获取高度
28
+
29
+ if (itemsHeight?.[props.index]) return;
30
+
31
+ nextTick(() => {
32
+ query
33
+ .select('.' + className)
34
+ .boundingClientRect((rect) => {
35
+ if (Array.isArray(rect)) return;
36
+ onSizeChange?.(props.index, rect.height ?? 0);
37
+ })
38
+ .exec();
39
+ });
40
+ });
41
+ </script>
42
+
43
+ <style scoped></style>
@@ -0,0 +1,169 @@
1
+ <template>
2
+ <scroll-view
3
+ :lower-threshold="preLodeHeight"
4
+ :class="['virtual-list', className]"
5
+ scroll-y
6
+ @scroll="onScroll"
7
+ @scrolltolower="onScrollToLower"
8
+ >
9
+ <!-- 阈值判断 -->
10
+ <view :style="{ height: `${state.currentHeight}px` }"></view>
11
+ <view>
12
+ <!-- 将可视数据传入到slot -->
13
+ <slot
14
+ :list="state.visitableData"
15
+ :current="state.current"
16
+ :start="(state.current - 1) * props.pageSize"
17
+ ></slot>
18
+ </view>
19
+ </scroll-view>
20
+ </template>
21
+ <script lang="ts" setup>
22
+ import { reactive, watch, onBeforeMount, provide } from 'vue';
23
+
24
+ const props = withDefaults(
25
+ defineProps<{
26
+ /** 渲染数据 */
27
+ data: any[];
28
+ /** 虚拟列表高度 */
29
+ height?: number;
30
+ /**
31
+ * 将对传入的data进行分页
32
+ * 如何获取pageSize?
33
+ * 虚拟列表高度 / 预估每条的最小高度 +
34
+ */
35
+ pageSize?: number;
36
+ /** 预加载高度 */
37
+ preLodeHeight?: number;
38
+ /** 类名 */
39
+ className?: string;
40
+ }>(),
41
+ {
42
+ height: 400,
43
+ pageSize: 10,
44
+ preLodeHeight: 50,
45
+ },
46
+ );
47
+
48
+ const state = reactive<{
49
+ /** 加载次数 */
50
+ current: number;
51
+ visitableData: any[];
52
+ currentHeight: number;
53
+ height: number;
54
+ }>({
55
+ height: 0,
56
+ current: 1,
57
+ visitableData: [],
58
+ currentHeight: 0,
59
+ });
60
+ /**
61
+ * 每一项的高度
62
+ */
63
+ const itemsHeight: number[] = [];
64
+
65
+ provide('itemsHeight', itemsHeight);
66
+
67
+ onBeforeMount(() => {
68
+ // 初始渲染数据
69
+ state.visitableData = props.data.slice(0, props.pageSize * 2);
70
+ });
71
+
72
+ // 数据更新时重置
73
+ watch(
74
+ () => [props.data, props.pageSize],
75
+ () => {
76
+ state.visitableData = props.data.slice(0, props.pageSize * 2);
77
+ },
78
+ );
79
+
80
+ /** 向上滚动和向下滚动 */
81
+ function updateVisitableData(direction: 'up' | 'down') {
82
+ let tempList = [...state.visitableData];
83
+
84
+ if (direction === 'down') {
85
+ // 将最前面的内容进行隐藏
86
+ tempList.splice(0, props.pageSize);
87
+
88
+ // 处理下一页内容
89
+ const start = props.pageSize * state.current;
90
+ let end = props.pageSize * (state.current + 1);
91
+
92
+ if (start >= props.data.length) {
93
+ return;
94
+ }
95
+
96
+ if (end > props.data.length) {
97
+ end = props.data.length;
98
+ }
99
+
100
+ const newData = props.data.slice(start, end);
101
+
102
+ tempList.push(...newData);
103
+ } else {
104
+ // 将最末尾的部分进行隐藏
105
+ const delCount =
106
+ tempList.length - props.pageSize > 0
107
+ ? props.pageSize
108
+ : tempList.length - props.pageSize;
109
+
110
+ tempList.splice(props.pageSize, delCount);
111
+
112
+ // 处理上一页内容
113
+ let start = props.pageSize * (state.current - 1);
114
+
115
+ const end = props.pageSize * state.current;
116
+
117
+ if (end < 0) return;
118
+
119
+ if (start < 0) {
120
+ start = 0;
121
+ }
122
+ const newData = props.data.slice(start, end);
123
+ tempList.unshift(...newData);
124
+ }
125
+ state.visitableData = tempList;
126
+ }
127
+
128
+ /** 计算合并的高度 */
129
+ function updateCurrentHeight() {
130
+ const total = itemsHeight
131
+ .slice(0, state.current * props.pageSize)
132
+ ?.reduce((acc, cur) => acc + cur, 0);
133
+
134
+ state.currentHeight = total;
135
+ }
136
+
137
+ /** 滚动到底部 */
138
+ function onScrollToLower() {
139
+ if ((state.current + 1) * props.pageSize < props.data.length) {
140
+ state.current++;
141
+
142
+ updateVisitableData('down');
143
+
144
+ updateCurrentHeight();
145
+ }
146
+ }
147
+
148
+ /** 滚动监听,是否小于合并高度 */
149
+ function onScroll(e: { detail: { scrollTop: number } }) {
150
+ if (
151
+ state.current > 0 &&
152
+ state.currentHeight > 0 &&
153
+ e.detail.scrollTop - props.preLodeHeight < state.currentHeight
154
+ ) {
155
+ state.current--;
156
+
157
+ updateVisitableData('up');
158
+
159
+ updateCurrentHeight();
160
+ }
161
+ }
162
+
163
+ /** 暴露给子组件获取子组件容器的高度 */
164
+ function onSizeChange(index: number, height: number) {
165
+ itemsHeight[index] = height;
166
+ }
167
+ provide('onSizeChange', onSizeChange);
168
+ </script>
169
+ <style scoped></style>
@@ -0,0 +1,40 @@
1
+ # VirtualListAutoHeight
2
+
3
+ 自适应高度虚拟列表
4
+
5
+ ## 核心
6
+
7
+ - 初始加载两页数据
8
+ - 初始获取当前加载的渲染列表的数据 ,得到第一页高度
9
+ - scroll-view
10
+ - 滚动到底部(顶部)current + 1
11
+ - createIntersectionObserver
12
+ - 辅助反向监听节点是否出现再可视范围之内
13
+ - current - 1
14
+ - 数据分页之后确定每页高度,采用合并高度支撑滚动条
15
+ - 合并高度计算逻辑: 第一页高度 = 第一页高度
16
+ - 后续页高度 = 前面所有高度 + 当前页高度.
17
+
18
+
19
+ 一页两条
20
+
21
+ pageSize = 2
22
+ current = 2
23
+ visibleDate = [3,4,5,6]
24
+
25
+ visibleDate.splice(1*2, visibleDate.length - 1*2)
26
+
27
+ const start = cur
28
+
29
+
30
+ 1
31
+ 2
32
+
33
+ 3
34
+ 4
35
+
36
+ 5
37
+ 6
38
+
39
+ current = 1
40
+ visibleDate = [1,2,3,4]
@@ -17,10 +17,19 @@
17
17
  {{ item.label }}
18
18
  </Tag>
19
19
  </view>
20
- <view class="websocket-list">
21
- <WebSocketItem v-for="ws in wsList" :key="ws.url" :ws="ws" />
22
- <Empty v-if="!wsList || wsList.length === 0" />
23
- </view>
20
+
21
+ <VirtualListPro :data="wsList" :pageSize="10" className="websocket-list">
22
+ <template v-slot="{ list, start }">
23
+ <AutoSize
24
+ v-for="(item, index) in list"
25
+ :index="start + index"
26
+ :key="start + index"
27
+ >
28
+ <WebSocketItem :ws="item" />
29
+ </AutoSize>
30
+ <Empty v-if="!wsList || wsList.length === 0" />
31
+ </template>
32
+ </VirtualListPro>
24
33
  </view>
25
34
  </template>
26
35
  <script lang="ts" setup>
@@ -29,6 +38,9 @@ import Tag from '../Tag/index.vue';
29
38
  import Empty from '../Empty/index.vue';
30
39
  import FilterInput from '../FilterInput/index.vue';
31
40
  import type { DevTool } from '../../type';
41
+ import VirtualListPro from '../VirtualListPro/index.vue';
42
+ import AutoSize from '../VirtualListPro/AutoSize.vue';
43
+
32
44
  defineProps<{
33
45
  wsList: DevTool.WS[];
34
46
  currentWebSocketType: string;
@@ -1,36 +1,36 @@
1
- "use strict";const C=require("path"),I=require("fs"),u=require("../utils/index.js");function _(a){const d=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(a){for(const l in a)if(l!=="default"){const e=Object.getOwnPropertyDescriptor(a,l);Object.defineProperty(d,l,e.get?e:{enumerable:!0,get:()=>a[l]})}}return d.default=a,Object.freeze(d)}const $=_(C),S=_(I),h=/<script[^>]*>([\s\S]*?)<\/script>/,g={isReady:!1,urls:[]};function D({pages:a,sourceFileServers:d,...l}){return{name:"vite-uni-dev-tool",enforce:"pre",configureServer(e){var p;e.middlewares.use((s,n,t)=>{const{originalUrl:r}=s;if(l.useDevSource&&(r!=null&&r.includes("__dev_sourcefile__"))){const i=r.replace("/__dev_sourcefile__","");try{const c=e.config.root,m=$.join(c,i),o=S.readFileSync(m,"utf-8");n.setHeader("Content-Type",u.getContentType(m)),n.end(o)}catch{t()}}else t()}),(p=e.httpServer)==null||p.once("listening",()=>{var t;const s=(t=e.httpServer)==null?void 0:t.address(),n=u.getLocalIPs();if(s&&!Array.isArray(s)&&typeof s!="string"){const r=n.map(i=>`http://${i}:${s.port}/__dev_sourcefile__`);g.isReady=!0,g.urls=r,console.warn(`
2
- ⚡️ vite-uni-dev-tool source file server running at:
3
- ${n.map(i=>`➜ Source File Network: http://${i}:${s==null?void 0:s.port}/__dev_sourcefile__`).join(`
4
- `)}
5
- `)}})},transform(e,p){var s;if(p.endsWith("/src/main.ts"))try{const n=e.split(`
6
- `);let t=[...n];const r=u.findInsertionIndex(t,c=>c.trim().startsWith("import")||c.trim().startsWith("export"));r!==-1&&t.splice(r,0,"import DevTool from 'vite-uni-dev-tool/dev/components/DevTool/index.vue';");const i=u.findInsertionIndex(t,c=>c.includes(".mount(")||c.includes("createApp("));if(i!==-1&&t.splice(i+1,0," app.component('DevTool', DevTool);"),t.length!==n.length)return{code:t.join(`
7
- `),map:null}}catch(n){return console.error("[DevTool] 转换 main 文件时出错:",n),{code:e,map:null}}if(p.endsWith("/src/App.vue")){const n=e.match(h);if(n&&n[1]){const t=n[1].trim(),r=u.hasImportCurrentInstance(t),i=u.hasImportOnLaunch(t),c=r?"":"import { getCurrentInstance } from 'vue'",m=i?"":"import { onLaunch } from '@dcloudio/uni-app'",o=`
8
- import { initDevTool, console } from 'vite-uni-dev-tool/dev/core';
9
- import pagesJson from './pages.json';
10
- ${t}
11
- onLaunch(() => {
12
- const vue3instance = getCurrentInstance();
13
- initDevTool({
14
- pagesJson,
15
- vue3instance,
16
- mode: import.meta.env.MODE,
17
- sourceFileServers: [
18
- ${[...g.urls??[],...d??[]].map(v=>`'${v}'`)}
19
- ],
20
- useDevSource: ${l.useDevSource},
21
- ...${JSON.stringify(l)},
22
- });
23
- });`;return{code:e.replace(h,`
24
- <script lang="ts" setup>
25
- ${c}
26
- ${m}
27
- ${o}
28
- <\/script>`),map:null}}return{code:e,map:null}}if(p.endsWith(".vue")){const n=e.includes("<template>"),t=a.pages.some(o=>p.includes(o.path)),r=(s=a.subPackages)==null?void 0:s.some(o=>o.pages.some(f=>p.includes(`${o.root}/${f.path}`))),i=["<DevTool />"],c=u.hasImportConsole(e),m=u.hasUseConsole(e);if(!c&&m&&!p.endsWith("/src/App.vue")){const o=e.match(h);if(o&&o[1]){const v=`
29
- import { console } from 'vite-uni-dev-tool/dev/core';
30
- ${o[1]}
31
- `;e=e.replace(h,`
32
- <script lang="ts" setup>
33
- ${v}
34
- <\/script>`)}}if((t||r)&&n){const o=u.getTemplateContent(e);let f=e;if(o){const v=`${o}
35
- ${i.join(`
36
- `)}`;f=e.replace(o,v)}return{code:f,map:null}}}return{code:e,map:null}}}}module.exports=D;
1
+ "use strict";const C=require("path"),I=require("fs"),u=require("../utils/index.js");function _(a){const v=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(a){for(const l in a)if(l!=="default"){const e=Object.getOwnPropertyDescriptor(a,l);Object.defineProperty(v,l,e.get?e:{enumerable:!0,get:()=>a[l]})}}return v.default=a,Object.freeze(v)}const $=_(C),S=_(I),h=/<script[^>]*>([\s\S]*?)<\/script>/,g={isReady:!1,urls:[]};function D({pages:a,sourceFileServers:v,...l}){return{name:"vite-uni-dev-tool",enforce:"pre",configureServer(e){var p;e.middlewares.use((s,n,t)=>{const{originalUrl:r}=s;if(l.useDevSource&&(r!=null&&r.includes("__dev_sourcefile__"))){const i=r.replace("/__dev_sourcefile__","");try{const c=e.config.root,m=$.join(c,i),o=S.readFileSync(m,"utf-8");n.setHeader("Content-Type",u.getContentType(m)),n.end(o)}catch{t()}}else t()}),(p=e.httpServer)==null||p.once("listening",()=>{var t;const s=(t=e.httpServer)==null?void 0:t.address(),n=u.getLocalIPs();if(s&&!Array.isArray(s)&&typeof s!="string"){const r=n.map(i=>`http://${i}:${s.port}/__dev_sourcefile__`);g.isReady=!0,g.urls=r,console.warn(`
2
+ ⚡️ vite-uni-dev-tool source file server running at:
3
+ ${n.map(i=>`➜ Source File Network: http://${i}:${s==null?void 0:s.port}/__dev_sourcefile__`).join(`
4
+ `)}
5
+ `)}})},transform(e,p){var s;if(p.endsWith("/src/main.ts"))try{const n=e.split(`
6
+ `);let t=[...n];const r=u.findInsertionIndex(t,c=>c.trim().startsWith("import")||c.trim().startsWith("export"));r!==-1&&t.splice(r,0,"import DevTool from 'vite-uni-dev-tool/dev/components/DevTool/index.vue';");const i=u.findInsertionIndex(t,c=>c.includes(".mount(")||c.includes("createApp("));if(i!==-1&&t.splice(i+1,0," app.component('DevTool', DevTool);"),t.length!==n.length)return{code:t.join(`
7
+ `),map:null}}catch(n){return console.error("[DevTool] 转换 main 文件时出错:",n),{code:e,map:null}}if(p.endsWith("/src/App.vue")){const n=e.match(h);if(n&&n[1]){const t=n[1].trim(),r=u.hasImportCurrentInstance(t),i=u.hasImportOnLaunch(t),c=r?"":"import { getCurrentInstance } from 'vue'",m=i?"":"import { onLaunch } from '@dcloudio/uni-app'",o=`
8
+ import { initDevTool, console } from 'vite-uni-dev-tool/dev/core';
9
+ import pagesJson from './pages.json';
10
+ ${t}
11
+ onLaunch(() => {
12
+ const vue3instance = getCurrentInstance();
13
+ initDevTool({
14
+ pagesJson,
15
+ vue3instance,
16
+ mode: import.meta.env.MODE,
17
+ sourceFileServers: [
18
+ ${[...g.urls??[],...v??[]].map(d=>`'${d}'`)}
19
+ ],
20
+ useDevSource: ${l.useDevSource},
21
+ ...${JSON.stringify(l)},
22
+ });
23
+ });`;return{code:e.replace(h,`
24
+ <script lang="ts" setup>
25
+ ${c}
26
+ ${m}
27
+ ${o}
28
+ <\/script>`),map:null}}return{code:e,map:null}}if(p.endsWith(".vue")){const n=e.includes("<template>"),t=a.pages.some(o=>p.includes(o.path)),r=(s=a.subPackages)==null?void 0:s.some(o=>o.pages.some(f=>p.includes(`${o.root}/${f.path}`))),i=["<DevTool />"],c=u.hasImportConsole(e),m=u.hasUseConsole(e);if(!c&&m&&!p.endsWith("/src/App.vue")){const o=e.match(h);if(o&&o[1]){const d=`
29
+ import { console } from 'vite-uni-dev-tool/dev/core';
30
+ ${o[1]}
31
+ `;e=e.replace(h,`
32
+ <script lang="ts" setup>
33
+ ${d}
34
+ <\/script>`)}}if((t||r)&&n){const o=u.getTemplateContent(e);let f=e;if(o){const d=`<view>${o}
35
+ ${i.join(`
36
+ `)}</view>`;f=e.replace(o,d)}return{code:f,map:null}}}return{code:e,map:null}}}}module.exports=D;
@@ -1,7 +1,7 @@
1
- "use strict";const d=require("path"),m=require("fs"),p=require("../utils/index.js");function f(s){const u=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const e in s)if(e!=="default"){const r=Object.getOwnPropertyDescriptor(s,e);Object.defineProperty(u,e,r.get?r:{enumerable:!0,get:()=>s[e]})}}return u.default=s,Object.freeze(u)}const g=f(d),h=f(m);function y({pages:s,components:u}){return{name:"uni-global-components",enforce:"pre",configureServer(e){var r;e.middlewares.use((t,c,i)=>{const{originalUrl:n}=t;if(n!=null&&n.includes("__dev_sourcefile__")){const o=n.replace("/__dev_sourcefile__","");try{const a=e.config.root,l=g.join(a,o),_=h.readFileSync(l,"utf-8");c.setHeader("Content-Type",p.getContentType(l)),c.end(_)}catch{i()}}else i()}),(r=e.httpServer)==null||r.once("listening",()=>{var i;const t=(i=e.httpServer)==null?void 0:i.address(),c=p.getLocalIPs();t&&!Array.isArray(t)&&typeof t!="string"&&(c.map(n=>`http://${n}:${t.port}/__dev_sourcefile__`),console.warn(`
2
- ⚡️ vite-uni-dev-tool source file server running at:
3
- ${c.map(n=>`➜ Source File Network: http://${n}:${t==null?void 0:t.port}/__dev_sourcefile__`).join(`
4
- `)}
5
- `))})},transform(e,r){var t;if(r.endsWith(".vue")){const c=e.includes("<template>"),i=s.pages.some(o=>r.includes(o.path)),n=(t=s.subPackages)==null?void 0:t.some(o=>o.pages.some(a=>r.includes(`${o.root}/${a.path}`)));if((i||n)&&c){const o=p.getTemplateContent(e);if(o){const a=`${o}
6
- ${u.join(`
7
- `)}`;return{code:e.replace(o,a),map:null}}}}return{code:e,map:null}}}}module.exports=y;
1
+ "use strict";const d=require("path"),m=require("fs"),p=require("../utils/index.js");function f(s){const u=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const e in s)if(e!=="default"){const r=Object.getOwnPropertyDescriptor(s,e);Object.defineProperty(u,e,r.get?r:{enumerable:!0,get:()=>s[e]})}}return u.default=s,Object.freeze(u)}const g=f(d),h=f(m);function v({pages:s,components:u}){return{name:"uni-global-components",enforce:"pre",configureServer(e){var r;e.middlewares.use((t,c,i)=>{const{originalUrl:n}=t;if(n!=null&&n.includes("__dev_sourcefile__")){const o=n.replace("/__dev_sourcefile__","");try{const a=e.config.root,l=g.join(a,o),_=h.readFileSync(l,"utf-8");c.setHeader("Content-Type",p.getContentType(l)),c.end(_)}catch{i()}}else i()}),(r=e.httpServer)==null||r.once("listening",()=>{var i;const t=(i=e.httpServer)==null?void 0:i.address(),c=p.getLocalIPs();t&&!Array.isArray(t)&&typeof t!="string"&&(c.map(n=>`http://${n}:${t.port}/__dev_sourcefile__`),console.warn(`
2
+ ⚡️ vite-uni-dev-tool source file server running at:
3
+ ${c.map(n=>`➜ Source File Network: http://${n}:${t==null?void 0:t.port}/__dev_sourcefile__`).join(`
4
+ `)}
5
+ `))})},transform(e,r){var t;if(r.endsWith(".vue")){const c=e.includes("<template>"),i=s.pages.some(o=>r.includes(o.path)),n=(t=s.subPackages)==null?void 0:t.some(o=>o.pages.some(a=>r.includes(`${o.root}/${a.path}`)));if((i||n)&&c){const o=p.getTemplateContent(e);if(o){const a=`<view>${o}
6
+ ${u.join(`
7
+ `)}</view>`;return{code:e.replace(o,a),map:null}}}}return{code:e,map:null}}}}module.exports=v;
@@ -0,0 +1,15 @@
1
+ export function chunk<T>(array: T[], size = 1): T[][] {
2
+ if (!Array.isArray(array) || size <= 0) {
3
+ return [];
4
+ }
5
+
6
+ const result: T[][] = [];
7
+ let index = 0;
8
+
9
+ while (index < array.length) {
10
+ result.push(array.slice(index, index + size));
11
+ index += size;
12
+ }
13
+
14
+ return result;
15
+ }
@@ -36,8 +36,11 @@ export {
36
36
  extractRowAndCol,
37
37
  parseStock,
38
38
  isMockWX,
39
+ uniqueId,
39
40
  } from './string';
40
41
 
41
42
  export { getWifiIp, getDeviceMac, getLanIp, getMicroAppIp } from './ip';
42
43
 
43
44
  export { isAndroid, isH5, isWX } from './platform';
45
+
46
+ export { chunk } from './array';
@@ -119,3 +119,15 @@ const mockWXReg = /https?:\/\/usr/;
119
119
  export function isMockWX(stack: string) {
120
120
  return mockWXReg.test(stack);
121
121
  }
122
+
123
+ export function uniqueId(
124
+ pre: string = '',
125
+ length = 16,
126
+ chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
127
+ ): string {
128
+ let result = '';
129
+ for (let i = 0; i < length; i++) {
130
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
131
+ }
132
+ return pre + result;
133
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-uni-dev-tool",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "vite-uni-dev-tool, debug, uni-app, 一处编写,到处调试",
5
5
  "keywords": [
6
6
  "vite",