mirror of
https://github.com/RhenCloud/Cloud-Home.git
synced 2026-06-11 00:24:56 +08:00
feat: 添加 PGP 公钥展示和复制功能
This commit is contained in:
@@ -31,7 +31,7 @@
|
|||||||
<template v-for="link in links" :key="link.url">
|
<template v-for="link in links" :key="link.url">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="link.url"
|
:to="link.url"
|
||||||
class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-white/10 backdrop-blur-sm border border-white/10 text-text-primary text-sm font-medium transition-all duration-200 hover:bg-primary/20 hover:border-primary/40 hover:text-primary hover:-translate-y-1"
|
class="social-link-chip inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-sm font-medium transition-all duration-200 hover:-translate-y-1"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="iconFor(link)"
|
v-if="iconFor(link)"
|
||||||
@@ -44,6 +44,41 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="pgpInfo.publicKey"
|
||||||
|
class="mt-4 rounded-2xl border border-white/20 bg-white/10 p-4 backdrop-blur-xl"
|
||||||
|
>
|
||||||
|
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||||
|
<div class="min-w-0">
|
||||||
|
<p class="m-0 text-sm font-semibold text-white">PGP 公钥</p>
|
||||||
|
<p class="m-0 mt-1 break-all text-xs text-text-muted">
|
||||||
|
指纹:{{ formatFingerprint(pgpInfo.fingerprint) || "未提供指纹" }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<a
|
||||||
|
v-if="pgpInfo.keyUrl"
|
||||||
|
:href="pgpInfo.keyUrl"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
class="social-link-chip inline-flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-medium"
|
||||||
|
>
|
||||||
|
查看公钥
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="social-link-chip inline-flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-medium"
|
||||||
|
@click="copyPgpPublicKey"
|
||||||
|
>
|
||||||
|
复制公钥
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p v-if="copyFeedback" class="m-0 mt-2 text-xs text-text-muted">{{ copyFeedback }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- <button
|
<!-- <button
|
||||||
v-show="canScrollRight"
|
v-show="canScrollRight"
|
||||||
aria-label="向右滚动"
|
aria-label="向右滚动"
|
||||||
@@ -72,7 +107,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
import { computed, ref, onMounted, onBeforeUnmount } from "vue";
|
||||||
|
import siteConfig from "~/config/siteConfig";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
links: {
|
links: {
|
||||||
@@ -83,6 +119,16 @@ defineProps({
|
|||||||
|
|
||||||
const container = ref(null);
|
const container = ref(null);
|
||||||
const showOnlyIcons = ref(false);
|
const showOnlyIcons = ref(false);
|
||||||
|
const copyFeedback = ref("");
|
||||||
|
|
||||||
|
const pgpInfo = computed(() => {
|
||||||
|
const pgp = siteConfig.profile?.pgp || {};
|
||||||
|
return {
|
||||||
|
fingerprint: pgp.fingerprint || "",
|
||||||
|
publicKey: pgp.publicKey || "",
|
||||||
|
keyUrl: pgp.keyUrl || "",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
bilibili: "simple-icons:bilibili",
|
bilibili: "simple-icons:bilibili",
|
||||||
@@ -114,6 +160,20 @@ const iconFor = (link) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatFingerprint = (fingerprint) => {
|
||||||
|
if (!fingerprint) return "";
|
||||||
|
return fingerprint.match(/.{1,4}/g)?.join(" ") || fingerprint;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function copyPgpPublicKey() {
|
||||||
|
if (!pgpInfo.value.publicKey || !navigator.clipboard) return;
|
||||||
|
await navigator.clipboard.writeText(pgpInfo.value.publicKey);
|
||||||
|
copyFeedback.value = "已复制公钥";
|
||||||
|
window.setTimeout(() => {
|
||||||
|
copyFeedback.value = "";
|
||||||
|
}, 1600);
|
||||||
|
}
|
||||||
|
|
||||||
function updateScrollButtons() {
|
function updateScrollButtons() {
|
||||||
const el = container.value;
|
const el = container.value;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
@@ -144,6 +204,26 @@ onBeforeUnmount(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.social-link-chip {
|
||||||
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.22), rgba(124, 193, 255, 0.12));
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
color: rgba(250, 253, 255, 0.98);
|
||||||
|
text-shadow: 0 1px 2px rgba(15, 22, 41, 0.4);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.28),
|
||||||
|
0 10px 28px rgba(8, 14, 28, 0.16);
|
||||||
|
backdrop-filter: blur(12px) saturate(160%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-link-chip:hover {
|
||||||
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), rgba(124, 193, 255, 0.2));
|
||||||
|
border-color: rgba(124, 193, 255, 0.58);
|
||||||
|
color: #f6fbff;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.35),
|
||||||
|
0 14px 30px rgba(124, 193, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
.social-links-scroll {
|
.social-links-scroll {
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none; /* Firefox */
|
||||||
|
|||||||
@@ -5,6 +5,33 @@ const siteConfig = {
|
|||||||
avatar: "/avatar-1.webp", // public/avatar.webp
|
avatar: "/avatar-1.webp", // public/avatar.webp
|
||||||
bio: "趁世界还未重启之前 约一次爱恋",
|
bio: "趁世界还未重启之前 约一次爱恋",
|
||||||
email: "i@rhen.cloud",
|
email: "i@rhen.cloud",
|
||||||
|
pgp: {
|
||||||
|
fingerprint: "4A0D0DE4379AEB4562ED5EC0A574A617378C4E0B",
|
||||||
|
publicKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mDMEaaqxVRYJKwYBBAHaRw8BAQdAGfMGv3ZrFvyC3aB69rrCe7hj19VXqfn+fxQ4
|
||||||
|
R1xxsK+0GFJoZW5DbG91ZCA8aUByaGVuLmNsb3VkPoiyBBMWCgBaGxSAAAAAAAQA
|
||||||
|
Dm1hbnUyLDIuNSsxLjExLDIsMQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIX
|
||||||
|
gBYhBEoNDeQ3mutFYu1ewKV0phc3jE4LBQJpuBlSAhkBAAoJEKV0phc3jE4L3cYB
|
||||||
|
AOVr0OASfXF7fv7hE9u82CYtCB3o70bc+hF0cvqdHn+RAQCfEgw5iQo0GA2BfhPK
|
||||||
|
U1VKL71dm/QxGJ12n9Q2SsWwDrQgUmhlbkNsb3VkIDxyaGVuY2xvdWRAc2lpd2F5
|
||||||
|
Lm9yZz6IrwQTFgoAVxYhBEoNDeQ3mutFYu1ewKV0phc3jE4LBQJpuBjzGxSAAAAA
|
||||||
|
AAQADm1hbnUyLDIuNSsxLjExLDIsMQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIe
|
||||||
|
BwIXgAAKCRCldKYXN4xOC+OtAP9UIlQwEBDKeOVvBTknykmrN2XfPH+9BBd5YUC+
|
||||||
|
l44rAQEAmjzieQWLCwz8D9Ythya+6rRE0eXa6Kd0cL8Stwe9wwq4MwRpqrFVFgkr
|
||||||
|
BgEEAdpHDwEBB0D7rYWSdRC5vUBQw1FgX83X0WZOSRPYhzi1o1PkEE0GxIiUBBgW
|
||||||
|
CgA8FiEESg0N5Dea60Vi7V7ApXSmFzeMTgsFAmmqsVUbFIAAAAAABAAObWFudTIs
|
||||||
|
Mi41KzEuMTEsMiwxAhsgAAoJEKV0phc3jE4LKp4BAIsaNWogAP0TxrRseS3zk+BE
|
||||||
|
/K5sdmIt4nJNYVC91keVAQC2PFhdfbVRIbisJ7k6atOPrjKSeUMKHhYbQWky0ptB
|
||||||
|
C7g4BGmqsVUSCisGAQQBl1UBBQEBB0CQAYihK+4Qeq0jMXhko5JFhztIcGM3muKb
|
||||||
|
tjY4KCPQYgMBCAeIlAQYFgoAPBYhBEoNDeQ3mutFYu1ewKV0phc3jE4LBQJpqrFV
|
||||||
|
GxSAAAAAAAQADm1hbnUyLDIuNSsxLjExLDIsMQIbDAAKCRCldKYXN4xOC1QLAQCx
|
||||||
|
e7ogTB50YVDIMF7A8iQMm9Kn29vjsLftSBsTDzUB4gD+NeGyST5c81RIbNf0eWUk
|
||||||
|
En5WfP0rfILKDkvm8jD0/AU=
|
||||||
|
=7KnD
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----`,
|
||||||
|
keyUrl: "/rhencloud.asc",
|
||||||
|
},
|
||||||
birthday: "2010-03-28",
|
birthday: "2010-03-28",
|
||||||
// gender: "女",
|
// gender: "女",
|
||||||
pronouns: "她",
|
pronouns: "她",
|
||||||
|
|||||||
23
public/rhencloud.asc
Normal file
23
public/rhencloud.asc
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mDMEaaqxVRYJKwYBBAHaRw8BAQdAGfMGv3ZrFvyC3aB69rrCe7hj19VXqfn+fxQ4
|
||||||
|
R1xxsK+0GFJoZW5DbG91ZCA8aUByaGVuLmNsb3VkPoiyBBMWCgBaGxSAAAAAAAQA
|
||||||
|
Dm1hbnUyLDIuNSsxLjExLDIsMQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIX
|
||||||
|
gBYhBEoNDeQ3mutFYu1ewKV0phc3jE4LBQJpuBlSAhkBAAoJEKV0phc3jE4L3cYB
|
||||||
|
AOVr0OASfXF7fv7hE9u82CYtCB3o70bc+hF0cvqdHn+RAQCfEgw5iQo0GA2BfhPK
|
||||||
|
U1VKL71dm/QxGJ12n9Q2SsWwDrQgUmhlbkNsb3VkIDxyaGVuY2xvdWRAc2lpd2F5
|
||||||
|
Lm9yZz6IrwQTFgoAVxYhBEoNDeQ3mutFYu1ewKV0phc3jE4LBQJpuBjzGxSAAAAA
|
||||||
|
AAQADm1hbnUyLDIuNSsxLjExLDIsMQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIe
|
||||||
|
BwIXgAAKCRCldKYXN4xOC+OtAP9UIlQwEBDKeOVvBTknykmrN2XfPH+9BBd5YUC+
|
||||||
|
l44rAQEAmjzieQWLCwz8D9Ythya+6rRE0eXa6Kd0cL8Stwe9wwq4MwRpqrFVFgkr
|
||||||
|
BgEEAdpHDwEBB0D7rYWSdRC5vUBQw1FgX83X0WZOSRPYhzi1o1PkEE0GxIiUBBgW
|
||||||
|
CgA8FiEESg0N5Dea60Vi7V7ApXSmFzeMTgsFAmmqsVUbFIAAAAAABAAObWFudTIs
|
||||||
|
Mi41KzEuMTEsMiwxAhsgAAoJEKV0phc3jE4LKp4BAIsaNWogAP0TxrRseS3zk+BE
|
||||||
|
/K5sdmIt4nJNYVC91keVAQC2PFhdfbVRIbisJ7k6atOPrjKSeUMKHhYbQWky0ptB
|
||||||
|
C7g4BGmqsVUSCisGAQQBl1UBBQEBB0CQAYihK+4Qeq0jMXhko5JFhztIcGM3muKb
|
||||||
|
tjY4KCPQYgMBCAeIlAQYFgoAPBYhBEoNDeQ3mutFYu1ewKV0phc3jE4LBQJpqrFV
|
||||||
|
GxSAAAAAAAQADm1hbnUyLDIuNSsxLjExLDIsMQIbDAAKCRCldKYXN4xOC1QLAQCx
|
||||||
|
e7ogTB50YVDIMF7A8iQMm9Kn29vjsLftSBsTDzUB4gD+NeGyST5c81RIbNf0eWUk
|
||||||
|
En5WfP0rfILKDkvm8jD0/AU=
|
||||||
|
=7KnD
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
{
|
{
|
||||||
"extends": "./.nuxt/tsconfig.json",
|
"extends": "./.nuxt/tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": ["node"]
|
"types": [
|
||||||
|
"node"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"vueCompilerOptions": {
|
"vueCompilerOptions": {
|
||||||
"globalTypesPath": "./node_modules/.vue-global-types"
|
"globalTypesPath": "./node_modules/.vue-global-types"
|
||||||
},
|
},
|
||||||
"include": ["nuxt.config.ts", "app/**/*.ts", "app/**/*.vue", "app/**/*.d.ts", "server/**/*.ts"]
|
"include": [
|
||||||
|
"nuxt.config.ts",
|
||||||
|
"app/**/*.ts",
|
||||||
|
"app/**/*.vue",
|
||||||
|
"app/**/*.d.ts",
|
||||||
|
"server/**/*.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user