add: article view

View for blog articles
This commit is contained in:
webfussel 2025-06-12 14:11:52 +02:00
parent 579491f216
commit 4104477533
15 changed files with 121 additions and 33 deletions

View file

@ -0,0 +1,20 @@
.BlogArticle {
display: flex;
flex-direction: column;
gap: 2rem;
& .image {
width: 100%;
height: 450px;
border-radius: 1rem;
overflow: hidden;
background: #000;
& img {
width: 100%;
height: 100%;
opacity: .8;
object-fit: cover;
}
}
}

View file

@ -0,0 +1,4 @@
.Excerpt {
font-size: 1.5rem;
font-style: italic;
}

View file

@ -1,6 +1,7 @@
.BlogOverview { .BlogOverview {
& .category-list { & .category-list {
display: flex; display: flex;
justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 1rem; gap: 1rem;
list-style: none; list-style: none;

View file

@ -7,10 +7,11 @@
<header> <header>
<span class="chip"><BlogCategory :name="category"/></span> <span class="chip"><BlogCategory :name="category"/></span>
<h2>{{ title }}</h2> <h2>{{ title }}</h2>
<small>{{ description }}</small>
</header> </header>
<main> <main>
<ContentRenderer :value="excerpt" /> <p>
{{ generatePlainText(excerpt.value).at(0)?.text ?? '' }}
</p>
</main> </main>
<footer> <footer>
<BlogAuthor :name="author.name" :image="author.image" :date="date"/> <BlogAuthor :name="author.name" :image="author.image" :date="date"/>
@ -25,13 +26,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Category } from './types' import type { Category } from './types'
import type { MinimalNode } from '@nuxt/content'
type Props = { type Props = {
title: string title: string
description: string description: string
image: string image: string
date: string date: string
excerpt : { type: string, children?: any } excerpt: { type: string, value: MinimalNode[], children?: any }
link: string link: string
tags: string[] tags: string[]
category: Category category: Category

View file

@ -0,0 +1,9 @@
<template>
<p class="Excerpt">
<slot/>
</p>
</template>
<script setup lang="ts">
</script>

View file

@ -1,9 +1,33 @@
<template> <template>
<section class="BlogArticle content">
<NuxtLink class="text inline-flex-row" to="/blog">
<Icon name="ph:arrow-left-duotone"/>
Zurück zur Übersicht
</NuxtLink>
<p v-if="!article">
Sorry bro, aber der Artikel existiert einfach nicht.
</p>
<div v-else>
<header>
<div class="image z-2">
<img :src="article.image" alt="Artikelbild" aria-hidden="true"/>
</div>
<h1 class="margin-top">{{ article.title }}</h1>
<h2>{{ article.description }}</h2>
</header>
<div class="flex-col gap-default">
<ContentRenderer v-if="article" :value="article" :style="{ display: 'contents' }"/>
</div>
</div>
</section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const route = useRoute()
const { data: article } = await useAsyncData('article', () => queryCollection('blog').path(route.path).first())
</script> </script>
<style scoped> <style scoped>

View file

@ -26,7 +26,16 @@ import type { Category } from '../../components/Blog/types'
const route = useRoute() const route = useRoute()
const { data: articles } = await useAsyncData('articles', () => queryCollection('blog').order('date', 'DESC').all()) const simpleDate = (date: Date) => {
date.setDate(date.getDate() + 1)
return `${date.getFullYear()}-${`${date.getMonth() + 1}`.padStart(2, '0')}-${date.getDate()}`
}
const { data: articles } = await useAsyncData('articles', () => queryCollection('blog')
.where('date', '<', simpleDate(new Date()))
.order('date', 'DESC')
.all(),
)
const firstTen = computed(() => { const firstTen = computed(() => {
if (route.query.category) { if (route.query.category) {
@ -49,13 +58,13 @@ const allCategoriesAndCount = computed(() => {
const makeBlogCard = (article: BlogCollectionItem) => ({ const makeBlogCard = (article: BlogCollectionItem) => ({
title: article.title, title: article.title,
description: article.description, description: article.description,
image: article.meta.image as string, image: article.thumbnail as string,
date: article.date as string, date: article.date as string,
excerpt: article.excerpt, excerpt: article.excerpt as any,
link: article.path, link: article.path,
tags: article.meta.tags as string[], tags: article.tags as string[],
category: article.meta.category as Category, category: article.category as Category,
author: article.meta.author as { name: string, image: string }, author: article.author as { name: string, image: string },
}) })
useHead({ useHead({

View file

@ -7,6 +7,14 @@ export default defineContentConfig({
source: 'blog/*.md', source: 'blog/*.md',
schema: z.object({ schema: z.object({
date: z.string(), date: z.string(),
image: z.string(),
thumbnail: z.string(),
category: z.string(),
tags: z.array(z.string()),
author: z.object({
name: z.string(),
image: z.string(),
}),
excerpt: z.object({ excerpt: z.object({
type: z.string(), type: z.string(),
children: z.any(), children: z.any(),

View file

@ -1,9 +1,9 @@
--- ---
image: 'https://picsum.photos/600/250?random=4' image: 'https://picsum.photos/1920/450?random=4'
thumbnail: 'https://picsum.photos/750/250?random=4'
title: 'Blogfussel - für mehr Fussel im Blog' title: 'Blogfussel - für mehr Fussel im Blog'
description: 'Und nochmal...'
navigation: true navigation: true
date: 2025-07-01 date: 2025-06-11
category: 'story' category: 'story'
tags: [ 'start', 'story' ] tags: [ 'start', 'story' ]
author: author:
@ -11,8 +11,9 @@ author:
image: '/img/og.webp' image: '/img/og.webp'
--- ---
Warum ich mich dazu entschlossen habe jetzt doch wieder mit dem Bloggen anzufangen? Nun ja, das hat alles angefangen, als man mich einfach so auf ::blog-excerpt
Instagram gesperrt hat. Warum ich mich dazu entschlossen habe jetzt doch wieder mit dem Bloggen anzufangen? Da muss ich etwas ausholen.
::
<!-- more --> <!-- more -->

View file

@ -3,7 +3,7 @@ image: 'https://picsum.photos/600/250?random=3'
title: 'Post 1' title: 'Post 1'
description: 'Blablabla' description: 'Blablabla'
navigation: true navigation: true
date: 2025-06-20 date: 2025-05-20
category: 'tutorial' category: 'tutorial'
tags: [ 'test', 'wasd' ] tags: [ 'test', 'wasd' ]
author: author:

View file

@ -3,7 +3,7 @@ image: 'https://picsum.photos/600/250?random=2'
title: 'Post 2' title: 'Post 2'
description: 'Blablabla' description: 'Blablabla'
navigation: true navigation: true
date: 2025-06-16 date: 2025-05-16
category: 'snippet' category: 'snippet'
tags: [ 'test', 'asdf' ] tags: [ 'test', 'asdf' ]
author: author:

View file

@ -3,7 +3,7 @@ image: 'https://picsum.photos/600/250?random=1'
title: 'Post 3' title: 'Post 3'
description: 'Blablabla' description: 'Blablabla'
navigation: true navigation: true
date: 2025-06-25 date: 2025-05-25
category: 'freelancing' category: 'freelancing'
tags: [ 'test', '123' ] tags: [ 'test', '123' ]
author: author:

View file

@ -3,7 +3,7 @@ image: 'https://picsum.photos/600/250?random=5'
title: 'Post 4' title: 'Post 4'
description: 'Blablabla' description: 'Blablabla'
navigation: true navigation: true
date: 2025-06-25 date: 2025-05-25
category: 'news' category: 'news'
tags: [ 'test', '123' ] tags: [ 'test', '123' ]
author: author:

View file

@ -46,6 +46,8 @@ export default defineNuxtConfig({
'~/assets/css/blog/card.css', '~/assets/css/blog/card.css',
'~/assets/css/blog/author.css', '~/assets/css/blog/author.css',
'~/assets/css/blog/overview.css', '~/assets/css/blog/overview.css',
'~/assets/css/blog/article.css',
'~/assets/css/blog/excerpt.css',
], ],
postcss: { postcss: {

View file

@ -17,11 +17,19 @@ const makeItem = (article: BlogCollectionItem, base: string) => `
</item> </item>
` `
const simpleDate = (date: Date) => {
date.setDate(date.getDate() + 1)
return `${date.getFullYear()}-${`${date.getMonth() + 1}`.padStart(2, '0')}-${date.getDate()}`
}
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const baseUrl = getRequestURL(event) const baseUrl = getRequestURL(event)
const blogUrl = `${baseUrl.protocol}//${baseUrl.host}` const blogUrl = `${baseUrl.protocol}//${baseUrl.host}`
const articles = (await queryCollection(event, 'blog').order('date', 'DESC').all()) const articles = await queryCollection(event, 'blog')
.where('date', '<', simpleDate(new Date()))
.order('date', 'DESC')
.all()
const xmlItems = articles.map(article => makeItem(article, blogUrl)).join('') const xmlItems = articles.map(article => makeItem(article, blogUrl)).join('')