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:
2025-11-08 13:40:23 +08:00
parent fb67355a15
commit f9e02372b2
10 changed files with 254 additions and 61 deletions

View File

@@ -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; return navLinks;

View 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>

View File

@@ -71,14 +71,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
const { data: page } = await useLocalizedCollection("index"); const { data: page } = await useLocalizedCollection("index", {
throwOnMissing: false,
});
useSeoMeta({ useSeoMeta({
title: page.value?.seo.title, title: page.value?.seo.title,
}); });
const colorMode = useColorMode();
const backgroundImages = [ const backgroundImages = [
"https://img.tootaio.com/i/2025/11/05/avc5ld.png", "https://img.tootaio.com/i/2025/11/05/avc5ld.png",
"https://img.tootaio.com/i/2025/11/05/avcaff.png", "https://img.tootaio.com/i/2025/11/05/avcaff.png",
@@ -111,67 +111,31 @@ const techIcons = computed(() => [
"skill-icons:javascript", "skill-icons:javascript",
"skill-icons:typescript", "skill-icons:typescript",
"skill-icons:docker", "skill-icons:docker",
colorMode.value === "dark" "skill-icons:vuejs-light",
? "skill-icons:vuejs-dark" "skill-icons:nuxtjs-light",
: "skill-icons:vuejs-light", "skill-icons:tailwindcss-light",
colorMode.value === "dark" "skill-icons:nodejs-light",
? "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:cs", "skill-icons:cs",
colorMode.value === "dark" "skill-icons:python-light",
? "skill-icons:python-dark"
: "skill-icons:python-light",
]); ]);
const toolsIcons = ref([ const toolsIcons = ref([
"skill-icons:photoshop", "skill-icons:photoshop",
"skill-icons:illustrator", "skill-icons:illustrator",
"skill-icons:git", "skill-icons:git",
colorMode.value === "dark" "skill-icons:vscode-light",
? "skill-icons:vscode-dark" "skill-icons:visualstudio-light",
: "skill-icons:vscode-light", "skill-icons:github-light",
colorMode.value === "dark" "skill-icons:godot-light",
? "skill-icons:visualstudio-dark" "skill-icons:unity-light",
: "skill-icons:visualstudio-light", "skill-icons:blender-light",
colorMode.value === "dark" "skill-icons:androidstudio-light",
? "skill-icons:github-dark" "skill-icons:windows-light",
: "skill-icons:github-light", "skill-icons:linux-light",
colorMode.value === "dark" "skill-icons:apple-light",
? "skill-icons:godot-dark" "skill-icons:idea-light",
: "skill-icons:godot-light", "skill-icons:pycharm-light",
colorMode.value === "dark" "skill-icons:rider-light",
? "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",
]); ]);
</script> </script>

13
app/utils/clipboard.ts Normal file
View 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",
});
});
}

View File

@@ -50,9 +50,7 @@ const defineWebDevSchema = () =>
label: z.string().min(1), label: z.string().min(1),
icon: z.string().optional(), // 比如 "lucide:mouse-pointer-click" icon: z.string().optional(), // 比如 "lucide:mouse-pointer-click"
// 你原结构里通过 createService 包装,但最终是一个对象 // 你原结构里通过 createService 包装,但最终是一个对象
plans: z plans: z.array(PricingPlanPropsSchema).min(1),
.array(PricingPlanPropsSchema)
.min(1),
// 预留扩展字段例如category、tags、hidden 等) // 预留扩展字段例如category、tags、hidden 等)
category: z.string().optional(), category: z.string().optional(),
tags: z.array(z.string()).optional(), tags: z.array(z.string()).optional(),
@@ -82,5 +80,10 @@ export default defineContentConfig({
source: "zh-CN/webDev.yml", source: "zh-CN/webDev.yml",
schema: defineWebDevSchema(), schema: defineWebDevSchema(),
}), }),
about: defineCollection({
type: "page",
source: "about/*.md",
schema: z.object({}),
}),
}, },
}); });

138
content/about/xiaomai.md Normal file
View File

