|
|
<template> <Simplebar @scroll="handleScroll"> <!-- 头部导航菜单 --> <div class="headerMenu" v-if="show_menu"> <a-menu v-model:selectedKeys="current" mode="horizontal" :items="items" style="border-bottom: none;" /> <a-input-search v-model:value="value" placeholder="search" style="width: 200px" @search="onSearch" /> </div> <div class="author" v-if="show_author"></div>
<!-- 轮播 --> <div class="carousel" v-if="show_carousel"> <a-carousel autoplay> <div class="img"><img src="/src/assets/images/nav1.png" alt=""></div> <div class="img"><img src="/src/assets/images/nav10.png" alt=""></div> <div class="img"><img src="/src/assets/images/nav13.png" alt=""></div> </a-carousel> </div> <!-- 操作按钮滚动 --> <div class="anchorDown" v-if="show_anchornDown"> <a-button type="link" shape="circle" size="large" @click="downScroll"> <template #icon> <DownCircleOutlined style="font-size: 35px;color: aliceblue;" spin /> </template> </a-button> </div>
<!-- 主要内容区域 --> <div class="mainContainer" :style="mainCss"> <div class="leftBar"> <a-card hoverable> <template #cover> <img alt="example" src="/src/assets/images/头像.jpg" /> </template> <h1>sunfree</h1> <div class="cardText"></div> <div class="button-group"> <a-button v-for="(button, index) in buttons" :key="index" shape="circle" size="large" @click="handleClick(button.url)"> <component :is="button.icon" /> </a-button> </div> <div class="fold-panel"> <a-collapse v-model:activeKey="activeKey" :bordered="false" expandIconPosition="end"> <template #expandIcon="{ isActive }"> <caret-right-outlined :rotate="isActive ? 90 : 0" /> </template> <a-collapse-panel v-for="panel in panels" :key=panel.id :header=panel.header :style="customStyle"> <p>{{ panel.text }}</p> </a-collapse-panel> </a-collapse> </div> </a-card> <a-card hoverable title="常用链接"> <div class="button-group"> <a-button type="dashed" v-for="linkbutton in linkbuttons" :key="linkbutton.id" @click="comLinkClick(linkbutton.linkurl)">{{ linkbutton.linktext }}</a-button> </div> </a-card> </div> <RouterView /> <div class="rightBar"> <a-card hoverable> <div class="statistic"> <a-row> <a-col :span="8" v-for="statistic in statistics" :key="statistic.id"> <a-statistic :title=statistic.title :value=statistic.counts /> </a-col> </a-row> </div> </a-card> <a-card hoverable> <template #cover> <div class="heatmap" style="border-right: rgba(0, 0, 0, 0.5);"> <div ref="heat" style="height: 100%;"></div> </div> </template> </a-card>
<a-card hoverable> <template #cover> <div id="aplayer"></div> </template> </a-card> <a-card title="随机文章" :bordered="false" hoverable> <div v-for="article in 3"> <a-image :preview="false" :width="300" src="https://cdn.naccl.top/blog/blogHosting/2024/02/B01/f56c5bbe-469c-4eb7-a994-9281d6eed689.png" />
<div class="article-text"> <span>2021-02-01</span> <span>操作系统</span> </div> </div>
</a-card>
<a-card title="标签云" :bordered="false" hoverable> sces </a-card> </div> </div>
</Simplebar> </template> <script lang="ts" setup> import { h, reactive, ref, nextTick } from 'vue'; import type { MenuProps } from 'ant-design-vue'; import { HomeOutlined, HighlightOutlined, ProfileOutlined, CameraOutlined, UsergroupDeleteOutlined, DownCircleOutlined } from '@ant-design/icons-vue'; import Typed from 'typed.js'; import { onMounted, watch } from 'vue'; import type { CSSProperties } from 'vue'; import { CaretRightOutlined } from '@ant-design/icons-vue'; import 'APlayer/dist/APlayer.min.css'; import APlayer from 'APlayer'; import { createEcharts } from "@/hooks/intex" import { useRouter, useRoute } from 'vue-router'; import iconComponents from "@/assets/index"; import { get } from "@/tools/request"; import { number } from 'echarts';
const router = useRouter() const route = useRoute()
/** * 导航菜单 */ const show_menu = ref(false); const show_carousel = ref(true); const show_author = ref(true) const show_anchornDown = ref(true) const mainCss = reactive({ marginTop: "0px" }) type MenuProps = any; const current = ref<string[]>(['home']); const items = ref<MenuProps['items']>([ { key: 'home', icon: () => h(HomeOutlined), label: h('a', { href: '/' }, '首页'), title: '首页',
}, { key: 'blog', icon: () => h(HighlightOutlined), label: h('a', { href: "/blog" }, '博客'), title: '博客', }, { key: 'diary', icon: () => h(ProfileOutlined), label: h('a', { href: "/diary" }, '日记'), title: '日记', }, { key: 'album', icon: () => h(CameraOutlined), label: '相册', title: '相册', children: [ { label: '相册1', key: 'album1', // url: "album1"
}, { label: '相册2', key: 'album2', }, { label: '相册3', key: 'album3', }, { label: '相册4', key: 'album4', }, ], }, { key: 'chart', icon: () => h(UsergroupDeleteOutlined), label: h('a', { href: "/chart" }, '收支图'), title: '收支图', }, { key: 'aboutme', icon: () => h(UsergroupDeleteOutlined), label: h('a', { href: "/aboutme" }, '关于sunfree'), title: '关于sunfree', }, ]); const updateCarouselVisibility = (routeName: any) => { handleScrollEnabled.value = false; if (scrollbar.value) { scrollbar.value.scrollTop = 0; } if (routeName === 'home') { handleScrollEnabled.value = true; show_carousel.value = true; show_menu.value = false } else { show_menu.value = true; show_author.value = false; show_carousel.value = false; show_anchornDown.value = false; mainCss.marginTop = '48px'; } }; router.beforeEach((to, from, next) => { updateCarouselVisibility(to.name); next(); }); // 查询功能
const articleTitle = ref<string>(''); const onSearch = (searchValue: string) => { console.log('use value', searchValue); console.log('or use this.value', articleTitle.value); };
/** * 滚动条操作 */ const scrollbar = ref<Element | null>(null); const handleScrollEnabled = ref(true); // 定义滚动条滚到一半显示导航菜单
const handleScroll = () => { if (!handleScrollEnabled.value) return; if (scrollbar.value) { const scrollOffset = scrollbar.value.scrollTop; const halfViewportHeight = scrollbar.value.clientHeight / 2; show_menu.value = scrollOffset > halfViewportHeight; } }; // 点击按钮,实现滚动到视窗高度距离
const downScroll = () => { if (scrollbar.value) { const scrollTop = scrollbar.value.scrollTop; const viewportHeight = scrollbar.value.clientHeight; const scrollDistance = viewportHeight - scrollTop - 48; setTimeout(() => { scrollbar.value!.scrollBy({ top: scrollDistance, behavior: 'smooth' }); }, 300); } };
/** * 左侧栏 */ // 折叠面板
const activeKey = ref(['']); const customStyle = 'background: #F5F5F5;border-radius: 4px;margin-bottom: 12px;border: 0;overflow: hidden'; interface panelList { id: number, header: string, text: string } const panels = ref<panelList[]>([]) const panelData = async () => { try { const response = await get("/classtic/list"); if (response) { panels.value = response.data.data.map((item: any) => ({ id: item.id, header: item.header, text: item.TEXT, })); } else { console.error("Response data structure is not as expected:"); } } catch (error) { console.error("Failed to fetch data", error); } } // console.log(panels)
// 社交按钮
const buttons = ref([ { icon: iconComponents.CravatarLined, url: 'https://cravatar.cn/' }, { icon: iconComponents.QQLined, url: '/qqcode' }, { icon: iconComponents.WechatLined, url: '/wechatcode' }, { icon: iconComponents.MusicLined, url: 'https://music.163.com/#/playlist?id=160266689' }, { icon: iconComponents.GitHubLined, url: 'https://gitee.com/c_panda' }, ]); const handleClick = (url: string) => { window.open(url) } // 链接按钮
interface comlinkList { id: number, linktext: string, linkurl: string } const linkbuttons = ref<comlinkList[]>([]) const comLinkData = async () => { try { const response = await get("/comlink/list"); if (response) { linkbuttons.value = response.data.data.map((items: any) => ({ id: items.id, linktext: items.linktext, linkurl: items.linkurl })) } else { console.log("response data is not exist") } } catch (error) { console.error("Failed to fetch data", error); }
} const comLinkClick = (url: string) => { if (url.startsWith('http://') || url.startsWith('https://')) { window.open (url,"_blank"); // 使用 window.location.href 打开外部链接
} else { router.push(url); // 否则使用 Vue Router 的 push 方法导航
} } /** * 右侧栏 */ // 统计
const statistics = reactive([ { id: "1", title: "DAYS", counts: "111" }, { id: "2", title: "DIARYS", counts: "111" }, { id: "3", title: "BLOGS", counts: "111" }, ]) // 日历热力图
const heat = ref(null); const newData = <any>[]; // 初始化60天的数据
const data = generateDates(60); console.log(data) // 重新排列数据
for (let i = 0; i < 60; i += 15) { // 取出每个15天的数据,并反转顺序
const chunk = data.slice(i, i + 15).reverse(); newData.push(...chunk); }
function generateDates(numDays: number) { const dates = []; const currentDate = new Date(); for (let i = 0; i < numDays; i++) { const date = new Date(currentDate); date.setDate(currentDate.getDate() - i); dates.push( { date: date.toISOString().split('T')[0], writCount: 0 } ); // 包含日期和评论数
} return dates; } const writList = [5, 4, 3, 2, 1, 0] writList.forEach((item, index) => { data[index].writCount = item; }) const formattedData = newData.map((value: any, index: number) => [index % 15, Math.floor(index / 15), value.writCount]); const heatMapData = { tooltip: { position: 'top', formatter: function (params: any) { const item = newData[params.dataIndex]; if (item.writCount > 0) { return `${item.date}<br/>COMMENTS: ${item.writCount}`; } else { return `${item.date}`; } } }, grid: { left: '0', // 左边距
right: '0', // 右边距
top: '0', // 上边距
bottom: '0', }, xAxis: { type: 'category', data: Array.from({ length: 15 }, (_, i) => `Col ${i + 1}`), splitArea: { show: true }, show: false }, yAxis: { type: 'category', data: ['Row 1', 'Row 2', 'Row 3', 'Row 4'], splitArea: { show: false }, show: false }, visualMap: { min: 0, max: 10, calculable: true, orient: 'horizontal', left: 'center', bottom: '15%', show: false, inRange: { color: ['rgba(182,181,178,0.01)', 'rgb(157,156,153,1)'] // 0评论是白色, 非0评论是黑色
} }, series: [{ type: 'heatmap', data: formattedData, itemStyle: { borderColor: 'rgb(231,229,225,0.5)', // 设置边框颜色
borderWidth: 0.5, // 设置边框宽度
}, label: { show: false, }, emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0, 0, 0, 0.5)', borderColor: '#fff', // 鼠标悬停时的边框颜色
borderWidth: 2 // 鼠标悬停时的边框宽度
} } }] }; // 音乐组件
const random = ref();
onMounted(() => { scrollbar.value = document.querySelector('.simplebar-content-wrapper'); panelData() comLinkData() nextTick(() => { const authorElement = document.querySelector('.author'); if (authorElement) { new Typed('.author', { strings: ['SunFree.'], typeSpeed: 200, backSpeed: 150, loop: true, loopCount: Infinity, showCursor: false }); }; const cardTextElement = document.querySelector('.cardText'); if (cardTextElement) { // 定义个人座右铭
new Typed('.cardText', { strings: ['做三流测试,品瀚霖人生!'], typeSpeed: 200, backSpeed: 150, loop: true, // 开启循环
loopCount: Infinity, // 无限循环
showCursor: false // 取消光标
}); }; const aplayerContainer = document.getElementById('aplayer'); if (aplayerContainer) { new APlayer({ container: aplayerContainer, mini: false, autoplay: false, theme: '#FADFA3', loop: 'all', order: 'random', preload: 'auto', volume: 0.7, mutex: true, listFolded: true, listMaxHeight: 90, lrcType: 3, audio: [ { name: 'name1', artist: 'artist1', url: 'url1.mp3', cover: 'cover1.jpg', lrc: 'lrc1.lrc', theme: '#ebd0c2' }, { name: 'name2', artist: 'artist2', url: 'url2.mp3', cover: 'cover2.jpg', lrc: 'lrc2.lrc', theme: '#46718b' } ] }); } }); createEcharts(heat, heatMapData);
}) watch( () => route.name, (newRouteName) => { updateCarouselVisibility(newRouteName); if (newRouteName) { const menuItem = items.value.find(item => item.key === newRouteName); if (menuItem) { current.value = [menuItem.key]; } else { current.value = ['home']; // 如果找不到对应的菜单项,默认选中 'home'
} } else { current.value = ['home']; // 如果 route.name 不存在,默认选中 'home'
} }, { immediate: true } ); </script> <style scoped> .headerMenu { display: flex; justify-content: center; align-items: center; width: 100%; height: 48px; border-bottom: 1px solid rgba(5, 5, 5, 0.06); position: fixed; background-color: white; transform: translateX(-50%); top: 0; left: 50%; z-index: 999; }
/* 作者名称 */ .author { position: absolute; display: inline-block; font-size: 100px; font-family: 'Courier New', Courier, monospace; background: linear-gradient(270deg, #f5f4f2, #d6cdc7, #4b4949); background-size: 400% 400%; -webkit-background-clip: text; color: transparent; background-clip: text; animation: salon-light-animation 3s ease infinite; top: 50%; left: 50%; transform: translateX(-50%); z-index: 999; }
@keyframes salon-light-animation { 0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; } }
.carousel img { height: 100vh; width: 100vw; }
.anchorDown { position: absolute; bottom: 100px; left: 50%; border-style: hidden; transform: translateX(-50%); }
.mainContainer { display: flex; justify-content: center; padding-top: 48px; background-color: rgba(5, 5, 5, 0.08); }
.leftBar { width: 15%; }
.leftBar img { /* 图片自适应容器并保持宽高比例 */ aspect-ratio: 1/1; }
.leftBar>* { margin-bottom: 24px; }
.leftBar h1 { text-align: center; font-family: Georgia, 'Times New Roman', Times, serif; }
.leftBar .cardText { min-height: 60px; text-align: center; }
.leftBar>:first-child .button-group { display: flex; margin: 0 12px 24px 12px; justify-content: space-between; }
.leftBar>:nth-child(2) .button-group { display: flex; flex-wrap: wrap; justify-content: space-between; }
.leftBar>:nth-child(2) .button-group>* { width: 40%; margin: 12px; }
.rightBar { width: 15%; }
.rightBar>* { margin-bottom: 24px; }
.rightBar>:nth-child(4) div { display: flex; justify-content: center; position: relative; margin-bottom: 24px; }
.rightBar>:nth-child(4) div .article-text { display: flex; flex-direction: column; position: absolute; bottom: 5%; left: 10%; color: rgb(187, 185, 187); }
#aplayer { margin: 0; }
.statistic { text-align: center; } </style>
|