前言
在将博客框架迁移至 Nuxt 的过程中,“关于”页面也一并迁移了过来。由于当时尚未掌握 Nuxt 的数据处理方式,我只能沿用原有结构,并暂未将其公开。后来,在理解了父组件与子组件之间的数据传递模式后,我重新编写了关于页面。尽管最终呈现的效果仍有改进空间,但已基本实现了预期功能。
效果展示

添加vue模块
在 BlogRoot:/app/components/about 中添加 skillinfo.vue:
<script setup lang="ts">
import { computed } from 'vue';
// 导入外部数据
import { creativityData } from '~/creativity';
// 从 creativityData 中提取技能数据(假设取第一个分类)
const skills = computed(() => {
const firstCategory = creativityData?.[0];
if (!firstCategory?.creativity_list) return null;
// 补充原始代码中需要的字段(根据实际数据结构调整)
return {
...firstCategory,
title: firstCategory.class_name, // 映射 class_name 到 title
subtitle: firstCategory.subtitle // 直接使用 subtitle
};
});
// 计算属性:技能标签分组(每两个一组)- 使用 reduce 重构
const skillTagGroups = computed(() => {
if (!skills.value?.creativity_list) return [];
return skills.value.creativity_list.reduce((groups, currentTag) => {
// 取最后一个分组(可能未填满)
const lastGroup = groups[groups.length - 1];
if (lastGroup?.length === 2) {
// 当前分组已满,创建新分组
groups.push([currentTag]);
} else {
// 当前分组未满,添加到最后一个分组
lastGroup ? lastGroup.push(currentTag) : groups.push([currentTag]);
}
return groups;
}, []); // 初始值为空数组
});
</script>
<template>
<div v-if="skills" class="author-content-item skills">
<div class="card-content">
<div class="author-content-item-tips">{{ skills.class_name }}</div>
<span class="author-content-item-title">{{ skills.subtitle }}</span>
<div class="skills-style-group">
<!-- 标签分组展示(每两个一组) -->
<div class="tags-group-all">
<div class="tags-group-wrapper">
<div
v-for="(group, groupIdx) in skillTagGroups"
:key="groupIdx"
class="tags-group-icon-pair"
>
<div
v-for="(tag, tagIdx) in group"
:key="tagIdx"
class="tags-group-icon"
:style="{ background: tag.color }"
>
<img
class="entered loading"
:src="tag.icon"
:title="tag.name"
:style="{ color: tag.icon_color || '' }"
alt=""
>
</div>
</div>
</div>
</div>
<!-- 全部技能列表 -->
<!-- <div class="skills-list">
<div
v-for="(tag, tagIdx) in skills.creativity_list"
:key="tagIdx"
class="skill-info"
>
<div
class="skill-icon"
:style="{ background: tag.color }"
>
<img
v-if="tag.img"
:src="tag.icon"
:title="tag.name"
:style="{ color: tag.icon_color || '' }"
alt=""
>
</div>
<div class="skill-name">
<span>{{ tag.name }}</span>
</div>
</div>
<div class="etc ...">...</div>
</div> -->
</div>
</div>
</div>
</template>
添加vue页面
在 BlogRoot:/app/pages 中添加 about.vue
<script setup lang="ts">
import Skillinfo from '../components/about/skillinfo.vue'
import Social from '../components/about/social.vue'
import Technology from '../components/about/technology.vue'
import Author from '../components/about/author.vue'
import Game from '../components/about/game.vue'
import Aboutsitetips from '../components/about/aboutsitetips.vue'
import Maxim from '../components/about/maxim.vue'
import MyInfoAndSayHello from '../components/about/myInfoAndSayHello.vue'
import Single from '../components/about/single.vue'
const layoutstore = useLayoutstore()
layoutstore.setAside(['blog-stats', 'blog-tech', 'blog-log'])
// 动态加载外部 JS 脚本
const loadScript = (url: string, callback?: () => void) => {
return new Promise<void>((resolve, reject) => {
// 检查是否已加载
if (document.querySelector(`script[src="${url}"]`)) {
console.log('JS脚本已加载');
resolve();
return;
}
// 创建 script 标签
const script = document.createElement('script');
script.src = url;
script.type = 'text/javascript';
script.async = true; // 异步加载(不阻塞页面渲染)
// 加载成功回调
script.onload = () => {
console.log('脚本加载完成');
callback?.();
resolve();
};
// 加载失败回调
script.onerror = (err) => {
console.error('脚本加载失败', err);
reject(err);
};
// 添加到 DOM(推荐添加到 head 或 body 末尾)
document.head.appendChild(script);
});
};
// 使用示例:加载百度统计脚本
loadScript('https://www.myxz.top/assets/js/about.js')
.then(() => {
console.log('友链顶部重要JS加载完毕');
})
.catch((err) => {
console.error('友链顶部重要JS加载完毕', err);
});
</script>
<template>
<link href="/assets/css/about.css" rel="stylesheet"></link>
<div id="about-page" style="margin-top: 1rem;margin-left: 1rem;margin-right: 1rem;">
<Author />
<div class="author-page-content">
<div class="author-content">
<MyInfoAndSayHello />
</div>
<div class="author-content">
<Aboutsitetips />
<Maxim />
</div>
<div class="author-content">
<!-- 来自于主流HEO主题的衍生版本 -->
<Skillinfo />
<!-- 来自于柳神的关于页面版本 -->
<Social />
</div>
<div class="author-content">
<Technology />
<Game />
</div>
<div class="author-content">
<Single />
</div>
</div>
</div>
</template>
<style lang="css" scoped>
/* 1.基础架构 */
#about-page .author-main {
display: -webkit-box;
display: -moz-box;
display: -webkit-flex;
display: -ms-flexbox;
display: box;
display: flex;
-webkit-box-align: center;
-moz-box-align: center;
-o-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-moz-box-pack: center;
-o-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
margin: 0 0 16px 0;
user-select: none;
}
#about-page .author-box {
position: relative;
width: 189px;
height: 189px;
background: rgba(253, 253, 253, 0.8);
border-radius: 50%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
#about-page .author-img {
margin: auto;
border-radius: 50%;
overflow: hidden;
width: 180px;
height: 180px;
z-index: 10;
background: var(--mj-card-bg);
}
.author-tag-left {
display: flex;
flex-direction: column;
align-items: flex-end;
margin-right: 30px;
}
.author-tag-right {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-left: 30px;
}
/* 2.头像美化 */
#about-page .author-box span {
position: absolute;
inset: 5px;
border-radius: 50%;
background: rgba(253, 253, 253, 0.8);
z-index: 1;
}
#about-page .author-box::before {
content: '';
position: absolute;
width: 500px;
height: 500px;
background-image: conic-gradient(transparent, transparent, transparent, #8758FF);
animation: animate 4s linear infinite;
animation-delay: -2s;
}
#about-page .author-box::after {
content: '';
position: absolute;
width: 500px;
height: 500px;
background-image: conic-gradient(transparent, transparent, transparent, #5CB8E4);
animation: animate 4s linear infinite;
}
/* 3.列表卡片美化 */
.author-tag {
transform: translate(0, -4px);
padding: 1px 8px;
background: var(--heo-card-bg);
border: var(--style-border-always);
border-radius: 40px;
margin-top: 6px;
font-size: 14px;
font-weight: bold;
box-shadow: var(--heo-shadow-lightblack);
animation: 6s ease-in-out 0s infinite normal none running floating;
}
/* 4.列表卡片美化以及动画 */
/* 4.1.左序列 */
.author-tag-left .author-tag:first-child, .author-tag-left .author-tag:last-child {
margin-right: -16px;
}
/* 4.2.右序列 */
.author-tag-right .author-tag:first-child, .author-tag-right .author-tag:last-child {
margin-left: -16px;
}
.author-tag:nth-child(1) {
animation-delay: 0s;
}
.author-tag:nth-child(2) {
animation-delay: 0.6s;
}
.author-tag:nth-child(3) {
animation-delay: 1.2s;
}
.author-tag:nth-child(4) {
animation-delay: 1.8s;
}
/* 5.动画css */
@keyframes floating {
0% {
transform: translate(0, -4px);
}
50% {
transform: translate(0, 4px);
}
100% {
transform: translate(0, -4px);
}
}
@keyframes animate {
0% {
transform: rotate(0)
}
100% {
transform: rotate(360deg)
}
}
/* 6.关于本站文字样式美化 */
[data-theme=dark] #about-page .author-title {
text-stroke: 1px #bccbe4;
-webkit-text-stroke: 1px #bccbe4;
}
#about-page .author-title {
font-size: 2.7rem;
font-weight: 700;
margin-top: 1px;
letter-spacing: 6px;
-webkit-background-clip: text;
background-image: linear-gradient(90deg, #2ca2b4, #5598de 24%, #7f87ff 45%, #f65aad 85%, #df80b4);
background-clip: text;
display: inline-block;
color: transparent;
text-stroke: 2px #3fdaee;
-webkit-text-stroke: 1px #3fdaee;
}
</style>
添加ts数据
注意事项
- 还未写完
在BlogRoot:/app/ 中添加 about.ts:
export interface aboutConnect {
author: author[]; //头像数据
large: string; //标题数据
myinfo: myinfo[]; //个人介绍数据
hello: string; //Hello there数据
maxim: maxim[]; //左右铭数据
technology: technology[]; //偏好数据
game: game[]; //游戏数据
single: single[]; //历程数据
}
// 头像数据
export interface author {
left: left[];
logo: string;
// box: box[];
right: right[];
}
export interface left {
tag1: string;
tag2: string;
tag3: string;
tag4: string;
}
export interface box {
logo: string;
}
export interface right {
tag1: string;
tag2: string;
tag3: string;
tag4: string;
}
//个人介绍数据
export interface myinfo {
title1: string;
title2: string;
inlineword1: string;
title3: string;
inlineword2: string;
card: card[];
}
export interface card {
tips: string;
conect1: string;
conect2: string;
inlineword: string;
mask: mask[];
}
export interface mask {
firstTips: string;
span: string;
up: string;
show: string;
}
//左右铭数据
export interface maxim {
tip: string;
title1: string;
title2: string;
}
//偏好数据
export interface technology {
tip: string;
title: string;
bottomTip: string;
}
//游戏数据
export interface game {
tip: string;
title: string;
uid: string;
image: string;
}
//历程数据
export interface single {
tip: string;
conect1: string;
strong1: string;
conect2: string;
strong2: string;
conect3: string
}
export const aboutPage: aboutConnect[] = [
{
author: [
{
left: [{
tag1: "💻 Like数码科技",
tag2: "🥣 干饭魂 干饭人",
tag3: "🕊 咕咕咕咕咕咕~",
tag4: "🧱 CV工程师"
}],
logo: "https://blog.myxz.top/img/avatar.avif",
right: [{
tag1: "吃饭不如碎觉 💤",
tag2: "乐观 积极 向上 🤝",
tag3: "专攻各种困难 🔨",
tag4: "人不狠话超多 💢"
}]
}
],
large: "关于本站",
myinfo: [{
title1: "你好,很高兴认识你👋",
title2: "我叫",
inlineword1: "渊",
title3: "是一名 前端工程师、学生、",
inlineword2: "博主",
card: [{
tips: "追求",
conect1: "源于",
conect2: "热爱而去",
inlineword: "感受",
mask: [{
firstTips: "学习",
span: "生活",
up: "程序",
show: "体验"
}]
}]
}],
hello: "Main Dis My Blogs",
maxim: [{
tip: "座右铭",
title1: "生活明朗,",
title2: "万物可爱。",
}],
technology: [{
tip: "关注偏好",
title: "数码科技",
bottomTip: "手机、电脑软硬件"
}],
game: [{
tip: "爱好游戏",
title: "暂时未公开",
uid: "暂时未公开",
image: ""
}],
single: [{
tip: "",
conect1: "",
strong1: "",
conect2: "",
strong2: "",
conect3: ""
}]
}
]

评论加载中...