@@ -0,0 +1,138 @@
---
name: 小麦Xiaomai
title: 游戏开发者 | 前后端开发者 | 工作室创办人
location: 马来西亚,柔佛
email: xsbugh@gmail.com
website: https://github.com/kingsmai
education:
university: 武汉工商学院
major: 计算机科学与技术
gpa: 3.93/4.0
period: 2019年9月 - 2024年8月
rank: 专业排名 8/303
scholarship: 四年全额奖学金获得者
awards:
- 2021年全国高校商业精英挑战赛创新创业竞赛全国总决赛一等奖
- 2021年"互联网+"大学生创新创业大赛湖北省复赛金奖
- 马来西亚外交部志愿者贡献表彰2022年
skills:
- Unity 2D/3D 游戏开发
- Godot 4 游戏开发
- JavaWeb 后端开发 (SpringBoot)
- Web 前端开发 (Vue, Node.js, Webpack)
- Python 数据分析与自动化
- SQLite, MySQL 数据库管理
- Blender LowPoly 建模
- 区块链开发与NFT技术
- 自动化测试 (Selenium)
- AI与机器学习基础
---
# 麦祖奕 - 工作室创办人介绍
## 个人简介
我是一名来自马来西亚的华裔游戏开发者,本科以优异成绩毕业于武汉工商学院计算机科学与技术专业。作为一名充满热情的技术创作者,我拥有丰富的全栈开发经验。
我的技术生涯始于对游戏开发的热爱逐渐扩展到区块链技术、Web 全栈开发和自动化系统等多个领域。我相信技术应该服务于创意,致力于通过代码实现具有社会价值的创新项目。
**人生使命**:开发具有教育价值且在全球广受欢迎的角色扮演游戏,通过游戏传递知识、启发思考。
## 教育背景与学术成就
在武汉工商学院就读期间,我以 **3.93 的 GPA** 成绩位列专业前 3%8/303连续四年获得全额奖学金。系统学习了计算机科学的核心课程包括数据结构、算法设计、软件工程、数据库原理等为后续的技术实践奠定了坚实的理论基础。
## 专业技术能力
### 游戏开发专长
- **Unity引擎开发者**:精通 2D 游戏开发熟悉物理系统、动画系统、UI系统
- **Godot引擎实践者**:掌握 Godot 4 及 GDScript具备快速原型开发能力
- **游戏架构设计**:熟练运用单例模式、工厂模式、观察者模式、对象池等设计模式
- **AI系统实现**掌握状态机、上下文转向寻路、行为树等AI技术
- **数据持久化**:精通 SQLite 嵌入式数据库、PlayerPrefs 等存储方案
- **安全加密**:实践 XOR 数据加密技术保护游戏数据
### 全栈开发能力
- **后端开发**SpringBoot, ASP .NET Core, PHP, Node.js, 数据库设计与优化
- **前端开发**Vue.js, Nuxt, Webpack, 响应式设计, 静态站点生成
- **移动端开发**Android 原生开发,跨平台适配
- **自动化脚本**Python + Selenium 自动化操作
### 其他技术技能
- **区块链开发**NFT 自动化交易开发,智能合约理解
- **3D建模**Blender LowPoly 建模,游戏资源制作
- **数据分析**Python 数据处理与可视化
- **项目管理**Git 版本控制,团队协作开发
## 代表性项目
### 🎮 《匠人英雄》- 2D生存类游戏毕业设计
**技术栈**: Unity, SQLite, Context Steering AI, Perlin Noise, 设计模式
作为大学毕业设计我独立设计并开发了这款2D俯视角生存游戏。项目实现了
- 基于 CSV 和 XOR 加密的游戏数据配置系统
- 嵌入式 SQLite 数据库存档系统
- 上下文转向 AI 寻路与 Perlin 噪声地图生成
- 完整的设计模式应用架构
### 🔥 《光追》- 地牢冒险游戏GameJam作品
**技术栈**: Godot 4, 有限状态机, GDScript
在 48 小时极限开发挑战中,我担任程序与策划,带领团队完成:
- 基于状态机的 AI 行为系统
- 光影核心玩法机制设计
- B站宣传片获得 700+ 观看量
- 吸引 200+ 玩家参与测试
### 🌐 马来西亚赴华资料整合网站
**技术栈**: Node.js, Webpack, GitHub Pages, JSON API
疫情期间,我联合多所高校留学生发起并开发了这个信息整合平台:
- 3 天内完成从需求分析到部署上线的全流程
- 自研基于 JSON 的静态页面 API 模拟框架
- 帮助近 5000 名马来西亚留学生顺利返校
- 获得马来西亚外交部官方认可
### 💰 慈善竞标大屏系统
**技术栈**: Unity, 双屏显示, 实时动画
为慈善机构开发的商业化项目:
- 实时竞价数据显示与动画效果
- 主控台与大屏幕的双屏协同
- 在马来西亚多个组织中成功应用
## 工作与创业经历
### 马来西亚民主行动党 - 软件开发实习生
**2022年2月-2022年9月**
在实习期间,我承担了多项技术任务:
- 使用 Python 进行选举数据分析和可视化
- 基于 SpringBoot + Vue 开发中大型数据管理系统
- 实现 NFT 交易平台的自动化操作脚本
- 协助多语言新闻稿的发布与传播
### 技术创业经历
基于在游戏开发和 Web 全栈领域的技术积累,我具备从零到一的产品开发能力,能够带领团队完成技术选型、架构设计、开发实施和运营维护的全过程。
## 技术理念与愿景
### 开发哲学
我相信优秀的技术产品应该具备三个特质:**用户体验优先**、**技术架构稳健**、**社会价值正向**。在每一个项目中,我都努力平衡技术创新与实际需求,确保代码质量与开发效率的最佳结合。
### 工作室愿景
作为工作室创办人,我致力于:
1. 打造具有教育意义的精品游戏
2. 探索游戏与新兴技术的结合点
3. 培养年轻开发者的技术能力
4. 通过技术解决方案创造社会价值
## 荣誉与认可
除了在学术和技术竞赛中获得的奖项外,我最珍视的认可是那些通过技术帮助他人的时刻。特别是在疫情期间开发的留学生返校网站,不仅获得了马来西亚外交部的正式表彰,更重要的是切实帮助了数千名学子重返校园。
我相信,技术的真正价值在于它能够为人们的生活带来积极的改变。作为一名技术创作者,这始终是我前进的动力和追求的目标。
---
*欢迎通过以上联系方式与我交流技术创意、项目合作或投资机会。我始终对新的技术挑战和具有社会价值的项目保持开放态度。*

