mirror of
https://github.com/sendevia/website.git
synced 2026-03-05 23:32:45 +08:00
feat(PageIndicator): add short link copy functionality and update styles
This commit is contained in:
@@ -1,15 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from "vue";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
import { useGlobalData } from "../composables/useGlobalData";
|
||||
import { usePostStore } from "../stores/posts";
|
||||
import { useScreenWidthStore } from "../stores/screenWidth";
|
||||
import { isClient } from "../utils/env";
|
||||
|
||||
const { page, frontmatter } = useGlobalData();
|
||||
const { copy: copyToClipboard, copied: isCopied } = useClipboard();
|
||||
const screenWidthStore = useScreenWidthStore();
|
||||
const pageIndicator = ref<HTMLElement | null>(null);
|
||||
const indicator = ref({ top: "0px", left: "0px", width: "100%", height: "0px", opacity: 0 });
|
||||
const headings = ref<Array<{ id: string; text: string; level: number }>>([]);
|
||||
const headingsActiveId = ref<string>("");
|
||||
const postStore = usePostStore();
|
||||
|
||||
let ro: ResizeObserver | null = null;
|
||||
let mo: MutationObserver | null = null;
|
||||
@@ -172,6 +176,26 @@ const resizeHandler = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 计算文章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;
|
||||
await copyToClipboard(`${window.location.origin}${shortLink.value}`);
|
||||
};
|
||||
|
||||
if (isClient()) {
|
||||
onMounted(() => {
|
||||
screenWidthStore.init();
|
||||
@@ -255,8 +279,14 @@ if (isClient()) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="pageIndicator" class="PageIndicator" aria-label="页面目录">
|
||||
<p>在此页上</p>
|
||||
<div ref="pageIndicator" class="PageIndicator">
|
||||
<div class="label">
|
||||
<p class="text">在此页上</p>
|
||||
<p class="icon">link</p>
|
||||
<p class="article-id" title="复制短链" v-if="articleId" @click="copyShortLink">
|
||||
{{ isCopied ? `已复制` : articleId }}
|
||||
</p>
|
||||
</div>
|
||||
<h3>{{ frontmatter.title ? frontmatter.title : page.title }}</h3>
|
||||
|
||||
<div
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
import { ref, onMounted, onBeforeUnmount, computed } from "vue";
|
||||
import { useClipboard, useTimestamp, useDateFormat } from "@vueuse/core";
|
||||
import { useGlobalData } from "../composables/useGlobalData";
|
||||
import { usePostStore } from "../stores/posts";
|
||||
import { isClient } from "../utils/env";
|
||||
|
||||
const postStore = usePostStore();
|
||||
const { page, frontmatter } = useGlobalData();
|
||||
const { copy: copyToClipboard, copied: isCopied } = useClipboard();
|
||||
|
||||
@@ -65,26 +63,6 @@ const formattedLastUpdated = computed(() => {
|
||||
return `${formatTimeAgo(uDate)}编辑`;
|
||||
});
|
||||
|
||||
// 计算文章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;
|
||||
await copyToClipboard(`${window.location.origin}${shortLink.value}`);
|
||||
};
|
||||
|
||||
// 图片查看器相关逻辑
|
||||
const showImageViewer = ref(false);
|
||||
const currentImageIndex = ref(0);
|
||||
@@ -225,13 +203,9 @@ if (isClient()) {
|
||||
{{ formattedLastUpdated }}
|
||||
</p>
|
||||
</ClientOnly>
|
||||
<p class="id" v-if="articleId">文章ID {{ articleId }}</p>
|
||||
</div>
|
||||
<ButtonGroup v-if="frontmatter?.external_links" :links="frontmatter.external_links" size="m" layout="vertical" />
|
||||
<PageIndicator />
|
||||
<MaterialButton v-if="articleId" :color="'text'" :icon="'content_copy'" @click="copyShortLink">
|
||||
复制短链
|
||||
</MaterialButton>
|
||||
</div>
|
||||
<ImageViewer
|
||||
v-if="showImageViewer"
|
||||
|
||||
@@ -6,12 +6,28 @@
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
|
||||
p {
|
||||
@include mixin.typescale-style("label-small");
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
|
||||
margin-inline-start: 18px;
|
||||
|
||||
z-index: 1;
|
||||
p {
|
||||
@include mixin.typescale-style("label-small");
|
||||
|
||||
&.icon {
|
||||
@include mixin.material-symbols($size: 16);
|
||||
}
|
||||
|
||||
&.article-id {
|
||||
text-transform: uppercase;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
||||
@@ -831,10 +831,6 @@
|
||||
&.date-update::before {
|
||||
content: "update";
|
||||
}
|
||||
|
||||
&.id::before {
|
||||
content: "flag";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user