vite-uni-dev-tool 0.0.6 → 0.0.8
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 +16 -0
- package/dev/components/Code/index.vue +227 -0
- package/dev/components/Connection/index.vue +11 -21
- package/dev/components/ConsoleList/Code.vue +1 -1
- package/dev/components/ConsoleList/ConsoleItem.vue +38 -12
- package/dev/components/ConsoleList/RunJSInput.vue +59 -0
- package/dev/components/ConsoleList/index.vue +24 -8
- package/dev/components/DevTool/index.vue +10 -9
- package/dev/components/DevToolButton/index.vue +10 -10
- package/dev/components/DevToolTitle/index.vue +21 -0
- package/dev/components/DevToolWindow/index.vue +62 -5
- package/dev/components/FilterInput/index.vue +1 -1
- package/dev/components/JsonPretty/components/CheckController/index.vue +22 -5
- package/dev/components/JsonPretty/components/TreeNode/index.vue +16 -15
- package/dev/components/JsonPretty/index.vue +77 -75
- package/dev/components/JsonPretty/type.ts +2 -0
- package/dev/components/NetworkList/NetworkDetail.vue +1 -1
- package/dev/components/RunJS/index.vue +128 -0
- package/dev/components/SettingList/index.vue +10 -20
- package/dev/components/UniEvent/UniEventItem.vue +124 -0
- package/dev/components/UniEvent/index.vue +94 -0
- package/dev/components/UploadList/UploadDetail.vue +1 -1
- package/dev/components/VirtualList/index.vue +112 -0
- package/dev/components/WebSocket/WebSocketList.vue +1 -1
- package/dev/const.ts +101 -28
- package/dev/core.ts +12 -3
- package/dev/devConsole/index.ts +21 -4
- package/dev/devEvent/index.ts +122 -8
- package/dev/devEventBus/index.ts +94 -0
- package/dev/devIntercept/index.ts +61 -18
- package/dev/devRunJS/index.ts +170 -0
- package/dev/devStore/index.ts +83 -0
- package/dev/index.d.ts +3 -2
- package/dev/index.js +1 -1
- package/dev/plugins/uniDevTool/uniDevTool.d.ts +2 -1
- package/dev/plugins/uniDevTool/uniDevTool.d.ts.map +1 -1
- package/dev/plugins/uniDevTool/uniDevTool.js +36 -38
- package/dev/plugins/uniGlobalComponents/uniGlobalComponents.d.ts +2 -1
- package/dev/plugins/uniGlobalComponents/uniGlobalComponents.d.ts.map +1 -1
- package/dev/plugins/uniGlobalComponents/uniGlobalComponents.js +7 -9
- package/dev/plugins/utils/index.d.ts +10 -2
- package/dev/plugins/utils/index.d.ts.map +1 -1
- package/dev/plugins/utils/index.js +1 -1
- package/dev/type.ts +58 -1
- package/dev/utils/index.ts +10 -1
- package/dev/utils/language.ts +53 -0
- package/dev/utils/object.ts +64 -1
- package/dev/utils/string.ts +5 -5
- package/package.json +2 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="run-js">
|
|
3
|
+
<view class="run-js-control">
|
|
4
|
+
<FilterInput
|
|
5
|
+
:modelValue="modelValue"
|
|
6
|
+
placeholder="查询"
|
|
7
|
+
@search="emit('search', $event)"
|
|
8
|
+
@update:modelValue="emit('update:modelValue', $event)"
|
|
9
|
+
/>
|
|
10
|
+
<Tag mode="clear" @click="emit('clear')">清空</Tag>
|
|
11
|
+
</view>
|
|
12
|
+
<view class="run-js-list">
|
|
13
|
+
<view class="run-js-item" v-for="item in runJSList">
|
|
14
|
+
<view :class="`run-js-item-icon run-js-item-${item.mode}`"></view>
|
|
15
|
+
<view class="run-js-item-duration" v-if="item.mode === 'output'">
|
|
16
|
+
{{ item.duration }}ms
|
|
17
|
+
</view>
|
|
18
|
+
<view
|
|
19
|
+
class="run-js-item-content"
|
|
20
|
+
v-html="item.mode === 'input' ? item.code : item.result"
|
|
21
|
+
>
|
|
22
|
+
</view>
|
|
23
|
+
</view>
|
|
24
|
+
<view class="run-js-item">
|
|
25
|
+
<view class="run-js-item-icon run-js-item-input"> </view>
|
|
26
|
+
|
|
27
|
+
<input v-model="code" class="run-js-input" @confirm="onConfirm" />
|
|
28
|
+
</view>
|
|
29
|
+
</view>
|
|
30
|
+
</view>
|
|
31
|
+
</template>
|
|
32
|
+
<script lang="ts" setup>
|
|
33
|
+
import { ref } from 'vue';
|
|
34
|
+
import type { DevTool } from '../../type';
|
|
35
|
+
import FilterInput from '../FilterInput/index.vue';
|
|
36
|
+
import Tag from '../Tag/index.vue';
|
|
37
|
+
|
|
38
|
+
defineProps<{ runJSList: DevTool.RunJSItem[]; modelValue: string }>();
|
|
39
|
+
|
|
40
|
+
const emit = defineEmits<{
|
|
41
|
+
(e: 'search', value: string): void;
|
|
42
|
+
(e: 'update:modelValue', value: string): void;
|
|
43
|
+
(e: 'clear'): void;
|
|
44
|
+
(e: 'run', value: string): void;
|
|
45
|
+
}>();
|
|
46
|
+
const code = ref('');
|
|
47
|
+
function onConfirm() {
|
|
48
|
+
const value = code.value;
|
|
49
|
+
if (!value) return;
|
|
50
|
+
code.value = '';
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
emit('run', value);
|
|
53
|
+
}, 100);
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
<style scoped>
|
|
57
|
+
.run-js {
|
|
58
|
+
height: 100%;
|
|
59
|
+
font-size: var(--dev-tool-base-font-size);
|
|
60
|
+
}
|
|
61
|
+
.run-js-control {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
justify-content: space-between;
|
|
65
|
+
gap: 8px;
|
|
66
|
+
padding: 0 16px;
|
|
67
|
+
height: 32px;
|
|
68
|
+
border-bottom: 1px solid var(--dev-tool-border-color);
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.run-js-list {
|
|
73
|
+
height: calc(100% - 32px);
|
|
74
|
+
overflow: auto;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.run-js-item {
|
|
78
|
+
position: relative;
|
|
79
|
+
display: flex;
|
|
80
|
+
padding: 0 16px;
|
|
81
|
+
min-height: 28px;
|
|
82
|
+
border-bottom: 1px solid var(--dev-tool-border-color);
|
|
83
|
+
overflow: hidden;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.run-js-item-icon {
|
|
87
|
+
width: 8px;
|
|
88
|
+
height: 8px;
|
|
89
|
+
border: 1px solid #000;
|
|
90
|
+
border-bottom-color: transparent;
|
|
91
|
+
border-left-color: transparent;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.run-js-item-input {
|
|
96
|
+
margin: 10px 0 10px -4px;
|
|
97
|
+
transform: rotate(45deg);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.run-js-item-output {
|
|
101
|
+
margin: 10px -4px 10px 0px;
|
|
102
|
+
transform: rotate(-135deg);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.run-js-item-content {
|
|
106
|
+
margin-left: 16px;
|
|
107
|
+
min-height: 28px;
|
|
108
|
+
line-height: 28px;
|
|
109
|
+
white-space: pre-line;
|
|
110
|
+
word-break: break-all;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.run-js-input {
|
|
114
|
+
margin: 1px 0 1px 16px;
|
|
115
|
+
min-height: 26px;
|
|
116
|
+
width: 100%;
|
|
117
|
+
border: 1px solid transparent;
|
|
118
|
+
font-size: var(--dev-tool-base-font-size);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.run-js-item-duration {
|
|
122
|
+
position: absolute;
|
|
123
|
+
top: 2px;
|
|
124
|
+
right: 16px;
|
|
125
|
+
height: 24px;
|
|
126
|
+
line-height: 24px;
|
|
127
|
+
}
|
|
128
|
+
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view class="setting-content">
|
|
3
3
|
<view class="setting-item">
|
|
4
|
-
<
|
|
4
|
+
<DevToolTitle>DevTool</DevToolTitle>
|
|
5
5
|
<view class="setting-item-content">
|
|
6
6
|
<view class="setting-row">
|
|
7
7
|
<view>显示调试按钮:</view>
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
</view>
|
|
26
26
|
|
|
27
27
|
<view class="setting-tips">
|
|
28
|
-
点击后将会销毁调试器,销毁后不再接收调试信息,需要通过
|
|
29
|
-
|
|
28
|
+
点击后将会销毁调试器,销毁后不再接收调试信息,需要通过 createDevTool()
|
|
29
|
+
重新创建
|
|
30
30
|
</view>
|
|
31
31
|
<DButton class="setting-button" @click="onDestructionDevTool">
|
|
32
32
|
销毁调试器
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
</view>
|
|
45
45
|
<view class="setting-tips">
|
|
46
46
|
当前所有信息占用的存储空间,只会清除 Console, Network, Upload,
|
|
47
|
-
Websocket
|
|
47
|
+
Websocket, UniEvent
|
|
48
48
|
</view>
|
|
49
49
|
<DButton class="setting-button" @click="onClearCache">
|
|
50
50
|
清除缓存
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
</view>
|
|
53
53
|
</view>
|
|
54
54
|
<view class="setting-item">
|
|
55
|
-
<
|
|
55
|
+
<DevToolTitle>Log</DevToolTitle>
|
|
56
56
|
<view class="setting-item-content">
|
|
57
57
|
<view class="setting-row">
|
|
58
58
|
<view>导出当前日志信息:</view>
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
import { reactive } from 'vue';
|
|
122
122
|
import Checkbox from '../Checkbox/index.vue';
|
|
123
123
|
import DButton from '../Button/index.vue';
|
|
124
|
+
import DevToolTitle from '../DevToolTitle/index.vue';
|
|
124
125
|
const props = defineProps<{
|
|
125
126
|
devToolVisible?: boolean;
|
|
126
127
|
sizeFormat?: string;
|
|
@@ -204,30 +205,19 @@ function onClearCache() {
|
|
|
204
205
|
overflow: auto;
|
|
205
206
|
font-size: var(--dev-tool-base-font-size);
|
|
206
207
|
}
|
|
207
|
-
.setting-
|
|
208
|
+
.setting-item {
|
|
208
209
|
padding: 16px 16px 0 16px;
|
|
209
210
|
box-sizing: border-box;
|
|
210
211
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
align-items: center;
|
|
214
|
-
}
|
|
215
|
-
.setting-content .setting-item .setting-item-title::before {
|
|
216
|
-
content: '';
|
|
217
|
-
margin-right: 8px;
|
|
218
|
-
width: 2px;
|
|
219
|
-
height: 18px;
|
|
220
|
-
border-radius: 2px;
|
|
221
|
-
background-color: var(--dev-tool-main-color);
|
|
222
|
-
}
|
|
223
|
-
.setting-content .setting-item .setting-item-content .setting-row {
|
|
212
|
+
|
|
213
|
+
.setting-row {
|
|
224
214
|
display: flex;
|
|
225
215
|
align-items: center;
|
|
226
216
|
justify-content: space-between;
|
|
227
217
|
margin: 8px 0;
|
|
228
218
|
min-height: 18px;
|
|
229
219
|
}
|
|
230
|
-
.setting-
|
|
220
|
+
.setting-tips {
|
|
231
221
|
color: #616161;
|
|
232
222
|
font-size: var(--dev-tool-tips-font-size);
|
|
233
223
|
margin-bottom: 8px;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="uni-event-item">
|
|
3
|
+
<view class="uni-event-item-row"> {{ eventItem.eventName }} </view>
|
|
4
|
+
<view class="uni-event-item-row">
|
|
5
|
+
<Tag mode="info" v-if="eventItem.type == 'on'">
|
|
6
|
+
{{ eventItem.type }}
|
|
7
|
+
</Tag>
|
|
8
|
+
<Tag mode="warn" v-if="eventItem.type == 'once'">
|
|
9
|
+
{{ eventItem.type }}
|
|
10
|
+
</Tag>
|
|
11
|
+
<Tag mode="main" v-if="eventItem.type == 'emit'">
|
|
12
|
+
{{ eventItem.type }}
|
|
13
|
+
</Tag>
|
|
14
|
+
<Tag mode="error" v-if="eventItem.type == 'off'">
|
|
15
|
+
{{ eventItem.type }}
|
|
16
|
+
</Tag>
|
|
17
|
+
<view class="uni-event-item-right"> {{ eventItem.timer }}</view>
|
|
18
|
+
</view>
|
|
19
|
+
|
|
20
|
+
<view
|
|
21
|
+
:class="`uni-event-item-right ${isUseDevSource ? 'link' : ''}`"
|
|
22
|
+
@click="onStackClick"
|
|
23
|
+
>
|
|
24
|
+
{{ eventItem.stack }}
|
|
25
|
+
</view>
|
|
26
|
+
|
|
27
|
+
<Code
|
|
28
|
+
v-if="openCode && eventItem.stack"
|
|
29
|
+
:url="eventItem.stack"
|
|
30
|
+
:sourceFileServers="sourceFileServers"
|
|
31
|
+
:mode="mode"
|
|
32
|
+
@close="onCloseCode"
|
|
33
|
+
/>
|
|
34
|
+
</view>
|
|
35
|
+
</template>
|
|
36
|
+
<script lang="ts" setup>
|
|
37
|
+
import Tag from '../Tag/index.vue';
|
|
38
|
+
import Code from '../Code/index.vue';
|
|
39
|
+
import type { DevTool } from '../../type';
|
|
40
|
+
import { computed, ref } from 'vue';
|
|
41
|
+
import { isAndroid, isMockWX } from '../../utils';
|
|
42
|
+
|
|
43
|
+
const props = defineProps<{
|
|
44
|
+
eventItem: DevTool.EventItem;
|
|
45
|
+
mode?: string;
|
|
46
|
+
useDevSource?: boolean;
|
|
47
|
+
sourceFileServers?: string[];
|
|
48
|
+
}>();
|
|
49
|
+
|
|
50
|
+
const openCode = ref(false);
|
|
51
|
+
|
|
52
|
+
const isUseDevSource = computed(() => {
|
|
53
|
+
return (
|
|
54
|
+
!isMockWX(props?.eventItem?.stack ?? '') &&
|
|
55
|
+
props.mode === 'development' &&
|
|
56
|
+
props.useDevSource
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const isWXLink = computed(() => {
|
|
61
|
+
console.log('props?.eventItem: ', props?.eventItem);
|
|
62
|
+
console.log('props.mode: ', props.mode);
|
|
63
|
+
return (
|
|
64
|
+
isMockWX(props?.eventItem?.stack ?? '') || props.mode !== 'development'
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function onStackClick() {
|
|
69
|
+
if (isWXLink.value) {
|
|
70
|
+
uni.showToast({
|
|
71
|
+
icon: 'none',
|
|
72
|
+
title: '[DevTool] 请在小程序真机调试模式下查看源码',
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!isUseDevSource.value) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (isAndroid()) {
|
|
82
|
+
if (props.sourceFileServers && props.sourceFileServers.length > 0) {
|
|
83
|
+
openCode.value = true;
|
|
84
|
+
} else {
|
|
85
|
+
uni.showToast({
|
|
86
|
+
icon: 'none',
|
|
87
|
+
title: '[DevTool] sourceFileServers 配置异常',
|
|
88
|
+
});
|
|
89
|
+
uni?.__dev__console?.log('[DevTool] sourceFileServers 配置异常');
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
openCode.value = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function onCloseCode() {
|
|
97
|
+
openCode.value = false;
|
|
98
|
+
}
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<style scoped>
|
|
102
|
+
.uni-event-item {
|
|
103
|
+
padding: 16px;
|
|
104
|
+
border-bottom: 1px solid var(--dev-tool-border-color);
|
|
105
|
+
box-sizing: border-box;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.uni-event-item-row {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
justify-content: space-between;
|
|
112
|
+
height: 28px;
|
|
113
|
+
word-break: break-all;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.uni-event-item-right {
|
|
117
|
+
text-align: right;
|
|
118
|
+
word-break: break-all;
|
|
119
|
+
}
|
|
120
|
+
.link {
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
text-decoration: underline;
|
|
123
|
+
}
|
|
124
|
+
</style>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="uni-event-content">
|
|
3
|
+
<DevToolTitle style="padding: 0 16px">事件触发统计</DevToolTitle>
|
|
4
|
+
<view class="uni-event-statistics">
|
|
5
|
+
<view class="uni-event-statistics-item">
|
|
6
|
+
<view>on: </view>
|
|
7
|
+
<Tag mode="log" style="flex: 1; justify-content: center">
|
|
8
|
+
{{ eventCount?.on ?? 0 }}
|
|
9
|
+
</Tag>
|
|
10
|
+
</view>
|
|
11
|
+
<view class="uni-event-statistics-item">
|
|
12
|
+
<view>once: </view>
|
|
13
|
+
<Tag mode="warn" style="flex: 1; justify-content: center">
|
|
14
|
+
{{ eventCount?.once ?? 0 }}
|
|
15
|
+
</Tag>
|
|
16
|
+
</view>
|
|
17
|
+
<view class="uni-event-statistics-item">
|
|
18
|
+
<view>emit: </view>
|
|
19
|
+
<Tag mode="main" style="flex: 1; justify-content: center">
|
|
20
|
+
{{ eventCount?.emit ?? 0 }}
|
|
21
|
+
</Tag>
|
|
22
|
+
</view>
|
|
23
|
+
<view class="uni-event-statistics-item">
|
|
24
|
+
<view>off: </view>
|
|
25
|
+
<Tag mode="error" style="flex: 1; justify-content: center">
|
|
26
|
+
{{ eventCount?.off ?? 0 }}
|
|
27
|
+
</Tag>
|
|
28
|
+
</view>
|
|
29
|
+
</view>
|
|
30
|
+
<DevToolTitle style="padding: 0 16px"
|
|
31
|
+
>事件触发列表
|
|
32
|
+
|
|
33
|
+
<Tag mode="clear" style="margin-left: auto" @click="emit('clear')">
|
|
34
|
+
清空
|
|
35
|
+
</Tag>
|
|
36
|
+
</DevToolTitle>
|
|
37
|
+
<view class="uni-event-list">
|
|
38
|
+
<UniEventItem
|
|
39
|
+
v-for="item in eventList"
|
|
40
|
+
:eventItem="item"
|
|
41
|
+
:mode="mode"
|
|
42
|
+
:useDevSource="useDevSource"
|
|
43
|
+
:sourceFileServers="sourceFileServers"
|
|
44
|
+
/>
|
|
45
|
+
</view>
|
|
46
|
+
</view>
|
|
47
|
+
</template>
|
|
48
|
+
<script lang="ts" setup>
|
|
49
|
+
import DevToolTitle from '../DevToolTitle/index.vue';
|
|
50
|
+
import UniEventItem from './UniEventItem.vue';
|
|
51
|
+
import Tag from '../Tag/index.vue';
|
|
52
|
+
import type { DevTool } from '@/dev/type';
|
|
53
|
+
|
|
54
|
+
defineProps<{
|
|
55
|
+
eventList?: DevTool.EventItem[];
|
|
56
|
+
eventCount?: DevTool.EventCount;
|
|
57
|
+
useDevSource?: boolean;
|
|
58
|
+
mode?: string;
|
|
59
|
+
sourceFileServers?: string[];
|
|
60
|
+
}>();
|
|
61
|
+
|
|
62
|
+
const emit = defineEmits<{
|
|
63
|
+
(e: 'clear'): void;
|
|
64
|
+
}>();
|
|
65
|
+
</script>
|
|
66
|
+
<style scoped>
|
|
67
|
+
.uni-event-content {
|
|
68
|
+
height: 100%;
|
|
69
|
+
overflow: auto;
|
|
70
|
+
font-size: var(--dev-tool-base-font-size);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.uni-event-statistics {
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
justify-content: space-around;
|
|
77
|
+
padding: 0 16px;
|
|
78
|
+
height: 32px;
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
gap: 12px;
|
|
81
|
+
}
|
|
82
|
+
.uni-event-statistics-item {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
width: 25%;
|
|
86
|
+
}
|
|
87
|
+
.uni-event-statistics-item view:first-child {
|
|
88
|
+
margin-right: 8px;
|
|
89
|
+
}
|
|
90
|
+
.uni-event-list {
|
|
91
|
+
height: calc(100% - 32px * 3);
|
|
92
|
+
overflow: auto;
|
|
93
|
+
}
|
|
94
|
+
</style>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<scroll-view
|
|
3
|
+
v-if="autoVirtual"
|
|
4
|
+
scroll-y
|
|
5
|
+
class="virtual-list"
|
|
6
|
+
:style="{
|
|
7
|
+
height: `${props.height}px`,
|
|
8
|
+
}"
|
|
9
|
+
@scroll="onVirtualScroll"
|
|
10
|
+
>
|
|
11
|
+
<view
|
|
12
|
+
class="virtual-list-holder"
|
|
13
|
+
:style="{
|
|
14
|
+
height: `${props.data.length * props.itemHeight}px`,
|
|
15
|
+
}"
|
|
16
|
+
>
|
|
17
|
+
<view
|
|
18
|
+
class="virtual-list-holder-inner"
|
|
19
|
+
:style="{
|
|
20
|
+
transform: `translateY(${state.translateY}px)`,
|
|
21
|
+
}"
|
|
22
|
+
>
|
|
23
|
+
<slot :list="state.visibleData"></slot>
|
|
24
|
+
</view>
|
|
25
|
+
</view>
|
|
26
|
+
</scroll-view>
|
|
27
|
+
<view
|
|
28
|
+
v-else
|
|
29
|
+
class="normal-list"
|
|
30
|
+
:style="{
|
|
31
|
+
height: `${props.height}px`,
|
|
32
|
+
}"
|
|
33
|
+
>
|
|
34
|
+
<slot :list="state.visibleData"></slot>
|
|
35
|
+
</view>
|
|
36
|
+
</template>
|
|
37
|
+
<script lang="ts" setup>
|
|
38
|
+
import { computed, onMounted, reactive, watchEffect } from 'vue';
|
|
39
|
+
const props = defineProps<{
|
|
40
|
+
/** 虚拟列表高度 */
|
|
41
|
+
height: number;
|
|
42
|
+
/** 每一项高度 */
|
|
43
|
+
itemHeight: number;
|
|
44
|
+
/** 渲染的数据列表 */
|
|
45
|
+
data: any[];
|
|
46
|
+
/** 虚拟列表自动开启行数 */
|
|
47
|
+
autoVirtualRow?: number;
|
|
48
|
+
}>();
|
|
49
|
+
|
|
50
|
+
const state = reactive<{ translateY: number; visibleData: any[] }>({
|
|
51
|
+
translateY: 0,
|
|
52
|
+
visibleData: [],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const autoVirtual = computed(() => {
|
|
56
|
+
if (typeof props.autoVirtualRow === 'number') {
|
|
57
|
+
if (props.data.length > props.autoVirtualRow) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
function updateVisibleData(scrollTop: number = 0) {
|
|
66
|
+
const flatDataValue = props.data ?? [];
|
|
67
|
+
if (autoVirtual.value) {
|
|
68
|
+
const visibleCount = props.height / props.itemHeight;
|
|
69
|
+
const scrollCount = Math.floor(scrollTop / props.itemHeight);
|
|
70
|
+
let start =
|
|
71
|
+
scrollCount < 0
|
|
72
|
+
? 0
|
|
73
|
+
: scrollCount + visibleCount > flatDataValue.length
|
|
74
|
+
? flatDataValue.length - visibleCount
|
|
75
|
+
: scrollCount;
|
|
76
|
+
if (start < 0) {
|
|
77
|
+
start = 0;
|
|
78
|
+
}
|
|
79
|
+
const end = start + visibleCount;
|
|
80
|
+
state.translateY = start * props.itemHeight;
|
|
81
|
+
state.visibleData = state.visibleData = props.data.slice(start, end);
|
|
82
|
+
} else {
|
|
83
|
+
state.visibleData = flatDataValue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function onVirtualScroll(e: { detail: { scrollTop: number } }) {
|
|
88
|
+
updateVisibleData(e?.detail?.scrollTop ?? 0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
watchEffect(() => {
|
|
92
|
+
if (props.data) {
|
|
93
|
+
updateVisibleData();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
onMounted(() => {
|
|
98
|
+
updateVisibleData();
|
|
99
|
+
});
|
|
100
|
+
</script>
|
|
101
|
+
<style scoped>
|
|
102
|
+
.normal-list,
|
|
103
|
+
.virtual-list {
|
|
104
|
+
position: relative;
|
|
105
|
+
width: 100%;
|
|
106
|
+
overflow: auto;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.virtual-list-holder {
|
|
110
|
+
position: relative;
|
|
111
|
+
}
|
|
112
|
+
</style>
|