mirror of
https://github.com/sendevia/website.git
synced 2026-03-05 23:32:45 +08:00
add: ArticleMasonry component
This commit is contained in:
88
.vitepress/theme/components/ArticleMasonry.vue
Normal file
88
.vitepress/theme/components/ArticleMasonry.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import { usePostStore, type PostData } from "../stores/posts";
|
||||
import { useGlobalData } from "../composables/useGlobalData";
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
|
||||
const postsStore = usePostStore();
|
||||
const articlesList = computed(() => postsStore.posts || []);
|
||||
const { theme } = useGlobalData();
|
||||
|
||||
// 定义断点配置:屏幕宽度 -> 列数
|
||||
const breakpoints = {
|
||||
1600: 3,
|
||||
1200: 3,
|
||||
840: 2,
|
||||
0: 1,
|
||||
};
|
||||
|
||||
const columnCount = ref(2);
|
||||
|
||||
// 根据屏幕宽度更新列数
|
||||
const updateColumnCount = () => {
|
||||
const width = window.innerWidth;
|
||||
const match = Object.keys(breakpoints)
|
||||
.map(Number)
|
||||
.sort((a, b) => b - a)
|
||||
.find((bp) => width > bp);
|
||||
|
||||
columnCount.value = match !== undefined ? breakpoints[match as keyof typeof breakpoints] : 1;
|
||||
};
|
||||
|
||||
// 将文章列表拆分成 N 个数组
|
||||
const masonryGroups = computed(() => {
|
||||
const groups: PostData[][] = Array.from({ length: columnCount.value }, () => []);
|
||||
|
||||
articlesList.value.forEach((item, index) => {
|
||||
const groupIndex = index % columnCount.value;
|
||||
groups[groupIndex].push(item);
|
||||
});
|
||||
|
||||
return groups;
|
||||
});
|
||||
|
||||
// 图片处理逻辑
|
||||
const getArticleImage = (item: PostData): string[] => {
|
||||
if (item.impression && item.impression.length > 0) {
|
||||
return item.impression;
|
||||
}
|
||||
|
||||
const themeValue = theme.value;
|
||||
if (themeValue?.defaultImpression) {
|
||||
return [themeValue.defaultImpression];
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
updateColumnCount();
|
||||
window.addEventListener("resize", updateColumnCount);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", updateColumnCount);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="articleMasonry">
|
||||
<div class="masonry-column" v-for="(column, index) in masonryGroups" :key="index">
|
||||
<MaterialCard
|
||||
v-for="item in column"
|
||||
class="feed"
|
||||
size="m"
|
||||
:key="item.id"
|
||||
:href="item.url"
|
||||
:title="item.title"
|
||||
:description="item.description"
|
||||
:date="item.date"
|
||||
:impression="getArticleImage(item)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "sass:meta";
|
||||
@include meta.load-css("../styles/components/ArticleMasonry");
|
||||
</style>
|
||||
@@ -3,6 +3,7 @@ import { createPinia } from "pinia";
|
||||
import Layout from "./layouts/Default.vue";
|
||||
|
||||
import AppBar from "./components/AppBar.vue";
|
||||
import ArticleMasonry from "./components/ArticleMasonry.vue";
|
||||
import Button from "./components/Button.vue";
|
||||
import Card from "./components/Card.vue";
|
||||
import Footer from "./components/Footer.vue";
|
||||
@@ -23,6 +24,7 @@ export default {
|
||||
app.use(pinia);
|
||||
|
||||
app.component("AppBar", AppBar);
|
||||
app.component("ArticleMasonry", ArticleMasonry);
|
||||
app.component("Footer", Footer);
|
||||
app.component("Header", Header);
|
||||
app.component("ImageViewer", ImageViewer);
|
||||
|
||||
24
.vitepress/theme/styles/components/ArticleMasonry.scss
Normal file
24
.vitepress/theme/styles/components/ArticleMasonry.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
#articleMasonry {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
|
||||
width: 100%;
|
||||
|
||||
.masonry-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
gap: 12px;
|
||||
|
||||
min-width: 0px;
|
||||
|
||||
.MaterialCard {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 840px) {
|
||||
}
|
||||
Reference in New Issue
Block a user