3-3 实现一个搜索列表组件
题目要求
查询触发条件
搜索框敲击回车
点击“Search”按钮
查询中,显示“Loading”
查询无结果时,显示“查无结果”
滚动列表时,支持懒加载
<!-- page页面 -->
<template>
<div class="search-container">
<input type="text" class="input-inner" />
<button>Search</button>
</div>
<Scroll :data="data" #default="{ result }" @update-loading="updateLoading">
<li class="p-0 m-0" v-for="item of result" :key="item.title">
<h3>{{ item.title }}</h3>
<p>{{ item.content }}</p>
</li>
</Scroll>
<div class="loading-page" v-if="loading">
<Loading />
</div>
</template>
<script lang="ts">
import { defineComponent, shallowReactive, ref } from "vue";
import Scroll from "./scroll.vue";
import Loading from "../loading.vue";
export default defineComponent({
components: {
Scroll,
Loading,
},
setup() {
const loading = ref(false);
const useResponse = () => {
const data = shallowReactive({
status: 0,
total: 60,
list: [],
});
for (let i = 0; i < 60 / 2; i++) {
data.list.push(
...[
{
title: "加总价比还",
content:
"然矿存条而带除增克众文较。风便物离放布所布导受感建成构题。命调式或则收算几比每思育产业。新结位阶现论通众构规真亲天龙证。青还龙容认商具山主角由带段。运她般系准任面研那保列定候听三深名火。例节山极市身分有次品你观者验。",
},
{
title: "转亲去新算",
content:
"白最物两力日达例把老由置按除些接。将理关参将如物等务打场直消事王保参世。音快何论群部照观总门花量工。车不须单号表龙积太面存证。",
},
]
);
}
return data;
};
function updateLoading(val: boolean) {
loading.value = val;
}
return {
data: useResponse(),
loading,
updateLoading,
};
},
});
</script>
<style scoped>
.p-0 {
padding: 0;
}
.m-0 {
margin: 0;
}
.loading-page {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.9);
}
</style>
// 获取滚动条当前的位置
function getScrollTop() {
let scrollTop = 0
if (document.documentElement && document.documentElement.scrollTop) {
scrollTop = document.documentElement.scrollTop
} else if (document.body) {
scrollTop = document.body.scrollTop
}
return scrollTop
}
// 获取当前可视范围的高度
function getClientHeight() {
let clientHeight = 0
if (document.body.clientHeight && document.documentElement.clientHeight) {
clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight)
} else {
clientHeight = Math.max(document.body.clientHeight, document.documentElement.clientHeight)
}
return clientHeight
}
// 获取文档完整的高度
function getScrollHeight() {
return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight)
}
// 分割数组
function splitArray (list, size) {
let length = list.length;
if (length < 1 || size < 1) {
return [];
}
let index = 0;
let lindex = 0;
let result = new Array(Math.ceil(length / size));
while (index < length) {
result[lindex] = list.slice(index, (index += size));
lindex++;
}
return result;
};
// 节流
export {
getScrollTop,
getClientHeight,
getScrollHeight,
splitArray
}
<!-- 分页组件 -->
<script lang="ts">
import {
defineComponent,
h,
onMounted,
onUnmounted,
nextTick,
computed,
ref,
reactive,
VNode,
Ref,
} from "vue";
import {
getClientHeight,
getScrollHeight,
getScrollTop,
splitArray,
} from "./utlis";
export default defineComponent({
props: {
/**
* 数据源
*/
data: {
type: Object,
default: () => {},
},
/**
* 页面默认渲染
*/
pageSize: {
type: Number,
default: 10,
},
loading: {
type: Boolean,
default: false,
},
delay: {
type: Number,
default: 3000,
},
},
emits: ["updateLoading"],
setup({ data, pageSize, delay }, { slots, emit }) {
const result = reactive<any[]>([]);
let pageIndex = ref(0); // 页数
let slotsElement = ref<Ref<null | VNode[]>>(null);
let timeId = ref<NodeJS.Timeout | null>(null);
const list = computed(() => splitArray(data.list, pageSize));
init();
slotsElement.value = slots?.default({ result }) || [];
function handle() {
if (getScrollHeight() - (getClientHeight() + getScrollTop()) <= 0) {
++pageIndex.value;
if (pageIndex.value >= list.value.length) return;
list.value[pageIndex.value].forEach((item) => result.push(item));
emit("updateLoading", true);
clearTimeout(timeId.value);
timeId.value = setTimeout(() => {
emit("updateLoading", false);
slotsElement.value = slots?.default({ result }) || [];
}, delay);
}
}
function init() {
if (Array.isArray(list.value[pageIndex.value])) {
list.value[pageIndex.value].forEach((item) => result.push(item));
}
}
onMounted(() => {
nextTick(() => {
window.addEventListener("scroll", handle, false);
});
});
onUnmounted(() => {
window.removeEventListener("scroll", handle);
});
return () => {
return h("ul", { class: "list-none p-0 m-0" }, slotsElement.value);
};
},
});
</script>
<style scoped>
.list-none {
list-style: none;
overflow-anchor: none;
}
</style>

浙公网安备 33010602011771号