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 +8 -0
- package/dev/components/AutoSizer/index.vue +193 -0
- package/dev/components/AutoSizer/index1.vue +186 -0
- package/dev/components/AutoSizer/utils.ts +49 -0
- package/dev/components/ConsoleList/index.vue +24 -20
- package/dev/components/DevToolWindow/index.vue +26 -4
- package/dev/components/JsonPretty/index.vue +50 -41
- package/dev/components/NetworkList/index.vue +16 -9
- package/dev/components/RouteList/index.vue +31 -19
- package/dev/components/UploadList/index.vue +15 -5
- package/dev/components/VirtualListPro/AutoSize.vue +43 -0
- package/dev/components/VirtualListPro/index.vue +169 -0
- package/dev/components/VirtualListPro/readme.md +40 -0
- package/dev/components/WebSocket/index.vue +16 -4
- package/dev/plugins/uniDevTool/uniDevTool.js +36 -36
- package/dev/plugins/uniGlobalComponents/uniGlobalComponents.js +7 -7
- package/dev/utils/array.ts +15 -0
- package/dev/utils/index.ts +3 -0
- package/dev/utils/string.ts +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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(
|
|
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) =>
|
|
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(
|
|
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) =>
|
|
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
|
-
<
|
|
19
|
+
<VirtualListPro
|
|
20
20
|
v-if="autoVirtual"
|
|
21
21
|
:height="state.height"
|
|
22
|
-
:
|
|
22
|
+
:pageSize="pageSize"
|
|
23
23
|
:data="flatData"
|
|
24
24
|
>
|
|
25
|
-
<template v-slot="{ list }">
|
|
26
|
-
<
|
|
27
|
-
v-for="item in list"
|
|
28
|
-
:
|
|
29
|
-
:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
</
|
|
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
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
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
|
|
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??[],...
|
|
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
|
|
29
|
-
import { console } from 'vite-uni-dev-tool/dev/core';
|
|
30
|
-
${o[1]}
|
|
31
|
-
`;e=e.replace(h,`
|
|
32
|
-
<script lang="ts" setup>
|
|
33
|
-
${
|
|
34
|
-
<\/script>`)}}if((t||r)&&n){const o=u.getTemplateContent(e);let f=e;if(o){const
|
|
35
|
-
${i.join(`
|
|
36
|
-
`)}
|
|
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
|
|
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
|
|
6
|
-
${u.join(`
|
|
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 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
|
+
}
|
package/dev/utils/index.ts
CHANGED
|
@@ -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';
|
package/dev/utils/string.ts
CHANGED
|
@@ -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
|
+
}
|