lichx-blog/pages/index/components/SimpleCard.vue

156 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { DataAnomaly, defaultMetaData } from '~/types/PostMetaData';
import type { PostMetaData } from '~/types/PostMetaData';
const props = withDefaults(defineProps<{
metaData?: PostMetaData;
}>(),
{
metaData: () => defaultMetaData,
});
const { data: rawbody } = useAsyncData('simpleCard:' + props.metaData.id, async () => (await queryCollection('content').where('id', '=', props.metaData.id).first())?.rawbody);
const collapsed = ref(true);
const typeChinese = new Map<string | undefined, string>([
['rambling', '絮语'],
['announcement', '公告'],
]);
function dateFormat(date: Date | DataAnomaly | undefined) {
if (!date) {
return 'date undefined';
}
if (date === DataAnomaly.DataNotFound || date === DataAnomaly.Invalid) {
return date;
}
return new Date(date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
function getCostTime(length: number | DataAnomaly) {
if (length === DataAnomaly.DataNotFound || length === DataAnomaly.Invalid) {
return length;
}
let time = length / 250;
time = Math.ceil(time);
const hours = Math.floor(time / 60);
const minutes = time % 60;
if (hours > 0) {
return `${hours}小时${minutes}分钟`;
} else {
return `${minutes}分钟`;
}
}
const safeEditorId = computed(() => {
const encoded = btoa(encodeURIComponent(props.metaData.id))
.replace(/[+/=]/g, '_'); // 替换 Base64 中的特殊字符
return `rambling_${encoded}`;
});
const showShadow = ref(true);
function reverseCollapsed() {
if (collapsed.value) {
collapsed.value = false;
return;
}
collapsed.value = true;
showShadow.value = false;
setTimeout(() => {
showShadow.value = true;
}, 500);
}
</script>
<template>
<div
class="p-5 light:bg-old-neutral-200 dark:bg-old-neutral-800 min-h-64"
@click="reverseCollapsed">
<div class="text-4xl">
{{ (typeChinese.get(metaData?.type) || 'unknown Type') + '' }}{{ props.metaData.title }}
</div>
<div class="flex items-center mt-2 max-w-[400px] overflow-hidden">
<div title="发布时间" class="flex items-center">
<Icon name="lucide:clock-arrow-up"/>
<div class="ml-1 text-nowrap">
{{ dateFormat(props.metaData.published_at) }}
</div>
</div>
<div title="分类" class="flex items-center ml-2">
<Icon name="material-symbols:category"/>
<div class="ml-1 text-nowrap">
{{ props.metaData.category }}
</div>
</div>
<div title="字数" class="flex items-center ml-2">
<Icon name="fluent:text-word-count-20-filled"/>
<div class="ml-1 text-nowrap">
{{ props.metaData.word_count }}
</div>
</div>
<div title="预计阅读时间" class="flex items-center ml-2">
<Icon name="octicon:stopwatch-16"/>
<div class="ml-1 text-nowrap">
{{ getCostTime(props.metaData.word_count) }}
</div>
</div>
<div v-if="metaData?.isPinned" class="flex items-center ml-2">
<Icon name="codicon:pinned"/>
<div class="ml-1 text-nowrap">
置顶
</div>
</div>
</div>
<div
class="relative flex mt-2 justify-between overflow-hidden duration-300 ease-in-out min-h-[8.5rem]"
:class="{'max-h-[8.5rem]' : collapsed, 'max-h-[100vh]':!collapsed}">
<ReadonlyMdEditor
v-if="rawbody" :editor-id="safeEditorId" :markdown="rawbody!"
class="w-full"/>
<div
class="absolute bottom-0 left-0 right-0 h-14 bg-gradient-to-t from-old-neutral-200 dark:from-old-neutral-800 to-transparent pointer-events-none transition-opacity duration-300"
:class="collapsed && showShadow?'opacity-100':'opacity-0'"
/>
</div>
<hr/>
<div class="flex mt-2">
<div title="创建时间" class="flex items-center">
<Icon name="lucide:file-clock"/>
<div class="ml-1">{{ dateFormat(props.metaData.created_at) }}</div>
</div>
<div v-if="Array.isArray(props.metaData.updated_at)" class="flex items-center ml-2">
<Icon name="lucide:clock-alert" title="上次更新时间"/>
<HoverContent>
<template #content>
<div class="ml-1">
{{ dateFormat(props.metaData?.updated_at[props.metaData.updated_at.length - 1]) }}
</div>
</template>
<template #hoverContent>
<div class="p-1 pr-2">
<div v-for="(date,index) of props.metaData.updated_at" :key="index">
<div class="block whitespace-nowrap">
&nbsp;{{ '第' + index + '次更新' + dateFormat(date) }}
</div>
</div>
</div>
</template>
</HoverContent>
</div>
</div>
</div>
</template>