feat(about): add team member profile page
This commit introduces a new section to showcase team members, starting with the founder's profile. - Adds a dynamic page route `/about/` to display individual member profiles. - Creates a new `about` content collection to source profile information from Markdown files. - Adds the first profile for 'Xiaomai', including a detailed resume and background image. - Integrates a 'Teams' dropdown into the main navigation header. - Implements a `copyToClipboard` utility with a toast notification for sharing profile links.
This commit is contained in:
@@ -80,6 +80,14 @@ export const useNavLinks = () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{label: t("common.header.teams.label"),
|
||||
icon: "mdi:account-group-outline",
|
||||
children: [{
|
||||
label: t("common.header.teams.children.xiaomai.label"),
|
||||
description: t("common.header.teams.children.xiaomai.description"),
|
||||
to: '/about/xiaomai'
|
||||
}]
|
||||
}
|
||||
]);
|
||||
|
||||
return navLinks;
|
||||
|
||||
49
app/pages/about/[slug].vue
Normal file
49
app/pages/about/[slug].vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script lang="ts" setup>
|
||||
const route = useRoute();
|
||||
|
||||
const articleLink = computed(() => `${window?.location}`);
|
||||
|
||||
const { data: page } = await useAsyncData(route.path, () =>
|
||||
queryCollection("about").path(`/about/${route.params.slug}`).first()
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPage>
|
||||
<div
|
||||
class="fixed top-0 left-0 -z-999 w-screen h-screen bg-[url('/images/xiaomai.png')] bg-cover lg:bg-contain bg-no-repeat opacity-40 animate-slide-in"
|
||||
></div>
|
||||
<UPageBody class="max-w-3xl mx-auto">
|
||||
<ContentRenderer v-if="page?.body" :value="page" />
|
||||
|
||||
<div class="flex items-center justify-end gap-2 text-sm text-muted">
|
||||
<UButton
|
||||
size="sm"
|
||||
variant="link"
|
||||
color="neutral"
|
||||
label="Copy link"
|
||||
@click="
|
||||
copyToClipboard(articleLink, 'Article link copied to clipboard')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</UPageBody>
|
||||
</UPage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@keyframes slide-in {
|
||||
from {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slide-in {
|
||||
animation: slide-in 1.2s ease-out forwards;
|
||||
}
|
||||
</style>
|
||||
@@ -71,14 +71,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const { data: page } = await useLocalizedCollection("index");
|
||||
const { data: page } = await useLocalizedCollection("index", {
|
||||
throwOnMissing: false,
|
||||
});
|
||||
|
||||
useSeoMeta({
|
||||
title: page.value?.seo.title,
|
||||
});
|
||||
|
||||
const colorMode = useColorMode();
|
||||
|
||||
const backgroundImages = [
|
||||
"https://img.tootaio.com/i/2025/11/05/avc5ld.png",
|
||||
"https://img.tootaio.com/i/2025/11/05/avcaff.png",
|
||||
@@ -111,67 +111,31 @@ const techIcons = computed(() => [
|
||||
"skill-icons:javascript",
|
||||
"skill-icons:typescript",
|
||||
"skill-icons:docker",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:vuejs-dark"
|
||||
: "skill-icons:vuejs-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:nuxtjs-dark"
|
||||
: "skill-icons:nuxtjs-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:tailwindcss-dark"
|
||||
: "skill-icons:tailwindcss-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:nodejs-dark"
|
||||
: "skill-icons:nodejs-light",
|
||||
"skill-icons:vuejs-light",
|
||||
"skill-icons:nuxtjs-light",
|
||||
"skill-icons:tailwindcss-light",
|
||||
"skill-icons:nodejs-light",
|
||||
"skill-icons:cs",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:python-dark"
|
||||
: "skill-icons:python-light",
|
||||
"skill-icons:python-light",
|
||||
]);
|
||||
|
||||
const toolsIcons = ref([
|
||||
"skill-icons:photoshop",
|
||||
"skill-icons:illustrator",
|
||||
"skill-icons:git",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:vscode-dark"
|
||||
: "skill-icons:vscode-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:visualstudio-dark"
|
||||
: "skill-icons:visualstudio-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:github-dark"
|
||||
: "skill-icons:github-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:godot-dark"
|
||||
: "skill-icons:godot-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:unity-dark"
|
||||
: "skill-icons:unity-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:blender-dark"
|
||||
: "skill-icons:blender-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:androidstudio-dark"
|
||||
: "skill-icons:androidstudio-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:windows-dark"
|
||||
: "skill-icons:windows-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:linux-dark"
|
||||
: "skill-icons:linux-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:apple-dark"
|
||||
: "skill-icons:apple-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:idea-dark"
|
||||
: "skill-icons:idea-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:pycharm-dark"
|
||||
: "skill-icons:pycharm-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:rider-dark"
|
||||
: "skill-icons:rider-light",
|
||||
"skill-icons:vscode-light",
|
||||
"skill-icons:visualstudio-light",
|
||||
"skill-icons:github-light",
|
||||
"skill-icons:godot-light",
|
||||
"skill-icons:unity-light",
|
||||
"skill-icons:blender-light",
|
||||
"skill-icons:androidstudio-light",
|
||||
"skill-icons:windows-light",
|
||||
"skill-icons:linux-light",
|
||||
"skill-icons:apple-light",
|
||||
"skill-icons:idea-light",
|
||||
"skill-icons:pycharm-light",
|
||||
"skill-icons:rider-light",
|
||||
]);
|
||||
</script>
|
||||
|
||||
|
||||
13
app/utils/clipboard.ts
Normal file
13
app/utils/clipboard.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export function copyToClipboard(
|
||||
toCopy: string,
|
||||
message: string = "Copied to clipboard"
|
||||
) {
|
||||
const toast = useToast();
|
||||
navigator.clipboard.writeText(toCopy).then(() => {
|
||||
toast.add({
|
||||
title: message,
|
||||
color: "success",
|
||||
icon: "i-lucide-check-circle",
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user