vite-uni-dev-tool 0.0.9 → 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 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
|
+
"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 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;
|
|
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
|
+
}
|