使用了AI重构项目,并完善了一部分后台问题

This commit is contained in:
2026-08-05 04:47:11 +08:00
parent 146b6b1e6c
commit ac740c24fd
35 changed files with 2876 additions and 980 deletions
+27 -8
View File
@@ -3,7 +3,7 @@
<div v-if="loading" class="site-grid site-grid--loading" aria-label="正在加载站点">
<span v-for="index in 6" :key="index" class="site-skeleton"></span>
</div>
<div v-else ref="swiperElement" class="swiper sites-swiper">
<div v-else-if="sites.length" ref="swiperElement" class="swiper sites-swiper">
<div class="swiper-wrapper">
<div v-for="(siteChunk, index) in chunkedSites" :key="index" class="swiper-slide">
<div class="site-grid">
@@ -11,8 +11,8 @@
v-for="site in siteChunk"
:key="site.id || site.url"
:href="site.url"
target="_blank"
rel="noopener noreferrer"
:target="config.openLinksInNewTab ? '_blank' : undefined"
:rel="config.openLinksInNewTab ? 'noopener noreferrer' : undefined"
class="site-box"
>
<i :class="site.icon" aria-hidden="true"></i>
@@ -23,6 +23,7 @@
</div>
<div ref="paginationElement" class="swiper-pagination"></div>
</div>
<div v-else class="site-empty"><i class="fas fa-link" aria-hidden="true"></i><span>暂无站点</span></div>
</section>
</template>
@@ -32,18 +33,20 @@ import Swiper from 'swiper/bundle'
import 'swiper/swiper-bundle.css'
import { getSites } from '../api'
import fallbackSites from '../config/site.json'
import { loadPublicConfig, publicConfig as config } from '../composables/usePublicConfig'
const sites = ref([])
const loading = ref(true)
const swiperElement = ref(null)
const paginationElement = ref(null)
const chunkedSites = computed(() => sites.value.reduce((chunks, site, index) => {
const chunkIndex = Math.floor(index / 6)
const chunkIndex = Math.floor(index / (Number(config.sitePageSize) || 6))
if (!chunks[chunkIndex]) chunks[chunkIndex] = []
chunks[chunkIndex].push(site)
return chunks
}, []))
let swiperInstance = null
let configChannel = null
const initializeSwiper = async () => {
await nextTick()
@@ -58,19 +61,33 @@ const initializeSwiper = async () => {
})
}
onMounted(async () => {
const loadSites = async () => {
loading.value = true
try {
const { data } = await getSites()
sites.value = Array.isArray(data) && data.length ? data : fallbackSites
const [{ data }] = await Promise.all([getSites(), loadPublicConfig()])
sites.value = Array.isArray(data) ? data : []
} catch {
sites.value = fallbackSites
} finally {
loading.value = false
initializeSwiper()
}
}
onMounted(async () => {
await loadSites()
if (window.BroadcastChannel) {
configChannel = new BroadcastChannel('config-update')
configChannel.onmessage = ({ data }) => {
if (data?.type === 'config-updated') loadSites()
}
}
})
onUnmounted(() => swiperInstance?.destroy(true, true))
onUnmounted(() => {
swiperInstance?.destroy(true, true)
configChannel?.close()
})
</script>
<style scoped lang="less">
@@ -90,6 +107,8 @@ onUnmounted(() => swiperInstance?.destroy(true, true))
.site-box i { flex: 0 0 auto; font-size: var(--icon-size); }
.site-box span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.site-grid--loading { padding: 10px; }
.site-empty { min-height: 150px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: var(--text-muted); font-size: 13px; }
.site-empty i { color: var(--hover-link-color); font-size: 22px; }
.site-skeleton { animation: pulse 1.2s ease-in-out infinite alternate; }
@keyframes pulse { to { opacity: 0.45; } }
:deep(.swiper-pagination-bullet) { background: var(--text-muted); transition: width 0.3s ease; }