View File

@@ -51,6 +51,15 @@
"description": "Have a look at our indie games!" "description": "Have a look at our indie games!"
} }
} }
},
"teams": {
"label": "Teams",
"children": {
"xiaomai": {
"label": "Xiaomai",
"description": "Founder of Tootaio Studio - Full Stack Developer"
}
}
} }
}, },
"button": { "button": {

View File

@@ -51,6 +51,15 @@
"description": "我们工作室创始人小麦的博客网站。" "description": "我们工作室创始人小麦的博客网站。"
} }
} }
},
"teams": {
"label": "团队",
"children": {
"xiaomai": {
"label": "小麦",
"description": "Tootaio Studio 创始人 - 全栈开发者"
}
}
} }
}, },
"button": { "button": {

View File

@@ -13,8 +13,8 @@ export default defineNuxtConfig({
compatibilityDate: "2025-07-15", compatibilityDate: "2025-07-15",
devtools: { enabled: process.env.NODE_ENV !== "production" }, devtools: { enabled: process.env.NODE_ENV !== "production" },
modules: [ modules: [
"@nuxt/content",
"@nuxt/ui", "@nuxt/ui",
"@nuxt/content",
"@nuxt/eslint", "@nuxt/eslint",
"@nuxtjs/i18n", "@nuxtjs/i18n",
"@nuxtjs/seo", "@nuxtjs/seo",

BIN
public/images/xiaomai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB