Files
jinshen-website/app/pages/search.vue
R2m1liA aa26731731 fix: 补全骨架屏渲染机制
- 补全各个页面的骨架屏渲染机制修改
2025-12-19 13:26:26 +08:00

136 lines
2.8 KiB
Vue

<template>
<div class="search-page">
<search-header v-model="keyword" />
<div class="search-state">
<el-skeleton
:loading="loading"
animated
:throttle="{ leading: 500, trailing: 500 }"
>
<template #template>
<el-skeleton-item
v-for="i in 10"
:key="i"
variant="rect"
class="skeleton-item"
/>
</template>
<template #default>
<search-tabs v-if="hasResults" :search-items="searchItems" />
<div v-else>
<el-empty
:description="
route.query.query
? $t('search.no-results', { query: route.query?.query })
: $t('search.no-query')
"
/>
</div>
</template>
</el-skeleton>
</div>
</div>
</template>
<script setup lang="ts">
// i18n相关
const { t } = useI18n();
const { getDirectusLocale } = useLocalizations();
const locale = getDirectusLocale();
// 路由相关
const route = useRoute();
// 搜索相关
const keyword = ref('');
if (typeof route.query.query === 'string' && route.query.query.trim()) {
keyword.value = route.query.query;
}
const {
data: searchItems,
pending: loading,
error,
refresh,
} = useAsyncData(`meilisearch-${keyword.value}-${locale}`, async () => {
try {
const data = await $fetch(`/api/search?query=${keyword.value}`, {
headers: { 'x-locale': locale },
});
return data;
} catch (error) {
logger.error('Error fetching search results: ', error);
throw error;
}
});
const hasResults = computed(() => searchItems.value.length > 0);
watch(
() => route.query.query,
async (newQuery) => {
if (typeof newQuery === 'string' && newQuery.trim()) {
keyword.value = newQuery;
await refresh();
}
}
);
watch(
() => locale,
async () => {
await refresh();
}
);
watch(error, (value) => {
if (value) {
logger.error('数据获取失败: ', value);
}
});
onMounted(() => {
if (typeof route.query.query === 'string' && route.query.query.trim())
keyword.value = route.query.query;
});
useHead(() => ({
title: t('search.head-title'),
}));
</script>
<style scoped>
.search-page {
margin: 0 auto;
max-width: 960px;
padding: 2.5rem 1.5rem 3rem;
min-height: 70vh;
}
.search-state {
display: flex;
justify-content: center;
}
.skeleton-item {
height: 80px;
margin-bottom: 1rem;
}
@media (max-width: 640px) {
.search-page {
padding: 2rem 1rem;
}
.search-bar {
flex-direction: column;
align-items: stretch;
}
.search-input {
width: 100%;
}
}
</style>