feat: blog
This commit is contained in:
242
components/PostItem.vue
Normal file
242
components/PostItem.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<p class="blog-item" >
|
||||
<!-- <i class="pin" v-if="!!pin"></i> -->
|
||||
<!-- 标题 -->
|
||||
<p class="title" v-if="inMobile" @click="openBlog(PostURL)">{{ Title }}</p>
|
||||
<div class="info-container">
|
||||
<!-- 左侧信息 -->
|
||||
<div class="info-part">
|
||||
<!-- 标题 -->
|
||||
<p class="title" v-if="!inMobile" @click="openBlog(PostURL)">{{ Title }}</p>
|
||||
<!-- 简短描述 -->
|
||||
<p class="description" v-if="!!Content" @click="openBlog(PostURL)">
|
||||
{{ Content }}
|
||||
</p>
|
||||
<!-- 底部补充描述 -->
|
||||
<div class="badge-list" v-if="!inMobile">
|
||||
<a class="split" v-if="Author" :href="withBase(AuthorURL)">{{ Author }}</a>
|
||||
<span class="split">{{ showTime }}</span>
|
||||
<span class="split" v-if="tag?.length">{{ tag.join(' · ') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧封面图 -->
|
||||
<div
|
||||
v-if="cover"
|
||||
class="cover-img"
|
||||
:style="`background-image: url(${cover});`"
|
||||
></div>
|
||||
</div>
|
||||
<!-- 底部补充描述 -->
|
||||
<div class="badge-list" v-if="inMobile">
|
||||
<a class="split" v-if="Author" :href="withBase(AuthorURL)">{{ Author }}</a>
|
||||
<span class="split">{{ showTime }}</span>
|
||||
<span class="split" v-if="tag?.length">{{ tag.join(' · ') }}</span>
|
||||
</div>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { withBase } from 'vitepress'
|
||||
import { computed } from 'vue'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
const openBlog = (url) => {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
const { width } = useWindowSize()
|
||||
const inMobile = computed(() => width.value <= 500)
|
||||
function formatDate(d, fmt = 'yyyy-MM-dd hh:mm:ss') {
|
||||
if (!(d instanceof Date)) {
|
||||
d = new Date(d)
|
||||
}
|
||||
const o = {
|
||||
'M+': d.getMonth() + 1, // 月份
|
||||
'd+': d.getDate(), // 日
|
||||
'h+': d.getHours(), // 小时
|
||||
'm+': d.getMinutes(), // 分
|
||||
's+': d.getSeconds(), // 秒
|
||||
'q+': Math.floor((d.getMonth() + 3) / 3), // 季度
|
||||
S: d.getMilliseconds() // 毫秒
|
||||
}
|
||||
if (/(y+)/.test(fmt)) {
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
`${d.getFullYear()}`.substr(4 - RegExp.$1.length)
|
||||
)
|
||||
}
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const k in o) {
|
||||
if (new RegExp(`(${k})`).test(fmt))
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length)
|
||||
)
|
||||
}
|
||||
return fmt
|
||||
}
|
||||
function formatShowDate(date) {
|
||||
const source = date ? +new Date(date) : +new Date()
|
||||
const now = +new Date()
|
||||
const diff = now - source
|
||||
const oneSeconds = 1000
|
||||
const oneMinute = oneSeconds * 60
|
||||
const oneHour = oneMinute * 60
|
||||
const oneDay = oneHour * 24
|
||||
const oneWeek = oneDay * 7
|
||||
if (diff < oneMinute) {
|
||||
return `${Math.floor(diff / oneSeconds)}秒前`
|
||||
}
|
||||
if (diff < oneHour) {
|
||||
return `${Math.floor(diff / oneMinute)}分钟前`
|
||||
}
|
||||
if (diff < oneDay) {
|
||||
return `${Math.floor(diff / oneHour)}小时前`
|
||||
}
|
||||
if (diff < oneWeek) {
|
||||
return `${Math.floor(diff / oneDay)}天前`
|
||||
}
|
||||
|
||||
return formatDate(new Date(date), 'yyyy-MM-dd')
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
Title: String,
|
||||
Date: String,
|
||||
Content: String,
|
||||
tag: {
|
||||
type: Array,
|
||||
required: false,
|
||||
},
|
||||
Author: String,
|
||||
cover: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
PostURL: String,
|
||||
AuthorURL: String
|
||||
})
|
||||
|
||||
const showTime = computed(() => {
|
||||
return formatShowDate(props.Date)
|
||||
})
|
||||
|
||||
// function isWrappedWithPreventDefault(element: HTMLElement) {
|
||||
// let parent = element.parentElement
|
||||
|
||||
// while (parent) {
|
||||
// if (parent.hasAttribute('preventDefault')) {
|
||||
// return true
|
||||
// }
|
||||
// parent = parent.parentElement
|
||||
// }
|
||||
|
||||
// return false
|
||||
// }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.blog-item .pin {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.blog-item:hover .pin {
|
||||
opacity: 1;
|
||||
}
|
||||
.blog-item .pin::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 120%;
|
||||
height: 30px;
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
var(--blog-theme-color),
|
||||
var(--blog-theme-color)
|
||||
);
|
||||
transform: rotate(-45deg) translateY(-20px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.23);
|
||||
}
|
||||
|
||||
.blog-item {
|
||||
position: relative;
|
||||
border: 1px solid rgba(82, 82, 89, .32);
|
||||
border-radius: .5rem;
|
||||
padding: 1.5rem;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: border-color .25s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.blog-item:hover {
|
||||
border: 1px solid #09f;
|
||||
}
|
||||
|
||||
.info-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.info-part {
|
||||
flex: 1;
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.description {
|
||||
color: var(--description-font-color);
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
.description-html {
|
||||
font-size: 14px;
|
||||
}
|
||||
.badge-list {
|
||||
font-size: 13px;
|
||||
color: var(--badge-font-color);
|
||||
margin-top: 8px;
|
||||
}
|
||||
.badge-list .split:not(:last-child)::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
height: 8px;
|
||||
margin: 0 10px;
|
||||
background-color: #4e5969;
|
||||
}
|
||||
.cover-img {
|
||||
width: 120px;
|
||||
height: 80px;
|
||||
margin-left: 24px;
|
||||
border-radius: 2px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 120px 80px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.cover-img {
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
background-size: 100px 60px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user