feat: 重构前端项目结构并添加新功能
重构项目目录结构,将组件和服务模块化 添加Element Plus UI库并集成到项目中 实现文章、留言和分类的类型定义 新增工具函数模块包括日期格式化和字符串处理 重写路由配置并添加全局路由守卫 优化页面布局和响应式设计 新增服务层封装API请求 完善文章详情页和相关文章推荐功能
This commit is contained in:
206
src/layouts/MainLayout.vue
Normal file
206
src/layouts/MainLayout.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<div class="elrow-top" :class="elrowtop">
|
||||
<el-row justify="center">
|
||||
<el-col :span="4" v-if="windowwidth">
|
||||
<div class="grid-content ep-bg-purple-dark">
|
||||
<div class="logo-text">清疯不颠</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14" justify="center">
|
||||
<div class="grid-content ep-bg-purple-dark">
|
||||
<el-menu :default-active="activeIndex" class="el-menu-demo" :collapse="false" @select="handleSelect">
|
||||
<el-menu-item index="/:type">
|
||||
首页
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/article-list">
|
||||
目录
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/nonsense">
|
||||
疯言疯语
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/about">
|
||||
关于
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/message">
|
||||
留言板
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="2" class="search-container" v-if="windowwidth">
|
||||
<!-- 搜索框可以在这里添加 -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- Hero 区域 -->
|
||||
<div class="hero" :class="{ 'newhero': classhero }" v-if="windowwidth">
|
||||
<h1 class="typewriter">{{ heroText }}</h1>
|
||||
</div>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<div id="content-section" :class="{ 'visible': isconts }">
|
||||
<div class="nonsensetitle" v-if="classnonsenset">
|
||||
<div class="nonsensetitleconst">
|
||||
<h1>发癫中QAQ</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 左侧模块 -->
|
||||
<LeftModule class="leftmodluepage" :class="{ 'nonsensetmargintop': classnonsenset }" v-if="windowwidth" />
|
||||
|
||||
<!-- 内容模块 -->
|
||||
<RouterView class="RouterViewpage" :class="{ 'nonsensetmargintop': classnonsenset }" />
|
||||
</div>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="Pagination">
|
||||
<!-- 分页组件可以在这里添加 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import LeftModule from '@/components/LeftModule.vue';
|
||||
|
||||
// 路由相关
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// 响应式状态
|
||||
const classhero = ref(false);
|
||||
const isconts = ref(false);
|
||||
const isScrollingleftmodlue = ref(false);
|
||||
const elrowtop = ref('transparent');
|
||||
const classnonsenset = ref(false);
|
||||
const windowwidth = ref(true);
|
||||
const activeIndex = ref('/:type');
|
||||
|
||||
// 打字机效果相关
|
||||
const fullHeroText = '如果感到累了撸一管就好了';
|
||||
const heroText = ref('');
|
||||
let heroIndex = 0;
|
||||
let heroTimer: number | undefined;
|
||||
|
||||
/**
|
||||
* 打字机效果函数
|
||||
*/
|
||||
const startTypewriter = () => {
|
||||
heroText.value = '';
|
||||
heroIndex = 0;
|
||||
if (heroTimer) clearInterval(heroTimer);
|
||||
heroTimer = window.setInterval(() => {
|
||||
if (heroIndex < fullHeroText.length) {
|
||||
heroText.value += fullHeroText[heroIndex];
|
||||
heroIndex++;
|
||||
} else {
|
||||
clearInterval(heroTimer);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* 菜单选择跳转
|
||||
*/
|
||||
const handleSelect = (key: string) => {
|
||||
router.push({ path: key });
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据路由路径设置页面状态
|
||||
*/
|
||||
const updatePageState = (url: string) => {
|
||||
classhero.value = url !== '/:type';
|
||||
classnonsenset.value = url === '/nonsense';
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置当前激活的菜单项
|
||||
*/
|
||||
const setActiveIndex = (path: string) => {
|
||||
activeIndex.value = path;
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理窗口大小变化
|
||||
*/
|
||||
const handleResize = () => {
|
||||
windowwidth.value = window.innerWidth > 768;
|
||||
|
||||
// 根据屏幕大小调整内容区可见性
|
||||
if (route.path === '/:type') {
|
||||
isconts.value = window.innerWidth <= 768 ? true : false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理滚动事件
|
||||
*/
|
||||
const handleScroll = () => {
|
||||
// 屏幕小于768时只切换导航栏样式,不做内容动画
|
||||
if (window.innerWidth < 768) {
|
||||
elrowtop.value = window.scrollY > 100 ? 'solid' : 'transparent';
|
||||
return;
|
||||
}
|
||||
|
||||
// 导航栏样式切换
|
||||
if (window.scrollY > 1200) {
|
||||
elrowtop.value = 'hide';
|
||||
} else {
|
||||
elrowtop.value = window.scrollY > 100 ? 'solid' : 'transparent';
|
||||
}
|
||||
|
||||
// 首页内容区滚动动画
|
||||
if (route.path === '/:type') {
|
||||
isconts.value = window.scrollY > 200;
|
||||
isScrollingleftmodlue.value = window.scrollY > 600;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 监听路由变化
|
||||
*/
|
||||
watch(() => route.path, (newPath) => {
|
||||
updatePageState(newPath);
|
||||
setActiveIndex(newPath);
|
||||
|
||||
// 跳转后回到顶部
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
|
||||
// 首页内容区滚动动画仅大屏下生效
|
||||
if (newPath === '/:type') {
|
||||
isconts.value = window.innerWidth <= 768 ? true : false;
|
||||
// 首页时启动打字机
|
||||
startTypewriter();
|
||||
} else {
|
||||
isconts.value = true;
|
||||
heroText.value = '';
|
||||
if (heroTimer) clearInterval(heroTimer);
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
/**
|
||||
* 生命周期钩子
|
||||
*/
|
||||
onMounted(() => {
|
||||
// 初始化窗口大小
|
||||
handleResize();
|
||||
|
||||
// 添加事件监听器
|
||||
window.addEventListener('resize', handleResize);
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理事件监听器
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
|
||||
// 清理定时器
|
||||
if (heroTimer) clearInterval(heroTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
Reference in New Issue
Block a user