1
0
mirror of https://github.com/sendevia/website.git synced 2026-03-06 07:40:50 +08:00

feat(ArticleLayout): add post metadata display and short link sharing

This commit is contained in:
2025-12-12 22:42:35 +08:00
parent 26a9fc9d50
commit 84ba7227f6
3 changed files with 173 additions and 9 deletions

View File

@@ -90,6 +90,8 @@ export default defineConfig({
},
],
],
metaChunk: true,
lastUpdated: true,
themeConfig: {
defaultColor: "#39c5bb",
defaultImpression: "/assets/images/121337686_p0.webp",

View File

@@ -1,11 +1,130 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from "vue";
import { ref, onMounted, onBeforeUnmount, computed } from "vue";
import { useGlobalData } from "../composables/useGlobalData";
import { usePostStore } from "../stores/posts";
import { isClient } from "../utils/env";
const showImageViewer = ref(false);
const currentImageIndex = ref(0);
const articleImages = ref<string[]>([]);
const imageOriginPosition = ref({ x: 0, y: 0, width: 0, height: 0 });
const { page, frontmatter } = useGlobalData();
const postStore = usePostStore();
// 计算当前文章 ID
const articleId = computed(() => {
const relativePath = page.value?.relativePath;
if (!relativePath) return "";
const path = relativePath.replace(/\.md$/, "");
const lookupUrl = path.startsWith("/") ? path : `/${path}`;
const post = postStore.getPostByUrl(lookupUrl);
return post?.id || "";
});
// 生成短链
const shortLink = computed(() => {
if (!articleId.value) return "";
return `/p/${articleId.value}`;
});
// 复制短链到剪贴板
const copyShortLink = async () => {
if (!shortLink.value) return;
const fullUrl = `${window.location.origin}${shortLink.value}`;
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(fullUrl);
}
} catch (err) {
console.error("复制失败:", err);
}
};
// 格式化时间戳
const formatTimestamp = (timestamp: number): string => {
if (!timestamp) return "";
const date = new Date(timestamp);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
return `${year}${month}${day}${hours}${minutes}`;
};
// 计算时间差
const formatTimeAgo = (diffMs: number): string => {
if (diffMs <= 0) return "刚刚";
const diffSeconds = Math.floor(diffMs / 1000);
const diffMinutes = Math.floor(diffSeconds / 60);
const diffHours = Math.floor(diffMinutes / 60);
const diffDays = Math.floor(diffHours / 24);
const diffMonths = Math.floor(diffDays / 30);
const diffYears = Math.floor(diffDays / 365);
if (diffYears > 0) {
return `${diffYears}年前`;
} else if (diffMonths > 0) {
return `${diffMonths}个月前`;
} else if (diffDays > 0) {
return `${diffDays}天前`;
} else if (diffHours > 0) {
return `${diffHours}小时前`;
} else if (diffMinutes > 0) {
return `${diffMinutes}分钟前`;
} else {
return `${diffSeconds}秒前`;
}
};
// 获取发布时间戳
const publishTimestamp = computed(() => {
const dateStr = frontmatter.value?.date;
if (!dateStr) return 0;
const timestamp = new Date(dateStr).getTime();
return isNaN(timestamp) ? 0 : timestamp;
});
// 获取最后修改时间戳
const lastUpdatedTimestamp = computed(() => {
const val = page.value?.lastUpdated;
if (!val) return 0;
if (typeof val === "number") return val;
const ts = new Date(val).getTime();
return isNaN(ts) ? 0 : ts;
});
// 格式化发布日期
const formattedPublishDate = computed(() => {
if (!publishTimestamp.value) return "";
return formatTimestamp(publishTimestamp.value);
});
// 格式化最后修改时间
const formattedLastUpdated = computed(() => {
const publishTs = publishTimestamp.value;
const lastUpdateTs = lastUpdatedTimestamp.value;
if (!lastUpdateTs) return "";
// 判定是否为发布即最后修改
const isSameTime = !publishTs || Math.abs(lastUpdateTs - publishTs) < 60000;
if (isSameTime) {
return formatTimestamp(lastUpdateTs);
} else {
const timeSinceUpdate = Date.now() - lastUpdateTs;
const timeAgo = formatTimeAgo(timeSinceUpdate);
return `${timeAgo}编辑`;
}
});
function openImageViewer(index: number, event: MouseEvent) {
const contentElement = document.querySelector("#article-content");
@@ -137,9 +256,15 @@ if (isClient()) {
<Header />
<main id="article-content">
<Content />
<MaterialButton v-if="articleId" :text="'复制短链'" :color="'outlined'" @click="copyShortLink" />
<PrevNext />
</main>
<div id="article-indicator">
<div id="article-beside">
<div class="post-info">
<p class="date-publish" v-if="formattedPublishDate">发布于 {{ formattedPublishDate }}</p>
<p class="date-update">{{ formattedLastUpdated }}</p>
<p class="id" v-if="articleId">文章ID {{ articleId }}</p>
</div>
<PageIndicator />
</div>
<ImageViewer

View File

@@ -698,11 +698,48 @@ main#article-content {
}
}
div#article-indicator {
div#article-beside {
grid-column: 10 / 13;
position: sticky;
top: 120px;
.post-info {
display: flex;
flex-direction: column;
gap: 3px;
margin-inline-start: 16px;
margin-bottom: 18px;
p {
@include mixin.typescale-style("label-small");
display: inline-flex;
align-items: center;
flex-direction: row;
flex-wrap: nowrap;
gap: 3px;
color: var(--md-sys-color-outline);
&::before {
@include mixin.material-symbols($size: 14);
}
&.date-publish::before {
content: "calendar_today";
}
&.date-update::before {
content: "update";
}
&.id::before {
content: "flag";
}
}
}
}
@media screen and (max-width: 1600px) {
@@ -710,7 +747,7 @@ div#article-indicator {
grid-column: 1 / 10;
}
div#article-indicator {
div#article-beside {
grid-column: 10 / 13;
}
}
@@ -720,7 +757,7 @@ div#article-indicator {
grid-column: 1 / 7;
}
div#article-indicator {
div#article-beside {
grid-column: 7 / 9;
}
}
@@ -730,8 +767,8 @@ div#article-indicator {
grid-column: 1 / 7;
}
div#article-indicator {
grid-column: 5 / 7;
div#article-beside {
grid-column: 1 / 7;
}
}
@@ -740,7 +777,7 @@ div#article-indicator {
grid-column: 1 / 5;
}
div#article-indicator {
grid-column: 3 / 5;
div#article-beside {
grid-column: 1 / 5;
}
}