65 lines
1.7 KiB
Vue
65 lines
1.7 KiB
Vue
<script setup lang="ts">
|
||
import type { WebNovel } from '~/types'
|
||
|
||
const props = defineProps<{
|
||
novel: WebNovel
|
||
}>()
|
||
|
||
const router = useRouter()
|
||
|
||
const handleClick = () => {
|
||
router.push(`/novels/${props.novel.id}`)
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<UCard
|
||
@click="handleClick"
|
||
class="cursor-pointer hover:shadow-lg transition-all duration-200 h-full"
|
||
>
|
||
<template #header>
|
||
<img
|
||
:src="novel.cover"
|
||
:alt="novel.title"
|
||
class="w-full h-48 object-cover rounded"
|
||
>
|
||
</template>
|
||
|
||
<div class="space-y-2">
|
||
<h3 class="font-bold text-lg line-clamp-2">{{ novel.title }}</h3>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||
by {{ typeof novel.author === 'string' ? novel.author : novel.author?.name }}
|
||
</p>
|
||
|
||
<div class="flex flex-wrap gap-1 pt-2">
|
||
<UBadge
|
||
v-for="genre in novel.genres.slice(0, 2)"
|
||
:key="genre"
|
||
size="xs"
|
||
>
|
||
{{ genre }}
|
||
</UBadge>
|
||
</div>
|
||
|
||
<div class="flex items-center justify-between pt-2">
|
||
<div class="flex items-center gap-1">
|
||
<UIcon name="i-lucide-star" class="size-4 text-yellow-500" />
|
||
<span class="text-sm font-semibold">{{ (novel.rating || 0).toFixed(1) }}</span>
|
||
</div>
|
||
<UBadge
|
||
:color="novel.status === 'completed' ? 'green' : novel.status === 'hiatus' ? 'amber' : 'blue'"
|
||
size="xs"
|
||
>
|
||
{{ novel.status }}
|
||
</UBadge>
|
||
</div>
|
||
|
||
<div class="text-xs text-gray-500 dark:text-gray-400 space-y-1 pt-2">
|
||
<p>📖 {{ novel.chapters || 0 }} chapters</p>
|
||
<p>👁️ {{ ((novel.views || 0) / 1000).toFixed(0) }}k views</p>
|
||
</div>
|
||
</div>
|
||
</UCard>
|
||
</template>
|
||
|