You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

207 lines
4.0 KiB

<script setup lang="ts">
import { computed } from 'vue'
import { marked } from 'marked'
const props = defineProps<{
title: string
description: string
content: string
tags: string[]
aspectRatio: number
categoryName?: string
}>()
const contentHtml = computed(() => {
if (!props.content) return ''
return marked(props.content, { breaks: true }) as string
})
</script>
<template>
<div class="card">
<span v-if="categoryName" class="card-cat-chip">{{ categoryName }}</span>
<!-- 上部分:正文内容预览 -->
<div class="card-preview">
<div class="preview-text" v-html="contentHtml" />
<div class="content-fade" />
</div>
<!-- 下部分:标签 / 标题 / 描述 -->
<div class="card-footer">
<div v-if="tags && tags.length" class="tags">
<span v-for="tag in tags" :key="tag" class="tag">{{ tag }}</span>
</div>
<h3>{{ title }}</h3>
<p v-if="description" class="desc">{{ description }}</p>
</div>
</div>
</template>
<style scoped>
.card-cat-chip {
position: absolute;
top: 8px;
left: 8px;
z-index: 2;
font-size: 10px;
font-weight: 600;
padding: 2px 8px;
border-radius: 9999px;
background: rgba(204, 120, 92, 0.12);
color: var(--color-primary);
letter-spacing: 0.3px;
}
.card {
position: relative;
border-radius: 12px;
background: var(--color-surface-card);
overflow: hidden;
border: 1px solid var(--color-hairline-soft);
display: flex;
flex-direction: column;
transition: transform 0.4s var(--ease-out-expo), box-shadow 0.4s var(--ease-out-expo);
}
.card:hover {
transform: translateY(-2px) scale(1.005);
box-shadow: 0 8px 24px rgba(20, 20, 19, 0.08);
}
/* ── 上部分:正文预览 ── */
.card-preview {
position: relative;
padding: 20px 20px 16px;
}
.preview-text {
font-size: 13px;
font-weight: 400;
line-height: 1.7;
color: var(--color-body);
max-height: 96px;
overflow: hidden;
}
/* markdown 元素降级样式(预览态,不让标题/代码过于抢眼) */
.preview-text :deep(h1),
.preview-text :deep(h2),
.preview-text :deep(h3) {
font-size: 14px;
font-weight: 600;
line-height: 1.5;
color: var(--color-ink);
margin: 0 0 4px;
}
.preview-text :deep(p) {
margin: 0 0 4px;
}
.preview-text :deep(ul),
.preview-text :deep(ol) {
margin: 0 0 4px;
padding-left: 18px;
}
.preview-text :deep(li) {
margin-bottom: 2px;
}
.preview-text :deep(code) {
font-size: 12px;
background: var(--color-surface-cream-strong);
padding: 1px 5px;
border-radius: 4px;
}
.preview-text :deep(pre) {
font-size: 12px;
background: var(--color-surface-cream-strong);
padding: 8px 12px;
border-radius: 6px;
margin: 0 0 6px;
overflow: hidden;
}
.preview-text :deep(blockquote) {
margin: 0 0 4px;
padding-left: 12px;
border-left: 2px solid var(--color-hairline);
color: var(--color-muted);
}
.preview-text :deep(a) {
color: var(--color-primary);
pointer-events: none;
}
.preview-text :deep(img) {
display: none;
}
.content-fade {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 52px;
background: linear-gradient(
to bottom,
transparent 0%,
var(--color-surface-card) 100%
);
pointer-events: none;
}
/* ── 下部分:标签 / 标题 / 描述 ── */
.card-footer {
padding: 0 20px 20px;
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 10px;
}
.tag {
font-size: 11px;
font-weight: 500;
color: var(--color-primary);
background: rgba(204, 120, 92, 0.08);
padding: 3px 10px;
border-radius: 9999px;
line-height: 1.3;
}
.card-footer h3 {
font-family: var(--font-display);
font-size: 15.5px;
font-weight: 400;
line-height: 1.3;
color: var(--color-ink);
margin: 0 0 6px;
letter-spacing: -0.01em;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.desc {
font-size: 12px;
font-weight: 400;
line-height: 1.55;
color: var(--color-muted);
margin: 0;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>