This commit is contained in:
2026-01-10 12:08:48 +08:00
parent 126a1463b8
commit 07b689fcd3
6 changed files with 45 additions and 18 deletions

View File

@@ -1,28 +1,35 @@
<script lang="ts" setup>
import { useState } from "#app";
interface Props {
path?: string;
title?: string;
date?: string;
description?: string;
image?: string;
image?: string | null;
alt?: string;
tags?: Array<string>;
categories?: Array<string>;
published?: boolean;
}
withDefaults(defineProps<Props>(), {
const props = withDefaults(defineProps<Props>(), {
path: "/",
title: "no-title",
date: "no-date",
description: "no-description",
image: getRandomFallbackImage(),
image: null,
alt: "no-alt",
ogImage: getRandomFallbackImage(),
tags: () => [],
categories: () => [],
published: false,
});
// Use a per-card state key so the chosen fallback is the same on server and client
const imageStateKey = `card-image-${props.path}`;
const imageSrc = useState<string>(imageStateKey, () => {
return props.image || getRandomFallbackImage();
});
</script>
<template>
@@ -32,8 +39,8 @@ withDefaults(defineProps<Props>(), {
<div class="overflow-hidden aspect-video">
<NuxtImg
class="h-full w-full object-cover object-center transition-transform duration-700 group-hover:scale-110"
:src="image"
:alt="alt" />
:src="imageSrc"
:alt="props.alt || 'no-alt'" />
</div>
<div class="px-5 py-5 flex flex-col grow">
<div class="flex items-center justify-between mb-3">

View File

@@ -3,9 +3,21 @@ import siteConfig from "~/config";
import SocialLinks from "./SocialLinks.vue";
import Typed from "typed.js";
const descriptions = siteConfig.hero.description;
// Normalize `siteConfig.hero.description` to a string[] to satisfy TypeScript
const rawDescription = siteConfig.hero?.description;
let descriptions: string[] = [];
if (Array.isArray(rawDescription)) {
descriptions = rawDescription.filter((d) => typeof d === "string" && d.length > 0) as string[];
} else if (typeof rawDescription === "string" && rawDescription.length > 0) {
descriptions = [rawDescription];
} else if (typeof siteConfig.hero?.title === "string" && siteConfig.hero.title.length > 0) {
descriptions = [siteConfig.hero.title];
} else {
descriptions = [""];
}
const typedElement = ref<HTMLElement | null>(null);
const randomDescription = ref(descriptions[0]);
const randomDescription = ref(descriptions[0] || "");
let typed: Typed | null = null;
onMounted(() => {
@@ -18,7 +30,7 @@ onMounted(() => {
backDelay: siteConfig.hero.typed.backDelay || 2000,
});
} else {
randomDescription.value = descriptions[Math.floor(Math.random() * descriptions.length)];
randomDescription.value = descriptions[Math.floor(Math.random() * descriptions.length)] || "";
}
});
@@ -42,6 +54,8 @@ onUnmounted(() => {
:src="siteConfig.profile.avatar"
alt="avatar"
class="relative h-full w-full object-cover rounded-full border-4 border-white/80 dark:border-slate-800/80 shadow-2xl transition-transform duration-500 group-hover:scale-105"
width="160px"
height="160px"
loading="eager" />
</div>
</div>

View File

@@ -32,7 +32,7 @@ const formattedData = computed(() => {
path: articles.path,
title: articles.title || "no-title available",
description: articles.description || "no-description available",
image: articles.image || getRandomFallbackImage(),
image: articles.image || getRandomFallbackImage(articles.path),
alt: articles.alt || "no alter data available",
date: articles.date || "not-date-available",
tags: articles.tags || [],